From 9b142b56f8987ced03ebd8f4b702967e1088cf63 Mon Sep 17 00:00:00 2001 From: Manuel Alejandro de Brito Fontes Date: Sat, 19 Mar 2016 20:00:11 -0300 Subject: [PATCH] Update godeps --- Godeps/Godeps.json | 597 +- .../ww/goautoneg/autoneg_test.go | 33 - .../beorn7/perks/quantile/bench_test.go | 63 - .../beorn7/perks/quantile/example_test.go | 121 - .../beorn7/perks/quantile/stream_test.go | 188 - .../github.com/blang/semver/examples/main.go | 83 - .../src/github.com/blang/semver/json_test.go | 45 - .../github.com/blang/semver/semver_test.go | 417 -- .../src/github.com/blang/semver/sort_test.go | 30 - .../src/github.com/blang/semver/sql_test.go | 38 - .../github.com/cpuguy83/go-md2man/LICENSE.md | 21 + .../src/github.com/davecgh/go-spew/LICENSE | 13 + .../davecgh/go-spew/spew/common_test.go | 298 - .../davecgh/go-spew/spew/dump_test.go | 1021 ---- .../davecgh/go-spew/spew/dumpcgo_test.go | 97 - .../davecgh/go-spew/spew/dumpnocgo_test.go | 26 - .../davecgh/go-spew/spew/example_test.go | 230 - .../davecgh/go-spew/spew/format_test.go | 1535 ----- .../davecgh/go-spew/spew/internal_test.go | 156 - .../davecgh/go-spew/spew/spew_test.go | 308 - .../davecgh/go-spew/spew/testdata/dumpcgo.go | 82 - .../src/github.com/docker/docker/LICENSE | 191 + .../src/github.com/docker/docker/NOTICE | 19 + .../docker/docker/pkg/mount/mount_test.go | 137 - .../docker/pkg/mount/mountinfo_linux_test.go | 477 -- .../pkg/mount/sharedsubtree_linux_test.go | 331 -- .../docker/pkg/parsers/filters/parse.go | 116 - .../docker/pkg/parsers/filters/parse_test.go | 218 - .../docker/pkg/parsers/kernel/kernel.go | 95 - .../docker/pkg/parsers/kernel/kernel_test.go | 92 - .../pkg/parsers/kernel/kernel_windows.go | 65 - .../docker/pkg/parsers/kernel/uname_linux.go | 16 - .../pkg/parsers/kernel/uname_unsupported.go | 15 - .../operatingsystem/operatingsystem_linux.go | 40 - .../operatingsystem/operatingsystem_test.go | 124 - .../operatingsystem_windows.go | 47 - .../docker/docker/pkg/parsers/parsers_test.go | 210 - .../docker/go-units/duration_test.go | 81 - .../github.com/docker/go-units/size_test.go | 160 - .../github.com/docker/go-units/ulimit_test.go | 74 - .../emicklei/go-restful/bench_curly_test.go | 51 - .../emicklei/go-restful/bench_test.go | 43 - .../emicklei/go-restful/compress_test.go | 127 - .../emicklei/go-restful/container_test.go | 61 - .../emicklei/go-restful/cors_filter_test.go | 125 - .../emicklei/go-restful/curly_test.go | 231 - .../emicklei/go-restful/doc_examples_test.go | 41 - .../go-restful/entity_accessors_test.go | 69 - .../emicklei/go-restful/examples/.goconvey | 1 - .../examples/google_app_engine/.goconvey | 1 - .../examples/google_app_engine/app.yaml | 20 - .../google_app_engine/datastore/.goconvey | 1 - .../google_app_engine/datastore/app.yaml | 18 - .../google_app_engine/datastore/main.go | 266 - .../restful-appstats-integration.go | 13 - .../google_app_engine/restful-user-service.go | 161 - .../emicklei/go-restful/examples/home.html | 7 - .../examples/restful-CORS-filter.go | 67 - .../examples/restful-NCSA-logging.go | 54 - .../examples/restful-basic-authentication.go | 35 - .../examples/restful-cpuprofiler-service.go | 65 - .../examples/restful-curly-router.go | 107 - .../examples/restful-encoding-filter.go | 61 - .../go-restful/examples/restful-filters.go | 114 - .../examples/restful-form-handling.go | 62 - .../examples/restful-hello-world.go | 22 - .../examples/restful-html-template.go | 35 - .../examples/restful-multi-containers.go | 43 - .../examples/restful-options-filter.go | 51 - .../go-restful/examples/restful-path-tail.go | 26 - .../examples/restful-pre-post-filters.go | 98 - .../examples/restful-resource-functions.go | 63 - .../go-restful/examples/restful-route_test.go | 39 - .../examples/restful-routefunction_test.go | 29 - .../examples/restful-serve-static.go | 47 - .../go-restful/examples/restful-swagger.go | 61 - .../examples/restful-user-resource.go | 152 - .../examples/restful-user-service.go | 137 - .../emicklei/go-restful/filter_test.go | 141 - .../emicklei/go-restful/jsr311_test.go | 212 - .../go-restful/options_filter_test.go | 34 - .../go-restful/path_expression_test.go | 37 - .../emicklei/go-restful/request_test.go | 204 - .../emicklei/go-restful/response_test.go | 204 - .../emicklei/go-restful/route_builder_test.go | 58 - .../emicklei/go-restful/route_test.go | 127 - .../go-restful/swagger/model_builder_test.go | 1142 ---- .../go-restful/swagger/model_list_test.go | 48 - .../swagger/model_property_ext_test.go | 60 - .../swagger/model_property_list_test.go | 47 - .../swagger/ordered_route_map_test.go | 29 - .../swagger/postbuild_model_test.go | 42 - .../go-restful/swagger/swagger_test.go | 284 - .../go-restful/swagger/test_package/struct.go | 5 - .../emicklei/go-restful/swagger/utils_test.go | 78 - .../emicklei/go-restful/tracer_test.go | 18 - .../emicklei/go-restful/web_service_test.go | 254 - .../evanphx/json-patch/merge_test.go | 322 -- .../evanphx/json-patch/patch_test.go | 234 - .../src/github.com/fatih/structs/field.go | 6 +- .../github.com/fatih/structs/field_test.go | 383 -- .../src/github.com/fatih/structs/structs.go | 35 +- .../fatih/structs/structs_example_test.go | 351 -- .../github.com/fatih/structs/structs_test.go | 898 --- .../src/github.com/fatih/structs/tags_test.go | 46 - .../fsouza/go-dockerclient/.travis.yml | 4 +- .../github.com/fsouza/go-dockerclient/AUTHORS | 2 + .../fsouza/go-dockerclient/Makefile | 2 +- .../fsouza/go-dockerclient/auth_test.go | 91 - .../fsouza/go-dockerclient/build_test.go | 154 - .../fsouza/go-dockerclient/change_test.go | 24 - .../fsouza/go-dockerclient/client.go | 5 +- .../fsouza/go-dockerclient/client_test.go | 518 -- .../fsouza/go-dockerclient/container.go | 4 + .../fsouza/go-dockerclient/container_test.go | 2248 ------- .../fsouza/go-dockerclient/env_test.go | 351 -- .../fsouza/go-dockerclient/event_test.go | 132 - .../fsouza/go-dockerclient/example_test.go | 168 - .../fsouza/go-dockerclient/exec_test.go | 262 - .../github.com/Sirupsen/logrus/entry_test.go | 77 - .../Sirupsen/logrus/formatter_bench_test.go | 98 - .../github.com/Sirupsen/logrus/hook_test.go | 122 - .../Sirupsen/logrus/json_formatter_test.go | 120 - .../github.com/Sirupsen/logrus/logrus_test.go | 301 - .../Sirupsen/logrus/text_formatter_test.go | 61 - .../docker/docker/opts/envfile_test.go | 142 - .../docker/docker/opts/hosts_test.go | 164 - .../github.com/docker/docker/opts/ip_test.go | 54 - .../docker/docker/opts/opts_test.go | 301 - .../docker/docker/pkg/archive/archive_test.go | 1248 ---- .../docker/pkg/archive/archive_unix_test.go | 60 - .../pkg/archive/archive_windows_test.go | 87 - .../docker/pkg/archive/changes_posix_test.go | 127 - .../docker/docker/pkg/archive/changes_test.go | 527 -- .../docker/docker/pkg/archive/copy_test.go | 974 ---- .../docker/docker/pkg/archive/diff_test.go | 370 -- .../docker/docker/pkg/archive/utils_test.go | 166 - .../docker/docker/pkg/archive/wrap_test.go | 98 - .../docker/pkg/fileutils/fileutils_test.go | 573 -- .../docker/docker/pkg/homedir/homedir_test.go | 24 - .../docker/pkg/idtools/idtools_unix_test.go | 243 - .../docker/pkg/ioutils/bytespipe_test.go | 158 - .../docker/docker/pkg/ioutils/fmt_test.go | 17 - .../docker/pkg/ioutils/multireader_test.go | 149 - .../docker/docker/pkg/ioutils/readers_test.go | 94 - .../docker/docker/pkg/ioutils/writers_test.go | 65 - .../docker/pkg/longpath/longpath_test.go | 22 - .../docker/docker/pkg/pools/pools_test.go | 162 - .../docker/docker/pkg/stdcopy/stdcopy_test.go | 261 - .../docker/docker/pkg/system/chtimes_test.go | 94 - .../docker/pkg/system/chtimes_unix_test.go | 91 - .../docker/pkg/system/chtimes_windows_test.go | 86 - .../docker/pkg/system/lstat_unix_test.go | 30 - .../docker/pkg/system/meminfo_unix_test.go | 40 - .../docker/pkg/system/stat_unix_test.go | 39 - .../docker/pkg/system/utimes_unix_test.go | 68 - .../docker/go-units/duration_test.go | 81 - .../github.com/docker/go-units/size_test.go | 160 - .../github.com/docker/go-units/ulimit_test.go | 74 - .../github.com/gorilla/context/README.md | 7 - .../github.com/gorilla/context/context.go | 143 - .../gorilla/context/context_test.go | 161 - .../github.com/gorilla/context/doc.go | 82 - .../external/github.com/gorilla/mux/README.md | 240 - .../github.com/gorilla/mux/bench_test.go | 21 - .../external/github.com/gorilla/mux/doc.go | 206 - .../external/github.com/gorilla/mux/mux.go | 481 -- .../github.com/gorilla/mux/mux_test.go | 1358 ----- .../github.com/gorilla/mux/old_test.go | 714 --- .../external/github.com/gorilla/mux/regexp.go | 317 - .../external/github.com/gorilla/mux/route.go | 595 -- .../runc/libcontainer/user/user_test.go | 472 -- .../golang.org/x/net/context/context_test.go | 575 -- .../x/net/context/withtimeout_test.go | 26 - .../golang.org/x/sys/unix/creds_test.go | 121 - .../golang.org/x/sys/unix/export_test.go | 9 - .../golang.org/x/sys/unix/mmap_unix_test.go | 23 - .../golang.org/x/sys/unix/syscall_bsd_test.go | 42 - .../x/sys/unix/syscall_freebsd_test.go | 20 - .../golang.org/x/sys/unix/syscall_test.go | 50 - .../x/sys/unix/syscall_unix_test.go | 318 - .../fsouza/go-dockerclient/image_test.go | 1018 ---- .../go-dockerclient/integration_test.go | 94 - .../fsouza/go-dockerclient/misc_test.go | 159 - .../fsouza/go-dockerclient/network_test.go | 173 - .../testing/data/.dockerignore | 3 - .../go-dockerclient/testing/data/Dockerfile | 15 - .../go-dockerclient/testing/data/barfile | 0 .../go-dockerclient/testing/data/ca.pem | 18 - .../go-dockerclient/testing/data/cert.pem | 18 - .../testing/data/container.tar | Bin 2048 -> 0 bytes .../testing/data/dockerfile.tar | Bin 2560 -> 0 bytes .../go-dockerclient/testing/data/foofile | 0 .../go-dockerclient/testing/data/key.pem | 27 - .../go-dockerclient/testing/data/server.pem | 18 - .../testing/data/serverkey.pem | 27 - .../fsouza/go-dockerclient/testing/server.go | 1246 ---- .../go-dockerclient/testing/server_test.go | 2103 ------- .../fsouza/go-dockerclient/volume_test.go | 142 - .../src/github.com/ghodss/yaml/yaml_test.go | 271 - .../src/github.com/golang/glog/glog_test.go | 415 -- .../src/github.com/golang/groupcache/LICENSE | 191 + .../golang/groupcache/lru/lru_test.go | 73 - .../src/github.com/golang/protobuf/LICENSE | 31 + .../github.com/golang/protobuf/proto/Makefile | 2 +- .../golang/protobuf/proto/all_test.go | 2059 ------- .../github.com/golang/protobuf/proto/clone.go | 42 +- .../golang/protobuf/proto/clone_test.go | 227 - .../golang/protobuf/proto/decode.go | 56 +- .../golang/protobuf/proto/encode.go | 88 +- .../github.com/golang/protobuf/proto/equal.go | 38 +- .../golang/protobuf/proto/equal_test.go | 191 - .../golang/protobuf/proto/extensions.go | 54 +- .../golang/protobuf/proto/extensions_test.go | 137 - .../github.com/golang/protobuf/proto/lib.go | 649 ++- .../golang/protobuf/proto/message_set.go | 41 +- .../golang/protobuf/proto/message_set_test.go | 66 - .../golang/protobuf/proto/pointer_reflect.go | 6 +- .../golang/protobuf/proto/pointer_unsafe.go | 6 +- .../golang/protobuf/proto/properties.go | 132 +- .../protobuf/proto/proto3_proto/Makefile | 44 - .../protobuf/proto/proto3_proto/proto3.proto | 58 - .../golang/protobuf/proto/proto3_test.go | 93 - .../golang/protobuf/proto/size2_test.go | 63 - .../golang/protobuf/proto/size_test.go | 135 - .../golang/protobuf/proto/testdata/Makefile | 50 - .../protobuf/proto/testdata/golden_test.go | 86 - .../golang/protobuf/proto/testdata/test.pb.go | 2389 -------- .../golang/protobuf/proto/testdata/test.proto | 434 -- .../github.com/golang/protobuf/proto/text.go | 126 +- .../golang/protobuf/proto/text_parser.go | 245 +- .../golang/protobuf/proto/text_parser_test.go | 509 -- .../golang/protobuf/proto/text_test.go | 436 -- .../src/github.com/google/cadvisor/LICENSE | 190 + .../google/cadvisor/info/v1/container.go | 8 + .../google/cadvisor/info/v1/container_test.go | 79 - .../google/cadvisor/info/v1/machine.go | 24 +- .../google/cadvisor/info/v1/test/datagen.go | 81 - .../github.com/google/gofuzz/example_test.go | 225 - .../src/github.com/google/gofuzz/fuzz_test.go | 384 -- .../github.com/imdario/mergo/mergo_test.go | 288 - .../imdario/mergo/testdata/license.yml | 3 - .../imdario/mergo/testdata/thing.yml | 5 - .../juju/ratelimit/ratelimit_test.go | 389 -- .../golang_protobuf_extensions/LICENSE | 201 + .../pbutil/all_test.go | 320 - .../pbutil/fixtures_test.go | 103 - .../github.com/opencontainers/runc/LICENSE | 191 + .../src/github.com/opencontainers/runc/NOTICE | 17 + .../runc/libcontainer/cgroups/cgroups_test.go | 18 - .../libcontainer/cgroups/fs/blkio_test.go | 636 -- .../runc/libcontainer/cgroups/fs/cpu_test.go | 163 - .../libcontainer/cgroups/fs/cpuset_test.go | 65 - .../libcontainer/cgroups/fs/devices_test.go | 84 - .../libcontainer/cgroups/fs/freezer_test.go | 47 - .../libcontainer/cgroups/fs/hugetlb_test.go | 154 - .../libcontainer/cgroups/fs/memory_test.go | 294 - .../libcontainer/cgroups/fs/net_cls_test.go | 38 - .../libcontainer/cgroups/fs/net_prio_test.go | 38 - .../runc/libcontainer/cgroups/fs/pids_test.go | 83 - .../cgroups/fs/stats_util_test.go | 113 - .../runc/libcontainer/cgroups/fs/util_test.go | 67 - .../libcontainer/cgroups/fs/utils_test.go | 97 - .../cgroups/systemd/apply_nosystemd.go | 55 - .../cgroups/systemd/apply_systemd.go | 596 -- .../libcontainer/configs/config_unix_test.go | 156 - .../configs/config_windows_test.go | 3 - .../libcontainer/configs/validate/config.go | 93 - .../src/github.com/pborman/uuid/json_test.go | 32 - .../src/github.com/pborman/uuid/seq_test.go | 66 - .../src/github.com/pborman/uuid/uuid_test.go | 390 -- .../prometheus/client_golang/LICENSE | 201 + .../prometheus/client_golang/NOTICE | 28 + .../prometheus/benchmark_test.go | 159 - .../client_golang/prometheus/counter_test.go | 58 - .../prometheus/example_clustermanager_test.go | 130 - .../prometheus/example_memstats_test.go | 87 - .../prometheus/example_selfcollector_test.go | 69 - .../client_golang/prometheus/examples_test.go | 649 --- .../client_golang/prometheus/expvar_test.go | 97 - .../client_golang/prometheus/gauge_test.go | 182 - .../prometheus/go_collector_test.go | 123 - .../prometheus/histogram_test.go | 326 -- .../client_golang/prometheus/http_test.go | 121 - .../client_golang/prometheus/metric_test.go | 35 - .../prometheus/process_collector_test.go | 54 - .../client_golang/prometheus/registry_test.go | 535 -- .../client_golang/prometheus/summary_test.go | 347 -- .../client_golang/prometheus/vec_test.go | 91 - .../prometheus/client_model/LICENSE | 201 + .../github.com/prometheus/client_model/NOTICE | 5 + .../prometheus/common/expfmt/bench_test.go | 171 - .../prometheus/common/expfmt/decode_test.go | 373 -- .../expfmt/fuzz/corpus/from_test_parse_0 | 2 - .../expfmt/fuzz/corpus/from_test_parse_1 | 6 - .../expfmt/fuzz/corpus/from_test_parse_2 | 12 - .../expfmt/fuzz/corpus/from_test_parse_3 | 22 - .../expfmt/fuzz/corpus/from_test_parse_4 | 10 - .../fuzz/corpus/from_test_parse_error_0 | 1 - .../fuzz/corpus/from_test_parse_error_1 | 1 - .../fuzz/corpus/from_test_parse_error_10 | 1 - .../fuzz/corpus/from_test_parse_error_11 | 1 - .../fuzz/corpus/from_test_parse_error_12 | 3 - .../fuzz/corpus/from_test_parse_error_13 | 3 - .../fuzz/corpus/from_test_parse_error_14 | 3 - .../fuzz/corpus/from_test_parse_error_15 | 2 - .../fuzz/corpus/from_test_parse_error_16 | 2 - .../fuzz/corpus/from_test_parse_error_17 | 1 - .../fuzz/corpus/from_test_parse_error_18 | 1 - .../fuzz/corpus/from_test_parse_error_19 | 3 - .../fuzz/corpus/from_test_parse_error_2 | 3 - .../fuzz/corpus/from_test_parse_error_3 | 1 - .../fuzz/corpus/from_test_parse_error_4 | 1 - .../fuzz/corpus/from_test_parse_error_5 | 1 - .../fuzz/corpus/from_test_parse_error_6 | 1 - .../fuzz/corpus/from_test_parse_error_7 | 3 - .../fuzz/corpus/from_test_parse_error_8 | 1 - .../fuzz/corpus/from_test_parse_error_9 | 1 - .../common/expfmt/fuzz/corpus/minimal | 1 - .../common/expfmt/json_decode_test.go | 124 - .../prometheus/common/expfmt/testdata/json2 | 46 - .../common/expfmt/testdata/protobuf | 516 -- .../common/expfmt/testdata/protobuf.gz | 129 - .../prometheus/common/expfmt/testdata/test.gz | 163 - .../prometheus/common/expfmt/testdata/text | 322 -- .../common/expfmt/text_create_test.go | 443 -- .../common/expfmt/text_parse_test.go | 586 -- .../prometheus/common/model/labels_test.go | 91 - .../prometheus/common/model/metric_test.go | 83 - .../prometheus/common/model/signature_test.go | 304 - .../prometheus/common/model/time_test.go | 86 - .../prometheus/common/model/value_test.go | 362 -- .../prometheus/procfs/fixtures/26231/cmdline | Bin 16 -> 0 bytes .../prometheus/procfs/fixtures/26231/fd/0 | 0 .../prometheus/procfs/fixtures/26231/fd/1 | 0 .../prometheus/procfs/fixtures/26231/fd/2 | 0 .../prometheus/procfs/fixtures/26231/fd/3 | 0 .../prometheus/procfs/fixtures/26231/fd/4 | 0 .../prometheus/procfs/fixtures/26231/limits | 17 - .../prometheus/procfs/fixtures/26231/stat | 1 - .../prometheus/procfs/fixtures/584/stat | 2 - .../prometheus/procfs/fixtures/stat | 16 - .../github.com/prometheus/procfs/fs_test.go | 13 - .../prometheus/procfs/proc_limits_test.go | 36 - .../prometheus/procfs/proc_stat_test.go | 112 - .../github.com/prometheus/procfs/proc_test.go | 123 - .../github.com/prometheus/procfs/stat_test.go | 19 - .../russross/blackfriday/block_test.go | 1296 ----- .../russross/blackfriday/inline_test.go | 859 --- .../upskirtref/Amps and angle encoding.html | 17 - .../upskirtref/Amps and angle encoding.text | 21 - .../blackfriday/upskirtref/Auto links.html | 18 - .../blackfriday/upskirtref/Auto links.text | 13 - .../upskirtref/Backslash escapes.html | 123 - .../upskirtref/Backslash escapes.text | 126 - .../Blockquotes with code blocks.html | 15 - .../Blockquotes with code blocks.text | 11 - .../blackfriday/upskirtref/Code Blocks.html | 18 - .../blackfriday/upskirtref/Code Blocks.text | 14 - .../blackfriday/upskirtref/Code Spans.html | 5 - .../blackfriday/upskirtref/Code Spans.text | 6 - ...like lines no empty line before block.html | 14 - ...like lines no empty line before block.text | 8 - ...apped paragraphs with list-like lines.html | 8 - ...apped paragraphs with list-like lines.text | 8 - .../upskirtref/Horizontal rules.html | 71 - .../upskirtref/Horizontal rules.text | 67 - .../upskirtref/Inline HTML (Advanced).html | 15 - .../upskirtref/Inline HTML (Advanced).text | 15 - .../upskirtref/Inline HTML (Simple).html | 72 - .../upskirtref/Inline HTML (Simple).text | 69 - .../upskirtref/Inline HTML comments.html | 13 - .../upskirtref/Inline HTML comments.text | 13 - .../upskirtref/Links, inline style.html | 11 - .../upskirtref/Links, inline style.text | 12 - .../upskirtref/Links, reference style.html | 52 - .../upskirtref/Links, reference style.text | 71 - .../Links, shortcut references.html | 9 - .../Links, shortcut references.text | 20 - .../upskirtref/Literal quotes in titles.html | 3 - .../upskirtref/Literal quotes in titles.text | 7 - .../Markdown Documentation - Basics.html | 314 - .../Markdown Documentation - Basics.text | 306 - .../Markdown Documentation - Syntax.html | 946 --- .../Markdown Documentation - Syntax.text | 888 --- .../upskirtref/Nested blockquotes.html | 9 - .../upskirtref/Nested blockquotes.text | 5 - .../Ordered and unordered lists.html | 166 - .../Ordered and unordered lists.text | 131 - .../upskirtref/Strong and em together.html | 7 - .../upskirtref/Strong and em together.text | 7 - .../russross/blackfriday/upskirtref/Tabs.html | 26 - .../russross/blackfriday/upskirtref/Tabs.text | 21 - .../blackfriday/upskirtref/Tidyness.html | 9 - .../blackfriday/upskirtref/Tidyness.text | 5 - .../russross/blackfriday/upskirtref_test.go | 128 - .../scalingdata/gcfg/example_test.go | 132 - .../scalingdata/gcfg/issues_test.go | 63 - .../github.com/scalingdata/gcfg/read_test.go | 333 -- .../scalingdata/gcfg/scanner/example_test.go | 46 - .../scalingdata/gcfg/scanner/scanner_test.go | 417 -- .../scalingdata/gcfg/testdata/gcfg_test.gcfg | 3 - .../gcfg/testdata/gcfg_unicode_test.gcfg | 3 - .../scalingdata/gcfg/token/position_test.go | 181 - .../scalingdata/gcfg/token/serialize_test.go | 111 - .../scalingdata/gcfg/types/enum_test.go | 29 - .../scalingdata/gcfg/types/int_test.go | 67 - .../scalingdata/gcfg/types/scan_test.go | 36 - .../sanitized_anchor_name/main_test.go | 35 - .../spf13/cobra/bash_completions_test.go | 94 - .../github.com/spf13/cobra/cobra/cmd/add.go | 128 - .../spf13/cobra/cobra/cmd/helpers.go | 347 -- .../spf13/cobra/cobra/cmd/helpers_test.go | 39 - .../github.com/spf13/cobra/cobra/cmd/init.go | 226 - .../spf13/cobra/cobra/cmd/licenses.go | 1133 ---- .../github.com/spf13/cobra/cobra/cmd/root.go | 84 - .../src/github.com/spf13/cobra/cobra/main.go | 20 - .../src/github.com/spf13/cobra/cobra_test.go | 1163 ---- .../github.com/spf13/cobra/command_test.go | 114 - .../github.com/spf13/cobra/examples_test.go | 34 - .../github.com/spf13/cobra/man_docs_test.go | 94 - .../github.com/spf13/cobra/md_docs_test.go | 84 - .../src/github.com/spf13/pflag/bool_test.go | 180 - .../src/github.com/spf13/pflag/count_test.go | 55 - .../github.com/spf13/pflag/example_test.go | 77 - .../src/github.com/spf13/pflag/export_test.go | 29 - .../src/github.com/spf13/pflag/flag_test.go | 874 --- .../github.com/spf13/pflag/golangflag_test.go | 39 - .../github.com/spf13/pflag/int_slice_test.go | 162 - .../src/github.com/spf13/pflag/ip_test.go | 63 - .../src/github.com/spf13/pflag/ipnet_test.go | 70 - .../spf13/pflag/string_slice_test.go | 161 - .../src/github.com/spf13/pflag/verify/all.sh | 69 - .../github.com/spf13/pflag/verify/gofmt.sh | 19 - .../github.com/spf13/pflag/verify/golint.sh | 15 - .../src/github.com/ugorji/go/LICENSE | 22 + .../src/github.com/ugorji/go/codec/0doc.go | 10 +- .../src/github.com/ugorji/go/codec/binc.go | 4 + .../src/github.com/ugorji/go/codec/cbor.go | 3 +- .../github.com/ugorji/go/codec/cbor_test.go | 205 - .../github.com/ugorji/go/codec/codec_test.go | 1185 ---- .../ugorji/go/codec/codecgen/README.md | 36 - .../ugorji/go/codec/codecgen/gen.go | 273 - .../github.com/ugorji/go/codec/codecgen/z.go | 3 - .../ugorji/go/codec/codecgen_test.go | 24 - .../src/github.com/ugorji/go/codec/decode.go | 20 +- .../src/github.com/ugorji/go/codec/encode.go | 124 +- .../ugorji/go/codec/fast-path.generated.go | 555 +- .../ugorji/go/codec/fast-path.go.tmpl | 35 +- .../ugorji/go/codec/gen-dec-array.go.tmpl | 5 +- .../ugorji/go/codec/gen.generated.go | 5 +- .../src/github.com/ugorji/go/codec/gen.go | 115 +- .../src/github.com/ugorji/go/codec/helper.go | 200 +- .../github.com/ugorji/go/codec/helper_test.go | 242 - .../src/github.com/ugorji/go/codec/json.go | 171 +- .../src/github.com/ugorji/go/codec/msgpack.go | 3 +- .../github.com/ugorji/go/codec/prebuild.sh | 2 +- .../src/github.com/ugorji/go/codec/py_test.go | 30 - .../src/github.com/ugorji/go/codec/simple.go | 1 + .../src/github.com/ugorji/go/codec/test.py | 18 +- .../src/github.com/ugorji/go/codec/tests.sh | 32 +- .../src/github.com/ugorji/go/codec/time.go | 13 +- .../github.com/ugorji/go/codec/values_test.go | 203 - .../gorilla/mux => golang.org/x/net}/LICENSE | 8 +- .../_workspace/src/golang.org/x/net/PATENTS | 22 + .../golang.org/x/net/context/context_test.go | 575 -- .../x/net/context/ctxhttp/ctxhttp_test.go | 72 - .../x/net/context/withtimeout_test.go | 26 - .../clientcredentials/clientcredentials.go | 112 - .../clientcredentials_test.go | 96 - .../src/golang.org/x/oauth2/example_test.go | 45 - .../golang.org/x/oauth2/facebook/facebook.go | 16 - .../src/golang.org/x/oauth2/github/github.go | 16 - .../x/oauth2/google/example_test.go | 150 - .../golang.org/x/oauth2/google/google_test.go | 67 - .../golang.org/x/oauth2/google/sdk_test.go | 46 - .../oauth2/google/testdata/gcloud/credentials | 122 - .../oauth2/google/testdata/gcloud/properties | 2 - .../x/oauth2/internal/oauth2_test.go | 62 - .../x/oauth2/internal/token_test.go | 28 - .../golang.org/x/oauth2/jwt/example_test.go | 31 - .../src/golang.org/x/oauth2/jwt/jwt_test.go | 134 - .../golang.org/x/oauth2/linkedin/linkedin.go | 16 - .../src/golang.org/x/oauth2/oauth2_test.go | 422 -- .../x/oauth2/odnoklassniki/odnoklassniki.go | 16 - .../src/golang.org/x/oauth2/paypal/paypal.go | 22 - .../src/golang.org/x/oauth2/token_test.go | 50 - .../src/golang.org/x/oauth2/transport_test.go | 86 - .../src/golang.org/x/oauth2/vk/vk.go | 16 - .../context => google.golang.org/api}/LICENSE | 8 +- .../api/gensupport/json_test.go | 367 -- .../api/gensupport/media_test.go | 113 - .../api/googleapi/googleapi_test.go | 599 -- .../api/googleapi/transport/apikey.go | 38 - .../api/googleapi/types_test.go | 44 - .../src/google.golang.org/cloud/LICENSE | 202 + .../internal/datastore/datastore_v1.pb.go | 1633 ------ .../internal/datastore/datastore_v1.proto | 594 -- .../cloud/internal/testutil/context.go | 57 - .../src/gopkg.in/yaml.v2/decode_test.go | 902 --- .../src/gopkg.in/yaml.v2/encode_test.go | 434 -- .../src/gopkg.in/yaml.v2/suite_test.go | 12 - .../_workspace/src/k8s.io/kubernetes/LICENSE | 202 + .../src/k8s.io/kubernetes/pkg/api/OWNERS | 6 + .../k8s.io/kubernetes/pkg/api/context_test.go | 68 - .../k8s.io/kubernetes/pkg/api/conversion.go | 11 + .../kubernetes/pkg/api/conversion_test.go | 110 - .../k8s.io/kubernetes/pkg/api/copy_test.go | 68 - .../kubernetes/pkg/api/deep_copy_generated.go | 10 + .../kubernetes/pkg/api/deep_copy_test.go | 95 - .../kubernetes/pkg/api/endpoints/util.go | 10 + .../kubernetes/pkg/api/endpoints/util_test.go | 444 -- .../kubernetes/pkg/api/errors/errors_test.go | 189 - .../kubernetes/pkg/api/errors/etcd/doc.go | 18 - .../kubernetes/pkg/api/errors/etcd/etcd.go | 88 - .../kubernetes/pkg/api/field_constants.go | 38 + .../kubernetes/pkg/api/generate_test.go | 79 - .../src/k8s.io/kubernetes/pkg/api/helpers.go | 88 +- .../k8s.io/kubernetes/pkg/api/helpers_test.go | 299 - .../pkg/api/install/install_test.go | 129 - .../src/k8s.io/kubernetes/pkg/api/mapper.go | 6 +- .../kubernetes/pkg/api/meta/help_test.go | 253 - .../kubernetes/pkg/api/meta/meta_test.go | 778 --- .../pkg/api/meta/multirestmapper.go | 139 +- .../pkg/api/meta/multirestmapper_test.go | 238 - .../kubernetes/pkg/api/meta/priority.go | 173 + .../kubernetes/pkg/api/meta/restmapper.go | 20 +- .../pkg/api/meta/restmapper_test.go | 550 -- .../k8s.io/kubernetes/pkg/api/meta_test.go | 51 - .../src/k8s.io/kubernetes/pkg/api/pod/util.go | 12 + .../kubernetes/pkg/api/pod/util_test.go | 110 - .../src/k8s.io/kubernetes/pkg/api/ref_test.go | 128 - .../pkg/api/resource/quantity_example_test.go | 59 - .../pkg/api/resource/quantity_test.go | 810 --- .../pkg/api/resource/scale_int_test.go | 85 - .../pkg/api/resource_helpers_test.go | 63 - .../k8s.io/kubernetes/pkg/api/rest/create.go | 126 - .../k8s.io/kubernetes/pkg/api/rest/delete.go | 83 - .../src/k8s.io/kubernetes/pkg/api/rest/doc.go | 18 - .../k8s.io/kubernetes/pkg/api/rest/export.go | 28 - .../k8s.io/kubernetes/pkg/api/rest/rest.go | 293 - .../pkg/api/rest/resttest/resttest.go | 1005 ---- .../k8s.io/kubernetes/pkg/api/rest/types.go | 42 - .../k8s.io/kubernetes/pkg/api/rest/update.go | 105 - .../pkg/api/serialization_proto_test.go | 95 - .../kubernetes/pkg/api/serialization_test.go | 417 -- .../service/annotations.go} | 18 +- .../k8s.io/kubernetes/pkg/api/service/util.go | 54 + .../kubernetes/pkg/api/testapi/testapi.go | 62 +- .../pkg/api/testapi/testapi_test.go | 133 - .../testing/compat/compatibility_tester.go | 144 - .../kubernetes/pkg/api/testing/conversion.go | 72 - .../kubernetes/pkg/api/testing/fuzzer.go | 413 -- .../kubernetes/pkg/api/testing/pod_specs.go | 32 - .../kubernetes/pkg/api/types.generated.go | 340 +- .../src/k8s.io/kubernetes/pkg/api/types.go | 64 +- .../pkg/api/unversioned/duration_test.go | 153 - .../pkg/api/unversioned/group_version.go | 26 + .../pkg/api/unversioned/group_version_test.go | 78 - .../kubernetes/pkg/api/unversioned/helpers.go | 62 +- .../pkg/api/unversioned/helpers_test.go | 83 - .../kubernetes/pkg/api/unversioned/time.go | 31 + .../pkg/api/unversioned/time_test.go | 147 - .../kubernetes/pkg/api/unversioned/types.go | 12 +- .../types_swagger_doc_generated.go | 14 +- .../api/unversioned/validation/validation.go | 53 - .../pkg/api/util/group_version_test.go | 63 - .../pkg/api/v1/backward_compatibility_test.go | 229 - .../pkg/api/v1/conversion_generated.go | 18 + .../kubernetes/pkg/api/v1/conversion_test.go | 125 - .../pkg/api/v1/deep_copy_generated.go | 9 + .../kubernetes/pkg/api/v1/defaults_test.go | 645 --- .../kubernetes/pkg/api/v1/types.generated.go | 338 +- .../src/k8s.io/kubernetes/pkg/api/v1/types.go | 444 +- .../pkg/api/v1/types_swagger_doc_generated.go | 404 +- .../pkg/api/validation/events_test.go | 60 - .../pkg/api/validation/name_test.go | 149 - .../kubernetes/pkg/api/validation/schema.go | 10 +- .../pkg/api/validation/schema_test.go | 156 - .../validation/testdata/v1/invalidPod.yaml | 11 - .../validation/testdata/v1/invalidPod1.json | 19 - .../validation/testdata/v1/invalidPod2.json | 35 - .../validation/testdata/v1/invalidPod3.json | 35 - .../api/validation/testdata/v1/validPod.yaml | 16 - .../pkg/api/validation/validation.go | 219 +- .../pkg/api/validation/validation_test.go | 5146 ----------------- .../pkg/apimachinery/registered/registered.go | 93 +- .../registered/registered_test.go | 68 - .../v1beta1/types_swagger_doc_generated.go | 2 +- .../authorization/validation/validation.go | 64 - .../validation/validation_test.go | 135 - .../pkg/apis/autoscaling/register.go | 3 + .../pkg/apis/autoscaling/types.generated.go | 794 +++ .../kubernetes/pkg/apis/autoscaling/types.go | 53 + .../autoscaling/v1/conversion_generated.go | 103 + .../autoscaling/v1/deep_copy_generated.go | 30 + .../pkg/apis/autoscaling/v1/register.go | 2 + .../apis/autoscaling/v1/types.generated.go | 722 +++ .../pkg/apis/autoscaling/v1/types.go | 40 +- .../v1/types_swagger_doc_generated.go | 42 +- .../batch/deep_copy_generated.go} | 20 +- .../pkg/apis/batch/install/install.go | 129 + .../kubernetes/pkg/apis/batch/register.go | 54 + .../pkg/apis/batch/v1/conversion.go | 63 + .../pkg/apis/batch/v1/conversion_generated.go | 3065 ++++++++++ .../pkg/apis/batch/v1/deep_copy_generated.go | 1233 ++++ .../kubernetes/pkg/apis/batch/v1/defaults.go | 40 + .../kubernetes/pkg/apis/batch/v1/register.go | 47 + .../pkg/apis/batch/v1/types.generated.go | 3186 ++++++++++ .../kubernetes/pkg/apis/batch/v1/types.go | 184 + .../batch/v1/types_swagger_doc_generated.go | 114 + .../pkg/apis/componentconfig/helpers_test.go | 71 - .../componentconfig/install/install_test.go | 89 - .../apis/componentconfig/types.generated.go | 2820 ++++----- .../pkg/apis/componentconfig/types.go | 54 +- .../kubernetes/pkg/apis/extensions/helpers.go | 47 - .../apis/extensions/install/install_test.go | 119 - .../pkg/apis/extensions/register.go | 4 - .../pkg/apis/extensions/types.generated.go | 1918 ++---- .../kubernetes/pkg/apis/extensions/types.go | 165 +- .../pkg/apis/extensions/v1beta1/conversion.go | 179 +- .../v1beta1/conversion_generated.go | 267 +- .../extensions/v1beta1/deep_copy_generated.go | 87 +- .../pkg/apis/extensions/v1beta1/defaults.go | 15 +- .../apis/extensions/v1beta1/defaults_test.go | 663 --- .../pkg/apis/extensions/v1beta1/register.go | 4 - .../extensions/v1beta1/types.generated.go | 1980 ++----- .../pkg/apis/extensions/v1beta1/types.go | 194 +- .../v1beta1/types_swagger_doc_generated.go | 170 +- .../apis/extensions/validation/validation.go | 820 --- .../extensions/validation/validation_test.go | 2072 ------- .../pkg/client/cache/delta_fifo_test.go | 385 -- .../pkg/client/cache/expiration_cache.go | 35 +- .../client/cache/expiration_cache_fakes.go | 1 + .../pkg/client/cache/expiration_cache_test.go | 136 - .../kubernetes/pkg/client/cache/fifo_test.go | 235 - .../kubernetes/pkg/client/cache/index_test.go | 135 - .../pkg/client/cache/listers_test.go | 721 --- .../kubernetes/pkg/client/cache/listwatch.go | 4 +- .../pkg/client/cache/listwatch_test.go | 173 - .../pkg/client/cache/reflector_test.go | 404 -- .../kubernetes/pkg/client/cache/store_test.go | 156 - .../pkg/client/cache/undelta_store_test.go | 131 - .../internalclientset/clientset.go | 21 +- .../internalclientset/clientset_adaption.go | 7 + .../fake/clientset_generated.go | 70 - .../internalclientset/fake/discovery.go | 76 - .../kubernetes/pkg/client/record/event.go | 4 +- .../pkg/client/record/event_test.go | 885 --- .../pkg/client/record/events_cache_test.go | 253 - .../restclient.go => restclient/client.go} | 2 +- .../pkg/client/restclient/config.go | 309 + .../{unversioned => restclient}/request.go | 78 +- .../{unversioned => restclient}/transport.go | 2 +- .../pkg/client/restclient/url_utils.go | 93 + .../{unversioned => restclient}/urlbackoff.go | 12 +- .../pkg/client/restclient/versions.go | 88 + .../pkg/client/transport/cache_test.go | 114 - .../client/transport/round_trippers_test.go | 101 - .../pkg/client/transport/transport_test.go | 204 - .../discovery}/discovery_client.go | 31 +- .../generated/core/unversioned/core_client.go | 16 +- .../generated/core/unversioned/fake/doc.go | 18 - .../unversioned/fake/fake_componentstatus.go | 95 - .../core/unversioned/fake/fake_configmap.go | 102 - .../core/unversioned/fake/fake_core_client.go | 90 - .../core/unversioned/fake/fake_endpoints.go | 102 - .../core/unversioned/fake/fake_event.go | 102 - .../unversioned/fake/fake_event_expansion.go | 88 - .../core/unversioned/fake/fake_limitrange.go | 102 - .../core/unversioned/fake/fake_namespace.go | 104 - .../fake/fake_namespace_expansion.go | 37 - .../core/unversioned/fake/fake_node.go | 104 - .../unversioned/fake/fake_persistentvolume.go | 104 - .../fake/fake_persistentvolumeclaim.go | 112 - .../core/unversioned/fake/fake_pod.go | 112 - .../unversioned/fake/fake_pod_expansion.go | 46 - .../core/unversioned/fake/fake_podtemplate.go | 102 - .../fake/fake_replicationcontroller.go | 112 - .../unversioned/fake/fake_resourcequota.go | 112 - .../core/unversioned/fake/fake_secret.go | 102 - .../core/unversioned/fake/fake_service.go | 112 - .../fake/fake_service_expansion.go | 26 - .../unversioned/fake/fake_serviceaccount.go | 102 - .../core/unversioned/pod_expansion.go | 6 +- .../core/unversioned/service_expansion.go | 6 +- .../unversioned/extensions_client.go | 16 +- .../extensions/unversioned/fake/doc.go | 18 - .../unversioned/fake/fake_daemonset.go | 113 - .../unversioned/fake/fake_deployment.go | 113 - .../fake/fake_deployment_expansion.go | 33 - .../fake/fake_extensions_client.go | 58 - .../fake/fake_horizontalpodautoscaler.go | 113 - .../unversioned/fake/fake_ingress.go | 113 - .../extensions/unversioned/fake/fake_job.go | 113 - .../unversioned/fake/fake_replicaset.go | 113 - .../unversioned/fake/fake_scale_expansion.go | 46 - .../fake/fake_thirdpartyresource.go | 103 - .../pkg/client/unversioned/auth/clientauth.go | 6 +- .../unversioned/auth/clientauth_test.go | 69 - .../pkg/client/unversioned/autoscaling.go | 13 +- .../pkg/client/unversioned/batch.go | 83 + .../pkg/client/unversioned/client.go | 26 +- .../pkg/client/unversioned/client_test.go | 308 - .../unversioned/clientcmd/api/helpers_test.go | 301 - .../client/unversioned/clientcmd/api/types.go | 6 +- .../unversioned/clientcmd/api/types_test.go | 123 - .../unversioned/clientcmd/api/v1/types.go | 6 +- .../unversioned/clientcmd/client_config.go | 50 +- .../clientcmd/client_config_test.go | 252 - .../unversioned/clientcmd/loader_test.go | 562 -- .../clientcmd/merged_client_builder.go | 4 +- .../client/unversioned/clientcmd/overrides.go | 4 +- .../unversioned/clientcmd/validation_test.go | 432 -- .../pkg/client/unversioned/conditions.go | 8 +- .../pkg/client/unversioned/conditions_test.go | 71 - .../client/unversioned/containerinfo_test.go | 200 - .../client/unversioned/daemon_sets_test.go | 199 - .../pkg/client/unversioned/deployment_test.go | 240 - .../pkg/client/unversioned/endpoints_test.go | 75 - .../pkg/client/unversioned/events_test.go | 205 - .../pkg/client/unversioned/extensions.go | 13 +- .../pkg/client/unversioned/fake/fake.go | 85 - .../pkg/client/unversioned/flags_test.go | 79 - .../pkg/client/unversioned/helper.go | 458 +- .../unversioned/helper_blackbox_test.go | 126 - .../pkg/client/unversioned/helper_test.go | 280 - .../horizontalpodautoscaler_test.go | 225 - .../unversioned/import_known_versions.go | 1 + .../pkg/client/unversioned/ingress_test.go | 240 - .../kubernetes/pkg/client/unversioned/jobs.go | 64 + .../pkg/client/unversioned/jobs_test.go | 232 - .../client/unversioned/limit_ranges_test.go | 189 - .../pkg/client/unversioned/namespaces_test.go | 189 - .../pkg/client/unversioned/nodes_test.go | 177 - .../unversioned/persistentvolume_test.go | 195 - .../unversioned/persistentvolumeclaim_test.go | 212 - .../client/unversioned/pod_templates_test.go | 151 - .../kubernetes/pkg/client/unversioned/pods.go | 5 +- .../pkg/client/unversioned/pods_test.go | 228 - .../unversioned/podsecuritypolicy_test.go | 138 - .../pkg/client/unversioned/portforward/doc.go | 19 - .../unversioned/portforward/portforward.go | 329 -- .../portforward/portforward_test.go | 400 -- .../client/unversioned/remotecommand/doc.go | 20 - .../remotecommand/remotecommand.go | 180 - .../remotecommand/remotecommand_test.go | 413 -- .../client/unversioned/remotecommand/v1.go | 156 - .../client/unversioned/remotecommand/v2.go | 166 - .../client/unversioned/replica_sets_test.go | 197 - .../replication_controllers_test.go | 204 - .../pkg/client/unversioned/request_test.go | 1313 ----- .../unversioned/resource_quotas_test.go | 208 - .../pkg/client/unversioned/restclient_test.go | 196 - .../pkg/client/unversioned/services.go | 5 +- .../pkg/client/unversioned/services_test.go | 242 - .../client/unversioned/testclient/actions.go | 446 -- .../testclient/fake_componentstatuses.go | 44 - .../unversioned/testclient/fake_configmaps.go | 78 - .../testclient/fake_daemon_sets.go | 83 - .../testclient/fake_deployments.go | 105 - .../unversioned/testclient/fake_endpoints.go | 74 - .../unversioned/testclient/fake_events.go | 151 - .../fake_horizontal_pod_autoscalers.go | 164 - .../unversioned/testclient/fake_ingress.go | 84 - .../unversioned/testclient/fake_jobs.go | 84 - .../testclient/fake_limit_ranges.go | 74 - .../unversioned/testclient/fake_namespaces.go | 103 - .../unversioned/testclient/fake_nodes.go | 88 - .../fake_persistent_volume_claims.go | 81 - .../testclient/fake_persistent_volumes.go | 86 - .../testclient/fake_pod_templates.go | 74 - .../unversioned/testclient/fake_pods.go | 117 - .../testclient/fake_podsecuritypolicy.go | 73 - .../testclient/fake_replica_sets.go | 83 - .../fake_replication_controllers.go | 82 - .../testclient/fake_resource_quotas.go | 83 - .../unversioned/testclient/fake_scales.go | 52 - .../unversioned/testclient/fake_secrets.go | 74 - .../testclient/fake_service_accounts.go | 74 - .../unversioned/testclient/fake_services.go | 88 - .../unversioned/testclient/fake_test.go | 39 - .../testclient/fake_thirdpartyresources.go | 83 - .../client/unversioned/testclient/fixture.go | 316 - .../testclient/simple/simple_testclient.go | 240 - .../unversioned/testclient/testclient.go | 399 -- .../unversioned/testclient/testclient_test.go | 75 - .../unversioned/thirdpartyresources_test.go | 185 - .../pkg/client/unversioned/urlbackoff_test.go | 78 - .../kubernetes/pkg/cloudprovider/OWNERS | 3 + .../kubernetes/pkg/cloudprovider/cloud.go | 2 +- .../pkg/cloudprovider/providers/aws/aws.go | 2380 -------- .../providers/aws/aws_instancegroups.go | 76 - .../providers/aws/aws_loadbalancer.go | 304 - .../cloudprovider/providers/aws/aws_routes.go | 188 - .../cloudprovider/providers/aws/aws_test.go | 939 --- .../cloudprovider/providers/aws/aws_utils.go | 51 - .../providers/aws/log_handler.go | 34 - .../pkg/cloudprovider/providers/fake/doc.go | 19 - .../pkg/cloudprovider/providers/fake/fake.go | 259 - .../pkg/cloudprovider/providers/gce/gce.go | 352 +- .../cloudprovider/providers/gce/gce_test.go | 150 - .../cloudprovider/providers/mesos/client.go | 376 -- .../providers/mesos/client_test.go | 271 - .../cloudprovider/providers/mesos/config.go | 79 - .../providers/mesos/config_test.go | 75 - .../cloudprovider/providers/mesos/mesos.go | 283 - .../providers/mesos/mesos_test.go | 279 - .../cloudprovider/providers/mesos/plugins.go | 21 - .../providers/openstack/MAINTAINERS.md | 6 - .../providers/openstack/openstack.go | 1087 ---- .../providers/openstack/openstack_test.go | 228 - .../cloudprovider/providers/ovirt/ovirt.go | 291 - .../providers/ovirt/ovirt_test.go | 126 - .../pkg/cloudprovider/providers/providers.go | 27 - .../providers/rackspace/MAINTAINERS.md | 6 - .../providers/rackspace/rackspace.go | 393 -- .../providers/rackspace/rackspace_test.go | 175 - .../k8s.io/kubernetes/pkg/controller/OWNERS | 5 + .../pkg/controller/controller_utils.go | 647 +++ .../pkg/{util/chown => controller}/doc.go | 5 +- .../controller/framework/controller_test.go | 404 -- .../framework/fake_controller_source_test.go | 94 - .../kubernetes/pkg/controller/lookup_cache.go | 90 + .../k8s.io/kubernetes/pkg/conversion/OWNERS | 5 + .../pkg/conversion/converter_test.go | 847 --- .../pkg/conversion/deep_copy_test.go | 161 - .../kubernetes/pkg/conversion/helper_test.go | 38 - .../pkg/conversion/queryparams/convert.go | 44 +- .../conversion/queryparams/convert_test.go | 174 - .../kubernetes/pkg/credentialprovider/OWNERS | 3 + .../credentialprovider/aws/aws_credentials.go | 163 - .../aws/aws_credentials_test.go | 108 - .../pkg/credentialprovider/config_test.go | 225 - .../pkg/credentialprovider/gcp/doc.go | 19 - .../pkg/credentialprovider/gcp/jwt.go | 111 - .../pkg/credentialprovider/gcp/jwt_test.go | 127 - .../pkg/credentialprovider/gcp/metadata.go | 202 - .../credentialprovider/gcp/metadata_test.go | 347 -- .../pkg/credentialprovider/keyring.go | 36 +- .../pkg/credentialprovider/keyring_test.go | 476 -- .../pkg/credentialprovider/provider_test.go | 62 - .../pkg/fieldpath/fieldpath_test.go | 117 - .../kubernetes/pkg/fields/fields_test.go | 57 - .../kubernetes/pkg/fields/selector_test.go | 208 - .../src/k8s.io/kubernetes/pkg/kubectl/OWNERS | 7 + .../kubernetes/pkg/kubectl/cmd/annotate.go | 350 -- .../pkg/kubectl/cmd/annotate_test.go | 571 -- .../kubernetes/pkg/kubectl/cmd/apiversions.go | 65 - .../kubernetes/pkg/kubectl/cmd/apply.go | 213 - .../kubernetes/pkg/kubectl/cmd/apply_test.go | 313 - .../kubernetes/pkg/kubectl/cmd/attach.go | 251 - .../kubernetes/pkg/kubectl/cmd/attach_test.go | 279 - .../kubernetes/pkg/kubectl/cmd/autoscale.go | 180 - .../kubernetes/pkg/kubectl/cmd/clusterinfo.go | 117 - .../k8s.io/kubernetes/pkg/kubectl/cmd/cmd.go | 201 - .../kubernetes/pkg/kubectl/cmd/cmd_test.go | 709 --- .../pkg/kubectl/cmd/config/config.go | 448 -- .../pkg/kubectl/cmd/config/config_test.go | 783 --- .../pkg/kubectl/cmd/config/create_authinfo.go | 234 - .../pkg/kubectl/cmd/config/create_cluster.go | 183 - .../pkg/kubectl/cmd/config/create_context.go | 133 - .../pkg/kubectl/cmd/config/current_context.go | 67 - .../cmd/config/current_context_test.go | 90 - .../cmd/config/navigation_step_parser.go | 152 - .../cmd/config/navigation_step_parser_test.go | 96 - .../kubernetes/pkg/kubectl/cmd/config/set.go | 208 - .../pkg/kubectl/cmd/config/unset.go | 104 - .../pkg/kubectl/cmd/config/use_context.go | 101 - .../kubernetes/pkg/kubectl/cmd/config/view.go | 158 - .../kubernetes/pkg/kubectl/cmd/convert.go | 164 - .../kubernetes/pkg/kubectl/cmd/create.go | 258 - .../pkg/kubectl/cmd/create_configmap.go | 96 - .../pkg/kubectl/cmd/create_configmap_test.go | 54 - .../pkg/kubectl/cmd/create_namespace.go | 76 - .../pkg/kubectl/cmd/create_namespace_test.go | 53 - .../pkg/kubectl/cmd/create_secret.go | 192 - .../pkg/kubectl/cmd/create_secret_test.go | 85 - .../kubernetes/pkg/kubectl/cmd/create_test.go | 232 - .../kubernetes/pkg/kubectl/cmd/delete.go | 210 - .../kubernetes/pkg/kubectl/cmd/delete_test.go | 451 -- .../kubernetes/pkg/kubectl/cmd/describe.go | 184 - .../pkg/kubectl/cmd/describe_test.go | 102 - .../kubernetes/pkg/kubectl/cmd/drain.go | 373 -- .../kubernetes/pkg/kubectl/cmd/drain_test.go | 494 -- .../k8s.io/kubernetes/pkg/kubectl/cmd/edit.go | 465 -- .../k8s.io/kubernetes/pkg/kubectl/cmd/exec.go | 239 - .../kubernetes/pkg/kubectl/cmd/exec_test.go | 258 - .../kubernetes/pkg/kubectl/cmd/explain.go | 107 - .../kubernetes/pkg/kubectl/cmd/expose.go | 244 - .../kubernetes/pkg/kubectl/cmd/expose_test.go | 351 -- .../k8s.io/kubernetes/pkg/kubectl/cmd/get.go | 288 - .../kubernetes/pkg/kubectl/cmd/get_test.go | 817 --- .../kubernetes/pkg/kubectl/cmd/label.go | 297 - .../kubernetes/pkg/kubectl/cmd/label_test.go | 418 -- .../k8s.io/kubernetes/pkg/kubectl/cmd/logs.go | 206 - .../kubernetes/pkg/kubectl/cmd/logs_test.go | 141 - .../kubernetes/pkg/kubectl/cmd/namespace.go | 41 - .../kubernetes/pkg/kubectl/cmd/patch.go | 157 - .../kubernetes/pkg/kubectl/cmd/patch_test.go | 90 - .../kubernetes/pkg/kubectl/cmd/portforward.go | 140 - .../pkg/kubectl/cmd/portforward_test.go | 176 - .../kubernetes/pkg/kubectl/cmd/proxy.go | 137 - .../kubernetes/pkg/kubectl/cmd/replace.go | 254 - .../pkg/kubectl/cmd/replace_test.go | 197 - .../pkg/kubectl/cmd/rollingupdate.go | 394 -- .../pkg/kubectl/cmd/rollingupdate_test.go | 90 - .../pkg/kubectl/cmd/rollout/rollout.go | 54 - .../kubectl/cmd/rollout/rollout_history.go | 122 - .../pkg/kubectl/cmd/rollout/rollout_pause.go | 118 - .../pkg/kubectl/cmd/rollout/rollout_resume.go | 116 - .../pkg/kubectl/cmd/rollout/rollout_undo.go | 111 - .../k8s.io/kubernetes/pkg/kubectl/cmd/run.go | 459 -- .../kubernetes/pkg/kubectl/cmd/run_test.go | 331 -- .../kubernetes/pkg/kubectl/cmd/scale.go | 176 - .../k8s.io/kubernetes/pkg/kubectl/cmd/stop.go | 102 - .../pkg/kubectl/cmd/util/clientcache.go | 9 +- .../pkg/kubectl/cmd/util/editor/editor.go | 226 - .../kubectl/cmd/util/editor/editor_test.go | 63 - .../cmd/util/editor/term_unsupported.go | 26 - .../pkg/kubectl/cmd/util/factory.go | 174 +- .../pkg/kubectl/cmd/util/factory_test.go | 338 -- .../pkg/kubectl/cmd/util/helpers_test.go | 364 -- .../kubectl/cmd/util/jsonmerge/jsonmerge.go | 193 - .../pkg/kubectl/cmd/util/printing.go | 4 +- .../kubernetes/pkg/kubectl/cmd/version.go | 55 - .../kubernetes/pkg/kubectl/configmap_test.go | 108 - .../pkg/kubectl/custom_column_printer_test.go | 276 - .../k8s.io/kubernetes/pkg/kubectl/describe.go | 186 +- .../kubernetes/pkg/kubectl/describe_test.go | 523 -- .../kubernetes/pkg/kubectl/generate_test.go | 140 - .../k8s.io/kubernetes/pkg/kubectl/history.go | 6 +- .../kubernetes/pkg/kubectl/interfaces.go | 2 +- .../k8s.io/kubernetes/pkg/kubectl/kubectl.go | 78 +- .../kubernetes/pkg/kubectl/kubectl.test | Bin 12585712 -> 0 bytes .../kubernetes/pkg/kubectl/kubectl_test.go | 194 - .../kubernetes/pkg/kubectl/namespace_test.go | 61 - .../kubernetes/pkg/kubectl/proxy_server.go | 6 +- .../pkg/kubectl/proxy_server_test.go | 337 -- .../pkg/kubectl/resource/builder.go | 38 +- .../pkg/kubectl/resource/builder_test.go | 1072 ---- .../pkg/kubectl/resource/helper_test.go | 498 -- .../pkg/kubectl/resource/interfaces.go | 2 +- .../kubernetes/pkg/kubectl/resource/mapper.go | 47 +- .../pkg/kubectl/resource/visitor.go | 31 +- .../pkg/kubectl/resource_printer.go | 235 +- .../pkg/kubectl/resource_printer_test.go | 1366 ----- .../kubernetes/pkg/kubectl/rolling_updater.go | 120 +- .../pkg/kubectl/rolling_updater_test.go | 1622 ------ .../src/k8s.io/kubernetes/pkg/kubectl/run.go | 261 +- .../k8s.io/kubernetes/pkg/kubectl/run_test.go | 883 --- .../k8s.io/kubernetes/pkg/kubectl/scale.go | 5 +- .../kubernetes/pkg/kubectl/scale_test.go | 728 --- .../secret_for_docker_registry_test.go | 81 - .../kubernetes/pkg/kubectl/secret_test.go | 109 - .../kubernetes/pkg/kubectl/service_test.go | 417 -- .../kubernetes/pkg/kubectl/serviceaccount.go | 51 + .../pkg/kubectl/sorted_event_list_test.go | 92 - .../pkg/kubectl/sorted_resource_name_list.go | 14 + .../pkg/kubectl/sorting_printer_test.go | 236 - .../src/k8s.io/kubernetes/pkg/kubectl/stop.go | 64 +- .../kubernetes/pkg/kubectl/stop_test.go | 718 --- .../pkg/kubectl/testing/types.generated.go | 555 -- .../kubernetes/pkg/kubectl/testing/types.go | 33 - .../pkg/kubelet/qos/memory_policy_test.go | 187 - .../kubernetes/pkg/labels/labels_test.go | 60 - .../k8s.io/kubernetes/pkg/labels/selector.go | 37 +- .../kubernetes/pkg/labels/selector_test.go | 574 -- .../src/k8s.io/kubernetes/pkg/runtime/OWNERS | 5 + .../kubernetes/pkg/runtime/conversion_test.go | 135 - .../kubernetes/pkg/runtime/embedded_test.go | 287 - .../kubernetes/pkg/runtime/extension_test.go | 39 - .../kubernetes/pkg/runtime/helper_test.go | 41 - .../kubernetes/pkg/runtime/protobuf/doc.go | 18 - .../pkg/runtime/protobuf/protobuf.go | 158 - .../kubernetes/pkg/runtime/scheme_test.go | 673 --- .../pkg/runtime/serializer/codec_test.go | 400 -- .../pkg/runtime/serializer/json/json_test.go | 268 - .../pkg/runtime/serializer/json/meta_test.go | 45 - .../serializer/recognizer/recognizer_test.go | 57 - .../serializer/versioning/versioning_test.go | 300 - .../pkg/runtime/serializer/yaml/yaml.go | 46 - .../pkg/runtime/swagger_doc_generator_test.go | 43 - .../pkg/runtime/unstructured_test.go | 123 - .../pkg/runtime/unversioned_test.go | 92 - .../fake_scale.go => types/unix_user_id.go} | 12 +- .../kubernetes/pkg/util/atomic/value.go | 42 - .../kubernetes/pkg/util/atomic/value_test.go | 52 - .../src/k8s.io/kubernetes/pkg/util/backoff.go | 15 + .../kubernetes/pkg/util/backoff_test.go | 202 - .../kubernetes/pkg/util/bandwidth/doc.go | 18 - .../pkg/util/bandwidth/fake_shaper.go | 49 - .../pkg/util/bandwidth/interfaces.go | 38 - .../kubernetes/pkg/util/bandwidth/linux.go | 322 -- .../pkg/util/bandwidth/linux_test.go | 634 -- .../pkg/util/bandwidth/unsupported.go | 52 - .../k8s.io/kubernetes/pkg/util/cache_test.go | 65 - .../k8s.io/kubernetes/pkg/util/chmod/chmod.go | 39 - .../k8s.io/kubernetes/pkg/util/chmod/doc.go | 19 - .../k8s.io/kubernetes/pkg/util/chown/chown.go | 39 - .../src/k8s.io/kubernetes/pkg/util/clock.go | 13 + .../k8s.io/kubernetes/pkg/util/clock_test.go | 96 - .../kubernetes/pkg/util/config/config.go | 140 - .../kubernetes/pkg/util/config/config_test.go | 120 - .../k8s.io/kubernetes/pkg/util/config/doc.go | 20 - .../k8s.io/kubernetes/pkg/util/dbus/dbus.go | 133 - .../kubernetes/pkg/util/dbus/dbus_test.go | 249 - .../k8s.io/kubernetes/pkg/util/dbus/doc.go | 18 - .../kubernetes/pkg/util/dbus/fake_dbus.go | 135 - .../pkg/util/deadlock-detector_test.go | 127 - .../pkg/util/deployment/deployment.go | 261 +- .../pkg/util/deployment/deployment_test.go | 344 -- .../k8s.io/kubernetes/pkg/util/env_test.go | 68 - .../kubernetes/pkg/util/errors/errors.go | 8 +- .../kubernetes/pkg/util/errors/errors_test.go | 286 - .../k8s.io/kubernetes/pkg/util/exec/doc.go | 18 - .../k8s.io/kubernetes/pkg/util/exec/exec.go | 129 - .../kubernetes/pkg/util/exec/exec_test.go | 103 - .../kubernetes/pkg/util/exec/fake_exec.go | 101 - .../kubernetes/pkg/util/flock/flock_unix.go | 51 - .../kubernetes/pkg/util/flushwriter/doc.go | 19 - .../kubernetes/pkg/util/flushwriter/writer.go | 53 - .../pkg/util/flushwriter/writer_test.go | 86 - .../kubernetes/pkg/util/hash/hash_test.go | 147 - .../kubernetes/pkg/util/httpstream/doc.go | 19 - .../pkg/util/httpstream/httpstream.go | 145 - .../pkg/util/httpstream/httpstream_test.go | 120 - .../pkg/util/httpstream/spdy/connection.go | 141 - .../pkg/util/httpstream/spdy/roundtripper.go | 229 - .../util/httpstream/spdy/roundtripper_test.go | 251 - .../pkg/util/httpstream/spdy/upgrade.go | 78 - .../pkg/util/httpstream/spdy/upgrade_test.go | 94 - .../pkg/util/integer/integer_test.go | 143 - .../kubernetes/pkg/util/intstr/intstr.go | 8 +- .../kubernetes/pkg/util/intstr/intstr_test.go | 160 - .../src/k8s.io/kubernetes/pkg/util/io/io.go | 61 - .../k8s.io/kubernetes/pkg/util/io/io_test.go | 56 - .../k8s.io/kubernetes/pkg/util/io/writer.go | 80 - .../kubernetes/pkg/util/iptables/doc.go | 18 - .../kubernetes/pkg/util/iptables/iptables.go | 595 -- .../pkg/util/iptables/iptables_test.go | 768 --- .../pkg/util/iptables/testing/fake.go | 75 - .../pkg/util/jsonpath/jsonpath_test.go | 255 - .../pkg/util/jsonpath/parser_test.go | 136 - .../kubernetes/pkg/util/keymutex/keymutex.go | 82 - .../pkg/util/keymutex/keymutex_test.go | 111 - .../kubernetes/pkg/util/labels/labels.go | 31 + .../kubernetes/pkg/util/labels/labels_test.go | 60 - .../kubernetes/pkg/util/limitwriter/doc.go | 19 - .../pkg/util/limitwriter/limitwriter.go | 53 - .../pkg/util/line_delimiter_test.go | 40 - .../k8s.io/kubernetes/pkg/util/mount/doc.go | 18 - .../k8s.io/kubernetes/pkg/util/mount/fake.go | 126 - .../k8s.io/kubernetes/pkg/util/mount/mount.go | 135 - .../kubernetes/pkg/util/mount/mount_linux.go | 311 - .../pkg/util/mount/mount_linux_test.go | 182 - .../pkg/util/mount/mount_unsupported.go | 45 - .../pkg/util/mount/nsenter_mount.go | 200 - .../util/mount/nsenter_mount_unsupported.go | 43 - .../util/mount/safe_format_and_mount_test.go | 224 - .../kubernetes/pkg/util/net/http_test.go | 102 - .../kubernetes/pkg/util/net/interface_test.go | 300 - .../pkg/util/net/port_range_test.go | 66 - .../pkg/util/net/port_split_test.go | 111 - .../kubernetes/pkg/util/net/sets/README.md | 17 + .../kubernetes/pkg/util/net/sets/ipnet.go | 119 + .../k8s.io/kubernetes/pkg/util/node/node.go | 61 - .../src/k8s.io/kubernetes/pkg/util/oom/doc.go | 18 - .../src/k8s.io/kubernetes/pkg/util/oom/oom.go | 26 - .../kubernetes/pkg/util/oom/oom_fake.go | 35 - .../kubernetes/pkg/util/oom/oom_linux.go | 128 - .../kubernetes/pkg/util/oom/oom_linux_test.go | 110 - .../pkg/util/oom/oom_unsupported.go | 40 - .../src/k8s.io/kubernetes/pkg/util/pod/pod.go | 59 + .../k8s.io/kubernetes/pkg/util/procfs/doc.go | 18 - .../pkg/util/procfs/example_proc_cgroup | 10 - .../kubernetes/pkg/util/procfs/procfs.go | 58 - .../kubernetes/pkg/util/procfs/procfs_fake.go | 30 - .../pkg/util/procfs/procfs_interface.go | 22 - .../kubernetes/pkg/util/procfs/procfs_test.go | 58 - .../k8s.io/kubernetes/pkg/util/proxy/dial.go | 106 - .../k8s.io/kubernetes/pkg/util/proxy/doc.go | 18 - .../kubernetes/pkg/util/proxy/transport.go | 241 - .../pkg/util/proxy/transport_test.go | 262 - .../kubernetes/pkg/util/rand/rand_test.go | 73 - .../k8s.io/kubernetes/pkg/util/runner_test.go | 55 - .../kubernetes/pkg/util/runtime/runtime.go | 11 + .../pkg/util/runtime/runtime_test.go | 69 - .../k8s.io/kubernetes/pkg/util/selinux/doc.go | 18 - .../kubernetes/pkg/util/selinux/selinux.go | 27 - .../pkg/util/selinux/selinux_linux.go | 34 - .../pkg/util/selinux/selinux_unsupported.go | 26 - .../kubernetes/pkg/util/sets/set_test.go | 270 - .../kubernetes/pkg/util/sets/types/types.go | 30 - .../k8s.io/kubernetes/pkg/util/slice/slice.go | 50 - .../kubernetes/pkg/util/slice/slice_test.go | 70 - .../pkg/util/strategicpatch/patch_test.go | 2026 ------- .../kubernetes/pkg/util/strings/escape.go | 49 - .../kubernetes/pkg/util/strings/strings.go | 47 - .../pkg/util/strings/strings_test.go | 54 - .../kubernetes/pkg/util/sysctl/sysctl.go | 56 - .../kubernetes/pkg/util/template_test.go | 61 - .../pkg/util/testing/fake_handler.go | 119 - .../pkg/util/testing/fake_handler_test.go | 186 - .../kubernetes/pkg/util/testing/tmpdir.go | 44 - .../kubernetes/pkg/util/throttle_test.go | 127 - .../k8s.io/kubernetes/pkg/util/util_test.go | 99 - .../pkg/util/validation/field/errors_test.go | 134 - .../pkg/util/validation/field/path_test.go | 123 - .../pkg/util/validation/validation_test.go | 337 -- .../kubernetes/pkg/util/wait/wait_test.go | 414 -- .../pkg/util/workqueue/queue_test.go | 131 - .../kubernetes/pkg/util/wsstream/conn.go | 318 - .../kubernetes/pkg/util/wsstream/conn_test.go | 173 - .../kubernetes/pkg/util/wsstream/doc.go | 21 - .../kubernetes/pkg/util/wsstream/stream.go | 124 - .../pkg/util/wsstream/stream_test.go | 235 - .../kubernetes/pkg/util/yaml/decoder_test.go | 217 - .../src/k8s.io/kubernetes/pkg/version/base.go | 6 +- .../kubernetes/pkg/version/semver_test.go | 47 - .../kubernetes/pkg/version/verflag/verflag.go | 101 - .../kubernetes/pkg/watch/filter_test.go | 82 - .../kubernetes/pkg/watch/iowatcher_test.go | 67 - .../kubernetes/pkg/watch/json/decoder_test.go | 105 - .../kubernetes/pkg/watch/json/encoder_test.go | 74 - .../k8s.io/kubernetes/pkg/watch/mux_test.go | 167 - .../k8s.io/kubernetes/pkg/watch/watch_test.go | 87 - .../forked/reflect/deep_equal_test.go | 137 - .../go/exp/math/dec/inf/benchmark_test.go | 210 - .../go/exp/math/dec/inf/dec_go1_2_test.go | 33 - .../go/exp/math/dec/inf/dec_internal_test.go | 40 - .../go/exp/math/dec/inf/dec_test.go | 379 -- .../go/exp/math/dec/inf/example_test.go | 62 - .../exp/math/dec/inf/rounder_example_test.go | 72 - .../go/exp/math/dec/inf/rounder_test.go | 109 - controllers/gce/controller/controller_test.go | 3 +- controllers/gce/main.go | 5 +- 1137 files changed, 22773 insertions(+), 189176 deletions(-) delete mode 100644 Godeps/_workspace/src/bitbucket.org/ww/goautoneg/autoneg_test.go delete mode 100644 Godeps/_workspace/src/github.com/beorn7/perks/quantile/bench_test.go delete mode 100644 Godeps/_workspace/src/github.com/beorn7/perks/quantile/example_test.go delete mode 100644 Godeps/_workspace/src/github.com/beorn7/perks/quantile/stream_test.go delete mode 100644 Godeps/_workspace/src/github.com/blang/semver/examples/main.go delete mode 100644 Godeps/_workspace/src/github.com/blang/semver/json_test.go delete mode 100644 Godeps/_workspace/src/github.com/blang/semver/semver_test.go delete mode 100644 Godeps/_workspace/src/github.com/blang/semver/sort_test.go delete mode 100644 Godeps/_workspace/src/github.com/blang/semver/sql_test.go create mode 100644 Godeps/_workspace/src/github.com/cpuguy83/go-md2man/LICENSE.md create mode 100644 Godeps/_workspace/src/github.com/davecgh/go-spew/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/davecgh/go-spew/spew/common_test.go delete mode 100644 Godeps/_workspace/src/github.com/davecgh/go-spew/spew/dump_test.go delete mode 100644 Godeps/_workspace/src/github.com/davecgh/go-spew/spew/dumpcgo_test.go delete mode 100644 Godeps/_workspace/src/github.com/davecgh/go-spew/spew/dumpnocgo_test.go delete mode 100644 Godeps/_workspace/src/github.com/davecgh/go-spew/spew/example_test.go delete mode 100644 Godeps/_workspace/src/github.com/davecgh/go-spew/spew/format_test.go delete mode 100644 Godeps/_workspace/src/github.com/davecgh/go-spew/spew/internal_test.go delete mode 100644 Godeps/_workspace/src/github.com/davecgh/go-spew/spew/spew_test.go delete mode 100644 Godeps/_workspace/src/github.com/davecgh/go-spew/spew/testdata/dumpcgo.go create mode 100644 Godeps/_workspace/src/github.com/docker/docker/LICENSE create mode 100644 Godeps/_workspace/src/github.com/docker/docker/NOTICE delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/mount/mount_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/mount/mountinfo_linux_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/mount/sharedsubtree_linux_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/filters/parse.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/filters/parse_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_windows.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_linux.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_unsupported.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_linux.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_windows.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/parsers_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/go-units/duration_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/go-units/size_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/go-units/ulimit_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/bench_curly_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/bench_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/compress_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/container_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/cors_filter_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/curly_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/doc_examples_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/entity_accessors_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/.goconvey delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/.goconvey delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/app.yaml delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/datastore/.goconvey delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/datastore/app.yaml delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/datastore/main.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/restful-appstats-integration.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/restful-user-service.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/home.html delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-CORS-filter.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-NCSA-logging.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-basic-authentication.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-cpuprofiler-service.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-curly-router.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-encoding-filter.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-filters.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-form-handling.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-hello-world.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-html-template.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-multi-containers.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-options-filter.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-path-tail.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-pre-post-filters.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-resource-functions.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-route_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-routefunction_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-serve-static.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-swagger.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-user-resource.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-user-service.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/filter_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/jsr311_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/options_filter_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/path_expression_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/request_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/response_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/route_builder_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/route_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_builder_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_list_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_ext_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_list_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/ordered_route_map_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/postbuild_model_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/test_package/struct.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/utils_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/tracer_test.go delete mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/web_service_test.go delete mode 100644 Godeps/_workspace/src/github.com/evanphx/json-patch/merge_test.go delete mode 100644 Godeps/_workspace/src/github.com/evanphx/json-patch/patch_test.go delete mode 100644 Godeps/_workspace/src/github.com/fatih/structs/field_test.go delete mode 100644 Godeps/_workspace/src/github.com/fatih/structs/structs_example_test.go delete mode 100644 Godeps/_workspace/src/github.com/fatih/structs/structs_test.go delete mode 100644 Godeps/_workspace/src/github.com/fatih/structs/tags_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/auth_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/build_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/change_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/env_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/event_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/example_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/entry_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/formatter_bench_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/hook_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/json_formatter_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/logrus_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/text_formatter_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/envfile_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/hosts_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/ip_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/opts_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/archive_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/archive_unix_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/archive_windows_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/changes_posix_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/changes_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/copy_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/diff_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/utils_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/wrap_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/fileutils/fileutils_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/homedir/homedir_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/idtools/idtools_unix_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/bytespipe_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/fmt_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/multireader_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/readers_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/writers_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/longpath/longpath_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/pools/pools_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/stdcopy/stdcopy_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_unix_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_windows_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/lstat_unix_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/meminfo_unix_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/stat_unix_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/utimes_unix_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/go-units/duration_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/go-units/size_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/go-units/ulimit_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/README.md delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/context.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/context_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/doc.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/README.md delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/bench_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/doc.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/mux.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/mux_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/old_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/regexp.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/route.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/opencontainers/runc/libcontainer/user/user_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/net/context/context_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/net/context/withtimeout_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/creds_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/export_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/mmap_unix_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_bsd_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_freebsd_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_unix_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/integration_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/misc_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/network_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/.dockerignore delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/Dockerfile delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/barfile delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/ca.pem delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/cert.pem delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/container.tar delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/dockerfile.tar delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/foofile delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/key.pem delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/server.pem delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/serverkey.pem delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go delete mode 100644 Godeps/_workspace/src/github.com/fsouza/go-dockerclient/volume_test.go delete mode 100644 Godeps/_workspace/src/github.com/ghodss/yaml/yaml_test.go delete mode 100644 Godeps/_workspace/src/github.com/golang/glog/glog_test.go create mode 100644 Godeps/_workspace/src/github.com/golang/groupcache/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/golang/groupcache/lru/lru_test.go create mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/all_test.go delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/clone_test.go delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/equal_test.go delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions_test.go delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set_test.go delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/Makefile delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/proto3.proto delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_test.go delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/size2_test.go delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/size_test.go delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/Makefile delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/golden_test.go delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.pb.go delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.proto delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser_test.go delete mode 100644 Godeps/_workspace/src/github.com/golang/protobuf/proto/text_test.go create mode 100644 Godeps/_workspace/src/github.com/google/cadvisor/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container_test.go delete mode 100644 Godeps/_workspace/src/github.com/google/cadvisor/info/v1/test/datagen.go delete mode 100644 Godeps/_workspace/src/github.com/google/gofuzz/example_test.go delete mode 100644 Godeps/_workspace/src/github.com/google/gofuzz/fuzz_test.go delete mode 100644 Godeps/_workspace/src/github.com/imdario/mergo/mergo_test.go delete mode 100644 Godeps/_workspace/src/github.com/imdario/mergo/testdata/license.yml delete mode 100644 Godeps/_workspace/src/github.com/imdario/mergo/testdata/thing.yml delete mode 100644 Godeps/_workspace/src/github.com/juju/ratelimit/ratelimit_test.go create mode 100644 Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/all_test.go delete mode 100644 Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/fixtures_test.go create mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/LICENSE create mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/NOTICE delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/cgroups_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/stats_util_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/util_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/utils_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_nosystemd.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/configs/config_unix_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/configs/config_windows_test.go delete mode 100644 Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/configs/validate/config.go delete mode 100644 Godeps/_workspace/src/github.com/pborman/uuid/json_test.go delete mode 100644 Godeps/_workspace/src/github.com/pborman/uuid/seq_test.go delete mode 100644 Godeps/_workspace/src/github.com/pborman/uuid/uuid_test.go create mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/LICENSE create mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/NOTICE delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/benchmark_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_clustermanager_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_memstats_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_selfcollector_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/examples_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/expvar_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/gauge_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/http_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/metric_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/process_collector_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/vec_test.go create mode 100644 Godeps/_workspace/src/github.com/prometheus/client_model/LICENSE create mode 100644 Godeps/_workspace/src/github.com/prometheus/client_model/NOTICE delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/bench_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_0 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_1 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_2 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_3 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_4 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_0 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_1 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_10 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_11 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_12 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_13 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_14 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_15 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_16 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_17 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_18 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_19 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_2 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_3 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_4 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_5 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_6 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_7 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_8 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_9 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/minimal delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/json2 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf.gz delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/test.gz delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/text delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/model/labels_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/model/time_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/cmdline delete mode 100644 Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/fd/0 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/fd/1 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/fd/2 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/fd/3 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/fd/4 delete mode 100644 Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/limits delete mode 100644 Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/stat delete mode 100644 Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/584/stat delete mode 100644 Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/stat delete mode 100644 Godeps/_workspace/src/github.com/prometheus/procfs/fs_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/procfs/proc_limits_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/procfs/proc_stat_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/procfs/proc_test.go delete mode 100644 Godeps/_workspace/src/github.com/prometheus/procfs/stat_test.go delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/block_test.go delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/inline_test.go delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Amps and angle encoding.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Amps and angle encoding.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Auto links.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Auto links.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Backslash escapes.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Backslash escapes.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Blockquotes with code blocks.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Blockquotes with code blocks.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Blocks.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Blocks.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Spans.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Spans.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines no empty line before block.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines no empty line before block.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Horizontal rules.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Horizontal rules.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Advanced).html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Advanced).text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Simple).html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Simple).text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML comments.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML comments.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, inline style.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, inline style.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, reference style.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, reference style.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, shortcut references.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, shortcut references.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Literal quotes in titles.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Literal quotes in titles.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Basics.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Basics.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Syntax.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Syntax.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Nested blockquotes.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Nested blockquotes.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Ordered and unordered lists.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Ordered and unordered lists.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Strong and em together.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Strong and em together.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tabs.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tabs.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tidyness.html delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tidyness.text delete mode 100644 Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref_test.go delete mode 100644 Godeps/_workspace/src/github.com/scalingdata/gcfg/example_test.go delete mode 100644 Godeps/_workspace/src/github.com/scalingdata/gcfg/issues_test.go delete mode 100644 Godeps/_workspace/src/github.com/scalingdata/gcfg/read_test.go delete mode 100644 Godeps/_workspace/src/github.com/scalingdata/gcfg/scanner/example_test.go delete mode 100644 Godeps/_workspace/src/github.com/scalingdata/gcfg/scanner/scanner_test.go delete mode 100644 Godeps/_workspace/src/github.com/scalingdata/gcfg/testdata/gcfg_test.gcfg delete mode 100644 Godeps/_workspace/src/github.com/scalingdata/gcfg/testdata/gcfg_unicode_test.gcfg delete mode 100644 Godeps/_workspace/src/github.com/scalingdata/gcfg/token/position_test.go delete mode 100644 Godeps/_workspace/src/github.com/scalingdata/gcfg/token/serialize_test.go delete mode 100644 Godeps/_workspace/src/github.com/scalingdata/gcfg/types/enum_test.go delete mode 100644 Godeps/_workspace/src/github.com/scalingdata/gcfg/types/int_test.go delete mode 100644 Godeps/_workspace/src/github.com/scalingdata/gcfg/types/scan_test.go delete mode 100644 Godeps/_workspace/src/github.com/shurcooL/sanitized_anchor_name/main_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/bash_completions_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/add.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/helpers.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/helpers_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/init.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/licenses.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/root.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/cobra/main.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/cobra_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/command_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/examples_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/man_docs_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/md_docs_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/pflag/bool_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/pflag/count_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/pflag/example_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/pflag/export_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/pflag/flag_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/pflag/golangflag_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/pflag/int_slice_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/pflag/ip_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/pflag/ipnet_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/pflag/string_slice_test.go delete mode 100644 Godeps/_workspace/src/github.com/spf13/pflag/verify/all.sh delete mode 100644 Godeps/_workspace/src/github.com/spf13/pflag/verify/gofmt.sh delete mode 100644 Godeps/_workspace/src/github.com/spf13/pflag/verify/golint.sh create mode 100644 Godeps/_workspace/src/github.com/ugorji/go/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/ugorji/go/codec/cbor_test.go delete mode 100644 Godeps/_workspace/src/github.com/ugorji/go/codec/codec_test.go delete mode 100644 Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/README.md delete mode 100644 Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/gen.go delete mode 100644 Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/z.go delete mode 100644 Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen_test.go delete mode 100644 Godeps/_workspace/src/github.com/ugorji/go/codec/helper_test.go delete mode 100644 Godeps/_workspace/src/github.com/ugorji/go/codec/py_test.go delete mode 100644 Godeps/_workspace/src/github.com/ugorji/go/codec/values_test.go rename Godeps/_workspace/src/{github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux => golang.org/x/net}/LICENSE (83%) create mode 100644 Godeps/_workspace/src/golang.org/x/net/PATENTS delete mode 100644 Godeps/_workspace/src/golang.org/x/net/context/context_test.go delete mode 100644 Godeps/_workspace/src/golang.org/x/net/context/ctxhttp/ctxhttp_test.go delete mode 100644 Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/clientcredentials/clientcredentials.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/clientcredentials/clientcredentials_test.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/example_test.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/facebook/facebook.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/github/github.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/google/example_test.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/google/google_test.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/google/sdk_test.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/credentials delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/properties delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2_test.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/internal/token_test.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/jwt/example_test.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt_test.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/linkedin/linkedin.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/oauth2_test.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/paypal/paypal.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/token_test.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/transport_test.go delete mode 100644 Godeps/_workspace/src/golang.org/x/oauth2/vk/vk.go rename Godeps/_workspace/src/{github.com/fsouza/go-dockerclient/external/github.com/gorilla/context => google.golang.org/api}/LICENSE (83%) delete mode 100644 Godeps/_workspace/src/google.golang.org/api/gensupport/json_test.go delete mode 100644 Godeps/_workspace/src/google.golang.org/api/gensupport/media_test.go delete mode 100644 Godeps/_workspace/src/google.golang.org/api/googleapi/googleapi_test.go delete mode 100644 Godeps/_workspace/src/google.golang.org/api/googleapi/transport/apikey.go delete mode 100644 Godeps/_workspace/src/google.golang.org/api/googleapi/types_test.go create mode 100644 Godeps/_workspace/src/google.golang.org/cloud/LICENSE delete mode 100644 Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.pb.go delete mode 100644 Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.proto delete mode 100644 Godeps/_workspace/src/google.golang.org/cloud/internal/testutil/context.go delete mode 100644 Godeps/_workspace/src/gopkg.in/yaml.v2/decode_test.go delete mode 100644 Godeps/_workspace/src/gopkg.in/yaml.v2/encode_test.go delete mode 100644 Godeps/_workspace/src/gopkg.in/yaml.v2/suite_test.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/LICENSE create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/OWNERS delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/context_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/conversion_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/copy_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/deep_copy_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/endpoints/util_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/errors/errors_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/errors/etcd/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/errors/etcd/etcd.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/field_constants.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/generate_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/helpers_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/install/install_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/help_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/meta_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/multirestmapper_test.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/priority.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/restmapper_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/pod/util_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/ref_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource/quantity_example_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource/quantity_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource/scale_int_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource_helpers_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/create.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/delete.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/export.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/rest.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/resttest/resttest.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/types.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/update.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/serialization_proto_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/serialization_test.go rename Godeps/_workspace/src/k8s.io/kubernetes/pkg/{util/flock/flock_other.go => api/service/annotations.go} (51%) create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/service/util.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testapi/testapi_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/compat/compatibility_tester.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/conversion.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/fuzzer.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/pod_specs.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/duration_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/group_version_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/helpers_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/time_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/validation/validation.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/util/group_version_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/backward_compatibility_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/conversion_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/defaults_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/events_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/name_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/schema_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod.yaml delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod1.json delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod2.json delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod3.json delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/validPod.yaml delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/validation_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apimachinery/registered/registered_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/authorization/validation/validation.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/authorization/validation/validation_test.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/types.generated.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/types.go rename Godeps/_workspace/src/k8s.io/kubernetes/pkg/{kubectl/cmd/util/editor/term.go => apis/batch/deep_copy_generated.go} (64%) create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/install/install.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/register.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/conversion.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/conversion_generated.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/deep_copy_generated.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/defaults.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/register.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/types.generated.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/types.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/types_swagger_doc_generated.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/helpers_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/install/install_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/helpers.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/install/install_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/defaults_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/validation/validation.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/validation/validation_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/delta_fifo_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/expiration_cache_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/fifo_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/index_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/listers_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/listwatch_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/reflector_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/store_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/undelta_store_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake/clientset_generated.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake/discovery.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/record/event_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/record/events_cache_test.go rename Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/{unversioned/restclient.go => restclient/client.go} (99%) create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/config.go rename Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/{unversioned => restclient}/request.go (94%) rename Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/{unversioned => restclient}/transport.go (99%) create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/url_utils.go rename Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/{unversioned => restclient}/urlbackoff.go (93%) create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/versions.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/transport/cache_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/transport/round_trippers_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/transport/transport_test.go rename Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/{unversioned => typed/discovery}/discovery_client.go (90%) delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_componentstatus.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_configmap.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_core_client.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_endpoints.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_event.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_event_expansion.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_limitrange.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_namespace.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_namespace_expansion.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_node.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_persistentvolume.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_persistentvolumeclaim.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_pod.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_pod_expansion.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_podtemplate.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_replicationcontroller.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_resourcequota.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_secret.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_service.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_service_expansion.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_serviceaccount.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_daemonset.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_deployment.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_deployment_expansion.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_extensions_client.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_horizontalpodautoscaler.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_ingress.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_job.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_replicaset.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_scale_expansion.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_thirdpartyresource.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/auth/clientauth_test.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/batch.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/client_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/helpers_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/types_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/client_config_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/loader_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/validation_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/conditions_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/containerinfo_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/daemon_sets_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/deployment_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/endpoints_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/events_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/fake/fake.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/flags_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/helper_blackbox_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/helper_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/horizontalpodautoscaler_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/ingress_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/jobs_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/limit_ranges_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/namespaces_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/nodes_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/persistentvolume_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/persistentvolumeclaim_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/pod_templates_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/pods_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/podsecuritypolicy_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/portforward/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/portforward/portforward.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/portforward/portforward_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/remotecommand.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/remotecommand_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/v1.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/v2.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/replica_sets_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/replication_controllers_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/request_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/resource_quotas_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/restclient_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/services_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/actions.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_componentstatuses.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_configmaps.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_daemon_sets.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_deployments.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_endpoints.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_events.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_horizontal_pod_autoscalers.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_ingress.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_jobs.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_limit_ranges.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_namespaces.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_nodes.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_persistent_volume_claims.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_persistent_volumes.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_pod_templates.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_pods.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_podsecuritypolicy.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_replica_sets.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_replication_controllers.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_resource_quotas.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_scales.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_secrets.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_service_accounts.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_services.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_thirdpartyresources.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fixture.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/simple/simple_testclient.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/testclient.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/testclient_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/thirdpartyresources_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/urlbackoff_test.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/OWNERS delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_instancegroups.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_routes.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_utils.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/log_handler.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/fake/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/fake/fake.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/client.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/client_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/config.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/config_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/mesos.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/mesos_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/plugins.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/MAINTAINERS.md delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/ovirt/ovirt.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/ovirt/ovirt_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/providers.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/rackspace/MAINTAINERS.md delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/rackspace/rackspace.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/rackspace/rackspace_test.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/OWNERS create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/controller_utils.go rename Godeps/_workspace/src/k8s.io/kubernetes/pkg/{util/chown => controller}/doc.go (84%) delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/framework/controller_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/framework/fake_controller_source_test.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/lookup_cache.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/OWNERS delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/converter_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/deep_copy_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/helper_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/queryparams/convert_test.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/OWNERS delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/aws/aws_credentials.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/aws/aws_credentials_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/config_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/jwt.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/jwt_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/metadata.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/metadata_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/keyring_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/provider_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/fieldpath/fieldpath_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/fields/fields_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/fields/selector_test.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/OWNERS delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/annotate.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/annotate_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/apiversions.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/apply.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/apply_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/attach.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/attach_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/autoscale.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/cmd.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/cmd_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/config.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/config_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_authinfo.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_cluster.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_context.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/current_context.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/current_context_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/set.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/unset.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/use_context.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/view.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/convert.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_configmap.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_configmap_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_namespace.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_namespace_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_secret.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_secret_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/delete.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/delete_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/describe.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/describe_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/drain.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/drain_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/edit.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/exec.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/exec_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/explain.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/expose.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/expose_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/get.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/get_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/label.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/label_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/logs.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/logs_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/namespace.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/patch.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/patch_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/portforward.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/portforward_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/proxy.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/replace.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/replace_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollingupdate.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollingupdate_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_history.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_pause.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_resume.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_undo.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/run.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/run_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/scale.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/stop.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/editor.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/editor_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/term_unsupported.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/helpers_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/jsonmerge/jsonmerge.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/version.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/configmap_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/custom_column_printer_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/describe_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/generate_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/kubectl.test delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/kubectl_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/namespace_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/proxy_server_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/builder_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/helper_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource_printer_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/rolling_updater_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/run_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/scale_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/secret_for_docker_registry_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/secret_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/service_test.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/serviceaccount.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/sorted_event_list_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/sorting_printer_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/stop_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/testing/types.generated.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/testing/types.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubelet/qos/memory_policy_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/labels/labels_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/labels/selector_test.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/OWNERS delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/conversion_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/embedded_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/extension_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/helper_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/protobuf/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/protobuf/protobuf.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/scheme_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/codec_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/json_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/meta_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/recognizer/recognizer_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/versioning/versioning_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/yaml/yaml.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/swagger_doc_generator_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/unstructured_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/unversioned_test.go rename Godeps/_workspace/src/k8s.io/kubernetes/pkg/{client/typed/generated/extensions/unversioned/fake/fake_scale.go => types/unix_user_id.go} (77%) delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/atomic/value.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/atomic/value_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/backoff_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/fake_shaper.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/interfaces.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/linux.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/linux_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/unsupported.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/cache_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/chmod/chmod.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/chmod/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/chown/chown.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/clock_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/config/config.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/config/config_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/config/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/dbus.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/dbus_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/fake_dbus.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/deadlock-detector_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/deployment/deployment_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/env_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/errors/errors_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/exec.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/exec_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/fake_exec.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flock/flock_unix.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flushwriter/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flushwriter/writer.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flushwriter/writer_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/hash/hash_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/httpstream.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/httpstream_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/connection.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/roundtripper.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/roundtripper_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/upgrade.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/upgrade_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/integer/integer_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/intstr/intstr_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/io/io.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/io/io_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/io/writer.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/iptables.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/iptables_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/testing/fake.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/jsonpath/jsonpath_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/jsonpath/parser_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/keymutex/keymutex.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/keymutex/keymutex_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/labels/labels_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/limitwriter/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/limitwriter/limitwriter.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/line_delimiter_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/fake.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_linux.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_linux_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/safe_format_and_mount_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/http_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/interface_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/port_range_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/port_split_test.go create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/sets/README.md create mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/sets/ipnet.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/node/node.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_fake.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_linux.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_linux_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_unsupported.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/example_proc_cgroup delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs_fake.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs_interface.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/dial.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/transport.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/transport_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/rand/rand_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/runner_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/runtime/runtime_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/selinux/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/selinux/selinux.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/selinux/selinux_linux.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/selinux/selinux_unsupported.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/sets/set_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/sets/types/types.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/slice/slice.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/slice/slice_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/strategicpatch/patch_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/strings/escape.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/strings/strings.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/strings/strings_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/sysctl/sysctl.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/template_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/testing/fake_handler.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/testing/fake_handler_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/testing/tmpdir.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/throttle_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/util_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/validation/field/errors_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/validation/field/path_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/validation/validation_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/wait/wait_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/workqueue/queue_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/wsstream/conn.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/wsstream/conn_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/wsstream/doc.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/wsstream/stream.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/wsstream/stream_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/yaml/decoder_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/version/semver_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/version/verflag/verflag.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/watch/filter_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/watch/iowatcher_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/watch/json/decoder_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/watch/json/encoder_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/watch/mux_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/pkg/watch/watch_test.go delete mode 100644 Godeps/_workspace/src/k8s.io/kubernetes/third_party/forked/reflect/deep_equal_test.go delete mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/benchmark_test.go delete mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/dec_go1_2_test.go delete mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/dec_internal_test.go delete mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/dec_test.go delete mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/example_test.go delete mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/rounder_example_test.go delete mode 100644 Godeps/_workspace/src/speter.net/go/exp/math/dec/inf/rounder_test.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index dd31b618c..b4e02a025 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,6 +1,6 @@ { "ImportPath": "k8s.io/contrib/ingress", - "GoVersion": "go1.5.3", + "GoVersion": "go1.5", "Packages": [ "./..." ], @@ -8,7 +8,7 @@ { "ImportPath": "bitbucket.org/ww/goautoneg", "Comment": "null-5", - "Rev": "75cd24fc2f2c2a2088577d12123ddee5f54e0675" + "Rev": "'75cd24fc2f2c2a2088577d12123ddee5f54e0675'" }, { "ImportPath": "github.com/beorn7/perks/quantile", @@ -21,7 +21,7 @@ }, { "ImportPath": "github.com/cpuguy83/go-md2man/md2man", - "Comment": "v1.0.3-2-g71acacd", + "Comment": "v1.0.4", "Rev": "71acacd42f85e5e82f70a55327789582a5200a90" }, { @@ -48,17 +48,95 @@ "Comment": "v1.2", "Rev": "777bb3f19bcafe2575ffb2a3e46af92509ae9594" }, + { + "ImportPath": "github.com/emicklei/go-restful/log", + "Comment": "v1.2", + "Rev": "777bb3f19bcafe2575ffb2a3e46af92509ae9594" + }, + { + "ImportPath": "github.com/emicklei/go-restful/swagger", + "Comment": "v1.2", + "Rev": "777bb3f19bcafe2575ffb2a3e46af92509ae9594" + }, { "ImportPath": "github.com/evanphx/json-patch", "Rev": "7dd4489c2eb6073e5a9d7746c3274c5b5f0387df" }, { "ImportPath": "github.com/fatih/structs", - "Rev": "d2e1722acaab51fc7fc55686706d08bbf9e4fafb" + "Rev": "a924a2250d1033753512e95dce41dca3fd793ad9" }, { "ImportPath": "github.com/fsouza/go-dockerclient", - "Rev": "25bc220b299845ae5489fd19bf89c5278864b050" + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/fileutils", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/homedir", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/idtools", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/longpath", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/pools", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/promise", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/stdcopy", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/github.com/docker/go-units", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/github.com/hashicorp/go-cleanhttp", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/github.com/opencontainers/runc/libcontainer/user", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/golang.org/x/net/context", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" + }, + { + "ImportPath": "github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix", + "Rev": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2" }, { "ImportPath": "github.com/ghodss/yaml", @@ -74,12 +152,12 @@ }, { "ImportPath": "github.com/golang/protobuf/proto", - "Rev": "7f07925444bb51fa4cf9dfe6f7661876f8852275" + "Rev": "b982704f8bb716bb608144408cff30e15fbde841" }, { "ImportPath": "github.com/google/cadvisor/info/v1", - "Comment": "v0.21.1", - "Rev": "49a08d5139ae0111757a110ea3b55fe9a607873d" + "Comment": "v0.22.2", + "Rev": "546a3771589bdb356777c646c6eca24914fdd48b" }, { "ImportPath": "github.com/google/gofuzz", @@ -107,6 +185,11 @@ "Comment": "v0.0.7", "Rev": "7ca2aa4873aea7cb4265b1726acb24b90d8726c6" }, + { + "ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/fs", + "Comment": "v0.0.7", + "Rev": "7ca2aa4873aea7cb4265b1726acb24b90d8726c6" + }, { "ImportPath": "github.com/opencontainers/runc/libcontainer/configs", "Comment": "v0.0.7", @@ -152,6 +235,18 @@ "ImportPath": "github.com/scalingdata/gcfg", "Rev": "37aabad69cfd3d20b8390d902a8b10e245c615ff" }, + { + "ImportPath": "github.com/scalingdata/gcfg/scanner", + "Rev": "37aabad69cfd3d20b8390d902a8b10e245c615ff" + }, + { + "ImportPath": "github.com/scalingdata/gcfg/token", + "Rev": "37aabad69cfd3d20b8390d902a8b10e245c615ff" + }, + { + "ImportPath": "github.com/scalingdata/gcfg/types", + "Rev": "37aabad69cfd3d20b8390d902a8b10e245c615ff" + }, { "ImportPath": "github.com/shurcooL/sanitized_anchor_name", "Rev": "9a8b7d4e8f347bfa230879db9d7d4e4d9e19f962" @@ -166,16 +261,36 @@ }, { "ImportPath": "github.com/ugorji/go/codec", - "Rev": "f1f1a805ed361a0e078bb537e4ea78cd37dcf065" + "Rev": "f4485b318aadd133842532f841dc205a8e339d74" }, { "ImportPath": "golang.org/x/net/context", "Rev": "c2528b2dd8352441850638a8bb678c2ad056fd3e" }, + { + "ImportPath": "golang.org/x/net/context/ctxhttp", + "Rev": "c2528b2dd8352441850638a8bb678c2ad056fd3e" + }, { "ImportPath": "golang.org/x/oauth2", "Rev": "b5adcc2dcdf009d0391547edc6ecbaff889f5bb9" }, + { + "ImportPath": "golang.org/x/oauth2/google", + "Rev": "b5adcc2dcdf009d0391547edc6ecbaff889f5bb9" + }, + { + "ImportPath": "golang.org/x/oauth2/internal", + "Rev": "b5adcc2dcdf009d0391547edc6ecbaff889f5bb9" + }, + { + "ImportPath": "golang.org/x/oauth2/jws", + "Rev": "b5adcc2dcdf009d0391547edc6ecbaff889f5bb9" + }, + { + "ImportPath": "golang.org/x/oauth2/jwt", + "Rev": "b5adcc2dcdf009d0391547edc6ecbaff889f5bb9" + }, { "ImportPath": "google.golang.org/api/compute/v1", "Rev": "77e7d383beb96054547729f49c372b3d01e196ff" @@ -192,6 +307,10 @@ "ImportPath": "google.golang.org/api/googleapi", "Rev": "77e7d383beb96054547729f49c372b3d01e196ff" }, + { + "ImportPath": "google.golang.org/api/googleapi/internal/uritemplates", + "Rev": "77e7d383beb96054547729f49c372b3d01e196ff" + }, { "ImportPath": "google.golang.org/cloud/compute/metadata", "Rev": "2e43671e4ad874a7bca65746ff3edb38e6e93762" @@ -206,178 +325,498 @@ }, { "ImportPath": "k8s.io/kubernetes/pkg/api", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/endpoints", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/errors", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/install", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/meta", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/pod", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/resource", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/service", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/testapi", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/unversioned", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/util", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/v1", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/validation", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/apimachinery", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apimachinery/registered", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/apis/authorization", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/authorization/install", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/authorization/v1beta1", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/apis/autoscaling", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/autoscaling/install", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/autoscaling/v1", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/batch", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/batch/install", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/batch/v1", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/apis/componentconfig", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/componentconfig/install", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/apis/extensions", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/extensions/install", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/apis/metrics", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/metrics/install", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/metrics/v1alpha1", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/auth/user", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/capabilities", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/client/cache", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/client/metrics", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/client/record", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/restclient", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/client/transport", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/typed/discovery", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/client/unversioned", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/unversioned/auth", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/unversioned/clientcmd", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/latest", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/v1", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/cloudprovider", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/cloudprovider/providers/gce", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/controller", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/controller/framework", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/conversion", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/conversion/queryparams", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/credentialprovider", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/fieldpath", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/fields", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/kubectl", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/kubectl/cmd/util", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/kubectl/resource", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/kubelet/qos", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/kubelet/qos/util", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/labels", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/master/ports", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/runtime", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer/json", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer/recognizer", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer/versioning", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/types", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/util", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/deployment", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/errors", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/hash", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/integer", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/intstr", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/jsonpath", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/labels", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/net", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/net/sets", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/parsers", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/pod", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/rand", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/runtime", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/sets", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/strategicpatch", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/validation", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/validation/field", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/wait", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/workqueue", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/yaml", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/version", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/pkg/watch", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/watch/json", + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/third_party/forked/json", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/third_party/forked/reflect", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "k8s.io/kubernetes/third_party/golang/template", - "Comment": "v1.2.0-alpha.8-71-g1ce188e", - "Rev": "1ce188e557769353063dc2233c89f4fa02133ecc" + "Comment": "v1.2.1-beta.0", + "Rev": "fd557a2c9f47c655f9c07d9cf9dee2539e935703" }, { "ImportPath": "speter.net/go/exp/math/dec/inf", diff --git a/Godeps/_workspace/src/bitbucket.org/ww/goautoneg/autoneg_test.go b/Godeps/_workspace/src/bitbucket.org/ww/goautoneg/autoneg_test.go deleted file mode 100644 index 41d328f1d..000000000 --- a/Godeps/_workspace/src/bitbucket.org/ww/goautoneg/autoneg_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package goautoneg - -import ( - "testing" -) - -var chrome = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" - -func TestParseAccept(t *testing.T) { - alternatives := []string{"text/html", "image/png"} - content_type := Negotiate(chrome, alternatives) - if content_type != "image/png" { - t.Errorf("got %s expected image/png", content_type) - } - - alternatives = []string{"text/html", "text/plain", "text/n3"} - content_type = Negotiate(chrome, alternatives) - if content_type != "text/html" { - t.Errorf("got %s expected text/html", content_type) - } - - alternatives = []string{"text/n3", "text/plain"} - content_type = Negotiate(chrome, alternatives) - if content_type != "text/plain" { - t.Errorf("got %s expected text/plain", content_type) - } - - alternatives = []string{"text/n3", "application/rdf+xml"} - content_type = Negotiate(chrome, alternatives) - if content_type != "text/n3" { - t.Errorf("got %s expected text/n3", content_type) - } -} diff --git a/Godeps/_workspace/src/github.com/beorn7/perks/quantile/bench_test.go b/Godeps/_workspace/src/github.com/beorn7/perks/quantile/bench_test.go deleted file mode 100644 index 0bd0e4e77..000000000 --- a/Godeps/_workspace/src/github.com/beorn7/perks/quantile/bench_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package quantile - -import ( - "testing" -) - -func BenchmarkInsertTargeted(b *testing.B) { - b.ReportAllocs() - - s := NewTargeted(Targets) - b.ResetTimer() - for i := float64(0); i < float64(b.N); i++ { - s.Insert(i) - } -} - -func BenchmarkInsertTargetedSmallEpsilon(b *testing.B) { - s := NewTargeted(TargetsSmallEpsilon) - b.ResetTimer() - for i := float64(0); i < float64(b.N); i++ { - s.Insert(i) - } -} - -func BenchmarkInsertBiased(b *testing.B) { - s := NewLowBiased(0.01) - b.ResetTimer() - for i := float64(0); i < float64(b.N); i++ { - s.Insert(i) - } -} - -func BenchmarkInsertBiasedSmallEpsilon(b *testing.B) { - s := NewLowBiased(0.0001) - b.ResetTimer() - for i := float64(0); i < float64(b.N); i++ { - s.Insert(i) - } -} - -func BenchmarkQuery(b *testing.B) { - s := NewTargeted(Targets) - for i := float64(0); i < 1e6; i++ { - s.Insert(i) - } - b.ResetTimer() - n := float64(b.N) - for i := float64(0); i < n; i++ { - s.Query(i / n) - } -} - -func BenchmarkQuerySmallEpsilon(b *testing.B) { - s := NewTargeted(TargetsSmallEpsilon) - for i := float64(0); i < 1e6; i++ { - s.Insert(i) - } - b.ResetTimer() - n := float64(b.N) - for i := float64(0); i < n; i++ { - s.Query(i / n) - } -} diff --git a/Godeps/_workspace/src/github.com/beorn7/perks/quantile/example_test.go b/Godeps/_workspace/src/github.com/beorn7/perks/quantile/example_test.go deleted file mode 100644 index ab3293aaf..000000000 --- a/Godeps/_workspace/src/github.com/beorn7/perks/quantile/example_test.go +++ /dev/null @@ -1,121 +0,0 @@ -// +build go1.1 - -package quantile_test - -import ( - "bufio" - "fmt" - "log" - "os" - "strconv" - "time" - - "github.com/beorn7/perks/quantile" -) - -func Example_simple() { - ch := make(chan float64) - go sendFloats(ch) - - // Compute the 50th, 90th, and 99th percentile. - q := quantile.NewTargeted(map[float64]float64{ - 0.50: 0.005, - 0.90: 0.001, - 0.99: 0.0001, - }) - for v := range ch { - q.Insert(v) - } - - fmt.Println("perc50:", q.Query(0.50)) - fmt.Println("perc90:", q.Query(0.90)) - fmt.Println("perc99:", q.Query(0.99)) - fmt.Println("count:", q.Count()) - // Output: - // perc50: 5 - // perc90: 16 - // perc99: 223 - // count: 2388 -} - -func Example_mergeMultipleStreams() { - // Scenario: - // We have multiple database shards. On each shard, there is a process - // collecting query response times from the database logs and inserting - // them into a Stream (created via NewTargeted(0.90)), much like the - // Simple example. These processes expose a network interface for us to - // ask them to serialize and send us the results of their - // Stream.Samples so we may Merge and Query them. - // - // NOTES: - // * These sample sets are small, allowing us to get them - // across the network much faster than sending the entire list of data - // points. - // - // * For this to work correctly, we must supply the same quantiles - // a priori the process collecting the samples supplied to NewTargeted, - // even if we do not plan to query them all here. - ch := make(chan quantile.Samples) - getDBQuerySamples(ch) - q := quantile.NewTargeted(map[float64]float64{0.90: 0.001}) - for samples := range ch { - q.Merge(samples) - } - fmt.Println("perc90:", q.Query(0.90)) -} - -func Example_window() { - // Scenario: We want the 90th, 95th, and 99th percentiles for each - // minute. - - ch := make(chan float64) - go sendStreamValues(ch) - - tick := time.NewTicker(1 * time.Minute) - q := quantile.NewTargeted(map[float64]float64{ - 0.90: 0.001, - 0.95: 0.0005, - 0.99: 0.0001, - }) - for { - select { - case t := <-tick.C: - flushToDB(t, q.Samples()) - q.Reset() - case v := <-ch: - q.Insert(v) - } - } -} - -func sendStreamValues(ch chan float64) { - // Use your imagination -} - -func flushToDB(t time.Time, samples quantile.Samples) { - // Use your imagination -} - -// This is a stub for the above example. In reality this would hit the remote -// servers via http or something like it. -func getDBQuerySamples(ch chan quantile.Samples) {} - -func sendFloats(ch chan<- float64) { - f, err := os.Open("exampledata.txt") - if err != nil { - log.Fatal(err) - } - sc := bufio.NewScanner(f) - for sc.Scan() { - b := sc.Bytes() - v, err := strconv.ParseFloat(string(b), 64) - if err != nil { - log.Fatal(err) - } - ch <- v - } - if sc.Err() != nil { - log.Fatal(sc.Err()) - } - close(ch) -} diff --git a/Godeps/_workspace/src/github.com/beorn7/perks/quantile/stream_test.go b/Godeps/_workspace/src/github.com/beorn7/perks/quantile/stream_test.go deleted file mode 100644 index 4dba05449..000000000 --- a/Godeps/_workspace/src/github.com/beorn7/perks/quantile/stream_test.go +++ /dev/null @@ -1,188 +0,0 @@ -package quantile - -import ( - "math" - "math/rand" - "sort" - "testing" -) - -var ( - Targets = map[float64]float64{ - 0.01: 0.001, - 0.10: 0.01, - 0.50: 0.05, - 0.90: 0.01, - 0.99: 0.001, - } - TargetsSmallEpsilon = map[float64]float64{ - 0.01: 0.0001, - 0.10: 0.001, - 0.50: 0.005, - 0.90: 0.001, - 0.99: 0.0001, - } - LowQuantiles = []float64{0.01, 0.1, 0.5} - HighQuantiles = []float64{0.99, 0.9, 0.5} -) - -const RelativeEpsilon = 0.01 - -func verifyPercsWithAbsoluteEpsilon(t *testing.T, a []float64, s *Stream) { - sort.Float64s(a) - for quantile, epsilon := range Targets { - n := float64(len(a)) - k := int(quantile * n) - lower := int((quantile - epsilon) * n) - if lower < 1 { - lower = 1 - } - upper := int(math.Ceil((quantile + epsilon) * n)) - if upper > len(a) { - upper = len(a) - } - w, min, max := a[k-1], a[lower-1], a[upper-1] - if g := s.Query(quantile); g < min || g > max { - t.Errorf("q=%f: want %v [%f,%f], got %v", quantile, w, min, max, g) - } - } -} - -func verifyLowPercsWithRelativeEpsilon(t *testing.T, a []float64, s *Stream) { - sort.Float64s(a) - for _, qu := range LowQuantiles { - n := float64(len(a)) - k := int(qu * n) - - lowerRank := int((1 - RelativeEpsilon) * qu * n) - upperRank := int(math.Ceil((1 + RelativeEpsilon) * qu * n)) - w, min, max := a[k-1], a[lowerRank-1], a[upperRank-1] - if g := s.Query(qu); g < min || g > max { - t.Errorf("q=%f: want %v [%f,%f], got %v", qu, w, min, max, g) - } - } -} - -func verifyHighPercsWithRelativeEpsilon(t *testing.T, a []float64, s *Stream) { - sort.Float64s(a) - for _, qu := range HighQuantiles { - n := float64(len(a)) - k := int(qu * n) - - lowerRank := int((1 - (1+RelativeEpsilon)*(1-qu)) * n) - upperRank := int(math.Ceil((1 - (1-RelativeEpsilon)*(1-qu)) * n)) - w, min, max := a[k-1], a[lowerRank-1], a[upperRank-1] - if g := s.Query(qu); g < min || g > max { - t.Errorf("q=%f: want %v [%f,%f], got %v", qu, w, min, max, g) - } - } -} - -func populateStream(s *Stream) []float64 { - a := make([]float64, 0, 1e5+100) - for i := 0; i < cap(a); i++ { - v := rand.NormFloat64() - // Add 5% asymmetric outliers. - if i%20 == 0 { - v = v*v + 1 - } - s.Insert(v) - a = append(a, v) - } - return a -} - -func TestTargetedQuery(t *testing.T) { - rand.Seed(42) - s := NewTargeted(Targets) - a := populateStream(s) - verifyPercsWithAbsoluteEpsilon(t, a, s) -} - -func TestLowBiasedQuery(t *testing.T) { - rand.Seed(42) - s := NewLowBiased(RelativeEpsilon) - a := populateStream(s) - verifyLowPercsWithRelativeEpsilon(t, a, s) -} - -func TestHighBiasedQuery(t *testing.T) { - rand.Seed(42) - s := NewHighBiased(RelativeEpsilon) - a := populateStream(s) - verifyHighPercsWithRelativeEpsilon(t, a, s) -} - -// BrokenTestTargetedMerge is broken, see Merge doc comment. -func BrokenTestTargetedMerge(t *testing.T) { - rand.Seed(42) - s1 := NewTargeted(Targets) - s2 := NewTargeted(Targets) - a := populateStream(s1) - a = append(a, populateStream(s2)...) - s1.Merge(s2.Samples()) - verifyPercsWithAbsoluteEpsilon(t, a, s1) -} - -// BrokenTestLowBiasedMerge is broken, see Merge doc comment. -func BrokenTestLowBiasedMerge(t *testing.T) { - rand.Seed(42) - s1 := NewLowBiased(RelativeEpsilon) - s2 := NewLowBiased(RelativeEpsilon) - a := populateStream(s1) - a = append(a, populateStream(s2)...) - s1.Merge(s2.Samples()) - verifyLowPercsWithRelativeEpsilon(t, a, s2) -} - -// BrokenTestHighBiasedMerge is broken, see Merge doc comment. -func BrokenTestHighBiasedMerge(t *testing.T) { - rand.Seed(42) - s1 := NewHighBiased(RelativeEpsilon) - s2 := NewHighBiased(RelativeEpsilon) - a := populateStream(s1) - a = append(a, populateStream(s2)...) - s1.Merge(s2.Samples()) - verifyHighPercsWithRelativeEpsilon(t, a, s2) -} - -func TestUncompressed(t *testing.T) { - q := NewTargeted(Targets) - for i := 100; i > 0; i-- { - q.Insert(float64(i)) - } - if g := q.Count(); g != 100 { - t.Errorf("want count 100, got %d", g) - } - // Before compression, Query should have 100% accuracy. - for quantile := range Targets { - w := quantile * 100 - if g := q.Query(quantile); g != w { - t.Errorf("want %f, got %f", w, g) - } - } -} - -func TestUncompressedSamples(t *testing.T) { - q := NewTargeted(map[float64]float64{0.99: 0.001}) - for i := 1; i <= 100; i++ { - q.Insert(float64(i)) - } - if g := q.Samples().Len(); g != 100 { - t.Errorf("want count 100, got %d", g) - } -} - -func TestUncompressedOne(t *testing.T) { - q := NewTargeted(map[float64]float64{0.99: 0.01}) - q.Insert(3.14) - if g := q.Query(0.90); g != 3.14 { - t.Error("want PI, got", g) - } -} - -func TestDefaults(t *testing.T) { - if g := NewTargeted(map[float64]float64{0.99: 0.001}).Query(0.99); g != 0 { - t.Errorf("want 0, got %f", g) - } -} diff --git a/Godeps/_workspace/src/github.com/blang/semver/examples/main.go b/Godeps/_workspace/src/github.com/blang/semver/examples/main.go deleted file mode 100644 index f36c983d9..000000000 --- a/Godeps/_workspace/src/github.com/blang/semver/examples/main.go +++ /dev/null @@ -1,83 +0,0 @@ -package main - -import ( - "fmt" - "github.com/blang/semver" -) - -func main() { - v, err := semver.Parse("0.0.1-alpha.preview.222+123.github") - if err != nil { - fmt.Printf("Error while parsing (not valid): %q", err) - } - fmt.Printf("Version to string: %q\n", v) - - fmt.Printf("Major: %d\n", v.Major) - fmt.Printf("Minor: %d\n", v.Minor) - fmt.Printf("Patch: %d\n", v.Patch) - - // Prerelease versions - if len(v.Pre) > 0 { - fmt.Println("Prerelease versions:") - for i, pre := range v.Pre { - fmt.Printf("%d: %q\n", i, pre) - } - } - - // Build meta data - if len(v.Build) > 0 { - fmt.Println("Build meta data:") - for i, build := range v.Build { - fmt.Printf("%d: %q\n", i, build) - } - } - - // Make == Parse (Value), New for Pointer - v001, err := semver.Make("0.0.1") - - fmt.Println("\nUse Version.Compare for comparisons (-1, 0, 1):") - fmt.Printf("%q is greater than %q: Compare == %d\n", v001, v, v001.Compare(v)) - fmt.Printf("%q is less than %q: Compare == %d\n", v, v001, v.Compare(v001)) - fmt.Printf("%q is equal to %q: Compare == %d\n", v, v, v.Compare(v)) - - fmt.Println("\nUse comparison helpers returning booleans:") - fmt.Printf("%q is greater than %q: %t\n", v001, v, v001.GT(v)) - fmt.Printf("%q is greater than equal %q: %t\n", v001, v, v001.GTE(v)) - fmt.Printf("%q is greater than equal %q: %t\n", v, v, v.GTE(v)) - fmt.Printf("%q is less than %q: %t\n", v, v001, v.LT(v001)) - fmt.Printf("%q is less than equal %q: %t\n", v, v001, v.LTE(v001)) - fmt.Printf("%q is less than equal %q: %t\n", v, v, v.LTE(v)) - - fmt.Println("\nManipulate Version in place:") - v.Pre[0], err = semver.NewPRVersion("beta") - if err != nil { - fmt.Printf("Error parsing pre release version: %q", err) - } - fmt.Printf("Version to string: %q\n", v) - - fmt.Println("\nCompare Prerelease versions:") - pre1, _ := semver.NewPRVersion("123") - pre2, _ := semver.NewPRVersion("alpha") - pre3, _ := semver.NewPRVersion("124") - fmt.Printf("%q is less than %q: Compare == %d\n", pre1, pre2, pre1.Compare(pre2)) - fmt.Printf("%q is greater than %q: Compare == %d\n", pre3, pre1, pre3.Compare(pre1)) - fmt.Printf("%q is equal to %q: Compare == %d\n", pre1, pre1, pre1.Compare(pre1)) - - fmt.Println("\nValidate versions:") - v.Build[0] = "?" - - err = v.Validate() - if err != nil { - fmt.Printf("Validation failed: %s\n", err) - } - - fmt.Println("Create valid build meta data:") - b1, _ := semver.NewBuildVersion("build123") - v.Build[0] = b1 - fmt.Printf("Version with new build version %q\n", v) - - _, err = semver.NewBuildVersion("build?123") - if err != nil { - fmt.Printf("Create build version failed: %s\n", err) - } -} diff --git a/Godeps/_workspace/src/github.com/blang/semver/json_test.go b/Godeps/_workspace/src/github.com/blang/semver/json_test.go deleted file mode 100644 index 039117da9..000000000 --- a/Godeps/_workspace/src/github.com/blang/semver/json_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package semver - -import ( - "encoding/json" - "strconv" - "testing" -) - -func TestJSONMarshal(t *testing.T) { - versionString := "3.1.4-alpha.1.5.9+build.2.6.5" - v, err := Parse(versionString) - if err != nil { - t.Fatal(err) - } - - versionJSON, err := json.Marshal(v) - if err != nil { - t.Fatal(err) - } - - quotedVersionString := strconv.Quote(versionString) - - if string(versionJSON) != quotedVersionString { - t.Fatalf("JSON marshaled semantic version not equal: expected %q, got %q", quotedVersionString, string(versionJSON)) - } -} - -func TestJSONUnmarshal(t *testing.T) { - versionString := "3.1.4-alpha.1.5.9+build.2.6.5" - quotedVersionString := strconv.Quote(versionString) - - var v Version - if err := json.Unmarshal([]byte(quotedVersionString), &v); err != nil { - t.Fatal(err) - } - - if v.String() != versionString { - t.Fatalf("JSON unmarshaled semantic version not equal: expected %q, got %q", versionString, v.String()) - } - - badVersionString := strconv.Quote("3.1.4.1.5.9.2.6.5-other-digits-of-pi") - if err := json.Unmarshal([]byte(badVersionString), &v); err == nil { - t.Fatal("expected JSON unmarshal error, got nil") - } -} diff --git a/Godeps/_workspace/src/github.com/blang/semver/semver_test.go b/Godeps/_workspace/src/github.com/blang/semver/semver_test.go deleted file mode 100644 index e56ebce05..000000000 --- a/Godeps/_workspace/src/github.com/blang/semver/semver_test.go +++ /dev/null @@ -1,417 +0,0 @@ -package semver - -import ( - "testing" -) - -func prstr(s string) PRVersion { - return PRVersion{s, 0, false} -} - -func prnum(i uint64) PRVersion { - return PRVersion{"", i, true} -} - -type formatTest struct { - v Version - result string -} - -var formatTests = []formatTest{ - {Version{1, 2, 3, nil, nil}, "1.2.3"}, - {Version{0, 0, 1, nil, nil}, "0.0.1"}, - {Version{0, 0, 1, []PRVersion{prstr("alpha"), prstr("preview")}, []string{"123", "456"}}, "0.0.1-alpha.preview+123.456"}, - {Version{1, 2, 3, []PRVersion{prstr("alpha"), prnum(1)}, []string{"123", "456"}}, "1.2.3-alpha.1+123.456"}, - {Version{1, 2, 3, []PRVersion{prstr("alpha"), prnum(1)}, nil}, "1.2.3-alpha.1"}, - {Version{1, 2, 3, nil, []string{"123", "456"}}, "1.2.3+123.456"}, - // Prereleases and build metadata hyphens - {Version{1, 2, 3, []PRVersion{prstr("alpha"), prstr("b-eta")}, []string{"123", "b-uild"}}, "1.2.3-alpha.b-eta+123.b-uild"}, - {Version{1, 2, 3, nil, []string{"123", "b-uild"}}, "1.2.3+123.b-uild"}, - {Version{1, 2, 3, []PRVersion{prstr("alpha"), prstr("b-eta")}, nil}, "1.2.3-alpha.b-eta"}, -} - -func TestStringer(t *testing.T) { - for _, test := range formatTests { - if res := test.v.String(); res != test.result { - t.Errorf("Stringer, expected %q but got %q", test.result, res) - } - } -} - -func TestParse(t *testing.T) { - for _, test := range formatTests { - if v, err := Parse(test.result); err != nil { - t.Errorf("Error parsing %q: %q", test.result, err) - } else if comp := v.Compare(test.v); comp != 0 { - t.Errorf("Parsing, expected %q but got %q, comp: %d ", test.v, v, comp) - } else if err := v.Validate(); err != nil { - t.Errorf("Error validating parsed version %q: %q", test.v, err) - } - } -} - -func TestMustParse(t *testing.T) { - _ = MustParse("32.2.1-alpha") -} - -func TestMustParse_panic(t *testing.T) { - defer func() { - if recover() == nil { - t.Errorf("Should have panicked") - } - }() - _ = MustParse("invalid version") -} - -func TestValidate(t *testing.T) { - for _, test := range formatTests { - if err := test.v.Validate(); err != nil { - t.Errorf("Error validating %q: %q", test.v, err) - } - } -} - -type compareTest struct { - v1 Version - v2 Version - result int -} - -var compareTests = []compareTest{ - {Version{1, 0, 0, nil, nil}, Version{1, 0, 0, nil, nil}, 0}, - {Version{2, 0, 0, nil, nil}, Version{1, 0, 0, nil, nil}, 1}, - {Version{0, 1, 0, nil, nil}, Version{0, 1, 0, nil, nil}, 0}, - {Version{0, 2, 0, nil, nil}, Version{0, 1, 0, nil, nil}, 1}, - {Version{0, 0, 1, nil, nil}, Version{0, 0, 1, nil, nil}, 0}, - {Version{0, 0, 2, nil, nil}, Version{0, 0, 1, nil, nil}, 1}, - {Version{1, 2, 3, nil, nil}, Version{1, 2, 3, nil, nil}, 0}, - {Version{2, 2, 4, nil, nil}, Version{1, 2, 4, nil, nil}, 1}, - {Version{1, 3, 3, nil, nil}, Version{1, 2, 3, nil, nil}, 1}, - {Version{1, 2, 4, nil, nil}, Version{1, 2, 3, nil, nil}, 1}, - - // Spec Examples #11 - {Version{1, 0, 0, nil, nil}, Version{2, 0, 0, nil, nil}, -1}, - {Version{2, 0, 0, nil, nil}, Version{2, 1, 0, nil, nil}, -1}, - {Version{2, 1, 0, nil, nil}, Version{2, 1, 1, nil, nil}, -1}, - - // Spec Examples #9 - {Version{1, 0, 0, nil, nil}, Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil}, 1}, - {Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil}, Version{1, 0, 0, []PRVersion{prstr("alpha"), prnum(1)}, nil}, -1}, - {Version{1, 0, 0, []PRVersion{prstr("alpha"), prnum(1)}, nil}, Version{1, 0, 0, []PRVersion{prstr("alpha"), prstr("beta")}, nil}, -1}, - {Version{1, 0, 0, []PRVersion{prstr("alpha"), prstr("beta")}, nil}, Version{1, 0, 0, []PRVersion{prstr("beta")}, nil}, -1}, - {Version{1, 0, 0, []PRVersion{prstr("beta")}, nil}, Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(2)}, nil}, -1}, - {Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(2)}, nil}, Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(11)}, nil}, -1}, - {Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(11)}, nil}, Version{1, 0, 0, []PRVersion{prstr("rc"), prnum(1)}, nil}, -1}, - {Version{1, 0, 0, []PRVersion{prstr("rc"), prnum(1)}, nil}, Version{1, 0, 0, nil, nil}, -1}, - - // Ignore Build metadata - {Version{1, 0, 0, nil, []string{"1", "2", "3"}}, Version{1, 0, 0, nil, nil}, 0}, -} - -func TestCompare(t *testing.T) { - for _, test := range compareTests { - if res := test.v1.Compare(test.v2); res != test.result { - t.Errorf("Comparing %q : %q, expected %d but got %d", test.v1, test.v2, test.result, res) - } - //Test counterpart - if res := test.v2.Compare(test.v1); res != -test.result { - t.Errorf("Comparing %q : %q, expected %d but got %d", test.v2, test.v1, -test.result, res) - } - } -} - -type wrongformatTest struct { - v *Version - str string -} - -var wrongformatTests = []wrongformatTest{ - {nil, ""}, - {nil, "."}, - {nil, "1."}, - {nil, ".1"}, - {nil, "a.b.c"}, - {nil, "1.a.b"}, - {nil, "1.1.a"}, - {nil, "1.a.1"}, - {nil, "a.1.1"}, - {nil, ".."}, - {nil, "1.."}, - {nil, "1.1."}, - {nil, "1..1"}, - {nil, "1.1.+123"}, - {nil, "1.1.-beta"}, - {nil, "-1.1.1"}, - {nil, "1.-1.1"}, - {nil, "1.1.-1"}, - // giant numbers - {nil, "20000000000000000000.1.1"}, - {nil, "1.20000000000000000000.1"}, - {nil, "1.1.20000000000000000000"}, - {nil, "1.1.1-20000000000000000000"}, - // Leading zeroes - {nil, "01.1.1"}, - {nil, "001.1.1"}, - {nil, "1.01.1"}, - {nil, "1.001.1"}, - {nil, "1.1.01"}, - {nil, "1.1.001"}, - {nil, "1.1.1-01"}, - {nil, "1.1.1-001"}, - {nil, "1.1.1-beta.01"}, - {nil, "1.1.1-beta.001"}, - {&Version{0, 0, 0, []PRVersion{prstr("!")}, nil}, "0.0.0-!"}, - {&Version{0, 0, 0, nil, []string{"!"}}, "0.0.0+!"}, - // empty prversion - {&Version{0, 0, 0, []PRVersion{prstr(""), prstr("alpha")}, nil}, "0.0.0-.alpha"}, - // empty build meta data - {&Version{0, 0, 0, []PRVersion{prstr("alpha")}, []string{""}}, "0.0.0-alpha+"}, - {&Version{0, 0, 0, []PRVersion{prstr("alpha")}, []string{"test", ""}}, "0.0.0-alpha+test."}, -} - -func TestWrongFormat(t *testing.T) { - for _, test := range wrongformatTests { - - if res, err := Parse(test.str); err == nil { - t.Errorf("Parsing wrong format version %q, expected error but got %q", test.str, res) - } - - if test.v != nil { - if err := test.v.Validate(); err == nil { - t.Errorf("Validating wrong format version %q (%q), expected error", test.v, test.str) - } - } - } -} - -func TestCompareHelper(t *testing.T) { - v := Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil} - v1 := Version{1, 0, 0, nil, nil} - if !v.EQ(v) { - t.Errorf("%q should be equal to %q", v, v) - } - if !v.Equals(v) { - t.Errorf("%q should be equal to %q", v, v) - } - if !v1.NE(v) { - t.Errorf("%q should not be equal to %q", v1, v) - } - if !v.GTE(v) { - t.Errorf("%q should be greater than or equal to %q", v, v) - } - if !v.LTE(v) { - t.Errorf("%q should be less than or equal to %q", v, v) - } - if !v.LT(v1) { - t.Errorf("%q should be less than %q", v, v1) - } - if !v.LTE(v1) { - t.Errorf("%q should be less than or equal %q", v, v1) - } - if !v.LE(v1) { - t.Errorf("%q should be less than or equal %q", v, v1) - } - if !v1.GT(v) { - t.Errorf("%q should be greater than %q", v1, v) - } - if !v1.GTE(v) { - t.Errorf("%q should be greater than or equal %q", v1, v) - } - if !v1.GE(v) { - t.Errorf("%q should be greater than or equal %q", v1, v) - } -} - -func TestPreReleaseVersions(t *testing.T) { - p1, err := NewPRVersion("123") - if !p1.IsNumeric() { - t.Errorf("Expected numeric prversion, got %q", p1) - } - if p1.VersionNum != 123 { - t.Error("Wrong prversion number") - } - if err != nil { - t.Errorf("Not expected error %q", err) - } - p2, err := NewPRVersion("alpha") - if p2.IsNumeric() { - t.Errorf("Expected non-numeric prversion, got %q", p2) - } - if p2.VersionStr != "alpha" { - t.Error("Wrong prversion string") - } - if err != nil { - t.Errorf("Not expected error %q", err) - } -} - -func TestBuildMetaDataVersions(t *testing.T) { - _, err := NewBuildVersion("123") - if err != nil { - t.Errorf("Unexpected error %q", err) - } - - _, err = NewBuildVersion("build") - if err != nil { - t.Errorf("Unexpected error %q", err) - } - - _, err = NewBuildVersion("test?") - if err == nil { - t.Error("Expected error, got none") - } - - _, err = NewBuildVersion("") - if err == nil { - t.Error("Expected error, got none") - } -} - -func TestNewHelper(t *testing.T) { - v, err := New("1.2.3") - if err != nil { - t.Fatalf("Unexpected error %q", err) - } - - // New returns pointer - if v == nil { - t.Fatal("Version is nil") - } - if v.Compare(Version{1, 2, 3, nil, nil}) != 0 { - t.Fatal("Unexpected comparison problem") - } -} - -func TestMakeHelper(t *testing.T) { - v, err := Make("1.2.3") - if err != nil { - t.Fatalf("Unexpected error %q", err) - } - if v.Compare(Version{1, 2, 3, nil, nil}) != 0 { - t.Fatal("Unexpected comparison problem") - } -} - -func BenchmarkParseSimple(b *testing.B) { - const VERSION = "0.0.1" - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - Parse(VERSION) - } -} - -func BenchmarkParseComplex(b *testing.B) { - const VERSION = "0.0.1-alpha.preview+123.456" - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - Parse(VERSION) - } -} - -func BenchmarkParseAverage(b *testing.B) { - l := len(formatTests) - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - Parse(formatTests[n%l].result) - } -} - -func BenchmarkStringSimple(b *testing.B) { - const VERSION = "0.0.1" - v, _ := Parse(VERSION) - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - v.String() - } -} - -func BenchmarkStringLarger(b *testing.B) { - const VERSION = "11.15.2012" - v, _ := Parse(VERSION) - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - v.String() - } -} - -func BenchmarkStringComplex(b *testing.B) { - const VERSION = "0.0.1-alpha.preview+123.456" - v, _ := Parse(VERSION) - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - v.String() - } -} - -func BenchmarkStringAverage(b *testing.B) { - l := len(formatTests) - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - formatTests[n%l].v.String() - } -} - -func BenchmarkValidateSimple(b *testing.B) { - const VERSION = "0.0.1" - v, _ := Parse(VERSION) - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - v.Validate() - } -} - -func BenchmarkValidateComplex(b *testing.B) { - const VERSION = "0.0.1-alpha.preview+123.456" - v, _ := Parse(VERSION) - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - v.Validate() - } -} - -func BenchmarkValidateAverage(b *testing.B) { - l := len(formatTests) - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - formatTests[n%l].v.Validate() - } -} - -func BenchmarkCompareSimple(b *testing.B) { - const VERSION = "0.0.1" - v, _ := Parse(VERSION) - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - v.Compare(v) - } -} - -func BenchmarkCompareComplex(b *testing.B) { - const VERSION = "0.0.1-alpha.preview+123.456" - v, _ := Parse(VERSION) - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - v.Compare(v) - } -} - -func BenchmarkCompareAverage(b *testing.B) { - l := len(compareTests) - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - compareTests[n%l].v1.Compare((compareTests[n%l].v2)) - } -} diff --git a/Godeps/_workspace/src/github.com/blang/semver/sort_test.go b/Godeps/_workspace/src/github.com/blang/semver/sort_test.go deleted file mode 100644 index 68893972a..000000000 --- a/Godeps/_workspace/src/github.com/blang/semver/sort_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package semver - -import ( - "reflect" - "testing" -) - -func TestSort(t *testing.T) { - v100, _ := Parse("1.0.0") - v010, _ := Parse("0.1.0") - v001, _ := Parse("0.0.1") - versions := []Version{v010, v100, v001} - Sort(versions) - - correct := []Version{v001, v010, v100} - if !reflect.DeepEqual(versions, correct) { - t.Fatalf("Sort returned wrong order: %s", versions) - } -} - -func BenchmarkSort(b *testing.B) { - v100, _ := Parse("1.0.0") - v010, _ := Parse("0.1.0") - v001, _ := Parse("0.0.1") - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - Sort([]Version{v010, v100, v001}) - } -} diff --git a/Godeps/_workspace/src/github.com/blang/semver/sql_test.go b/Godeps/_workspace/src/github.com/blang/semver/sql_test.go deleted file mode 100644 index ebf48b584..000000000 --- a/Godeps/_workspace/src/github.com/blang/semver/sql_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package semver - -import ( - "testing" -) - -type scanTest struct { - val interface{} - shouldError bool - expected string -} - -var scanTests = []scanTest{ - {"1.2.3", false, "1.2.3"}, - {[]byte("1.2.3"), false, "1.2.3"}, - {7, true, ""}, - {7e4, true, ""}, - {true, true, ""}, -} - -func TestScanString(t *testing.T) { - for _, tc := range scanTests { - s := &Version{} - err := s.Scan(tc.val) - if tc.shouldError { - if err == nil { - t.Fatalf("Scan did not return an error on %v (%T)", tc.val, tc.val) - } - } else { - if err != nil { - t.Fatalf("Scan returned an unexpected error: %s (%T) on %v (%T)", tc.val, tc.val, tc.val, tc.val) - } - if val, _ := s.Value(); val != tc.expected { - t.Errorf("Wrong Value returned, expected %q, got %q", tc.expected, val) - } - } - } -} diff --git a/Godeps/_workspace/src/github.com/cpuguy83/go-md2man/LICENSE.md b/Godeps/_workspace/src/github.com/cpuguy83/go-md2man/LICENSE.md new file mode 100644 index 000000000..1cade6cef --- /dev/null +++ b/Godeps/_workspace/src/github.com/cpuguy83/go-md2man/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Brian Goff + +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/Godeps/_workspace/src/github.com/davecgh/go-spew/LICENSE b/Godeps/_workspace/src/github.com/davecgh/go-spew/LICENSE new file mode 100644 index 000000000..2a7cfd2bf --- /dev/null +++ b/Godeps/_workspace/src/github.com/davecgh/go-spew/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2012-2013 Dave Collins + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/common_test.go b/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/common_test.go deleted file mode 100644 index 39b7525b3..000000000 --- a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/common_test.go +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (c) 2013 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package spew_test - -import ( - "fmt" - "reflect" - "testing" - - "github.com/davecgh/go-spew/spew" -) - -// custom type to test Stinger interface on non-pointer receiver. -type stringer string - -// String implements the Stringer interface for testing invocation of custom -// stringers on types with non-pointer receivers. -func (s stringer) String() string { - return "stringer " + string(s) -} - -// custom type to test Stinger interface on pointer receiver. -type pstringer string - -// String implements the Stringer interface for testing invocation of custom -// stringers on types with only pointer receivers. -func (s *pstringer) String() string { - return "stringer " + string(*s) -} - -// xref1 and xref2 are cross referencing structs for testing circular reference -// detection. -type xref1 struct { - ps2 *xref2 -} -type xref2 struct { - ps1 *xref1 -} - -// indirCir1, indirCir2, and indirCir3 are used to generate an indirect circular -// reference for testing detection. -type indirCir1 struct { - ps2 *indirCir2 -} -type indirCir2 struct { - ps3 *indirCir3 -} -type indirCir3 struct { - ps1 *indirCir1 -} - -// embed is used to test embedded structures. -type embed struct { - a string -} - -// embedwrap is used to test embedded structures. -type embedwrap struct { - *embed - e *embed -} - -// panicer is used to intentionally cause a panic for testing spew properly -// handles them -type panicer int - -func (p panicer) String() string { - panic("test panic") -} - -// customError is used to test custom error interface invocation. -type customError int - -func (e customError) Error() string { - return fmt.Sprintf("error: %d", int(e)) -} - -// stringizeWants converts a slice of wanted test output into a format suitable -// for a test error message. -func stringizeWants(wants []string) string { - s := "" - for i, want := range wants { - if i > 0 { - s += fmt.Sprintf("want%d: %s", i+1, want) - } else { - s += "want: " + want - } - } - return s -} - -// testFailed returns whether or not a test failed by checking if the result -// of the test is in the slice of wanted strings. -func testFailed(result string, wants []string) bool { - for _, want := range wants { - if result == want { - return false - } - } - return true -} - -type sortableStruct struct { - x int -} - -func (ss sortableStruct) String() string { - return fmt.Sprintf("ss.%d", ss.x) -} - -type unsortableStruct struct { - x int -} - -type sortTestCase struct { - input []reflect.Value - expected []reflect.Value -} - -func helpTestSortValues(tests []sortTestCase, cs *spew.ConfigState, t *testing.T) { - getInterfaces := func(values []reflect.Value) []interface{} { - interfaces := []interface{}{} - for _, v := range values { - interfaces = append(interfaces, v.Interface()) - } - return interfaces - } - - for _, test := range tests { - spew.SortValues(test.input, cs) - // reflect.DeepEqual cannot really make sense of reflect.Value, - // probably because of all the pointer tricks. For instance, - // v(2.0) != v(2.0) on a 32-bits system. Turn them into interface{} - // instead. - input := getInterfaces(test.input) - expected := getInterfaces(test.expected) - if !reflect.DeepEqual(input, expected) { - t.Errorf("Sort mismatch:\n %v != %v", input, expected) - } - } -} - -// TestSortValues ensures the sort functionality for relect.Value based sorting -// works as intended. -func TestSortValues(t *testing.T) { - v := reflect.ValueOf - - a := v("a") - b := v("b") - c := v("c") - embedA := v(embed{"a"}) - embedB := v(embed{"b"}) - embedC := v(embed{"c"}) - tests := []sortTestCase{ - // No values. - { - []reflect.Value{}, - []reflect.Value{}, - }, - // Bools. - { - []reflect.Value{v(false), v(true), v(false)}, - []reflect.Value{v(false), v(false), v(true)}, - }, - // Ints. - { - []reflect.Value{v(2), v(1), v(3)}, - []reflect.Value{v(1), v(2), v(3)}, - }, - // Uints. - { - []reflect.Value{v(uint8(2)), v(uint8(1)), v(uint8(3))}, - []reflect.Value{v(uint8(1)), v(uint8(2)), v(uint8(3))}, - }, - // Floats. - { - []reflect.Value{v(2.0), v(1.0), v(3.0)}, - []reflect.Value{v(1.0), v(2.0), v(3.0)}, - }, - // Strings. - { - []reflect.Value{b, a, c}, - []reflect.Value{a, b, c}, - }, - // Array - { - []reflect.Value{v([3]int{3, 2, 1}), v([3]int{1, 3, 2}), v([3]int{1, 2, 3})}, - []reflect.Value{v([3]int{1, 2, 3}), v([3]int{1, 3, 2}), v([3]int{3, 2, 1})}, - }, - // Uintptrs. - { - []reflect.Value{v(uintptr(2)), v(uintptr(1)), v(uintptr(3))}, - []reflect.Value{v(uintptr(1)), v(uintptr(2)), v(uintptr(3))}, - }, - // SortableStructs. - { - // Note: not sorted - DisableMethods is set. - []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})}, - []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})}, - }, - // UnsortableStructs. - { - // Note: not sorted - SpewKeys is false. - []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})}, - []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})}, - }, - // Invalid. - { - []reflect.Value{embedB, embedA, embedC}, - []reflect.Value{embedB, embedA, embedC}, - }, - } - cs := spew.ConfigState{DisableMethods: true, SpewKeys: false} - helpTestSortValues(tests, &cs, t) -} - -// TestSortValuesWithMethods ensures the sort functionality for relect.Value -// based sorting works as intended when using string methods. -func TestSortValuesWithMethods(t *testing.T) { - v := reflect.ValueOf - - a := v("a") - b := v("b") - c := v("c") - tests := []sortTestCase{ - // Ints. - { - []reflect.Value{v(2), v(1), v(3)}, - []reflect.Value{v(1), v(2), v(3)}, - }, - // Strings. - { - []reflect.Value{b, a, c}, - []reflect.Value{a, b, c}, - }, - // SortableStructs. - { - []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})}, - []reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})}, - }, - // UnsortableStructs. - { - // Note: not sorted - SpewKeys is false. - []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})}, - []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})}, - }, - } - cs := spew.ConfigState{DisableMethods: false, SpewKeys: false} - helpTestSortValues(tests, &cs, t) -} - -// TestSortValuesWithSpew ensures the sort functionality for relect.Value -// based sorting works as intended when using spew to stringify keys. -func TestSortValuesWithSpew(t *testing.T) { - v := reflect.ValueOf - - a := v("a") - b := v("b") - c := v("c") - tests := []sortTestCase{ - // Ints. - { - []reflect.Value{v(2), v(1), v(3)}, - []reflect.Value{v(1), v(2), v(3)}, - }, - // Strings. - { - []reflect.Value{b, a, c}, - []reflect.Value{a, b, c}, - }, - // SortableStructs. - { - []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})}, - []reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})}, - }, - // UnsortableStructs. - { - []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})}, - []reflect.Value{v(unsortableStruct{1}), v(unsortableStruct{2}), v(unsortableStruct{3})}, - }, - } - cs := spew.ConfigState{DisableMethods: true, SpewKeys: true} - helpTestSortValues(tests, &cs, t) -} diff --git a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/dump_test.go b/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/dump_test.go deleted file mode 100644 index 3dd908917..000000000 --- a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/dump_test.go +++ /dev/null @@ -1,1021 +0,0 @@ -/* - * Copyright (c) 2013 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* -Test Summary: -NOTE: For each test, a nil pointer, a single pointer and double pointer to the -base test element are also tested to ensure proper indirection across all types. - -- Max int8, int16, int32, int64, int -- Max uint8, uint16, uint32, uint64, uint -- Boolean true and false -- Standard complex64 and complex128 -- Array containing standard ints -- Array containing type with custom formatter on pointer receiver only -- Array containing interfaces -- Array containing bytes -- Slice containing standard float32 values -- Slice containing type with custom formatter on pointer receiver only -- Slice containing interfaces -- Slice containing bytes -- Nil slice -- Standard string -- Nil interface -- Sub-interface -- Map with string keys and int vals -- Map with custom formatter type on pointer receiver only keys and vals -- Map with interface keys and values -- Map with nil interface value -- Struct with primitives -- Struct that contains another struct -- Struct that contains custom type with Stringer pointer interface via both - exported and unexported fields -- Struct that contains embedded struct and field to same struct -- Uintptr to 0 (null pointer) -- Uintptr address of real variable -- Unsafe.Pointer to 0 (null pointer) -- Unsafe.Pointer to address of real variable -- Nil channel -- Standard int channel -- Function with no params and no returns -- Function with param and no returns -- Function with multiple params and multiple returns -- Struct that is circular through self referencing -- Structs that are circular through cross referencing -- Structs that are indirectly circular -- Type that panics in its Stringer interface -*/ - -package spew_test - -import ( - "bytes" - "fmt" - "testing" - "unsafe" - - "github.com/davecgh/go-spew/spew" -) - -// dumpTest is used to describe a test to be perfomed against the Dump method. -type dumpTest struct { - in interface{} - wants []string -} - -// dumpTests houses all of the tests to be performed against the Dump method. -var dumpTests = make([]dumpTest, 0) - -// addDumpTest is a helper method to append the passed input and desired result -// to dumpTests -func addDumpTest(in interface{}, wants ...string) { - test := dumpTest{in, wants} - dumpTests = append(dumpTests, test) -} - -func addIntDumpTests() { - // Max int8. - v := int8(127) - nv := (*int8)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "int8" - vs := "127" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") - - // Max int16. - v2 := int16(32767) - nv2 := (*int16)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "int16" - v2s := "32767" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*"+v2t+")()\n") - - // Max int32. - v3 := int32(2147483647) - nv3 := (*int32)(nil) - pv3 := &v3 - v3Addr := fmt.Sprintf("%p", pv3) - pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "int32" - v3s := "2147483647" - addDumpTest(v3, "("+v3t+") "+v3s+"\n") - addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") - addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") - addDumpTest(nv3, "(*"+v3t+")()\n") - - // Max int64. - v4 := int64(9223372036854775807) - nv4 := (*int64)(nil) - pv4 := &v4 - v4Addr := fmt.Sprintf("%p", pv4) - pv4Addr := fmt.Sprintf("%p", &pv4) - v4t := "int64" - v4s := "9223372036854775807" - addDumpTest(v4, "("+v4t+") "+v4s+"\n") - addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") - addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") - addDumpTest(nv4, "(*"+v4t+")()\n") - - // Max int. - v5 := int(2147483647) - nv5 := (*int)(nil) - pv5 := &v5 - v5Addr := fmt.Sprintf("%p", pv5) - pv5Addr := fmt.Sprintf("%p", &pv5) - v5t := "int" - v5s := "2147483647" - addDumpTest(v5, "("+v5t+") "+v5s+"\n") - addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n") - addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n") - addDumpTest(nv5, "(*"+v5t+")()\n") -} - -func addUintDumpTests() { - // Max uint8. - v := uint8(255) - nv := (*uint8)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "uint8" - vs := "255" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") - - // Max uint16. - v2 := uint16(65535) - nv2 := (*uint16)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "uint16" - v2s := "65535" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*"+v2t+")()\n") - - // Max uint32. - v3 := uint32(4294967295) - nv3 := (*uint32)(nil) - pv3 := &v3 - v3Addr := fmt.Sprintf("%p", pv3) - pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "uint32" - v3s := "4294967295" - addDumpTest(v3, "("+v3t+") "+v3s+"\n") - addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") - addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") - addDumpTest(nv3, "(*"+v3t+")()\n") - - // Max uint64. - v4 := uint64(18446744073709551615) - nv4 := (*uint64)(nil) - pv4 := &v4 - v4Addr := fmt.Sprintf("%p", pv4) - pv4Addr := fmt.Sprintf("%p", &pv4) - v4t := "uint64" - v4s := "18446744073709551615" - addDumpTest(v4, "("+v4t+") "+v4s+"\n") - addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") - addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") - addDumpTest(nv4, "(*"+v4t+")()\n") - - // Max uint. - v5 := uint(4294967295) - nv5 := (*uint)(nil) - pv5 := &v5 - v5Addr := fmt.Sprintf("%p", pv5) - pv5Addr := fmt.Sprintf("%p", &pv5) - v5t := "uint" - v5s := "4294967295" - addDumpTest(v5, "("+v5t+") "+v5s+"\n") - addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n") - addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n") - addDumpTest(nv5, "(*"+v5t+")()\n") -} - -func addBoolDumpTests() { - // Boolean true. - v := bool(true) - nv := (*bool)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "bool" - vs := "true" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") - - // Boolean false. - v2 := bool(false) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "bool" - v2s := "false" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") -} - -func addFloatDumpTests() { - // Standard float32. - v := float32(3.1415) - nv := (*float32)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "float32" - vs := "3.1415" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") - - // Standard float64. - v2 := float64(3.1415926) - nv2 := (*float64)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "float64" - v2s := "3.1415926" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*"+v2t+")()\n") -} - -func addComplexDumpTests() { - // Standard complex64. - v := complex(float32(6), -2) - nv := (*complex64)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "complex64" - vs := "(6-2i)" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") - - // Standard complex128. - v2 := complex(float64(-6), 2) - nv2 := (*complex128)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "complex128" - v2s := "(-6+2i)" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*"+v2t+")()\n") -} - -func addArrayDumpTests() { - // Array containing standard ints. - v := [3]int{1, 2, 3} - vLen := fmt.Sprintf("%d", len(v)) - vCap := fmt.Sprintf("%d", cap(v)) - nv := (*[3]int)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "int" - vs := "(len=" + vLen + " cap=" + vCap + ") {\n (" + vt + ") 1,\n (" + - vt + ") 2,\n (" + vt + ") 3\n}" - addDumpTest(v, "([3]"+vt+") "+vs+"\n") - addDumpTest(pv, "(*[3]"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**[3]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*[3]"+vt+")()\n") - - // Array containing type with custom formatter on pointer receiver only. - v2i0 := pstringer("1") - v2i1 := pstringer("2") - v2i2 := pstringer("3") - v2 := [3]pstringer{v2i0, v2i1, v2i2} - v2i0Len := fmt.Sprintf("%d", len(v2i0)) - v2i1Len := fmt.Sprintf("%d", len(v2i1)) - v2i2Len := fmt.Sprintf("%d", len(v2i2)) - v2Len := fmt.Sprintf("%d", len(v2)) - v2Cap := fmt.Sprintf("%d", cap(v2)) - nv2 := (*[3]pstringer)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "spew_test.pstringer" - v2s := "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t + ") (len=" + - v2i0Len + ") stringer 1,\n (" + v2t + ") (len=" + v2i1Len + - ") stringer 2,\n (" + v2t + ") (len=" + v2i2Len + ") " + - "stringer 3\n}" - addDumpTest(v2, "([3]"+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*[3]"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**[3]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*[3]"+v2t+")()\n") - - // Array containing interfaces. - v3i0 := "one" - v3 := [3]interface{}{v3i0, int(2), uint(3)} - v3i0Len := fmt.Sprintf("%d", len(v3i0)) - v3Len := fmt.Sprintf("%d", len(v3)) - v3Cap := fmt.Sprintf("%d", cap(v3)) - nv3 := (*[3]interface{})(nil) - pv3 := &v3 - v3Addr := fmt.Sprintf("%p", pv3) - pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "[3]interface {}" - v3t2 := "string" - v3t3 := "int" - v3t4 := "uint" - v3s := "(len=" + v3Len + " cap=" + v3Cap + ") {\n (" + v3t2 + ") " + - "(len=" + v3i0Len + ") \"one\",\n (" + v3t3 + ") 2,\n (" + - v3t4 + ") 3\n}" - addDumpTest(v3, "("+v3t+") "+v3s+"\n") - addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") - addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") - addDumpTest(nv3, "(*"+v3t+")()\n") - - // Array containing bytes. - v4 := [34]byte{ - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, - 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, - 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, - 0x31, 0x32, - } - v4Len := fmt.Sprintf("%d", len(v4)) - v4Cap := fmt.Sprintf("%d", cap(v4)) - nv4 := (*[34]byte)(nil) - pv4 := &v4 - v4Addr := fmt.Sprintf("%p", pv4) - pv4Addr := fmt.Sprintf("%p", &pv4) - v4t := "[34]uint8" - v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " + - "{\n 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20" + - " |............... |\n" + - " 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30" + - " |!\"#$%&'()*+,-./0|\n" + - " 00000020 31 32 " + - " |12|\n}" - addDumpTest(v4, "("+v4t+") "+v4s+"\n") - addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") - addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") - addDumpTest(nv4, "(*"+v4t+")()\n") -} - -func addSliceDumpTests() { - // Slice containing standard float32 values. - v := []float32{3.14, 6.28, 12.56} - vLen := fmt.Sprintf("%d", len(v)) - vCap := fmt.Sprintf("%d", cap(v)) - nv := (*[]float32)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "float32" - vs := "(len=" + vLen + " cap=" + vCap + ") {\n (" + vt + ") 3.14,\n (" + - vt + ") 6.28,\n (" + vt + ") 12.56\n}" - addDumpTest(v, "([]"+vt+") "+vs+"\n") - addDumpTest(pv, "(*[]"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**[]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*[]"+vt+")()\n") - - // Slice containing type with custom formatter on pointer receiver only. - v2i0 := pstringer("1") - v2i1 := pstringer("2") - v2i2 := pstringer("3") - v2 := []pstringer{v2i0, v2i1, v2i2} - v2i0Len := fmt.Sprintf("%d", len(v2i0)) - v2i1Len := fmt.Sprintf("%d", len(v2i1)) - v2i2Len := fmt.Sprintf("%d", len(v2i2)) - v2Len := fmt.Sprintf("%d", len(v2)) - v2Cap := fmt.Sprintf("%d", cap(v2)) - nv2 := (*[]pstringer)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "spew_test.pstringer" - v2s := "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t + ") (len=" + - v2i0Len + ") stringer 1,\n (" + v2t + ") (len=" + v2i1Len + - ") stringer 2,\n (" + v2t + ") (len=" + v2i2Len + ") " + - "stringer 3\n}" - addDumpTest(v2, "([]"+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*[]"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**[]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*[]"+v2t+")()\n") - - // Slice containing interfaces. - v3i0 := "one" - v3 := []interface{}{v3i0, int(2), uint(3), nil} - v3i0Len := fmt.Sprintf("%d", len(v3i0)) - v3Len := fmt.Sprintf("%d", len(v3)) - v3Cap := fmt.Sprintf("%d", cap(v3)) - nv3 := (*[]interface{})(nil) - pv3 := &v3 - v3Addr := fmt.Sprintf("%p", pv3) - pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "[]interface {}" - v3t2 := "string" - v3t3 := "int" - v3t4 := "uint" - v3t5 := "interface {}" - v3s := "(len=" + v3Len + " cap=" + v3Cap + ") {\n (" + v3t2 + ") " + - "(len=" + v3i0Len + ") \"one\",\n (" + v3t3 + ") 2,\n (" + - v3t4 + ") 3,\n (" + v3t5 + ") \n}" - addDumpTest(v3, "("+v3t+") "+v3s+"\n") - addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") - addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") - addDumpTest(nv3, "(*"+v3t+")()\n") - - // Slice containing bytes. - v4 := []byte{ - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, - 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, - 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, - 0x31, 0x32, - } - v4Len := fmt.Sprintf("%d", len(v4)) - v4Cap := fmt.Sprintf("%d", cap(v4)) - nv4 := (*[]byte)(nil) - pv4 := &v4 - v4Addr := fmt.Sprintf("%p", pv4) - pv4Addr := fmt.Sprintf("%p", &pv4) - v4t := "[]uint8" - v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " + - "{\n 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20" + - " |............... |\n" + - " 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30" + - " |!\"#$%&'()*+,-./0|\n" + - " 00000020 31 32 " + - " |12|\n}" - addDumpTest(v4, "("+v4t+") "+v4s+"\n") - addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") - addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") - addDumpTest(nv4, "(*"+v4t+")()\n") - - // Nil slice. - v5 := []int(nil) - nv5 := (*[]int)(nil) - pv5 := &v5 - v5Addr := fmt.Sprintf("%p", pv5) - pv5Addr := fmt.Sprintf("%p", &pv5) - v5t := "[]int" - v5s := "" - addDumpTest(v5, "("+v5t+") "+v5s+"\n") - addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n") - addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n") - addDumpTest(nv5, "(*"+v5t+")()\n") -} - -func addStringDumpTests() { - // Standard string. - v := "test" - vLen := fmt.Sprintf("%d", len(v)) - nv := (*string)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "string" - vs := "(len=" + vLen + ") \"test\"" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") -} - -func addInterfaceDumpTests() { - // Nil interface. - var v interface{} - nv := (*interface{})(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "interface {}" - vs := "" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") - - // Sub-interface. - v2 := interface{}(uint16(65535)) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "uint16" - v2s := "65535" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") -} - -func addMapDumpTests() { - // Map with string keys and int vals. - k := "one" - kk := "two" - m := map[string]int{k: 1, kk: 2} - klen := fmt.Sprintf("%d", len(k)) // not kLen to shut golint up - kkLen := fmt.Sprintf("%d", len(kk)) - mLen := fmt.Sprintf("%d", len(m)) - nilMap := map[string]int(nil) - nm := (*map[string]int)(nil) - pm := &m - mAddr := fmt.Sprintf("%p", pm) - pmAddr := fmt.Sprintf("%p", &pm) - mt := "map[string]int" - mt1 := "string" - mt2 := "int" - ms := "(len=" + mLen + ") {\n (" + mt1 + ") (len=" + klen + ") " + - "\"one\": (" + mt2 + ") 1,\n (" + mt1 + ") (len=" + kkLen + - ") \"two\": (" + mt2 + ") 2\n}" - ms2 := "(len=" + mLen + ") {\n (" + mt1 + ") (len=" + kkLen + ") " + - "\"two\": (" + mt2 + ") 2,\n (" + mt1 + ") (len=" + klen + - ") \"one\": (" + mt2 + ") 1\n}" - addDumpTest(m, "("+mt+") "+ms+"\n", "("+mt+") "+ms2+"\n") - addDumpTest(pm, "(*"+mt+")("+mAddr+")("+ms+")\n", - "(*"+mt+")("+mAddr+")("+ms2+")\n") - addDumpTest(&pm, "(**"+mt+")("+pmAddr+"->"+mAddr+")("+ms+")\n", - "(**"+mt+")("+pmAddr+"->"+mAddr+")("+ms2+")\n") - addDumpTest(nm, "(*"+mt+")()\n") - addDumpTest(nilMap, "("+mt+") \n") - - // Map with custom formatter type on pointer receiver only keys and vals. - k2 := pstringer("one") - v2 := pstringer("1") - m2 := map[pstringer]pstringer{k2: v2} - k2Len := fmt.Sprintf("%d", len(k2)) - v2Len := fmt.Sprintf("%d", len(v2)) - m2Len := fmt.Sprintf("%d", len(m2)) - nilMap2 := map[pstringer]pstringer(nil) - nm2 := (*map[pstringer]pstringer)(nil) - pm2 := &m2 - m2Addr := fmt.Sprintf("%p", pm2) - pm2Addr := fmt.Sprintf("%p", &pm2) - m2t := "map[spew_test.pstringer]spew_test.pstringer" - m2t1 := "spew_test.pstringer" - m2t2 := "spew_test.pstringer" - m2s := "(len=" + m2Len + ") {\n (" + m2t1 + ") (len=" + k2Len + ") " + - "stringer one: (" + m2t2 + ") (len=" + v2Len + ") stringer 1\n}" - addDumpTest(m2, "("+m2t+") "+m2s+"\n") - addDumpTest(pm2, "(*"+m2t+")("+m2Addr+")("+m2s+")\n") - addDumpTest(&pm2, "(**"+m2t+")("+pm2Addr+"->"+m2Addr+")("+m2s+")\n") - addDumpTest(nm2, "(*"+m2t+")()\n") - addDumpTest(nilMap2, "("+m2t+") \n") - - // Map with interface keys and values. - k3 := "one" - k3Len := fmt.Sprintf("%d", len(k3)) - m3 := map[interface{}]interface{}{k3: 1} - m3Len := fmt.Sprintf("%d", len(m3)) - nilMap3 := map[interface{}]interface{}(nil) - nm3 := (*map[interface{}]interface{})(nil) - pm3 := &m3 - m3Addr := fmt.Sprintf("%p", pm3) - pm3Addr := fmt.Sprintf("%p", &pm3) - m3t := "map[interface {}]interface {}" - m3t1 := "string" - m3t2 := "int" - m3s := "(len=" + m3Len + ") {\n (" + m3t1 + ") (len=" + k3Len + ") " + - "\"one\": (" + m3t2 + ") 1\n}" - addDumpTest(m3, "("+m3t+") "+m3s+"\n") - addDumpTest(pm3, "(*"+m3t+")("+m3Addr+")("+m3s+")\n") - addDumpTest(&pm3, "(**"+m3t+")("+pm3Addr+"->"+m3Addr+")("+m3s+")\n") - addDumpTest(nm3, "(*"+m3t+")()\n") - addDumpTest(nilMap3, "("+m3t+") \n") - - // Map with nil interface value. - k4 := "nil" - k4Len := fmt.Sprintf("%d", len(k4)) - m4 := map[string]interface{}{k4: nil} - m4Len := fmt.Sprintf("%d", len(m4)) - nilMap4 := map[string]interface{}(nil) - nm4 := (*map[string]interface{})(nil) - pm4 := &m4 - m4Addr := fmt.Sprintf("%p", pm4) - pm4Addr := fmt.Sprintf("%p", &pm4) - m4t := "map[string]interface {}" - m4t1 := "string" - m4t2 := "interface {}" - m4s := "(len=" + m4Len + ") {\n (" + m4t1 + ") (len=" + k4Len + ")" + - " \"nil\": (" + m4t2 + ") \n}" - addDumpTest(m4, "("+m4t+") "+m4s+"\n") - addDumpTest(pm4, "(*"+m4t+")("+m4Addr+")("+m4s+")\n") - addDumpTest(&pm4, "(**"+m4t+")("+pm4Addr+"->"+m4Addr+")("+m4s+")\n") - addDumpTest(nm4, "(*"+m4t+")()\n") - addDumpTest(nilMap4, "("+m4t+") \n") -} - -func addStructDumpTests() { - // Struct with primitives. - type s1 struct { - a int8 - b uint8 - } - v := s1{127, 255} - nv := (*s1)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "spew_test.s1" - vt2 := "int8" - vt3 := "uint8" - vs := "{\n a: (" + vt2 + ") 127,\n b: (" + vt3 + ") 255\n}" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") - - // Struct that contains another struct. - type s2 struct { - s1 s1 - b bool - } - v2 := s2{s1{127, 255}, true} - nv2 := (*s2)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "spew_test.s2" - v2t2 := "spew_test.s1" - v2t3 := "int8" - v2t4 := "uint8" - v2t5 := "bool" - v2s := "{\n s1: (" + v2t2 + ") {\n a: (" + v2t3 + ") 127,\n b: (" + - v2t4 + ") 255\n },\n b: (" + v2t5 + ") true\n}" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*"+v2t+")()\n") - - // Struct that contains custom type with Stringer pointer interface via both - // exported and unexported fields. - type s3 struct { - s pstringer - S pstringer - } - v3 := s3{"test", "test2"} - nv3 := (*s3)(nil) - pv3 := &v3 - v3Addr := fmt.Sprintf("%p", pv3) - pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "spew_test.s3" - v3t2 := "spew_test.pstringer" - v3s := "{\n s: (" + v3t2 + ") (len=4) stringer test,\n S: (" + v3t2 + - ") (len=5) stringer test2\n}" - addDumpTest(v3, "("+v3t+") "+v3s+"\n") - addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") - addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") - addDumpTest(nv3, "(*"+v3t+")()\n") - - // Struct that contains embedded struct and field to same struct. - e := embed{"embedstr"} - eLen := fmt.Sprintf("%d", len("embedstr")) - v4 := embedwrap{embed: &e, e: &e} - nv4 := (*embedwrap)(nil) - pv4 := &v4 - eAddr := fmt.Sprintf("%p", &e) - v4Addr := fmt.Sprintf("%p", pv4) - pv4Addr := fmt.Sprintf("%p", &pv4) - v4t := "spew_test.embedwrap" - v4t2 := "spew_test.embed" - v4t3 := "string" - v4s := "{\n embed: (*" + v4t2 + ")(" + eAddr + ")({\n a: (" + v4t3 + - ") (len=" + eLen + ") \"embedstr\"\n }),\n e: (*" + v4t2 + - ")(" + eAddr + ")({\n a: (" + v4t3 + ") (len=" + eLen + ")" + - " \"embedstr\"\n })\n}" - addDumpTest(v4, "("+v4t+") "+v4s+"\n") - addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n") - addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n") - addDumpTest(nv4, "(*"+v4t+")()\n") -} - -func addUintptrDumpTests() { - // Null pointer. - v := uintptr(0) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "uintptr" - vs := "" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - - // Address of real variable. - i := 1 - v2 := uintptr(unsafe.Pointer(&i)) - nv2 := (*uintptr)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "uintptr" - v2s := fmt.Sprintf("%p", &i) - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*"+v2t+")()\n") -} - -func addUnsafePointerDumpTests() { - // Null pointer. - v := unsafe.Pointer(uintptr(0)) - nv := (*unsafe.Pointer)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "unsafe.Pointer" - vs := "" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") - - // Address of real variable. - i := 1 - v2 := unsafe.Pointer(&i) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "unsafe.Pointer" - v2s := fmt.Sprintf("%p", &i) - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv, "(*"+vt+")()\n") -} - -func addChanDumpTests() { - // Nil channel. - var v chan int - pv := &v - nv := (*chan int)(nil) - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "chan int" - vs := "" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") - - // Real channel. - v2 := make(chan int) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "chan int" - v2s := fmt.Sprintf("%p", v2) - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") -} - -func addFuncDumpTests() { - // Function with no params and no returns. - v := addIntDumpTests - nv := (*func())(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "func()" - vs := fmt.Sprintf("%p", v) - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") - - // Function with param and no returns. - v2 := TestDump - nv2 := (*func(*testing.T))(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "func(*testing.T)" - v2s := fmt.Sprintf("%p", v2) - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n") - addDumpTest(nv2, "(*"+v2t+")()\n") - - // Function with multiple params and multiple returns. - var v3 = func(i int, s string) (b bool, err error) { - return true, nil - } - nv3 := (*func(int, string) (bool, error))(nil) - pv3 := &v3 - v3Addr := fmt.Sprintf("%p", pv3) - pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "func(int, string) (bool, error)" - v3s := fmt.Sprintf("%p", v3) - addDumpTest(v3, "("+v3t+") "+v3s+"\n") - addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n") - addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n") - addDumpTest(nv3, "(*"+v3t+")()\n") -} - -func addCircularDumpTests() { - // Struct that is circular through self referencing. - type circular struct { - c *circular - } - v := circular{nil} - v.c = &v - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "spew_test.circular" - vs := "{\n c: (*" + vt + ")(" + vAddr + ")({\n c: (*" + vt + ")(" + - vAddr + ")()\n })\n}" - vs2 := "{\n c: (*" + vt + ")(" + vAddr + ")()\n}" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs2+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs2+")\n") - - // Structs that are circular through cross referencing. - v2 := xref1{nil} - ts2 := xref2{&v2} - v2.ps2 = &ts2 - pv2 := &v2 - ts2Addr := fmt.Sprintf("%p", &ts2) - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "spew_test.xref1" - v2t2 := "spew_test.xref2" - v2s := "{\n ps2: (*" + v2t2 + ")(" + ts2Addr + ")({\n ps1: (*" + v2t + - ")(" + v2Addr + ")({\n ps2: (*" + v2t2 + ")(" + ts2Addr + - ")()\n })\n })\n}" - v2s2 := "{\n ps2: (*" + v2t2 + ")(" + ts2Addr + ")({\n ps1: (*" + v2t + - ")(" + v2Addr + ")()\n })\n}" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s2+")\n") - addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s2+")\n") - - // Structs that are indirectly circular. - v3 := indirCir1{nil} - tic2 := indirCir2{nil} - tic3 := indirCir3{&v3} - tic2.ps3 = &tic3 - v3.ps2 = &tic2 - pv3 := &v3 - tic2Addr := fmt.Sprintf("%p", &tic2) - tic3Addr := fmt.Sprintf("%p", &tic3) - v3Addr := fmt.Sprintf("%p", pv3) - pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "spew_test.indirCir1" - v3t2 := "spew_test.indirCir2" - v3t3 := "spew_test.indirCir3" - v3s := "{\n ps2: (*" + v3t2 + ")(" + tic2Addr + ")({\n ps3: (*" + v3t3 + - ")(" + tic3Addr + ")({\n ps1: (*" + v3t + ")(" + v3Addr + - ")({\n ps2: (*" + v3t2 + ")(" + tic2Addr + - ")()\n })\n })\n })\n}" - v3s2 := "{\n ps2: (*" + v3t2 + ")(" + tic2Addr + ")({\n ps3: (*" + v3t3 + - ")(" + tic3Addr + ")({\n ps1: (*" + v3t + ")(" + v3Addr + - ")()\n })\n })\n}" - addDumpTest(v3, "("+v3t+") "+v3s+"\n") - addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s2+")\n") - addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s2+")\n") -} - -func addPanicDumpTests() { - // Type that panics in its Stringer interface. - v := panicer(127) - nv := (*panicer)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "spew_test.panicer" - vs := "(PANIC=test panic)127" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") -} - -func addErrorDumpTests() { - // Type that has a custom Error interface. - v := customError(127) - nv := (*customError)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "spew_test.customError" - vs := "error: 127" - addDumpTest(v, "("+vt+") "+vs+"\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n") - addDumpTest(nv, "(*"+vt+")()\n") -} - -// TestDump executes all of the tests described by dumpTests. -func TestDump(t *testing.T) { - // Setup tests. - addIntDumpTests() - addUintDumpTests() - addBoolDumpTests() - addFloatDumpTests() - addComplexDumpTests() - addArrayDumpTests() - addSliceDumpTests() - addStringDumpTests() - addInterfaceDumpTests() - addMapDumpTests() - addStructDumpTests() - addUintptrDumpTests() - addUnsafePointerDumpTests() - addChanDumpTests() - addFuncDumpTests() - addCircularDumpTests() - addPanicDumpTests() - addErrorDumpTests() - addCgoDumpTests() - - t.Logf("Running %d tests", len(dumpTests)) - for i, test := range dumpTests { - buf := new(bytes.Buffer) - spew.Fdump(buf, test.in) - s := buf.String() - if testFailed(s, test.wants) { - t.Errorf("Dump #%d\n got: %s %s", i, s, stringizeWants(test.wants)) - continue - } - } -} - -func TestDumpSortedKeys(t *testing.T) { - cfg := spew.ConfigState{SortKeys: true} - s := cfg.Sdump(map[int]string{1: "1", 3: "3", 2: "2"}) - expected := `(map[int]string) (len=3) { -(int) 1: (string) (len=1) "1", -(int) 2: (string) (len=1) "2", -(int) 3: (string) (len=1) "3" -} -` - if s != expected { - t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) - } - - s = cfg.Sdump(map[stringer]int{"1": 1, "3": 3, "2": 2}) - expected = `(map[spew_test.stringer]int) (len=3) { -(spew_test.stringer) (len=1) stringer 1: (int) 1, -(spew_test.stringer) (len=1) stringer 2: (int) 2, -(spew_test.stringer) (len=1) stringer 3: (int) 3 -} -` - if s != expected { - t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) - } - - s = cfg.Sdump(map[pstringer]int{pstringer("1"): 1, pstringer("3"): 3, pstringer("2"): 2}) - expected = `(map[spew_test.pstringer]int) (len=3) { -(spew_test.pstringer) (len=1) stringer 1: (int) 1, -(spew_test.pstringer) (len=1) stringer 2: (int) 2, -(spew_test.pstringer) (len=1) stringer 3: (int) 3 -} -` - if s != expected { - t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) - } - - s = cfg.Sdump(map[customError]int{customError(1): 1, customError(3): 3, customError(2): 2}) - expected = `(map[spew_test.customError]int) (len=3) { -(spew_test.customError) error: 1: (int) 1, -(spew_test.customError) error: 2: (int) 2, -(spew_test.customError) error: 3: (int) 3 -} -` - if s != expected { - t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) - } - -} diff --git a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/dumpcgo_test.go b/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/dumpcgo_test.go deleted file mode 100644 index 9b8a358ec..000000000 --- a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/dumpcgo_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2013 Dave Collins -// -// Permission to use, copy, modify, and distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -// NOTE: Due to the following build constraints, this file will only be compiled -// when both cgo is supported and "-tags testcgo" is added to the go test -// command line. This means the cgo tests are only added (and hence run) when -// specifially requested. This configuration is used because spew itself -// does not require cgo to run even though it does handle certain cgo types -// specially. Rather than forcing all clients to require cgo and an external -// C compiler just to run the tests, this scheme makes them optional. -// +build cgo,testcgo - -package spew_test - -import ( - "fmt" - "github.com/davecgh/go-spew/spew/testdata" -) - -func addCgoDumpTests() { - // C char pointer. - v := testdata.GetCgoCharPointer() - nv := testdata.GetCgoNullCharPointer() - pv := &v - vcAddr := fmt.Sprintf("%p", v) - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "*testdata._Ctype_char" - vs := "116" - addDumpTest(v, "("+vt+")("+vcAddr+")("+vs+")\n") - addDumpTest(pv, "(*"+vt+")("+vAddr+"->"+vcAddr+")("+vs+")\n") - addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+"->"+vcAddr+")("+vs+")\n") - addDumpTest(nv, "("+vt+")()\n") - - // C char array. - v2, v2l, v2c := testdata.GetCgoCharArray() - v2Len := fmt.Sprintf("%d", v2l) - v2Cap := fmt.Sprintf("%d", v2c) - v2t := "[6]testdata._Ctype_char" - v2s := "(len=" + v2Len + " cap=" + v2Cap + ") " + - "{\n 00000000 74 65 73 74 32 00 " + - " |test2.|\n}" - addDumpTest(v2, "("+v2t+") "+v2s+"\n") - - // C unsigned char array. - v3, v3l, v3c := testdata.GetCgoUnsignedCharArray() - v3Len := fmt.Sprintf("%d", v3l) - v3Cap := fmt.Sprintf("%d", v3c) - v3t := "[6]testdata._Ctype_unsignedchar" - v3s := "(len=" + v3Len + " cap=" + v3Cap + ") " + - "{\n 00000000 74 65 73 74 33 00 " + - " |test3.|\n}" - addDumpTest(v3, "("+v3t+") "+v3s+"\n") - - // C signed char array. - v4, v4l, v4c := testdata.GetCgoSignedCharArray() - v4Len := fmt.Sprintf("%d", v4l) - v4Cap := fmt.Sprintf("%d", v4c) - v4t := "[6]testdata._Ctype_schar" - v4t2 := "testdata._Ctype_schar" - v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " + - "{\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 101,\n (" + v4t2 + - ") 115,\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 52,\n (" + v4t2 + - ") 0\n}" - addDumpTest(v4, "("+v4t+") "+v4s+"\n") - - // C uint8_t array. - v5, v5l, v5c := testdata.GetCgoUint8tArray() - v5Len := fmt.Sprintf("%d", v5l) - v5Cap := fmt.Sprintf("%d", v5c) - v5t := "[6]testdata._Ctype_uint8_t" - v5s := "(len=" + v5Len + " cap=" + v5Cap + ") " + - "{\n 00000000 74 65 73 74 35 00 " + - " |test5.|\n}" - addDumpTest(v5, "("+v5t+") "+v5s+"\n") - - // C typedefed unsigned char array. - v6, v6l, v6c := testdata.GetCgoTypdefedUnsignedCharArray() - v6Len := fmt.Sprintf("%d", v6l) - v6Cap := fmt.Sprintf("%d", v6c) - v6t := "[6]testdata._Ctype_custom_uchar_t" - v6s := "(len=" + v6Len + " cap=" + v6Cap + ") " + - "{\n 00000000 74 65 73 74 36 00 " + - " |test6.|\n}" - addDumpTest(v6, "("+v6t+") "+v6s+"\n") -} diff --git a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/dumpnocgo_test.go b/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/dumpnocgo_test.go deleted file mode 100644 index 52a0971fb..000000000 --- a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/dumpnocgo_test.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2013 Dave Collins -// -// Permission to use, copy, modify, and distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -// NOTE: Due to the following build constraints, this file will only be compiled -// when either cgo is not supported or "-tags testcgo" is not added to the go -// test command line. This file intentionally does not setup any cgo tests in -// this scenario. -// +build !cgo !testcgo - -package spew_test - -func addCgoDumpTests() { - // Don't add any tests for cgo since this file is only compiled when - // there should not be any cgo tests. -} diff --git a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/example_test.go b/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/example_test.go deleted file mode 100644 index a7acd1412..000000000 --- a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/example_test.go +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (c) 2013 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package spew_test - -import ( - "fmt" - "github.com/davecgh/go-spew/spew" -) - -type Flag int - -const ( - flagOne Flag = iota - flagTwo -) - -var flagStrings = map[Flag]string{ - flagOne: "flagOne", - flagTwo: "flagTwo", -} - -func (f Flag) String() string { - if s, ok := flagStrings[f]; ok { - return s - } - return fmt.Sprintf("Unknown flag (%d)", int(f)) -} - -type Bar struct { - flag Flag - data uintptr -} - -type Foo struct { - unexportedField Bar - ExportedField map[interface{}]interface{} -} - -// This example demonstrates how to use Dump to dump variables to stdout. -func ExampleDump() { - // The following package level declarations are assumed for this example: - /* - type Flag int - - const ( - flagOne Flag = iota - flagTwo - ) - - var flagStrings = map[Flag]string{ - flagOne: "flagOne", - flagTwo: "flagTwo", - } - - func (f Flag) String() string { - if s, ok := flagStrings[f]; ok { - return s - } - return fmt.Sprintf("Unknown flag (%d)", int(f)) - } - - type Bar struct { - flag Flag - data uintptr - } - - type Foo struct { - unexportedField Bar - ExportedField map[interface{}]interface{} - } - */ - - // Setup some sample data structures for the example. - bar := Bar{Flag(flagTwo), uintptr(0)} - s1 := Foo{bar, map[interface{}]interface{}{"one": true}} - f := Flag(5) - b := []byte{ - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, - 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, - 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, - 0x31, 0x32, - } - - // Dump! - spew.Dump(s1, f, b) - - // Output: - // (spew_test.Foo) { - // unexportedField: (spew_test.Bar) { - // flag: (spew_test.Flag) flagTwo, - // data: (uintptr) - // }, - // ExportedField: (map[interface {}]interface {}) (len=1) { - // (string) (len=3) "one": (bool) true - // } - // } - // (spew_test.Flag) Unknown flag (5) - // ([]uint8) (len=34 cap=34) { - // 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | - // 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| - // 00000020 31 32 |12| - // } - // -} - -// This example demonstrates how to use Printf to display a variable with a -// format string and inline formatting. -func ExamplePrintf() { - // Create a double pointer to a uint 8. - ui8 := uint8(5) - pui8 := &ui8 - ppui8 := &pui8 - - // Create a circular data type. - type circular struct { - ui8 uint8 - c *circular - } - c := circular{ui8: 1} - c.c = &c - - // Print! - spew.Printf("ppui8: %v\n", ppui8) - spew.Printf("circular: %v\n", c) - - // Output: - // ppui8: <**>5 - // circular: {1 <*>{1 <*>}} -} - -// This example demonstrates how to use a ConfigState. -func ExampleConfigState() { - // Modify the indent level of the ConfigState only. The global - // configuration is not modified. - scs := spew.ConfigState{Indent: "\t"} - - // Output using the ConfigState instance. - v := map[string]int{"one": 1} - scs.Printf("v: %v\n", v) - scs.Dump(v) - - // Output: - // v: map[one:1] - // (map[string]int) (len=1) { - // (string) (len=3) "one": (int) 1 - // } -} - -// This example demonstrates how to use ConfigState.Dump to dump variables to -// stdout -func ExampleConfigState_Dump() { - // See the top-level Dump example for details on the types used in this - // example. - - // Create two ConfigState instances with different indentation. - scs := spew.ConfigState{Indent: "\t"} - scs2 := spew.ConfigState{Indent: " "} - - // Setup some sample data structures for the example. - bar := Bar{Flag(flagTwo), uintptr(0)} - s1 := Foo{bar, map[interface{}]interface{}{"one": true}} - - // Dump using the ConfigState instances. - scs.Dump(s1) - scs2.Dump(s1) - - // Output: - // (spew_test.Foo) { - // unexportedField: (spew_test.Bar) { - // flag: (spew_test.Flag) flagTwo, - // data: (uintptr) - // }, - // ExportedField: (map[interface {}]interface {}) (len=1) { - // (string) (len=3) "one": (bool) true - // } - // } - // (spew_test.Foo) { - // unexportedField: (spew_test.Bar) { - // flag: (spew_test.Flag) flagTwo, - // data: (uintptr) - // }, - // ExportedField: (map[interface {}]interface {}) (len=1) { - // (string) (len=3) "one": (bool) true - // } - // } - // -} - -// This example demonstrates how to use ConfigState.Printf to display a variable -// with a format string and inline formatting. -func ExampleConfigState_Printf() { - // See the top-level Dump example for details on the types used in this - // example. - - // Create two ConfigState instances and modify the method handling of the - // first ConfigState only. - scs := spew.NewDefaultConfig() - scs2 := spew.NewDefaultConfig() - scs.DisableMethods = true - - // Alternatively - // scs := spew.ConfigState{Indent: " ", DisableMethods: true} - // scs2 := spew.ConfigState{Indent: " "} - - // This is of type Flag which implements a Stringer and has raw value 1. - f := flagTwo - - // Dump using the ConfigState instances. - scs.Printf("f: %v\n", f) - scs2.Printf("f: %v\n", f) - - // Output: - // f: 1 - // f: flagTwo -} diff --git a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/format_test.go b/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/format_test.go deleted file mode 100644 index b0f9761a4..000000000 --- a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/format_test.go +++ /dev/null @@ -1,1535 +0,0 @@ -/* - * Copyright (c) 2013 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* -Test Summary: -NOTE: For each test, a nil pointer, a single pointer and double pointer to the -base test element are also tested to ensure proper indirection across all types. - -- Max int8, int16, int32, int64, int -- Max uint8, uint16, uint32, uint64, uint -- Boolean true and false -- Standard complex64 and complex128 -- Array containing standard ints -- Array containing type with custom formatter on pointer receiver only -- Array containing interfaces -- Slice containing standard float32 values -- Slice containing type with custom formatter on pointer receiver only -- Slice containing interfaces -- Nil slice -- Standard string -- Nil interface -- Sub-interface -- Map with string keys and int vals -- Map with custom formatter type on pointer receiver only keys and vals -- Map with interface keys and values -- Map with nil interface value -- Struct with primitives -- Struct that contains another struct -- Struct that contains custom type with Stringer pointer interface via both - exported and unexported fields -- Struct that contains embedded struct and field to same struct -- Uintptr to 0 (null pointer) -- Uintptr address of real variable -- Unsafe.Pointer to 0 (null pointer) -- Unsafe.Pointer to address of real variable -- Nil channel -- Standard int channel -- Function with no params and no returns -- Function with param and no returns -- Function with multiple params and multiple returns -- Struct that is circular through self referencing -- Structs that are circular through cross referencing -- Structs that are indirectly circular -- Type that panics in its Stringer interface -- Type that has a custom Error interface -- %x passthrough with uint -- %#x passthrough with uint -- %f passthrough with precision -- %f passthrough with width and precision -- %d passthrough with width -- %q passthrough with string -*/ - -package spew_test - -import ( - "bytes" - "fmt" - "testing" - "unsafe" - - "github.com/davecgh/go-spew/spew" -) - -// formatterTest is used to describe a test to be perfomed against NewFormatter. -type formatterTest struct { - format string - in interface{} - wants []string -} - -// formatterTests houses all of the tests to be performed against NewFormatter. -var formatterTests = make([]formatterTest, 0) - -// addFormatterTest is a helper method to append the passed input and desired -// result to formatterTests. -func addFormatterTest(format string, in interface{}, wants ...string) { - test := formatterTest{format, in, wants} - formatterTests = append(formatterTests, test) -} - -func addIntFormatterTests() { - // Max int8. - v := int8(127) - nv := (*int8)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "int8" - vs := "127" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") - - // Max int16. - v2 := int16(32767) - nv2 := (*int16)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "int16" - v2s := "32767" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") - - // Max int32. - v3 := int32(2147483647) - nv3 := (*int32)(nil) - pv3 := &v3 - v3Addr := fmt.Sprintf("%p", pv3) - pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "int32" - v3s := "2147483647" - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3s) - addFormatterTest("%v", &pv3, "<**>"+v3s) - addFormatterTest("%v", nv3, "") - addFormatterTest("%+v", v3, v3s) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%#v", v3, "("+v3t+")"+v3s) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - - // Max int64. - v4 := int64(9223372036854775807) - nv4 := (*int64)(nil) - pv4 := &v4 - v4Addr := fmt.Sprintf("%p", pv4) - pv4Addr := fmt.Sprintf("%p", &pv4) - v4t := "int64" - v4s := "9223372036854775807" - addFormatterTest("%v", v4, v4s) - addFormatterTest("%v", pv4, "<*>"+v4s) - addFormatterTest("%v", &pv4, "<**>"+v4s) - addFormatterTest("%v", nv4, "") - addFormatterTest("%+v", v4, v4s) - addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s) - addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%#v", v4, "("+v4t+")"+v4s) - addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s) - addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s) - addFormatterTest("%#v", nv4, "(*"+v4t+")"+"") - addFormatterTest("%#+v", v4, "("+v4t+")"+v4s) - addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s) - addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s) - addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"") - - // Max int. - v5 := int(2147483647) - nv5 := (*int)(nil) - pv5 := &v5 - v5Addr := fmt.Sprintf("%p", pv5) - pv5Addr := fmt.Sprintf("%p", &pv5) - v5t := "int" - v5s := "2147483647" - addFormatterTest("%v", v5, v5s) - addFormatterTest("%v", pv5, "<*>"+v5s) - addFormatterTest("%v", &pv5, "<**>"+v5s) - addFormatterTest("%v", nv5, "") - addFormatterTest("%+v", v5, v5s) - addFormatterTest("%+v", pv5, "<*>("+v5Addr+")"+v5s) - addFormatterTest("%+v", &pv5, "<**>("+pv5Addr+"->"+v5Addr+")"+v5s) - addFormatterTest("%+v", nv5, "") - addFormatterTest("%#v", v5, "("+v5t+")"+v5s) - addFormatterTest("%#v", pv5, "(*"+v5t+")"+v5s) - addFormatterTest("%#v", &pv5, "(**"+v5t+")"+v5s) - addFormatterTest("%#v", nv5, "(*"+v5t+")"+"") - addFormatterTest("%#+v", v5, "("+v5t+")"+v5s) - addFormatterTest("%#+v", pv5, "(*"+v5t+")("+v5Addr+")"+v5s) - addFormatterTest("%#+v", &pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")"+v5s) - addFormatterTest("%#+v", nv5, "(*"+v5t+")"+"") -} - -func addUintFormatterTests() { - // Max uint8. - v := uint8(255) - nv := (*uint8)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "uint8" - vs := "255" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") - - // Max uint16. - v2 := uint16(65535) - nv2 := (*uint16)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "uint16" - v2s := "65535" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") - - // Max uint32. - v3 := uint32(4294967295) - nv3 := (*uint32)(nil) - pv3 := &v3 - v3Addr := fmt.Sprintf("%p", pv3) - pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "uint32" - v3s := "4294967295" - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3s) - addFormatterTest("%v", &pv3, "<**>"+v3s) - addFormatterTest("%v", nv3, "") - addFormatterTest("%+v", v3, v3s) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%#v", v3, "("+v3t+")"+v3s) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - - // Max uint64. - v4 := uint64(18446744073709551615) - nv4 := (*uint64)(nil) - pv4 := &v4 - v4Addr := fmt.Sprintf("%p", pv4) - pv4Addr := fmt.Sprintf("%p", &pv4) - v4t := "uint64" - v4s := "18446744073709551615" - addFormatterTest("%v", v4, v4s) - addFormatterTest("%v", pv4, "<*>"+v4s) - addFormatterTest("%v", &pv4, "<**>"+v4s) - addFormatterTest("%v", nv4, "") - addFormatterTest("%+v", v4, v4s) - addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s) - addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%#v", v4, "("+v4t+")"+v4s) - addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s) - addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s) - addFormatterTest("%#v", nv4, "(*"+v4t+")"+"") - addFormatterTest("%#+v", v4, "("+v4t+")"+v4s) - addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s) - addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s) - addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"") - - // Max uint. - v5 := uint(4294967295) - nv5 := (*uint)(nil) - pv5 := &v5 - v5Addr := fmt.Sprintf("%p", pv5) - pv5Addr := fmt.Sprintf("%p", &pv5) - v5t := "uint" - v5s := "4294967295" - addFormatterTest("%v", v5, v5s) - addFormatterTest("%v", pv5, "<*>"+v5s) - addFormatterTest("%v", &pv5, "<**>"+v5s) - addFormatterTest("%v", nv5, "") - addFormatterTest("%+v", v5, v5s) - addFormatterTest("%+v", pv5, "<*>("+v5Addr+")"+v5s) - addFormatterTest("%+v", &pv5, "<**>("+pv5Addr+"->"+v5Addr+")"+v5s) - addFormatterTest("%+v", nv5, "") - addFormatterTest("%#v", v5, "("+v5t+")"+v5s) - addFormatterTest("%#v", pv5, "(*"+v5t+")"+v5s) - addFormatterTest("%#v", &pv5, "(**"+v5t+")"+v5s) - addFormatterTest("%#v", nv5, "(*"+v5t+")"+"") - addFormatterTest("%#+v", v5, "("+v5t+")"+v5s) - addFormatterTest("%#+v", pv5, "(*"+v5t+")("+v5Addr+")"+v5s) - addFormatterTest("%#+v", &pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")"+v5s) - addFormatterTest("%#v", nv5, "(*"+v5t+")"+"") -} - -func addBoolFormatterTests() { - // Boolean true. - v := bool(true) - nv := (*bool)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "bool" - vs := "true" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") - - // Boolean false. - v2 := bool(false) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "bool" - v2s := "false" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) -} - -func addFloatFormatterTests() { - // Standard float32. - v := float32(3.1415) - nv := (*float32)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "float32" - vs := "3.1415" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") - - // Standard float64. - v2 := float64(3.1415926) - nv2 := (*float64)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "float64" - v2s := "3.1415926" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") -} - -func addComplexFormatterTests() { - // Standard complex64. - v := complex(float32(6), -2) - nv := (*complex64)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "complex64" - vs := "(6-2i)" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") - - // Standard complex128. - v2 := complex(float64(-6), 2) - nv2 := (*complex128)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "complex128" - v2s := "(-6+2i)" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") -} - -func addArrayFormatterTests() { - // Array containing standard ints. - v := [3]int{1, 2, 3} - nv := (*[3]int)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "[3]int" - vs := "[1 2 3]" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") - - // Array containing type with custom formatter on pointer receiver only. - v2 := [3]pstringer{"1", "2", "3"} - nv2 := (*[3]pstringer)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "[3]spew_test.pstringer" - v2s := "[stringer 1 stringer 2 stringer 3]" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") - - // Array containing interfaces. - v3 := [3]interface{}{"one", int(2), uint(3)} - nv3 := (*[3]interface{})(nil) - pv3 := &v3 - v3Addr := fmt.Sprintf("%p", pv3) - pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "[3]interface {}" - v3t2 := "string" - v3t3 := "int" - v3t4 := "uint" - v3s := "[one 2 3]" - v3s2 := "[(" + v3t2 + ")one (" + v3t3 + ")2 (" + v3t4 + ")3]" - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3s) - addFormatterTest("%v", &pv3, "<**>"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%+v", v3, v3s) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%#v", v3, "("+v3t+")"+v3s2) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s2) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s2) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s2) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2) - addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"") -} - -func addSliceFormatterTests() { - // Slice containing standard float32 values. - v := []float32{3.14, 6.28, 12.56} - nv := (*[]float32)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "[]float32" - vs := "[3.14 6.28 12.56]" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") - - // Slice containing type with custom formatter on pointer receiver only. - v2 := []pstringer{"1", "2", "3"} - nv2 := (*[]pstringer)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "[]spew_test.pstringer" - v2s := "[stringer 1 stringer 2 stringer 3]" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") - - // Slice containing interfaces. - v3 := []interface{}{"one", int(2), uint(3), nil} - nv3 := (*[]interface{})(nil) - pv3 := &v3 - v3Addr := fmt.Sprintf("%p", pv3) - pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "[]interface {}" - v3t2 := "string" - v3t3 := "int" - v3t4 := "uint" - v3t5 := "interface {}" - v3s := "[one 2 3 ]" - v3s2 := "[(" + v3t2 + ")one (" + v3t3 + ")2 (" + v3t4 + ")3 (" + v3t5 + - ")]" - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3s) - addFormatterTest("%v", &pv3, "<**>"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%+v", v3, v3s) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%#v", v3, "("+v3t+")"+v3s2) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s2) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s2) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s2) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2) - addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"") - - // Nil slice. - var v4 []int - nv4 := (*[]int)(nil) - pv4 := &v4 - v4Addr := fmt.Sprintf("%p", pv4) - pv4Addr := fmt.Sprintf("%p", &pv4) - v4t := "[]int" - v4s := "" - addFormatterTest("%v", v4, v4s) - addFormatterTest("%v", pv4, "<*>"+v4s) - addFormatterTest("%v", &pv4, "<**>"+v4s) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%+v", v4, v4s) - addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s) - addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%#v", v4, "("+v4t+")"+v4s) - addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s) - addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s) - addFormatterTest("%#v", nv4, "(*"+v4t+")"+"") - addFormatterTest("%#+v", v4, "("+v4t+")"+v4s) - addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s) - addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s) - addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"") -} - -func addStringFormatterTests() { - // Standard string. - v := "test" - nv := (*string)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "string" - vs := "test" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") -} - -func addInterfaceFormatterTests() { - // Nil interface. - var v interface{} - nv := (*interface{})(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "interface {}" - vs := "" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") - - // Sub-interface. - v2 := interface{}(uint16(65535)) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "uint16" - v2s := "65535" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) -} - -func addMapFormatterTests() { - // Map with string keys and int vals. - v := map[string]int{"one": 1, "two": 2} - nilMap := map[string]int(nil) - nv := (*map[string]int)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "map[string]int" - vs := "map[one:1 two:2]" - vs2 := "map[two:2 one:1]" - addFormatterTest("%v", v, vs, vs2) - addFormatterTest("%v", pv, "<*>"+vs, "<*>"+vs2) - addFormatterTest("%v", &pv, "<**>"+vs, "<**>"+vs2) - addFormatterTest("%+v", nilMap, "") - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs, vs2) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs, "<*>("+vAddr+")"+vs2) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs, - "<**>("+pvAddr+"->"+vAddr+")"+vs2) - addFormatterTest("%+v", nilMap, "") - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs, "("+vt+")"+vs2) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs, "(*"+vt+")"+vs2) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs, "(**"+vt+")"+vs2) - addFormatterTest("%#v", nilMap, "("+vt+")"+"") - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs, "("+vt+")"+vs2) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs, - "(*"+vt+")("+vAddr+")"+vs2) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs, - "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs2) - addFormatterTest("%#+v", nilMap, "("+vt+")"+"") - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") - - // Map with custom formatter type on pointer receiver only keys and vals. - v2 := map[pstringer]pstringer{"one": "1"} - nv2 := (*map[pstringer]pstringer)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "map[spew_test.pstringer]spew_test.pstringer" - v2s := "map[stringer one:stringer 1]" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") - - // Map with interface keys and values. - v3 := map[interface{}]interface{}{"one": 1} - nv3 := (*map[interface{}]interface{})(nil) - pv3 := &v3 - v3Addr := fmt.Sprintf("%p", pv3) - pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "map[interface {}]interface {}" - v3t1 := "string" - v3t2 := "int" - v3s := "map[one:1]" - v3s2 := "map[(" + v3t1 + ")one:(" + v3t2 + ")1]" - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3s) - addFormatterTest("%v", &pv3, "<**>"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%+v", v3, v3s) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%#v", v3, "("+v3t+")"+v3s2) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s2) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s2) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s2) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2) - addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"") - - // Map with nil interface value - v4 := map[string]interface{}{"nil": nil} - nv4 := (*map[string]interface{})(nil) - pv4 := &v4 - v4Addr := fmt.Sprintf("%p", pv4) - pv4Addr := fmt.Sprintf("%p", &pv4) - v4t := "map[string]interface {}" - v4t1 := "interface {}" - v4s := "map[nil:]" - v4s2 := "map[nil:(" + v4t1 + ")]" - addFormatterTest("%v", v4, v4s) - addFormatterTest("%v", pv4, "<*>"+v4s) - addFormatterTest("%v", &pv4, "<**>"+v4s) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%+v", v4, v4s) - addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s) - addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%#v", v4, "("+v4t+")"+v4s2) - addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s2) - addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s2) - addFormatterTest("%#v", nv4, "(*"+v4t+")"+"") - addFormatterTest("%#+v", v4, "("+v4t+")"+v4s2) - addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s2) - addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s2) - addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"") -} - -func addStructFormatterTests() { - // Struct with primitives. - type s1 struct { - a int8 - b uint8 - } - v := s1{127, 255} - nv := (*s1)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "spew_test.s1" - vt2 := "int8" - vt3 := "uint8" - vs := "{127 255}" - vs2 := "{a:127 b:255}" - vs3 := "{a:(" + vt2 + ")127 b:(" + vt3 + ")255}" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs2) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs2) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs2) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs3) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs3) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs3) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs3) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs3) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs3) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") - - // Struct that contains another struct. - type s2 struct { - s1 s1 - b bool - } - v2 := s2{s1{127, 255}, true} - nv2 := (*s2)(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "spew_test.s2" - v2t2 := "spew_test.s1" - v2t3 := "int8" - v2t4 := "uint8" - v2t5 := "bool" - v2s := "{{127 255} true}" - v2s2 := "{s1:{a:127 b:255} b:true}" - v2s3 := "{s1:(" + v2t2 + "){a:(" + v2t3 + ")127 b:(" + v2t4 + ")255} b:(" + - v2t5 + ")true}" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%+v", v2, v2s2) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s2) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s2) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s3) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s3) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s3) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s3) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s3) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s3) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") - - // Struct that contains custom type with Stringer pointer interface via both - // exported and unexported fields. - type s3 struct { - s pstringer - S pstringer - } - v3 := s3{"test", "test2"} - nv3 := (*s3)(nil) - pv3 := &v3 - v3Addr := fmt.Sprintf("%p", pv3) - pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "spew_test.s3" - v3t2 := "spew_test.pstringer" - v3s := "{stringer test stringer test2}" - v3s2 := "{s:stringer test S:stringer test2}" - v3s3 := "{s:(" + v3t2 + ")stringer test S:(" + v3t2 + ")stringer test2}" - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3s) - addFormatterTest("%v", &pv3, "<**>"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%+v", v3, v3s2) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s2) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s2) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%#v", v3, "("+v3t+")"+v3s3) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s3) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s3) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s3) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s3) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s3) - addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"") - - // Struct that contains embedded struct and field to same struct. - e := embed{"embedstr"} - v4 := embedwrap{embed: &e, e: &e} - nv4 := (*embedwrap)(nil) - pv4 := &v4 - eAddr := fmt.Sprintf("%p", &e) - v4Addr := fmt.Sprintf("%p", pv4) - pv4Addr := fmt.Sprintf("%p", &pv4) - v4t := "spew_test.embedwrap" - v4t2 := "spew_test.embed" - v4t3 := "string" - v4s := "{<*>{embedstr} <*>{embedstr}}" - v4s2 := "{embed:<*>(" + eAddr + "){a:embedstr} e:<*>(" + eAddr + - "){a:embedstr}}" - v4s3 := "{embed:(*" + v4t2 + "){a:(" + v4t3 + ")embedstr} e:(*" + v4t2 + - "){a:(" + v4t3 + ")embedstr}}" - v4s4 := "{embed:(*" + v4t2 + ")(" + eAddr + "){a:(" + v4t3 + - ")embedstr} e:(*" + v4t2 + ")(" + eAddr + "){a:(" + v4t3 + ")embedstr}}" - addFormatterTest("%v", v4, v4s) - addFormatterTest("%v", pv4, "<*>"+v4s) - addFormatterTest("%v", &pv4, "<**>"+v4s) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%+v", v4, v4s2) - addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s2) - addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s2) - addFormatterTest("%+v", nv4, "") - addFormatterTest("%#v", v4, "("+v4t+")"+v4s3) - addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s3) - addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s3) - addFormatterTest("%#v", nv4, "(*"+v4t+")"+"") - addFormatterTest("%#+v", v4, "("+v4t+")"+v4s4) - addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s4) - addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s4) - addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"") -} - -func addUintptrFormatterTests() { - // Null pointer. - v := uintptr(0) - nv := (*uintptr)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "uintptr" - vs := "" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") - - // Address of real variable. - i := 1 - v2 := uintptr(unsafe.Pointer(&i)) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "uintptr" - v2s := fmt.Sprintf("%p", &i) - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) -} - -func addUnsafePointerFormatterTests() { - // Null pointer. - v := unsafe.Pointer(uintptr(0)) - nv := (*unsafe.Pointer)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "unsafe.Pointer" - vs := "" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") - - // Address of real variable. - i := 1 - v2 := unsafe.Pointer(&i) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "unsafe.Pointer" - v2s := fmt.Sprintf("%p", &i) - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) -} - -func addChanFormatterTests() { - // Nil channel. - var v chan int - pv := &v - nv := (*chan int)(nil) - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "chan int" - vs := "" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") - - // Real channel. - v2 := make(chan int) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "chan int" - v2s := fmt.Sprintf("%p", v2) - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) -} - -func addFuncFormatterTests() { - // Function with no params and no returns. - v := addIntFormatterTests - nv := (*func())(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "func()" - vs := fmt.Sprintf("%p", v) - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") - - // Function with param and no returns. - v2 := TestFormatter - nv2 := (*func(*testing.T))(nil) - pv2 := &v2 - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "func(*testing.T)" - v2s := fmt.Sprintf("%p", v2) - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s) - addFormatterTest("%v", &pv2, "<**>"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%+v", v2, v2s) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%+v", nv2, "") - addFormatterTest("%#v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s) - addFormatterTest("%#v", nv2, "(*"+v2t+")"+"") - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s) - addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"") - - // Function with multiple params and multiple returns. - var v3 = func(i int, s string) (b bool, err error) { - return true, nil - } - nv3 := (*func(int, string) (bool, error))(nil) - pv3 := &v3 - v3Addr := fmt.Sprintf("%p", pv3) - pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "func(int, string) (bool, error)" - v3s := fmt.Sprintf("%p", v3) - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3s) - addFormatterTest("%v", &pv3, "<**>"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%+v", v3, v3s) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%+v", nv3, "") - addFormatterTest("%#v", v3, "("+v3t+")"+v3s) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s) - addFormatterTest("%#v", nv3, "(*"+v3t+")"+"") - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s) - addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"") -} - -func addCircularFormatterTests() { - // Struct that is circular through self referencing. - type circular struct { - c *circular - } - v := circular{nil} - v.c = &v - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "spew_test.circular" - vs := "{<*>{<*>}}" - vs2 := "{<*>}" - vs3 := "{c:<*>(" + vAddr + "){c:<*>(" + vAddr + ")}}" - vs4 := "{c:<*>(" + vAddr + ")}" - vs5 := "{c:(*" + vt + "){c:(*" + vt + ")}}" - vs6 := "{c:(*" + vt + ")}" - vs7 := "{c:(*" + vt + ")(" + vAddr + "){c:(*" + vt + ")(" + vAddr + - ")}}" - vs8 := "{c:(*" + vt + ")(" + vAddr + ")}" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs2) - addFormatterTest("%v", &pv, "<**>"+vs2) - addFormatterTest("%+v", v, vs3) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs4) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs4) - addFormatterTest("%#v", v, "("+vt+")"+vs5) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs6) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs6) - addFormatterTest("%#+v", v, "("+vt+")"+vs7) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs8) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs8) - - // Structs that are circular through cross referencing. - v2 := xref1{nil} - ts2 := xref2{&v2} - v2.ps2 = &ts2 - pv2 := &v2 - ts2Addr := fmt.Sprintf("%p", &ts2) - v2Addr := fmt.Sprintf("%p", pv2) - pv2Addr := fmt.Sprintf("%p", &pv2) - v2t := "spew_test.xref1" - v2t2 := "spew_test.xref2" - v2s := "{<*>{<*>{<*>}}}" - v2s2 := "{<*>{<*>}}" - v2s3 := "{ps2:<*>(" + ts2Addr + "){ps1:<*>(" + v2Addr + "){ps2:<*>(" + - ts2Addr + ")}}}" - v2s4 := "{ps2:<*>(" + ts2Addr + "){ps1:<*>(" + v2Addr + ")}}" - v2s5 := "{ps2:(*" + v2t2 + "){ps1:(*" + v2t + "){ps2:(*" + v2t2 + - ")}}}" - v2s6 := "{ps2:(*" + v2t2 + "){ps1:(*" + v2t + ")}}" - v2s7 := "{ps2:(*" + v2t2 + ")(" + ts2Addr + "){ps1:(*" + v2t + - ")(" + v2Addr + "){ps2:(*" + v2t2 + ")(" + ts2Addr + - ")}}}" - v2s8 := "{ps2:(*" + v2t2 + ")(" + ts2Addr + "){ps1:(*" + v2t + - ")(" + v2Addr + ")}}" - addFormatterTest("%v", v2, v2s) - addFormatterTest("%v", pv2, "<*>"+v2s2) - addFormatterTest("%v", &pv2, "<**>"+v2s2) - addFormatterTest("%+v", v2, v2s3) - addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s4) - addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s4) - addFormatterTest("%#v", v2, "("+v2t+")"+v2s5) - addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s6) - addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s6) - addFormatterTest("%#+v", v2, "("+v2t+")"+v2s7) - addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s8) - addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s8) - - // Structs that are indirectly circular. - v3 := indirCir1{nil} - tic2 := indirCir2{nil} - tic3 := indirCir3{&v3} - tic2.ps3 = &tic3 - v3.ps2 = &tic2 - pv3 := &v3 - tic2Addr := fmt.Sprintf("%p", &tic2) - tic3Addr := fmt.Sprintf("%p", &tic3) - v3Addr := fmt.Sprintf("%p", pv3) - pv3Addr := fmt.Sprintf("%p", &pv3) - v3t := "spew_test.indirCir1" - v3t2 := "spew_test.indirCir2" - v3t3 := "spew_test.indirCir3" - v3s := "{<*>{<*>{<*>{<*>}}}}" - v3s2 := "{<*>{<*>{<*>}}}" - v3s3 := "{ps2:<*>(" + tic2Addr + "){ps3:<*>(" + tic3Addr + "){ps1:<*>(" + - v3Addr + "){ps2:<*>(" + tic2Addr + ")}}}}" - v3s4 := "{ps2:<*>(" + tic2Addr + "){ps3:<*>(" + tic3Addr + "){ps1:<*>(" + - v3Addr + ")}}}" - v3s5 := "{ps2:(*" + v3t2 + "){ps3:(*" + v3t3 + "){ps1:(*" + v3t + - "){ps2:(*" + v3t2 + ")}}}}" - v3s6 := "{ps2:(*" + v3t2 + "){ps3:(*" + v3t3 + "){ps1:(*" + v3t + - ")}}}" - v3s7 := "{ps2:(*" + v3t2 + ")(" + tic2Addr + "){ps3:(*" + v3t3 + ")(" + - tic3Addr + "){ps1:(*" + v3t + ")(" + v3Addr + "){ps2:(*" + v3t2 + - ")(" + tic2Addr + ")}}}}" - v3s8 := "{ps2:(*" + v3t2 + ")(" + tic2Addr + "){ps3:(*" + v3t3 + ")(" + - tic3Addr + "){ps1:(*" + v3t + ")(" + v3Addr + ")}}}" - addFormatterTest("%v", v3, v3s) - addFormatterTest("%v", pv3, "<*>"+v3s2) - addFormatterTest("%v", &pv3, "<**>"+v3s2) - addFormatterTest("%+v", v3, v3s3) - addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s4) - addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s4) - addFormatterTest("%#v", v3, "("+v3t+")"+v3s5) - addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s6) - addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s6) - addFormatterTest("%#+v", v3, "("+v3t+")"+v3s7) - addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s8) - addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s8) -} - -func addPanicFormatterTests() { - // Type that panics in its Stringer interface. - v := panicer(127) - nv := (*panicer)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "spew_test.panicer" - vs := "(PANIC=test panic)127" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") -} - -func addErrorFormatterTests() { - // Type that has a custom Error interface. - v := customError(127) - nv := (*customError)(nil) - pv := &v - vAddr := fmt.Sprintf("%p", pv) - pvAddr := fmt.Sprintf("%p", &pv) - vt := "spew_test.customError" - vs := "error: 127" - addFormatterTest("%v", v, vs) - addFormatterTest("%v", pv, "<*>"+vs) - addFormatterTest("%v", &pv, "<**>"+vs) - addFormatterTest("%v", nv, "") - addFormatterTest("%+v", v, vs) - addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs) - addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%+v", nv, "") - addFormatterTest("%#v", v, "("+vt+")"+vs) - addFormatterTest("%#v", pv, "(*"+vt+")"+vs) - addFormatterTest("%#v", &pv, "(**"+vt+")"+vs) - addFormatterTest("%#v", nv, "(*"+vt+")"+"") - addFormatterTest("%#+v", v, "("+vt+")"+vs) - addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs) - addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs) - addFormatterTest("%#+v", nv, "(*"+vt+")"+"") -} - -func addPassthroughFormatterTests() { - // %x passthrough with uint. - v := uint(4294967295) - pv := &v - vAddr := fmt.Sprintf("%x", pv) - pvAddr := fmt.Sprintf("%x", &pv) - vs := "ffffffff" - addFormatterTest("%x", v, vs) - addFormatterTest("%x", pv, vAddr) - addFormatterTest("%x", &pv, pvAddr) - - // %#x passthrough with uint. - v2 := int(2147483647) - pv2 := &v2 - v2Addr := fmt.Sprintf("%#x", pv2) - pv2Addr := fmt.Sprintf("%#x", &pv2) - v2s := "0x7fffffff" - addFormatterTest("%#x", v2, v2s) - addFormatterTest("%#x", pv2, v2Addr) - addFormatterTest("%#x", &pv2, pv2Addr) - - // %f passthrough with precision. - addFormatterTest("%.2f", 3.1415, "3.14") - addFormatterTest("%.3f", 3.1415, "3.142") - addFormatterTest("%.4f", 3.1415, "3.1415") - - // %f passthrough with width and precision. - addFormatterTest("%5.2f", 3.1415, " 3.14") - addFormatterTest("%6.3f", 3.1415, " 3.142") - addFormatterTest("%7.4f", 3.1415, " 3.1415") - - // %d passthrough with width. - addFormatterTest("%3d", 127, "127") - addFormatterTest("%4d", 127, " 127") - addFormatterTest("%5d", 127, " 127") - - // %q passthrough with string. - addFormatterTest("%q", "test", "\"test\"") -} - -// TestFormatter executes all of the tests described by formatterTests. -func TestFormatter(t *testing.T) { - // Setup tests. - addIntFormatterTests() - addUintFormatterTests() - addBoolFormatterTests() - addFloatFormatterTests() - addComplexFormatterTests() - addArrayFormatterTests() - addSliceFormatterTests() - addStringFormatterTests() - addInterfaceFormatterTests() - addMapFormatterTests() - addStructFormatterTests() - addUintptrFormatterTests() - addUnsafePointerFormatterTests() - addChanFormatterTests() - addFuncFormatterTests() - addCircularFormatterTests() - addPanicFormatterTests() - addErrorFormatterTests() - addPassthroughFormatterTests() - - t.Logf("Running %d tests", len(formatterTests)) - for i, test := range formatterTests { - buf := new(bytes.Buffer) - spew.Fprintf(buf, test.format, test.in) - s := buf.String() - if testFailed(s, test.wants) { - t.Errorf("Formatter #%d format: %s got: %s %s", i, test.format, s, - stringizeWants(test.wants)) - continue - } - } -} - -type testStruct struct { - x int -} - -func (ts testStruct) String() string { - return fmt.Sprintf("ts.%d", ts.x) -} - -type testStructP struct { - x int -} - -func (ts *testStructP) String() string { - return fmt.Sprintf("ts.%d", ts.x) -} - -func TestPrintSortedKeys(t *testing.T) { - cfg := spew.ConfigState{SortKeys: true} - s := cfg.Sprint(map[int]string{1: "1", 3: "3", 2: "2"}) - expected := "map[1:1 2:2 3:3]" - if s != expected { - t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) - } - - s = cfg.Sprint(map[stringer]int{"1": 1, "3": 3, "2": 2}) - expected = "map[stringer 1:1 stringer 2:2 stringer 3:3]" - if s != expected { - t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) - } - - s = cfg.Sprint(map[pstringer]int{pstringer("1"): 1, pstringer("3"): 3, pstringer("2"): 2}) - expected = "map[stringer 1:1 stringer 2:2 stringer 3:3]" - if s != expected { - t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) - } - - s = cfg.Sprint(map[testStruct]int{testStruct{1}: 1, testStruct{3}: 3, testStruct{2}: 2}) - expected = "map[ts.1:1 ts.2:2 ts.3:3]" - if s != expected { - t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) - } - - s = cfg.Sprint(map[testStructP]int{testStructP{1}: 1, testStructP{3}: 3, testStructP{2}: 2}) - expected = "map[ts.1:1 ts.2:2 ts.3:3]" - if s != expected { - t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) - } - - s = cfg.Sprint(map[customError]int{customError(1): 1, customError(3): 3, customError(2): 2}) - expected = "map[error: 1:1 error: 2:2 error: 3:3]" - if s != expected { - t.Errorf("Sorted keys mismatch:\n %v %v", s, expected) - } -} diff --git a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/internal_test.go b/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/internal_test.go deleted file mode 100644 index b583bfdef..000000000 --- a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/internal_test.go +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2013 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* -This test file is part of the spew package rather than than the spew_test -package because it needs access to internals to properly test certain cases -which are not possible via the public interface since they should never happen. -*/ - -package spew - -import ( - "bytes" - "reflect" - "testing" - "unsafe" -) - -// dummyFmtState implements a fake fmt.State to use for testing invalid -// reflect.Value handling. This is necessary because the fmt package catches -// invalid values before invoking the formatter on them. -type dummyFmtState struct { - bytes.Buffer -} - -func (dfs *dummyFmtState) Flag(f int) bool { - if f == int('+') { - return true - } - return false -} - -func (dfs *dummyFmtState) Precision() (int, bool) { - return 0, false -} - -func (dfs *dummyFmtState) Width() (int, bool) { - return 0, false -} - -// TestInvalidReflectValue ensures the dump and formatter code handles an -// invalid reflect value properly. This needs access to internal state since it -// should never happen in real code and therefore can't be tested via the public -// API. -func TestInvalidReflectValue(t *testing.T) { - i := 1 - - // Dump invalid reflect value. - v := new(reflect.Value) - buf := new(bytes.Buffer) - d := dumpState{w: buf, cs: &Config} - d.dump(*v) - s := buf.String() - want := "" - if s != want { - t.Errorf("InvalidReflectValue #%d\n got: %s want: %s", i, s, want) - } - i++ - - // Formatter invalid reflect value. - buf2 := new(dummyFmtState) - f := formatState{value: *v, cs: &Config, fs: buf2} - f.format(*v) - s = buf2.String() - want = "" - if s != want { - t.Errorf("InvalidReflectValue #%d got: %s want: %s", i, s, want) - } -} - -// changeKind uses unsafe to intentionally change the kind of a reflect.Value to -// the maximum kind value which does not exist. This is needed to test the -// fallback code which punts to the standard fmt library for new types that -// might get added to the language. -func changeKind(v *reflect.Value, readOnly bool) { - rvf := (*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + offsetFlag)) - *rvf = *rvf | ((1< - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package spew_test - -import ( - "bytes" - "fmt" - "github.com/davecgh/go-spew/spew" - "io/ioutil" - "os" - "testing" -) - -// spewFunc is used to identify which public function of the spew package or -// ConfigState a test applies to. -type spewFunc int - -const ( - fCSFdump spewFunc = iota - fCSFprint - fCSFprintf - fCSFprintln - fCSPrint - fCSPrintln - fCSSdump - fCSSprint - fCSSprintf - fCSSprintln - fCSErrorf - fCSNewFormatter - fErrorf - fFprint - fFprintln - fPrint - fPrintln - fSdump - fSprint - fSprintf - fSprintln -) - -// Map of spewFunc values to names for pretty printing. -var spewFuncStrings = map[spewFunc]string{ - fCSFdump: "ConfigState.Fdump", - fCSFprint: "ConfigState.Fprint", - fCSFprintf: "ConfigState.Fprintf", - fCSFprintln: "ConfigState.Fprintln", - fCSSdump: "ConfigState.Sdump", - fCSPrint: "ConfigState.Print", - fCSPrintln: "ConfigState.Println", - fCSSprint: "ConfigState.Sprint", - fCSSprintf: "ConfigState.Sprintf", - fCSSprintln: "ConfigState.Sprintln", - fCSErrorf: "ConfigState.Errorf", - fCSNewFormatter: "ConfigState.NewFormatter", - fErrorf: "spew.Errorf", - fFprint: "spew.Fprint", - fFprintln: "spew.Fprintln", - fPrint: "spew.Print", - fPrintln: "spew.Println", - fSdump: "spew.Sdump", - fSprint: "spew.Sprint", - fSprintf: "spew.Sprintf", - fSprintln: "spew.Sprintln", -} - -func (f spewFunc) String() string { - if s, ok := spewFuncStrings[f]; ok { - return s - } - return fmt.Sprintf("Unknown spewFunc (%d)", int(f)) -} - -// spewTest is used to describe a test to be performed against the public -// functions of the spew package or ConfigState. -type spewTest struct { - cs *spew.ConfigState - f spewFunc - format string - in interface{} - want string -} - -// spewTests houses the tests to be performed against the public functions of -// the spew package and ConfigState. -// -// These tests are only intended to ensure the public functions are exercised -// and are intentionally not exhaustive of types. The exhaustive type -// tests are handled in the dump and format tests. -var spewTests []spewTest - -// redirStdout is a helper function to return the standard output from f as a -// byte slice. -func redirStdout(f func()) ([]byte, error) { - tempFile, err := ioutil.TempFile("", "ss-test") - if err != nil { - return nil, err - } - fileName := tempFile.Name() - defer os.Remove(fileName) // Ignore error - - origStdout := os.Stdout - os.Stdout = tempFile - f() - os.Stdout = origStdout - tempFile.Close() - - return ioutil.ReadFile(fileName) -} - -func initSpewTests() { - // Config states with various settings. - scsDefault := spew.NewDefaultConfig() - scsNoMethods := &spew.ConfigState{Indent: " ", DisableMethods: true} - scsNoPmethods := &spew.ConfigState{Indent: " ", DisablePointerMethods: true} - scsMaxDepth := &spew.ConfigState{Indent: " ", MaxDepth: 1} - scsContinue := &spew.ConfigState{Indent: " ", ContinueOnMethod: true} - - // Variables for tests on types which implement Stringer interface with and - // without a pointer receiver. - ts := stringer("test") - tps := pstringer("test") - - // depthTester is used to test max depth handling for structs, array, slices - // and maps. - type depthTester struct { - ic indirCir1 - arr [1]string - slice []string - m map[string]int - } - dt := depthTester{indirCir1{nil}, [1]string{"arr"}, []string{"slice"}, - map[string]int{"one": 1}} - - // Variable for tests on types which implement error interface. - te := customError(10) - - spewTests = []spewTest{ - {scsDefault, fCSFdump, "", int8(127), "(int8) 127\n"}, - {scsDefault, fCSFprint, "", int16(32767), "32767"}, - {scsDefault, fCSFprintf, "%v", int32(2147483647), "2147483647"}, - {scsDefault, fCSFprintln, "", int(2147483647), "2147483647\n"}, - {scsDefault, fCSPrint, "", int64(9223372036854775807), "9223372036854775807"}, - {scsDefault, fCSPrintln, "", uint8(255), "255\n"}, - {scsDefault, fCSSdump, "", uint8(64), "(uint8) 64\n"}, - {scsDefault, fCSSprint, "", complex(1, 2), "(1+2i)"}, - {scsDefault, fCSSprintf, "%v", complex(float32(3), 4), "(3+4i)"}, - {scsDefault, fCSSprintln, "", complex(float64(5), 6), "(5+6i)\n"}, - {scsDefault, fCSErrorf, "%#v", uint16(65535), "(uint16)65535"}, - {scsDefault, fCSNewFormatter, "%v", uint32(4294967295), "4294967295"}, - {scsDefault, fErrorf, "%v", uint64(18446744073709551615), "18446744073709551615"}, - {scsDefault, fFprint, "", float32(3.14), "3.14"}, - {scsDefault, fFprintln, "", float64(6.28), "6.28\n"}, - {scsDefault, fPrint, "", true, "true"}, - {scsDefault, fPrintln, "", false, "false\n"}, - {scsDefault, fSdump, "", complex(-10, -20), "(complex128) (-10-20i)\n"}, - {scsDefault, fSprint, "", complex(-1, -2), "(-1-2i)"}, - {scsDefault, fSprintf, "%v", complex(float32(-3), -4), "(-3-4i)"}, - {scsDefault, fSprintln, "", complex(float64(-5), -6), "(-5-6i)\n"}, - {scsNoMethods, fCSFprint, "", ts, "test"}, - {scsNoMethods, fCSFprint, "", &ts, "<*>test"}, - {scsNoMethods, fCSFprint, "", tps, "test"}, - {scsNoMethods, fCSFprint, "", &tps, "<*>test"}, - {scsNoPmethods, fCSFprint, "", ts, "stringer test"}, - {scsNoPmethods, fCSFprint, "", &ts, "<*>stringer test"}, - {scsNoPmethods, fCSFprint, "", tps, "test"}, - {scsNoPmethods, fCSFprint, "", &tps, "<*>stringer test"}, - {scsMaxDepth, fCSFprint, "", dt, "{{} [] [] map[]}"}, - {scsMaxDepth, fCSFdump, "", dt, "(spew_test.depthTester) {\n" + - " ic: (spew_test.indirCir1) {\n \n },\n" + - " arr: ([1]string) (len=1 cap=1) {\n \n },\n" + - " slice: ([]string) (len=1 cap=1) {\n \n },\n" + - " m: (map[string]int) (len=1) {\n \n }\n}\n"}, - {scsContinue, fCSFprint, "", ts, "(stringer test) test"}, - {scsContinue, fCSFdump, "", ts, "(spew_test.stringer) " + - "(len=4) (stringer test) \"test\"\n"}, - {scsContinue, fCSFprint, "", te, "(error: 10) 10"}, - {scsContinue, fCSFdump, "", te, "(spew_test.customError) " + - "(error: 10) 10\n"}, - } -} - -// TestSpew executes all of the tests described by spewTests. -func TestSpew(t *testing.T) { - initSpewTests() - - t.Logf("Running %d tests", len(spewTests)) - for i, test := range spewTests { - buf := new(bytes.Buffer) - switch test.f { - case fCSFdump: - test.cs.Fdump(buf, test.in) - - case fCSFprint: - test.cs.Fprint(buf, test.in) - - case fCSFprintf: - test.cs.Fprintf(buf, test.format, test.in) - - case fCSFprintln: - test.cs.Fprintln(buf, test.in) - - case fCSPrint: - b, err := redirStdout(func() { test.cs.Print(test.in) }) - if err != nil { - t.Errorf("%v #%d %v", test.f, i, err) - continue - } - buf.Write(b) - - case fCSPrintln: - b, err := redirStdout(func() { test.cs.Println(test.in) }) - if err != nil { - t.Errorf("%v #%d %v", test.f, i, err) - continue - } - buf.Write(b) - - case fCSSdump: - str := test.cs.Sdump(test.in) - buf.WriteString(str) - - case fCSSprint: - str := test.cs.Sprint(test.in) - buf.WriteString(str) - - case fCSSprintf: - str := test.cs.Sprintf(test.format, test.in) - buf.WriteString(str) - - case fCSSprintln: - str := test.cs.Sprintln(test.in) - buf.WriteString(str) - - case fCSErrorf: - err := test.cs.Errorf(test.format, test.in) - buf.WriteString(err.Error()) - - case fCSNewFormatter: - fmt.Fprintf(buf, test.format, test.cs.NewFormatter(test.in)) - - case fErrorf: - err := spew.Errorf(test.format, test.in) - buf.WriteString(err.Error()) - - case fFprint: - spew.Fprint(buf, test.in) - - case fFprintln: - spew.Fprintln(buf, test.in) - - case fPrint: - b, err := redirStdout(func() { spew.Print(test.in) }) - if err != nil { - t.Errorf("%v #%d %v", test.f, i, err) - continue - } - buf.Write(b) - - case fPrintln: - b, err := redirStdout(func() { spew.Println(test.in) }) - if err != nil { - t.Errorf("%v #%d %v", test.f, i, err) - continue - } - buf.Write(b) - - case fSdump: - str := spew.Sdump(test.in) - buf.WriteString(str) - - case fSprint: - str := spew.Sprint(test.in) - buf.WriteString(str) - - case fSprintf: - str := spew.Sprintf(test.format, test.in) - buf.WriteString(str) - - case fSprintln: - str := spew.Sprintln(test.in) - buf.WriteString(str) - - default: - t.Errorf("%v #%d unrecognized function", test.f, i) - continue - } - s := buf.String() - if test.want != s { - t.Errorf("ConfigState #%d\n got: %s want: %s", i, s, test.want) - continue - } - } -} diff --git a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/testdata/dumpcgo.go b/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/testdata/dumpcgo.go deleted file mode 100644 index 5c87dd456..000000000 --- a/Godeps/_workspace/src/github.com/davecgh/go-spew/spew/testdata/dumpcgo.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2013 Dave Collins -// -// Permission to use, copy, modify, and distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -// NOTE: Due to the following build constraints, this file will only be compiled -// when both cgo is supported and "-tags testcgo" is added to the go test -// command line. This code should really only be in the dumpcgo_test.go file, -// but unfortunately Go will not allow cgo in test files, so this is a -// workaround to allow cgo types to be tested. This configuration is used -// because spew itself does not require cgo to run even though it does handle -// certain cgo types specially. Rather than forcing all clients to require cgo -// and an external C compiler just to run the tests, this scheme makes them -// optional. -// +build cgo,testcgo - -package testdata - -/* -#include -typedef unsigned char custom_uchar_t; - -char *ncp = 0; -char *cp = "test"; -char ca[6] = {'t', 'e', 's', 't', '2', '\0'}; -unsigned char uca[6] = {'t', 'e', 's', 't', '3', '\0'}; -signed char sca[6] = {'t', 'e', 's', 't', '4', '\0'}; -uint8_t ui8ta[6] = {'t', 'e', 's', 't', '5', '\0'}; -custom_uchar_t tuca[6] = {'t', 'e', 's', 't', '6', '\0'}; -*/ -import "C" - -// GetCgoNullCharPointer returns a null char pointer via cgo. This is only -// used for tests. -func GetCgoNullCharPointer() interface{} { - return C.ncp -} - -// GetCgoCharPointer returns a char pointer via cgo. This is only used for -// tests. -func GetCgoCharPointer() interface{} { - return C.cp -} - -// GetCgoCharArray returns a char array via cgo and the array's len and cap. -// This is only used for tests. -func GetCgoCharArray() (interface{}, int, int) { - return C.ca, len(C.ca), cap(C.ca) -} - -// GetCgoUnsignedCharArray returns an unsigned char array via cgo and the -// array's len and cap. This is only used for tests. -func GetCgoUnsignedCharArray() (interface{}, int, int) { - return C.uca, len(C.uca), cap(C.uca) -} - -// GetCgoSignedCharArray returns a signed char array via cgo and the array's len -// and cap. This is only used for tests. -func GetCgoSignedCharArray() (interface{}, int, int) { - return C.sca, len(C.sca), cap(C.sca) -} - -// GetCgoUint8tArray returns a uint8_t array via cgo and the array's len and -// cap. This is only used for tests. -func GetCgoUint8tArray() (interface{}, int, int) { - return C.ui8ta, len(C.ui8ta), cap(C.ui8ta) -} - -// GetCgoTypdefedUnsignedCharArray returns a typedefed unsigned char array via -// cgo and the array's len and cap. This is only used for tests. -func GetCgoTypdefedUnsignedCharArray() (interface{}, int, int) { - return C.tuca, len(C.tuca), cap(C.tuca) -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/LICENSE b/Godeps/_workspace/src/github.com/docker/docker/LICENSE new file mode 100644 index 000000000..c7a3f0cfd --- /dev/null +++ b/Godeps/_workspace/src/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-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/Godeps/_workspace/src/github.com/docker/docker/NOTICE b/Godeps/_workspace/src/github.com/docker/docker/NOTICE new file mode 100644 index 000000000..6e6f469ab --- /dev/null +++ b/Godeps/_workspace/src/github.com/docker/docker/NOTICE @@ -0,0 +1,19 @@ +Docker +Copyright 2012-2015 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/Godeps/_workspace/src/github.com/docker/docker/pkg/mount/mount_test.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/mount/mount_test.go deleted file mode 100644 index 5c7f1b86a..000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/mount/mount_test.go +++ /dev/null @@ -1,137 +0,0 @@ -package mount - -import ( - "os" - "path" - "testing" -) - -func TestMountOptionsParsing(t *testing.T) { - options := "noatime,ro,size=10k" - - flag, data := parseOptions(options) - - if data != "size=10k" { - t.Fatalf("Expected size=10 got %s", data) - } - - expectedFlag := NOATIME | RDONLY - - if flag != expectedFlag { - t.Fatalf("Expected %d got %d", expectedFlag, flag) - } -} - -func TestMounted(t *testing.T) { - tmp := path.Join(os.TempDir(), "mount-tests") - if err := os.MkdirAll(tmp, 0777); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - var ( - sourceDir = path.Join(tmp, "source") - targetDir = path.Join(tmp, "target") - sourcePath = path.Join(sourceDir, "file.txt") - targetPath = path.Join(targetDir, "file.txt") - ) - - os.Mkdir(sourceDir, 0777) - os.Mkdir(targetDir, 0777) - - f, err := os.Create(sourcePath) - if err != nil { - t.Fatal(err) - } - f.WriteString("hello") - f.Close() - - f, err = os.Create(targetPath) - if err != nil { - t.Fatal(err) - } - f.Close() - - if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() - - mounted, err := Mounted(targetDir) - if err != nil { - t.Fatal(err) - } - if !mounted { - t.Fatalf("Expected %s to be mounted", targetDir) - } - if _, err := os.Stat(targetDir); err != nil { - t.Fatal(err) - } -} - -func TestMountReadonly(t *testing.T) { - tmp := path.Join(os.TempDir(), "mount-tests") - if err := os.MkdirAll(tmp, 0777); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - var ( - sourceDir = path.Join(tmp, "source") - targetDir = path.Join(tmp, "target") - sourcePath = path.Join(sourceDir, "file.txt") - targetPath = path.Join(targetDir, "file.txt") - ) - - os.Mkdir(sourceDir, 0777) - os.Mkdir(targetDir, 0777) - - f, err := os.Create(sourcePath) - if err != nil { - t.Fatal(err) - } - f.WriteString("hello") - f.Close() - - f, err = os.Create(targetPath) - if err != nil { - t.Fatal(err) - } - f.Close() - - if err := Mount(sourceDir, targetDir, "none", "bind,ro"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() - - f, err = os.OpenFile(targetPath, os.O_RDWR, 0777) - if err == nil { - t.Fatal("Should not be able to open a ro file as rw") - } -} - -func TestGetMounts(t *testing.T) { - mounts, err := GetMounts() - if err != nil { - t.Fatal(err) - } - - root := false - for _, entry := range mounts { - if entry.Mountpoint == "/" { - root = true - } - } - - if !root { - t.Fatal("/ should be mounted at least") - } -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/mount/mountinfo_linux_test.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/mount/mountinfo_linux_test.go deleted file mode 100644 index 812d12e82..000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/mount/mountinfo_linux_test.go +++ /dev/null @@ -1,477 +0,0 @@ -// +build linux - -package mount - -import ( - "bytes" - "testing" -) - -const ( - fedoraMountinfo = `15 35 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:5 - proc proc rw - 16 35 0:14 / /sys rw,nosuid,nodev,noexec,relatime shared:6 - sysfs sysfs rw,seclabel - 17 35 0:5 / /dev rw,nosuid shared:2 - devtmpfs devtmpfs rw,seclabel,size=8056484k,nr_inodes=2014121,mode=755 - 18 16 0:15 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime shared:7 - securityfs securityfs rw - 19 16 0:13 / /sys/fs/selinux rw,relatime shared:8 - selinuxfs selinuxfs rw - 20 17 0:16 / /dev/shm rw,nosuid,nodev shared:3 - tmpfs tmpfs rw,seclabel - 21 17 0:10 / /dev/pts rw,nosuid,noexec,relatime shared:4 - devpts devpts rw,seclabel,gid=5,mode=620,ptmxmode=000 - 22 35 0:17 / /run rw,nosuid,nodev shared:21 - tmpfs tmpfs rw,seclabel,mode=755 - 23 16 0:18 / /sys/fs/cgroup rw,nosuid,nodev,noexec shared:9 - tmpfs tmpfs rw,seclabel,mode=755 - 24 23 0:19 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd - 25 16 0:20 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime shared:20 - pstore pstore rw - 26 23 0:21 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:11 - cgroup cgroup rw,cpuset,clone_children - 27 23 0:22 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:12 - cgroup cgroup rw,cpuacct,cpu,clone_children - 28 23 0:23 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:13 - cgroup cgroup rw,memory,clone_children - 29 23 0:24 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,devices,clone_children - 30 23 0:25 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,freezer,clone_children - 31 23 0:26 / /sys/fs/cgroup/net_cls rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,net_cls,clone_children - 32 23 0:27 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,blkio,clone_children - 33 23 0:28 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,perf_event,clone_children - 34 23 0:29 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,hugetlb,clone_children - 35 1 253:2 / / rw,relatime shared:1 - ext4 /dev/mapper/ssd-root--f20 rw,seclabel,data=ordered - 36 15 0:30 / /proc/sys/fs/binfmt_misc rw,relatime shared:22 - autofs systemd-1 rw,fd=38,pgrp=1,timeout=300,minproto=5,maxproto=5,direct - 37 17 0:12 / /dev/mqueue rw,relatime shared:23 - mqueue mqueue rw,seclabel - 38 35 0:31 / /tmp rw shared:24 - tmpfs tmpfs rw,seclabel - 39 17 0:32 / /dev/hugepages rw,relatime shared:25 - hugetlbfs hugetlbfs rw,seclabel - 40 16 0:7 / /sys/kernel/debug rw,relatime shared:26 - debugfs debugfs rw - 41 16 0:33 / /sys/kernel/config rw,relatime shared:27 - configfs configfs rw - 42 35 0:34 / /var/lib/nfs/rpc_pipefs rw,relatime shared:28 - rpc_pipefs sunrpc rw - 43 15 0:35 / /proc/fs/nfsd rw,relatime shared:29 - nfsd sunrpc rw - 45 35 8:17 / /boot rw,relatime shared:30 - ext4 /dev/sdb1 rw,seclabel,data=ordered - 46 35 253:4 / /home rw,relatime shared:31 - ext4 /dev/mapper/ssd-home rw,seclabel,data=ordered - 47 35 253:5 / /var/lib/libvirt/images rw,noatime,nodiratime shared:32 - ext4 /dev/mapper/ssd-virt rw,seclabel,discard,data=ordered - 48 35 253:12 / /mnt/old rw,relatime shared:33 - ext4 /dev/mapper/HelpDeskRHEL6-FedoraRoot rw,seclabel,data=ordered - 121 22 0:36 / /run/user/1000/gvfs rw,nosuid,nodev,relatime shared:104 - fuse.gvfsd-fuse gvfsd-fuse rw,user_id=1000,group_id=1000 - 124 16 0:37 / /sys/fs/fuse/connections rw,relatime shared:107 - fusectl fusectl rw - 165 38 253:3 / /tmp/mnt rw,relatime shared:147 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered - 167 35 253:15 / /var/lib/docker/devicemapper/mnt/aae4076022f0e2b80a2afbf8fc6df450c52080191fcef7fb679a73e6f073e5c2 rw,relatime shared:149 - ext4 /dev/mapper/docker-253:2-425882-aae4076022f0e2b80a2afbf8fc6df450c52080191fcef7fb679a73e6f073e5c2 rw,seclabel,discard,stripe=16,data=ordered - 171 35 253:16 / /var/lib/docker/devicemapper/mnt/c71be651f114db95180e472f7871b74fa597ee70a58ccc35cb87139ddea15373 rw,relatime shared:153 - ext4 /dev/mapper/docker-253:2-425882-c71be651f114db95180e472f7871b74fa597ee70a58ccc35cb87139ddea15373 rw,seclabel,discard,stripe=16,data=ordered - 175 35 253:17 / /var/lib/docker/devicemapper/mnt/1bac6ab72862d2d5626560df6197cf12036b82e258c53d981fa29adce6f06c3c rw,relatime shared:157 - ext4 /dev/mapper/docker-253:2-425882-1bac6ab72862d2d5626560df6197cf12036b82e258c53d981fa29adce6f06c3c rw,seclabel,discard,stripe=16,data=ordered - 179 35 253:18 / /var/lib/docker/devicemapper/mnt/d710a357d77158e80d5b2c55710ae07c94e76d34d21ee7bae65ce5418f739b09 rw,relatime shared:161 - ext4 /dev/mapper/docker-253:2-425882-d710a357d77158e80d5b2c55710ae07c94e76d34d21ee7bae65ce5418f739b09 rw,seclabel,discard,stripe=16,data=ordered - 183 35 253:19 / /var/lib/docker/devicemapper/mnt/6479f52366114d5f518db6837254baab48fab39f2ac38d5099250e9a6ceae6c7 rw,relatime shared:165 - ext4 /dev/mapper/docker-253:2-425882-6479f52366114d5f518db6837254baab48fab39f2ac38d5099250e9a6ceae6c7 rw,seclabel,discard,stripe=16,data=ordered - 187 35 253:20 / /var/lib/docker/devicemapper/mnt/8d9df91c4cca5aef49eeb2725292aab324646f723a7feab56be34c2ad08268e1 rw,relatime shared:169 - ext4 /dev/mapper/docker-253:2-425882-8d9df91c4cca5aef49eeb2725292aab324646f723a7feab56be34c2ad08268e1 rw,seclabel,discard,stripe=16,data=ordered - 191 35 253:21 / /var/lib/docker/devicemapper/mnt/c8240b768603d32e920d365dc9d1dc2a6af46cd23e7ae819947f969e1b4ec661 rw,relatime shared:173 - ext4 /dev/mapper/docker-253:2-425882-c8240b768603d32e920d365dc9d1dc2a6af46cd23e7ae819947f969e1b4ec661 rw,seclabel,discard,stripe=16,data=ordered - 195 35 253:22 / /var/lib/docker/devicemapper/mnt/2eb3a01278380bbf3ed12d86ac629eaa70a4351301ee307a5cabe7b5f3b1615f rw,relatime shared:177 - ext4 /dev/mapper/docker-253:2-425882-2eb3a01278380bbf3ed12d86ac629eaa70a4351301ee307a5cabe7b5f3b1615f rw,seclabel,discard,stripe=16,data=ordered - 199 35 253:23 / /var/lib/docker/devicemapper/mnt/37a17fb7c9d9b80821235d5f2662879bd3483915f245f9b49cdaa0e38779b70b rw,relatime shared:181 - ext4 /dev/mapper/docker-253:2-425882-37a17fb7c9d9b80821235d5f2662879bd3483915f245f9b49cdaa0e38779b70b rw,seclabel,discard,stripe=16,data=ordered - 203 35 253:24 / /var/lib/docker/devicemapper/mnt/aea459ae930bf1de913e2f29428fd80ee678a1e962d4080019d9f9774331ee2b rw,relatime shared:185 - ext4 /dev/mapper/docker-253:2-425882-aea459ae930bf1de913e2f29428fd80ee678a1e962d4080019d9f9774331ee2b rw,seclabel,discard,stripe=16,data=ordered - 207 35 253:25 / /var/lib/docker/devicemapper/mnt/928ead0bc06c454bd9f269e8585aeae0a6bd697f46dc8754c2a91309bc810882 rw,relatime shared:189 - ext4 /dev/mapper/docker-253:2-425882-928ead0bc06c454bd9f269e8585aeae0a6bd697f46dc8754c2a91309bc810882 rw,seclabel,discard,stripe=16,data=ordered - 211 35 253:26 / /var/lib/docker/devicemapper/mnt/0f284d18481d671644706e7a7244cbcf63d590d634cc882cb8721821929d0420 rw,relatime shared:193 - ext4 /dev/mapper/docker-253:2-425882-0f284d18481d671644706e7a7244cbcf63d590d634cc882cb8721821929d0420 rw,seclabel,discard,stripe=16,data=ordered - 215 35 253:27 / /var/lib/docker/devicemapper/mnt/d9dd16722ab34c38db2733e23f69e8f4803ce59658250dd63e98adff95d04919 rw,relatime shared:197 - ext4 /dev/mapper/docker-253:2-425882-d9dd16722ab34c38db2733e23f69e8f4803ce59658250dd63e98adff95d04919 rw,seclabel,discard,stripe=16,data=ordered - 219 35 253:28 / /var/lib/docker/devicemapper/mnt/bc4500479f18c2c08c21ad5282e5f826a016a386177d9874c2764751c031d634 rw,relatime shared:201 - ext4 /dev/mapper/docker-253:2-425882-bc4500479f18c2c08c21ad5282e5f826a016a386177d9874c2764751c031d634 rw,seclabel,discard,stripe=16,data=ordered - 223 35 253:29 / /var/lib/docker/devicemapper/mnt/7770c8b24eb3d5cc159a065910076938910d307ab2f5d94e1dc3b24c06ee2c8a rw,relatime shared:205 - ext4 /dev/mapper/docker-253:2-425882-7770c8b24eb3d5cc159a065910076938910d307ab2f5d94e1dc3b24c06ee2c8a rw,seclabel,discard,stripe=16,data=ordered - 227 35 253:30 / /var/lib/docker/devicemapper/mnt/c280cd3d0bf0aa36b478b292279671624cceafc1a67eaa920fa1082601297adf rw,relatime shared:209 - ext4 /dev/mapper/docker-253:2-425882-c280cd3d0bf0aa36b478b292279671624cceafc1a67eaa920fa1082601297adf rw,seclabel,discard,stripe=16,data=ordered - 231 35 253:31 / /var/lib/docker/devicemapper/mnt/8b59a7d9340279f09fea67fd6ad89ddef711e9e7050eb647984f8b5ef006335f rw,relatime shared:213 - ext4 /dev/mapper/docker-253:2-425882-8b59a7d9340279f09fea67fd6ad89ddef711e9e7050eb647984f8b5ef006335f rw,seclabel,discard,stripe=16,data=ordered - 235 35 253:32 / /var/lib/docker/devicemapper/mnt/1a28059f29eda821578b1bb27a60cc71f76f846a551abefabce6efd0146dce9f rw,relatime shared:217 - ext4 /dev/mapper/docker-253:2-425882-1a28059f29eda821578b1bb27a60cc71f76f846a551abefabce6efd0146dce9f rw,seclabel,discard,stripe=16,data=ordered - 239 35 253:33 / /var/lib/docker/devicemapper/mnt/e9aa60c60128cad1 rw,relatime shared:221 - ext4 /dev/mapper/docker-253:2-425882-e9aa60c60128cad1 rw,seclabel,discard,stripe=16,data=ordered - 243 35 253:34 / /var/lib/docker/devicemapper/mnt/5fec11304b6f4713fea7b6ccdcc1adc0a1966187f590fe25a8227428a8df275d-init rw,relatime shared:225 - ext4 /dev/mapper/docker-253:2-425882-5fec11304b6f4713fea7b6ccdcc1adc0a1966187f590fe25a8227428a8df275d-init rw,seclabel,discard,stripe=16,data=ordered - 247 35 253:35 / /var/lib/docker/devicemapper/mnt/5fec11304b6f4713fea7b6ccdcc1adc0a1966187f590fe25a8227428a8df275d rw,relatime shared:229 - ext4 /dev/mapper/docker-253:2-425882-5fec11304b6f4713fea7b6ccdcc1adc0a1966187f590fe25a8227428a8df275d rw,seclabel,discard,stripe=16,data=ordered - 31 21 0:23 / /DATA/foo_bla_bla rw,relatime - cifs //foo/BLA\040BLA\040BLA/ rw,sec=ntlm,cache=loose,unc=\\foo\BLA BLA BLA,username=my_login,domain=mydomain.com,uid=12345678,forceuid,gid=12345678,forcegid,addr=10.1.30.10,file_mode=0755,dir_mode=0755,nounix,rsize=61440,wsize=65536,actimeo=1` - - ubuntuMountInfo = `15 20 0:14 / /sys rw,nosuid,nodev,noexec,relatime - sysfs sysfs rw -16 20 0:3 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw -17 20 0:5 / /dev rw,relatime - devtmpfs udev rw,size=1015140k,nr_inodes=253785,mode=755 -18 17 0:11 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=000 -19 20 0:15 / /run rw,nosuid,noexec,relatime - tmpfs tmpfs rw,size=205044k,mode=755 -20 1 253:0 / / rw,relatime - ext4 /dev/disk/by-label/DOROOT rw,errors=remount-ro,data=ordered -21 15 0:16 / /sys/fs/cgroup rw,relatime - tmpfs none rw,size=4k,mode=755 -22 15 0:17 / /sys/fs/fuse/connections rw,relatime - fusectl none rw -23 15 0:6 / /sys/kernel/debug rw,relatime - debugfs none rw -24 15 0:10 / /sys/kernel/security rw,relatime - securityfs none rw -25 19 0:18 / /run/lock rw,nosuid,nodev,noexec,relatime - tmpfs none rw,size=5120k -26 21 0:19 / /sys/fs/cgroup/cpuset rw,relatime - cgroup cgroup rw,cpuset,clone_children -27 19 0:20 / /run/shm rw,nosuid,nodev,relatime - tmpfs none rw -28 21 0:21 / /sys/fs/cgroup/cpu rw,relatime - cgroup cgroup rw,cpu -29 19 0:22 / /run/user rw,nosuid,nodev,noexec,relatime - tmpfs none rw,size=102400k,mode=755 -30 15 0:23 / /sys/fs/pstore rw,relatime - pstore none rw -31 21 0:24 / /sys/fs/cgroup/cpuacct rw,relatime - cgroup cgroup rw,cpuacct -32 21 0:25 / /sys/fs/cgroup/memory rw,relatime - cgroup cgroup rw,memory -33 21 0:26 / /sys/fs/cgroup/devices rw,relatime - cgroup cgroup rw,devices -34 21 0:27 / /sys/fs/cgroup/freezer rw,relatime - cgroup cgroup rw,freezer -35 21 0:28 / /sys/fs/cgroup/blkio rw,relatime - cgroup cgroup rw,blkio -36 21 0:29 / /sys/fs/cgroup/perf_event rw,relatime - cgroup cgroup rw,perf_event -37 21 0:30 / /sys/fs/cgroup/hugetlb rw,relatime - cgroup cgroup rw,hugetlb -38 21 0:31 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime - cgroup systemd rw,name=systemd -39 20 0:32 / /var/lib/docker/aufs/mnt/b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc rw,relatime - aufs none rw,si=caafa54fdc06525 -40 20 0:33 / /var/lib/docker/aufs/mnt/2eed44ac7ce7c75af04f088ed6cb4ce9d164801e91d78c6db65d7ef6d572bba8-init rw,relatime - aufs none rw,si=caafa54f882b525 -41 20 0:34 / /var/lib/docker/aufs/mnt/2eed44ac7ce7c75af04f088ed6cb4ce9d164801e91d78c6db65d7ef6d572bba8 rw,relatime - aufs none rw,si=caafa54f8829525 -42 20 0:35 / /var/lib/docker/aufs/mnt/16f4d7e96dd612903f425bfe856762f291ff2e36a8ecd55a2209b7d7cd81c30b rw,relatime - aufs none rw,si=caafa54f882d525 -43 20 0:36 / /var/lib/docker/aufs/mnt/63ca08b75d7438a9469a5954e003f48ffede73541f6286ce1cb4d7dd4811da7e-init rw,relatime - aufs none rw,si=caafa54f882f525 -44 20 0:37 / /var/lib/docker/aufs/mnt/63ca08b75d7438a9469a5954e003f48ffede73541f6286ce1cb4d7dd4811da7e rw,relatime - aufs none rw,si=caafa54f88ba525 -45 20 0:38 / /var/lib/docker/aufs/mnt/283f35a910233c756409313be71ecd8fcfef0df57108b8d740b61b3e88860452 rw,relatime - aufs none rw,si=caafa54f88b8525 -46 20 0:39 / /var/lib/docker/aufs/mnt/2c6c7253d4090faa3886871fb21bd660609daeb0206588c0602007f7d0f254b1-init rw,relatime - aufs none rw,si=caafa54f88be525 -47 20 0:40 / /var/lib/docker/aufs/mnt/2c6c7253d4090faa3886871fb21bd660609daeb0206588c0602007f7d0f254b1 rw,relatime - aufs none rw,si=caafa54f882c525 -48 20 0:41 / /var/lib/docker/aufs/mnt/de2b538c97d6366cc80e8658547c923ea1d042f85580df379846f36a4df7049d rw,relatime - aufs none rw,si=caafa54f85bb525 -49 20 0:42 / /var/lib/docker/aufs/mnt/94a3d8ed7c27e5b0aa71eba46c736bfb2742afda038e74f2dd6035fb28415b49-init rw,relatime - aufs none rw,si=caafa54fdc00525 -50 20 0:43 / /var/lib/docker/aufs/mnt/94a3d8ed7c27e5b0aa71eba46c736bfb2742afda038e74f2dd6035fb28415b49 rw,relatime - aufs none rw,si=caafa54fbaec525 -51 20 0:44 / /var/lib/docker/aufs/mnt/6ac1cace985c9fc9bea32234de8b36dba49bdd5e29a2972b327ff939d78a6274 rw,relatime - aufs none rw,si=caafa54f8e1a525 -52 20 0:45 / /var/lib/docker/aufs/mnt/dff147033e3a0ef061e1de1ad34256b523d4a8c1fa6bba71a0ab538e8628ff0b-init rw,relatime - aufs none rw,si=caafa54f8e1d525 -53 20 0:46 / /var/lib/docker/aufs/mnt/dff147033e3a0ef061e1de1ad34256b523d4a8c1fa6bba71a0ab538e8628ff0b rw,relatime - aufs none rw,si=caafa54f8e1b525 -54 20 0:47 / /var/lib/docker/aufs/mnt/cabb117d997f0f93519185aea58389a9762770b7496ed0b74a3e4a083fa45902 rw,relatime - aufs none rw,si=caafa54f810a525 -55 20 0:48 / /var/lib/docker/aufs/mnt/e1c8a94ffaa9d532bbbdc6ef771ce8a6c2c06757806ecaf8b68e9108fec65f33-init rw,relatime - aufs none rw,si=caafa54f8529525 -56 20 0:49 / /var/lib/docker/aufs/mnt/e1c8a94ffaa9d532bbbdc6ef771ce8a6c2c06757806ecaf8b68e9108fec65f33 rw,relatime - aufs none rw,si=caafa54f852f525 -57 20 0:50 / /var/lib/docker/aufs/mnt/16a1526fa445b84ce84f89506d219e87fa488a814063baf045d88b02f21166b3 rw,relatime - aufs none rw,si=caafa54f9e1d525 -58 20 0:51 / /var/lib/docker/aufs/mnt/57b9c92e1e368fa7dbe5079f7462e917777829caae732828b003c355fe49da9f-init rw,relatime - aufs none rw,si=caafa54f854d525 -59 20 0:52 / /var/lib/docker/aufs/mnt/57b9c92e1e368fa7dbe5079f7462e917777829caae732828b003c355fe49da9f rw,relatime - aufs none rw,si=caafa54f854e525 -60 20 0:53 / /var/lib/docker/aufs/mnt/e370c3e286bea027917baa0e4d251262681a472a87056e880dfd0513516dffd9 rw,relatime - aufs none rw,si=caafa54f840a525 -61 20 0:54 / /var/lib/docker/aufs/mnt/6b00d3b4f32b41997ec07412b5e18204f82fbe643e7122251cdeb3582abd424e-init rw,relatime - aufs none rw,si=caafa54f8408525 -62 20 0:55 / /var/lib/docker/aufs/mnt/6b00d3b4f32b41997ec07412b5e18204f82fbe643e7122251cdeb3582abd424e rw,relatime - aufs none rw,si=caafa54f8409525 -63 20 0:56 / /var/lib/docker/aufs/mnt/abd0b5ea5d355a67f911475e271924a5388ee60c27185fcd60d095afc4a09dc7 rw,relatime - aufs none rw,si=caafa54f9eb1525 -64 20 0:57 / /var/lib/docker/aufs/mnt/336222effc3f7b89867bb39ff7792ae5412c35c749f127c29159d046b6feedd2-init rw,relatime - aufs none rw,si=caafa54f85bf525 -65 20 0:58 / /var/lib/docker/aufs/mnt/336222effc3f7b89867bb39ff7792ae5412c35c749f127c29159d046b6feedd2 rw,relatime - aufs none rw,si=caafa54f85b8525 -66 20 0:59 / /var/lib/docker/aufs/mnt/912e1bf28b80a09644503924a8a1a4fb8ed10b808ca847bda27a369919aa52fa rw,relatime - aufs none rw,si=caafa54fbaea525 -67 20 0:60 / /var/lib/docker/aufs/mnt/386f722875013b4a875118367abc783fc6617a3cb7cf08b2b4dcf550b4b9c576-init rw,relatime - aufs none rw,si=caafa54f8472525 -68 20 0:61 / /var/lib/docker/aufs/mnt/386f722875013b4a875118367abc783fc6617a3cb7cf08b2b4dcf550b4b9c576 rw,relatime - aufs none rw,si=caafa54f8474525 -69 20 0:62 / /var/lib/docker/aufs/mnt/5aaebb79ef3097dfca377889aeb61a0c9d5e3795117d2b08d0751473c671dfb2 rw,relatime - aufs none rw,si=caafa54f8c5e525 -70 20 0:63 / /var/lib/docker/aufs/mnt/5ba3e493279d01277d583600b81c7c079e691b73c3a2bdea8e4b12a35a418be2-init rw,relatime - aufs none rw,si=caafa54f8c3b525 -71 20 0:64 / /var/lib/docker/aufs/mnt/5ba3e493279d01277d583600b81c7c079e691b73c3a2bdea8e4b12a35a418be2 rw,relatime - aufs none rw,si=caafa54f8c3d525 -72 20 0:65 / /var/lib/docker/aufs/mnt/2777f0763da4de93f8bebbe1595cc77f739806a158657b033eca06f827b6028a rw,relatime - aufs none rw,si=caafa54f8c3e525 -73 20 0:66 / /var/lib/docker/aufs/mnt/5d7445562acf73c6f0ae34c3dd0921d7457de1ba92a587d9e06a44fa209eeb3e-init rw,relatime - aufs none rw,si=caafa54f8c39525 -74 20 0:67 / /var/lib/docker/aufs/mnt/5d7445562acf73c6f0ae34c3dd0921d7457de1ba92a587d9e06a44fa209eeb3e rw,relatime - aufs none rw,si=caafa54f854f525 -75 20 0:68 / /var/lib/docker/aufs/mnt/06400b526ec18b66639c96efc41a84f4ae0b117cb28dafd56be420651b4084a0 rw,relatime - aufs none rw,si=caafa54f840b525 -76 20 0:69 / /var/lib/docker/aufs/mnt/e051d45ec42d8e3e1cc57bb39871a40de486dc123522e9c067fbf2ca6a357785-init rw,relatime - aufs none rw,si=caafa54fdddf525 -77 20 0:70 / /var/lib/docker/aufs/mnt/e051d45ec42d8e3e1cc57bb39871a40de486dc123522e9c067fbf2ca6a357785 rw,relatime - aufs none rw,si=caafa54f854b525 -78 20 0:71 / /var/lib/docker/aufs/mnt/1ff414fa93fd61ec81b0ab7b365a841ff6545accae03cceac702833aaeaf718f rw,relatime - aufs none rw,si=caafa54f8d85525 -79 20 0:72 / /var/lib/docker/aufs/mnt/c661b2f871dd5360e46a2aebf8f970f6d39a2ff64e06979aa0361227c88128b8-init rw,relatime - aufs none rw,si=caafa54f8da3525 -80 20 0:73 / /var/lib/docker/aufs/mnt/c661b2f871dd5360e46a2aebf8f970f6d39a2ff64e06979aa0361227c88128b8 rw,relatime - aufs none rw,si=caafa54f8da2525 -81 20 0:74 / /var/lib/docker/aufs/mnt/b68b1d4fe4d30016c552398e78b379a39f651661d8e1fa5f2460c24a5e723420 rw,relatime - aufs none rw,si=caafa54f8d81525 -82 20 0:75 / /var/lib/docker/aufs/mnt/c5c5979c936cd0153a4c626fa9d69ce4fce7d924cc74fa68b025d2f585031739-init rw,relatime - aufs none rw,si=caafa54f8da1525 -83 20 0:76 / /var/lib/docker/aufs/mnt/c5c5979c936cd0153a4c626fa9d69ce4fce7d924cc74fa68b025d2f585031739 rw,relatime - aufs none rw,si=caafa54f8da0525 -84 20 0:77 / /var/lib/docker/aufs/mnt/53e10b0329afc0e0d3322d31efaed4064139dc7027fe6ae445cffd7104bcc94f rw,relatime - aufs none rw,si=caafa54f8c35525 -85 20 0:78 / /var/lib/docker/aufs/mnt/3bfafd09ff2603e2165efacc2215c1f51afabba6c42d04a68cc2df0e8cc31494-init rw,relatime - aufs none rw,si=caafa54f8db8525 -86 20 0:79 / /var/lib/docker/aufs/mnt/3bfafd09ff2603e2165efacc2215c1f51afabba6c42d04a68cc2df0e8cc31494 rw,relatime - aufs none rw,si=caafa54f8dba525 -87 20 0:80 / /var/lib/docker/aufs/mnt/90fdd2c03eeaf65311f88f4200e18aef6d2772482712d9aea01cd793c64781b5 rw,relatime - aufs none rw,si=caafa54f8315525 -88 20 0:81 / /var/lib/docker/aufs/mnt/7bdf2591c06c154ceb23f5e74b1d03b18fbf6fe96e35fbf539b82d446922442f-init rw,relatime - aufs none rw,si=caafa54f8fc6525 -89 20 0:82 / /var/lib/docker/aufs/mnt/7bdf2591c06c154ceb23f5e74b1d03b18fbf6fe96e35fbf539b82d446922442f rw,relatime - aufs none rw,si=caafa54f8468525 -90 20 0:83 / /var/lib/docker/aufs/mnt/8cf9a993f50f3305abad3da268c0fc44ff78a1e7bba595ef9de963497496c3f9 rw,relatime - aufs none rw,si=caafa54f8c59525 -91 20 0:84 / /var/lib/docker/aufs/mnt/ecc896fd74b21840a8d35e8316b92a08b1b9c83d722a12acff847e9f0ff17173-init rw,relatime - aufs none rw,si=caafa54f846a525 -92 20 0:85 / /var/lib/docker/aufs/mnt/ecc896fd74b21840a8d35e8316b92a08b1b9c83d722a12acff847e9f0ff17173 rw,relatime - aufs none rw,si=caafa54f846b525 -93 20 0:86 / /var/lib/docker/aufs/mnt/d8c8288ec920439a48b5796bab5883ee47a019240da65e8d8f33400c31bac5df rw,relatime - aufs none rw,si=caafa54f8dbf525 -94 20 0:87 / /var/lib/docker/aufs/mnt/ecba66710bcd03199b9398e46c005cd6b68d0266ec81dc8b722a29cc417997c6-init rw,relatime - aufs none rw,si=caafa54f810f525 -95 20 0:88 / /var/lib/docker/aufs/mnt/ecba66710bcd03199b9398e46c005cd6b68d0266ec81dc8b722a29cc417997c6 rw,relatime - aufs none rw,si=caafa54fbae9525 -96 20 0:89 / /var/lib/docker/aufs/mnt/befc1c67600df449dddbe796c0d06da7caff1d2bbff64cde1f0ba82d224996b5 rw,relatime - aufs none rw,si=caafa54f8dab525 -97 20 0:90 / /var/lib/docker/aufs/mnt/c9f470e73d2742629cdc4084a1b2c1a8302914f2aa0d0ec4542371df9a050562-init rw,relatime - aufs none rw,si=caafa54fdc02525 -98 20 0:91 / /var/lib/docker/aufs/mnt/c9f470e73d2742629cdc4084a1b2c1a8302914f2aa0d0ec4542371df9a050562 rw,relatime - aufs none rw,si=caafa54f9eb0525 -99 20 0:92 / /var/lib/docker/aufs/mnt/2a31f10029f04ff9d4381167a9b739609853d7220d55a56cb654779a700ee246 rw,relatime - aufs none rw,si=caafa54f8c37525 -100 20 0:93 / /var/lib/docker/aufs/mnt/8c4261b8e3e4b21ebba60389bd64b6261217e7e6b9fd09e201d5a7f6760f6927-init rw,relatime - aufs none rw,si=caafa54fd173525 -101 20 0:94 / /var/lib/docker/aufs/mnt/8c4261b8e3e4b21ebba60389bd64b6261217e7e6b9fd09e201d5a7f6760f6927 rw,relatime - aufs none rw,si=caafa54f8108525 -102 20 0:95 / /var/lib/docker/aufs/mnt/eaa0f57403a3dc685268f91df3fbcd7a8423cee50e1a9ee5c3e1688d9d676bb4 rw,relatime - aufs none rw,si=caafa54f852d525 -103 20 0:96 / /var/lib/docker/aufs/mnt/9cfe69a2cbffd9bfc7f396d4754f6fe5cc457ef417b277797be3762dfe955a6b-init rw,relatime - aufs none rw,si=caafa54f8d80525 -104 20 0:97 / /var/lib/docker/aufs/mnt/9cfe69a2cbffd9bfc7f396d4754f6fe5cc457ef417b277797be3762dfe955a6b rw,relatime - aufs none rw,si=caafa54f8fc3525 -105 20 0:98 / /var/lib/docker/aufs/mnt/d1b322ae17613c6adee84e709641a9244ac56675244a89a64dc0075075fcbb83 rw,relatime - aufs none rw,si=caafa54f8c58525 -106 20 0:99 / /var/lib/docker/aufs/mnt/d46c2a8e9da7e91ab34fd9c192851c246a4e770a46720bda09e55c7554b9dbbd-init rw,relatime - aufs none rw,si=caafa54f8c63525 -107 20 0:100 / /var/lib/docker/aufs/mnt/d46c2a8e9da7e91ab34fd9c192851c246a4e770a46720bda09e55c7554b9dbbd rw,relatime - aufs none rw,si=caafa54f8c67525 -108 20 0:101 / /var/lib/docker/aufs/mnt/bc9d2a264158f83a617a069bf17cbbf2a2ba453db7d3951d9dc63cc1558b1c2b rw,relatime - aufs none rw,si=caafa54f8dbe525 -109 20 0:102 / /var/lib/docker/aufs/mnt/9e6abb8d72bbeb4d5cf24b96018528015ba830ce42b4859965bd482cbd034e99-init rw,relatime - aufs none rw,si=caafa54f9e0d525 -110 20 0:103 / /var/lib/docker/aufs/mnt/9e6abb8d72bbeb4d5cf24b96018528015ba830ce42b4859965bd482cbd034e99 rw,relatime - aufs none rw,si=caafa54f9e1b525 -111 20 0:104 / /var/lib/docker/aufs/mnt/d4dca7b02569c732e740071e1c654d4ad282de5c41edb619af1f0aafa618be26 rw,relatime - aufs none rw,si=caafa54f8dae525 -112 20 0:105 / /var/lib/docker/aufs/mnt/fea63da40fa1c5ffbad430dde0bc64a8fc2edab09a051fff55b673c40a08f6b7-init rw,relatime - aufs none rw,si=caafa54f8c5c525 -113 20 0:106 / /var/lib/docker/aufs/mnt/fea63da40fa1c5ffbad430dde0bc64a8fc2edab09a051fff55b673c40a08f6b7 rw,relatime - aufs none rw,si=caafa54fd172525 -114 20 0:107 / /var/lib/docker/aufs/mnt/e60c57499c0b198a6734f77f660cdbbd950a5b78aa23f470ca4f0cfcc376abef rw,relatime - aufs none rw,si=caafa54909c4525 -115 20 0:108 / /var/lib/docker/aufs/mnt/099c78e7ccd9c8717471bb1bbfff838c0a9913321ba2f214fbeaf92c678e5b35-init rw,relatime - aufs none rw,si=caafa54909c3525 -116 20 0:109 / /var/lib/docker/aufs/mnt/099c78e7ccd9c8717471bb1bbfff838c0a9913321ba2f214fbeaf92c678e5b35 rw,relatime - aufs none rw,si=caafa54909c7525 -117 20 0:110 / /var/lib/docker/aufs/mnt/2997be666d58b9e71469759bcb8bd9608dad0e533a1a7570a896919ba3388825 rw,relatime - aufs none rw,si=caafa54f8557525 -118 20 0:111 / /var/lib/docker/aufs/mnt/730694eff438ef20569df38dfb38a920969d7ff2170cc9aa7cb32a7ed8147a93-init rw,relatime - aufs none rw,si=caafa54c6e88525 -119 20 0:112 / /var/lib/docker/aufs/mnt/730694eff438ef20569df38dfb38a920969d7ff2170cc9aa7cb32a7ed8147a93 rw,relatime - aufs none rw,si=caafa54c6e8e525 -120 20 0:113 / /var/lib/docker/aufs/mnt/a672a1e2f2f051f6e19ed1dfbe80860a2d774174c49f7c476695f5dd1d5b2f67 rw,relatime - aufs none rw,si=caafa54c6e15525 -121 20 0:114 / /var/lib/docker/aufs/mnt/aba3570e17859f76cf29d282d0d150659c6bd80780fdc52a465ba05245c2a420-init rw,relatime - aufs none rw,si=caafa54f8dad525 -122 20 0:115 / /var/lib/docker/aufs/mnt/aba3570e17859f76cf29d282d0d150659c6bd80780fdc52a465ba05245c2a420 rw,relatime - aufs none rw,si=caafa54f8d84525 -123 20 0:116 / /var/lib/docker/aufs/mnt/2abc86007aca46fb4a817a033e2a05ccacae40b78ea4b03f8ea616b9ada40e2e rw,relatime - aufs none rw,si=caafa54c6e8b525 -124 20 0:117 / /var/lib/docker/aufs/mnt/36352f27f7878e648367a135bd1ec3ed497adcb8ac13577ee892a0bd921d2374-init rw,relatime - aufs none rw,si=caafa54c6e8d525 -125 20 0:118 / /var/lib/docker/aufs/mnt/36352f27f7878e648367a135bd1ec3ed497adcb8ac13577ee892a0bd921d2374 rw,relatime - aufs none rw,si=caafa54f8c34525 -126 20 0:119 / /var/lib/docker/aufs/mnt/2f95ca1a629cea8363b829faa727dd52896d5561f2c96ddee4f697ea2fc872c2 rw,relatime - aufs none rw,si=caafa54c6e8a525 -127 20 0:120 / /var/lib/docker/aufs/mnt/f108c8291654f179ef143a3e07de2b5a34adbc0b28194a0ab17742b6db9a7fb2-init rw,relatime - aufs none rw,si=caafa54f8e19525 -128 20 0:121 / /var/lib/docker/aufs/mnt/f108c8291654f179ef143a3e07de2b5a34adbc0b28194a0ab17742b6db9a7fb2 rw,relatime - aufs none rw,si=caafa54fa8c6525 -129 20 0:122 / /var/lib/docker/aufs/mnt/c1d04dfdf8cccb3676d5a91e84e9b0781ce40623d127d038bcfbe4c761b27401 rw,relatime - aufs none rw,si=caafa54f8c30525 -130 20 0:123 / /var/lib/docker/aufs/mnt/3f4898ffd0e1239aeebf1d1412590cdb7254207fa3883663e2c40cf772e5f05a-init rw,relatime - aufs none rw,si=caafa54c6e1a525 -131 20 0:124 / /var/lib/docker/aufs/mnt/3f4898ffd0e1239aeebf1d1412590cdb7254207fa3883663e2c40cf772e5f05a rw,relatime - aufs none rw,si=caafa54c6e1c525 -132 20 0:125 / /var/lib/docker/aufs/mnt/5ae3b6fccb1539fc02d420e86f3e9637bef5b711fed2ca31a2f426c8f5deddbf rw,relatime - aufs none rw,si=caafa54c4fea525 -133 20 0:126 / /var/lib/docker/aufs/mnt/310bfaf80d57020f2e73b06aeffb0b9b0ca2f54895f88bf5e4d1529ccac58fe0-init rw,relatime - aufs none rw,si=caafa54c6e1e525 -134 20 0:127 / /var/lib/docker/aufs/mnt/310bfaf80d57020f2e73b06aeffb0b9b0ca2f54895f88bf5e4d1529ccac58fe0 rw,relatime - aufs none rw,si=caafa54fa8c0525 -135 20 0:128 / /var/lib/docker/aufs/mnt/f382bd5aaccaf2d04a59089ac7cb12ec87efd769fd0c14d623358fbfd2a3f896 rw,relatime - aufs none rw,si=caafa54c4fec525 -136 20 0:129 / /var/lib/docker/aufs/mnt/50d45e9bb2d779bc6362824085564c7578c231af5ae3b3da116acf7e17d00735-init rw,relatime - aufs none rw,si=caafa54c4fef525 -137 20 0:130 / /var/lib/docker/aufs/mnt/50d45e9bb2d779bc6362824085564c7578c231af5ae3b3da116acf7e17d00735 rw,relatime - aufs none rw,si=caafa54c4feb525 -138 20 0:131 / /var/lib/docker/aufs/mnt/a9c5ee0854dc083b6bf62b7eb1e5291aefbb10702289a446471ce73aba0d5d7d rw,relatime - aufs none rw,si=caafa54909c6525 -139 20 0:134 / /var/lib/docker/aufs/mnt/03a613e7bd5078819d1fd92df4e671c0127559a5e0b5a885cc8d5616875162f0-init rw,relatime - aufs none rw,si=caafa54804fe525 -140 20 0:135 / /var/lib/docker/aufs/mnt/03a613e7bd5078819d1fd92df4e671c0127559a5e0b5a885cc8d5616875162f0 rw,relatime - aufs none rw,si=caafa54804fa525 -141 20 0:136 / /var/lib/docker/aufs/mnt/7ec3277e5c04c907051caf9c9c35889f5fcd6463e5485971b25404566830bb70 rw,relatime - aufs none rw,si=caafa54804f9525 -142 20 0:139 / /var/lib/docker/aufs/mnt/26b5b5d71d79a5b2bfcf8bc4b2280ee829f261eb886745dd90997ed410f7e8b8-init rw,relatime - aufs none rw,si=caafa54c6ef6525 -143 20 0:140 / /var/lib/docker/aufs/mnt/26b5b5d71d79a5b2bfcf8bc4b2280ee829f261eb886745dd90997ed410f7e8b8 rw,relatime - aufs none rw,si=caafa54c6ef5525 -144 20 0:356 / /var/lib/docker/aufs/mnt/e6ecde9e2c18cd3c75f424c67b6d89685cfee0fc67abf2cb6bdc0867eb998026 rw,relatime - aufs none rw,si=caafa548068e525` - - gentooMountinfo = `15 1 8:6 / / rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered -16 15 0:3 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw -17 15 0:14 / /run rw,nosuid,nodev,relatime - tmpfs tmpfs rw,size=3292172k,mode=755 -18 15 0:5 / /dev rw,nosuid,relatime - devtmpfs udev rw,size=10240k,nr_inodes=4106451,mode=755 -19 18 0:12 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw -20 18 0:10 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=000 -21 18 0:15 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw -22 15 0:16 / /sys rw,nosuid,nodev,noexec,relatime - sysfs sysfs rw -23 22 0:7 / /sys/kernel/debug rw,nosuid,nodev,noexec,relatime - debugfs debugfs rw -24 22 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs cgroup_root rw,size=10240k,mode=755 -25 24 0:18 / /sys/fs/cgroup/openrc rw,nosuid,nodev,noexec,relatime - cgroup openrc rw,release_agent=/lib64/rc/sh/cgroup-release-agent.sh,name=openrc -26 24 0:19 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime - cgroup cpuset rw,cpuset,clone_children -27 24 0:20 / /sys/fs/cgroup/cpu rw,nosuid,nodev,noexec,relatime - cgroup cpu rw,cpu,clone_children -28 24 0:21 / /sys/fs/cgroup/cpuacct rw,nosuid,nodev,noexec,relatime - cgroup cpuacct rw,cpuacct,clone_children -29 24 0:22 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime - cgroup memory rw,memory,clone_children -30 24 0:23 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime - cgroup devices rw,devices,clone_children -31 24 0:24 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime - cgroup freezer rw,freezer,clone_children -32 24 0:25 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime - cgroup blkio rw,blkio,clone_children -33 15 8:1 / /boot rw,noatime,nodiratime - vfat /dev/sda1 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro -34 15 8:18 / /mnt/xfs rw,noatime,nodiratime - xfs /dev/sdb2 rw,attr2,inode64,noquota -35 15 0:26 / /tmp rw,relatime - tmpfs tmpfs rw -36 16 0:27 / /proc/sys/fs/binfmt_misc rw,nosuid,nodev,noexec,relatime - binfmt_misc binfmt_misc rw -42 15 0:33 / /var/lib/nfs/rpc_pipefs rw,relatime - rpc_pipefs rpc_pipefs rw -43 16 0:34 / /proc/fs/nfsd rw,nosuid,nodev,noexec,relatime - nfsd nfsd rw -44 15 0:35 / /home/tianon/.gvfs rw,nosuid,nodev,relatime - fuse.gvfs-fuse-daemon gvfs-fuse-daemon rw,user_id=1000,group_id=1000 -68 15 0:3336 / /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd rw,relatime - aufs none rw,si=9b4a7640128db39c -85 68 8:6 /var/lib/docker/init/dockerinit-0.7.2-dev//deleted /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/.dockerinit rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered -86 68 8:6 /var/lib/docker/containers/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/config.env /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/.dockerenv rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered -87 68 8:6 /etc/resolv.conf /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/etc/resolv.conf rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered -88 68 8:6 /var/lib/docker/containers/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/hostname /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/etc/hostname rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered -89 68 8:6 /var/lib/docker/containers/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/hosts /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/etc/hosts rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered -38 15 0:3384 / /var/lib/docker/aufs/mnt/0292005a9292401bb5197657f2b682d97d8edcb3b72b5e390d2a680139985b55 rw,relatime - aufs none rw,si=9b4a7642b584939c -39 15 0:3385 / /var/lib/docker/aufs/mnt/59db98c889de5f71b70cfb82c40cbe47b64332f0f56042a2987a9e5df6e5e3aa rw,relatime - aufs none rw,si=9b4a7642b584e39c -40 15 0:3386 / /var/lib/docker/aufs/mnt/0545f0f2b6548eb9601d08f35a08f5a0a385407d36027a28f58e06e9f61e0278 rw,relatime - aufs none rw,si=9b4a7642b584b39c -41 15 0:3387 / /var/lib/docker/aufs/mnt/d882cfa16d1aa8fe0331a36e79be3d80b151e49f24fc39a39c3fed1735d5feb5 rw,relatime - aufs none rw,si=9b4a76453040039c -45 15 0:3388 / /var/lib/docker/aufs/mnt/055ca3befcb1626e74f5344b3398724ff05c0de0e20021683d04305c9e70a3f6 rw,relatime - aufs none rw,si=9b4a76453040739c -46 15 0:3389 / /var/lib/docker/aufs/mnt/b899e4567a351745d4285e7f1c18fdece75d877deb3041981cd290be348b7aa6 rw,relatime - aufs none rw,si=9b4a7647def4039c -47 15 0:3390 / /var/lib/docker/aufs/mnt/067ca040292c58954c5129f953219accfae0d40faca26b4d05e76ca76a998f16 rw,relatime - aufs none rw,si=9b4a7647def4239c -48 15 0:3391 / /var/lib/docker/aufs/mnt/8c995e7cb6e5082742daeea720e340b021d288d25d92e0412c03d200df308a11 rw,relatime - aufs none rw,si=9b4a764479c1639c -49 15 0:3392 / /var/lib/docker/aufs/mnt/07cc54dfae5b45300efdacdd53cc72c01b9044956a86ce7bff42d087e426096d rw,relatime - aufs none rw,si=9b4a764479c1739c -50 15 0:3393 / /var/lib/docker/aufs/mnt/0a9c95cf4c589c05b06baa79150b0cc1d8e7102759fe3ce4afaabb8247ca4f85 rw,relatime - aufs none rw,si=9b4a7644059c839c -51 15 0:3394 / /var/lib/docker/aufs/mnt/468fa98cececcf4e226e8370f18f4f848d63faf287fb8321a07f73086441a3a0 rw,relatime - aufs none rw,si=9b4a7644059ca39c -52 15 0:3395 / /var/lib/docker/aufs/mnt/0b826192231c5ce066fffb5beff4397337b5fc19a377aa7c6282c7c0ce7f111f rw,relatime - aufs none rw,si=9b4a764479c1339c -53 15 0:3396 / /var/lib/docker/aufs/mnt/93b8ba1b772fbe79709b909c43ea4b2c30d712e53548f467db1ffdc7a384f196 rw,relatime - aufs none rw,si=9b4a7640798a739c -54 15 0:3397 / /var/lib/docker/aufs/mnt/0c0d0acfb506859b12ef18cdfef9ebed0b43a611482403564224bde9149d373c rw,relatime - aufs none rw,si=9b4a7640798a039c -55 15 0:3398 / /var/lib/docker/aufs/mnt/33648c39ab6c7c74af0243d6d6a81b052e9e25ad1e04b19892eb2dde013e358b rw,relatime - aufs none rw,si=9b4a7644b439b39c -56 15 0:3399 / /var/lib/docker/aufs/mnt/0c12bea97a1c958a3c739fb148536c1c89351d48e885ecda8f0499b5cc44407e rw,relatime - aufs none rw,si=9b4a7640798a239c -57 15 0:3400 / /var/lib/docker/aufs/mnt/ed443988ce125f172d7512e84a4de2627405990fd767a16adefa8ce700c19ce8 rw,relatime - aufs none rw,si=9b4a7644c8ed339c -59 15 0:3402 / /var/lib/docker/aufs/mnt/f61612c324ff3c924d3f7a82fb00a0f8d8f73c248c41897061949e9f5ab7e3b1 rw,relatime - aufs none rw,si=9b4a76442810c39c -60 15 0:3403 / /var/lib/docker/aufs/mnt/0f1ee55c6c4e25027b80de8e64b8b6fb542b3b41aa0caab9261da75752e22bfd rw,relatime - aufs none rw,si=9b4a76442810e39c -61 15 0:3404 / /var/lib/docker/aufs/mnt/956f6cc4af5785cb3ee6963dcbca668219437d9b28f513290b1453ac64a34f97 rw,relatime - aufs none rw,si=9b4a7644303ec39c -62 15 0:3405 / /var/lib/docker/aufs/mnt/1099769158c4b4773e2569e38024e8717e400f87a002c41d8cf47cb81b051ba6 rw,relatime - aufs none rw,si=9b4a7644303ee39c -63 15 0:3406 / /var/lib/docker/aufs/mnt/11890ceb98d4442595b676085cd7b21550ab85c5df841e0fba997ff54e3d522d rw,relatime - aufs none rw,si=9b4a7644303ed39c -64 15 0:3407 / /var/lib/docker/aufs/mnt/acdb90dc378e8ed2420b43a6d291f1c789a081cd1904018780cc038fcd7aae53 rw,relatime - aufs none rw,si=9b4a76434be2139c -65 15 0:3408 / /var/lib/docker/aufs/mnt/120e716f19d4714fbe63cc1ed246204f2c1106eefebc6537ba2587d7e7711959 rw,relatime - aufs none rw,si=9b4a76434be2339c -66 15 0:3409 / /var/lib/docker/aufs/mnt/b197b7fffb61d89e0ba1c40de9a9fc0d912e778b3c1bd828cf981ff37c1963bc rw,relatime - aufs none rw,si=9b4a76434be2039c -70 15 0:3412 / /var/lib/docker/aufs/mnt/1434b69d2e1bb18a9f0b96b9cdac30132b2688f5d1379f68a39a5e120c2f93eb rw,relatime - aufs none rw,si=9b4a76434be2639c -71 15 0:3413 / /var/lib/docker/aufs/mnt/16006e83caf33ab5eb0cd6afc92ea2ee8edeff897496b0bb3ec3a75b767374b3 rw,relatime - aufs none rw,si=9b4a7644d790439c -72 15 0:3414 / /var/lib/docker/aufs/mnt/55bfa5f44e94d27f91f79ba901b118b15098449165c87abf1b53ffff147ff164 rw,relatime - aufs none rw,si=9b4a7644d790239c -73 15 0:3415 / /var/lib/docker/aufs/mnt/1912b97a07ab21ccd98a2a27bc779bf3cf364a3138afa3c3e6f7f169a3c3eab5 rw,relatime - aufs none rw,si=9b4a76441822739c -76 15 0:3418 / /var/lib/docker/aufs/mnt/1a7c3292e8879bd91ffd9282e954f643b1db5683093574c248ff14a9609f2f56 rw,relatime - aufs none rw,si=9b4a76438cb7239c -77 15 0:3419 / /var/lib/docker/aufs/mnt/bb1faaf0d076ddba82c2318305a85f490dafa4e8a8640a8db8ed657c439120cc rw,relatime - aufs none rw,si=9b4a76438cb7339c -78 15 0:3420 / /var/lib/docker/aufs/mnt/1ab869f21d2241a73ac840c7f988490313f909ac642eba71d092204fec66dd7c rw,relatime - aufs none rw,si=9b4a76438cb7639c -79 15 0:3421 / /var/lib/docker/aufs/mnt/fd7245b2cfe3890fa5f5b452260e4edf9e7fb7746532ed9d83f7a0d7dbaa610e rw,relatime - aufs none rw,si=9b4a7644bdc0139c -80 15 0:3422 / /var/lib/docker/aufs/mnt/1e5686c5301f26b9b3cd24e322c608913465cc6c5d0dcd7c5e498d1314747d61 rw,relatime - aufs none rw,si=9b4a7644bdc0639c -81 15 0:3423 / /var/lib/docker/aufs/mnt/52edf6ee6e40bfec1e9301a4d4a92ab83d144e2ae4ce5099e99df6138cb844bf rw,relatime - aufs none rw,si=9b4a7644bdc0239c -82 15 0:3424 / /var/lib/docker/aufs/mnt/1ea10fb7085d28cda4904657dff0454e52598d28e1d77e4f2965bbc3666e808f rw,relatime - aufs none rw,si=9b4a76438cb7139c -83 15 0:3425 / /var/lib/docker/aufs/mnt/9c03e98c3593946dbd4087f8d83f9ca262f4a2efdc952ce60690838b9ba6c526 rw,relatime - aufs none rw,si=9b4a76443020639c -84 15 0:3426 / /var/lib/docker/aufs/mnt/220a2344d67437602c6d2cee9a98c46be13f82c2a8063919dd2fad52bf2fb7dd rw,relatime - aufs none rw,si=9b4a76434bff339c -94 15 0:3427 / /var/lib/docker/aufs/mnt/3b32876c5b200312c50baa476ff342248e88c8ea96e6a1032cd53a88738a1cf2 rw,relatime - aufs none rw,si=9b4a76434bff139c -95 15 0:3428 / /var/lib/docker/aufs/mnt/23ee2b8b0d4ae8db6f6d1e168e2c6f79f8a18f953b09f65e0d22cc1e67a3a6fa rw,relatime - aufs none rw,si=9b4a7646c305c39c -96 15 0:3429 / /var/lib/docker/aufs/mnt/e86e6daa70b61b57945fa178222615f3c3d6bcef12c9f28e9f8623d44dc2d429 rw,relatime - aufs none rw,si=9b4a7646c305f39c -97 15 0:3430 / /var/lib/docker/aufs/mnt/2413d07623e80860bb2e9e306fbdee699afd07525785c025c591231e864aa162 rw,relatime - aufs none rw,si=9b4a76434bff039c -98 15 0:3431 / /var/lib/docker/aufs/mnt/adfd622eb22340fc80b429e5564b125668e260bf9068096c46dd59f1386a4b7d rw,relatime - aufs none rw,si=9b4a7646a7a1039c -102 15 0:3435 / /var/lib/docker/aufs/mnt/27cd92e7a91d02e2d6b44d16679a00fb6d169b19b88822891084e7fd1a84882d rw,relatime - aufs none rw,si=9b4a7646f25ec39c -103 15 0:3436 / /var/lib/docker/aufs/mnt/27dfdaf94cfbf45055c748293c37dd68d9140240bff4c646cb09216015914a88 rw,relatime - aufs none rw,si=9b4a7646732f939c -104 15 0:3437 / /var/lib/docker/aufs/mnt/5ed7524aff68dfbf0fc601cbaeac01bab14391850a973dabf3653282a627920f rw,relatime - aufs none rw,si=9b4a7646732f839c -105 15 0:3438 / /var/lib/docker/aufs/mnt/2a0d4767e536beb5785b60e071e3ac8e5e812613ab143a9627bee77d0c9ab062 rw,relatime - aufs none rw,si=9b4a7646732fe39c -106 15 0:3439 / /var/lib/docker/aufs/mnt/dea3fc045d9f4ae51ba952450b948a822cf85c39411489ca5224f6d9a8d02bad rw,relatime - aufs none rw,si=9b4a764012ad839c -107 15 0:3440 / /var/lib/docker/aufs/mnt/2d140a787160798da60cb67c21b1210054ad4dafecdcf832f015995b9aa99cfd rw,relatime - aufs none rw,si=9b4a764012add39c -108 15 0:3441 / /var/lib/docker/aufs/mnt/cb190b2a8e984475914430fbad2382e0d20b9b659f8ef83ae8d170cc672e519c rw,relatime - aufs none rw,si=9b4a76454d9c239c -109 15 0:3442 / /var/lib/docker/aufs/mnt/2f4a012d5a7ffd90256a6e9aa479054b3dddbc3c6a343f26dafbf3196890223b rw,relatime - aufs none rw,si=9b4a76454d9c439c -110 15 0:3443 / /var/lib/docker/aufs/mnt/63cc77904b80c4ffbf49cb974c5d8733dc52ad7640d3ae87554b325d7312d87f rw,relatime - aufs none rw,si=9b4a76454d9c339c -111 15 0:3444 / /var/lib/docker/aufs/mnt/30333e872c451482ea2d235ff2192e875bd234006b238ae2bdde3b91a86d7522 rw,relatime - aufs none rw,si=9b4a76422cebf39c -112 15 0:3445 / /var/lib/docker/aufs/mnt/6c54fc1125da3925cae65b5c9a98f3be55b0a2c2666082e5094a4ba71beb5bff rw,relatime - aufs none rw,si=9b4a7646dd5a439c -113 15 0:3446 / /var/lib/docker/aufs/mnt/3087d48cb01cda9d0a83a9ca301e6ea40e8593d18c4921be4794c91a420ab9a3 rw,relatime - aufs none rw,si=9b4a7646dd5a739c -114 15 0:3447 / /var/lib/docker/aufs/mnt/cc2607462a8f55b179a749b144c3fdbb50678e1a4f3065ea04e283e9b1f1d8e2 rw,relatime - aufs none rw,si=9b4a7646dd5a239c -117 15 0:3450 / /var/lib/docker/aufs/mnt/310c5e8392b29e8658a22e08d96d63936633b7e2c38e8d220047928b00a03d24 rw,relatime - aufs none rw,si=9b4a7647932d739c -118 15 0:3451 / /var/lib/docker/aufs/mnt/38a1f0029406ba9c3b6058f2f406d8a1d23c855046cf355c91d87d446fcc1460 rw,relatime - aufs none rw,si=9b4a76445abc939c -119 15 0:3452 / /var/lib/docker/aufs/mnt/42e109ab7914ae997a11ccd860fd18e4d488c50c044c3240423ce15774b8b62e rw,relatime - aufs none rw,si=9b4a76445abca39c -120 15 0:3453 / /var/lib/docker/aufs/mnt/365d832af0402d052b389c1e9c0d353b48487533d20cd4351df8e24ec4e4f9d8 rw,relatime - aufs none rw,si=9b4a7644066aa39c -121 15 0:3454 / /var/lib/docker/aufs/mnt/d3fa8a24d695b6cda9b64f96188f701963d28bef0473343f8b212df1a2cf1d2b rw,relatime - aufs none rw,si=9b4a7644066af39c -122 15 0:3455 / /var/lib/docker/aufs/mnt/37d4f491919abc49a15d0c7a7cc8383f087573525d7d288accd14f0b4af9eae0 rw,relatime - aufs none rw,si=9b4a7644066ad39c -123 15 0:3456 / /var/lib/docker/aufs/mnt/93902707fe12cbdd0068ce73f2baad4b3a299189b1b19cb5f8a2025e106ae3f5 rw,relatime - aufs none rw,si=9b4a76444445f39c -126 15 0:3459 / /var/lib/docker/aufs/mnt/3b49291670a625b9bbb329ffba99bf7fa7abff80cefef040f8b89e2b3aad4f9f rw,relatime - aufs none rw,si=9b4a7640798a339c -127 15 0:3460 / /var/lib/docker/aufs/mnt/8d9c7b943cc8f854f4d0d4ec19f7c16c13b0cc4f67a41472a072648610cecb59 rw,relatime - aufs none rw,si=9b4a76427383039c -128 15 0:3461 / /var/lib/docker/aufs/mnt/3b6c90036526c376307df71d49c9f5fce334c01b926faa6a78186842de74beac rw,relatime - aufs none rw,si=9b4a7644badd439c -130 15 0:3463 / /var/lib/docker/aufs/mnt/7b24158eeddfb5d31b7e932e406ea4899fd728344335ff8e0765e89ddeb351dd rw,relatime - aufs none rw,si=9b4a7644badd539c -131 15 0:3464 / /var/lib/docker/aufs/mnt/3ead6dd5773765c74850cf6c769f21fe65c29d622ffa712664f9f5b80364ce27 rw,relatime - aufs none rw,si=9b4a7642f469939c -132 15 0:3465 / /var/lib/docker/aufs/mnt/3f825573b29547744a37b65597a9d6d15a8350be4429b7038d126a4c9a8e178f rw,relatime - aufs none rw,si=9b4a7642f469c39c -133 15 0:3466 / /var/lib/docker/aufs/mnt/f67aaaeb3681e5dcb99a41f847087370bd1c206680cb8c7b6a9819fd6c97a331 rw,relatime - aufs none rw,si=9b4a7647cc25939c -134 15 0:3467 / /var/lib/docker/aufs/mnt/41afe6cfb3c1fc2280b869db07699da88552786e28793f0bc048a265c01bd942 rw,relatime - aufs none rw,si=9b4a7647cc25c39c -135 15 0:3468 / /var/lib/docker/aufs/mnt/b8092ea59da34a40b120e8718c3ae9fa8436996edc4fc50e4b99c72dfd81e1af rw,relatime - aufs none rw,si=9b4a76445abc439c -136 15 0:3469 / /var/lib/docker/aufs/mnt/42c69d2cc179e2684458bb8596a9da6dad182c08eae9b74d5f0e615b399f75a5 rw,relatime - aufs none rw,si=9b4a76455ddbe39c -137 15 0:3470 / /var/lib/docker/aufs/mnt/ea0871954acd2d62a211ac60e05969622044d4c74597870c4f818fbb0c56b09b rw,relatime - aufs none rw,si=9b4a76455ddbf39c -138 15 0:3471 / /var/lib/docker/aufs/mnt/4307906b275ab3fc971786b3841ae3217ac85b6756ddeb7ad4ba09cd044c2597 rw,relatime - aufs none rw,si=9b4a76455ddb839c -139 15 0:3472 / /var/lib/docker/aufs/mnt/4390b872928c53500a5035634f3421622ed6299dc1472b631fc45de9f56dc180 rw,relatime - aufs none rw,si=9b4a76402f2fd39c -140 15 0:3473 / /var/lib/docker/aufs/mnt/6bb41e78863b85e4aa7da89455314855c8c3bda64e52a583bab15dc1fa2e80c2 rw,relatime - aufs none rw,si=9b4a76402f2fa39c -141 15 0:3474 / /var/lib/docker/aufs/mnt/4444f583c2a79c66608f4673a32c9c812154f027045fbd558c2d69920c53f835 rw,relatime - aufs none rw,si=9b4a764479dbd39c -142 15 0:3475 / /var/lib/docker/aufs/mnt/6f11883af4a05ea362e0c54df89058da4859f977efd07b6f539e1f55c1d2a668 rw,relatime - aufs none rw,si=9b4a76402f30b39c -143 15 0:3476 / /var/lib/docker/aufs/mnt/453490dd32e7c2e9ef906f995d8fb3c2753923d1a5e0ba3fd3296e2e4dc238e7 rw,relatime - aufs none rw,si=9b4a76402f30c39c -144 15 0:3477 / /var/lib/docker/aufs/mnt/45e5945735ee102b5e891c91650c57ec4b52bb53017d68f02d50ea8a6e230610 rw,relatime - aufs none rw,si=9b4a76423260739c -147 15 0:3480 / /var/lib/docker/aufs/mnt/4727a64a5553a1125f315b96bed10d3073d6988225a292cce732617c925b56ab rw,relatime - aufs none rw,si=9b4a76443030339c -150 15 0:3483 / /var/lib/docker/aufs/mnt/4e348b5187b9a567059306afc72d42e0ec5c893b0d4abd547526d5f9b6fb4590 rw,relatime - aufs none rw,si=9b4a7644f5d8c39c -151 15 0:3484 / /var/lib/docker/aufs/mnt/4efc616bfbc3f906718b052da22e4335f8e9f91ee9b15866ed3a8029645189ef rw,relatime - aufs none rw,si=9b4a7644f5d8939c -152 15 0:3485 / /var/lib/docker/aufs/mnt/83e730ae9754d5adb853b64735472d98dfa17136b8812ac9cfcd1eba7f4e7d2d rw,relatime - aufs none rw,si=9b4a76469aa7139c -153 15 0:3486 / /var/lib/docker/aufs/mnt/4fc5ba8a5b333be2b7eefacccb626772eeec0ae8a6975112b56c9fb36c0d342f rw,relatime - aufs none rw,si=9b4a7640128dc39c -154 15 0:3487 / /var/lib/docker/aufs/mnt/50200d5edff5dfe8d1ef3c78b0bbd709793ac6e936aa16d74ff66f7ea577b6f9 rw,relatime - aufs none rw,si=9b4a7640128da39c -155 15 0:3488 / /var/lib/docker/aufs/mnt/51e5e51604361448f0b9777f38329f414bc5ba9cf238f26d465ff479bd574b61 rw,relatime - aufs none rw,si=9b4a76444f68939c -156 15 0:3489 / /var/lib/docker/aufs/mnt/52a142149aa98bba83df8766bbb1c629a97b9799944ead90dd206c4bdf0b8385 rw,relatime - aufs none rw,si=9b4a76444f68b39c -157 15 0:3490 / /var/lib/docker/aufs/mnt/52dd21a94a00f58a1ed489312fcfffb91578089c76c5650364476f1d5de031bc rw,relatime - aufs none rw,si=9b4a76444f68f39c -158 15 0:3491 / /var/lib/docker/aufs/mnt/ee562415ddaad353ed22c88d0ca768a0c74bfba6333b6e25c46849ee22d990da rw,relatime - aufs none rw,si=9b4a7640128d839c -159 15 0:3492 / /var/lib/docker/aufs/mnt/db47a9e87173f7554f550c8a01891de79cf12acdd32e01f95c1a527a08bdfb2c rw,relatime - aufs none rw,si=9b4a764405a1d39c -160 15 0:3493 / /var/lib/docker/aufs/mnt/55e827bf6d44d930ec0b827c98356eb8b68c3301e2d60d1429aa72e05b4c17df rw,relatime - aufs none rw,si=9b4a764405a1a39c -162 15 0:3495 / /var/lib/docker/aufs/mnt/578dc4e0a87fc37ec081ca098430499a59639c09f6f12a8f48de29828a091aa6 rw,relatime - aufs none rw,si=9b4a76406d7d439c -163 15 0:3496 / /var/lib/docker/aufs/mnt/728cc1cb04fa4bc6f7bf7a90980beda6d8fc0beb71630874c0747b994efb0798 rw,relatime - aufs none rw,si=9b4a76444f20e39c -164 15 0:3497 / /var/lib/docker/aufs/mnt/5850cc4bd9b55aea46c7ad598f1785117607974084ea643580f58ce3222e683a rw,relatime - aufs none rw,si=9b4a7644a824239c -165 15 0:3498 / /var/lib/docker/aufs/mnt/89443b3f766d5a37bc8b84e29da8b84e6a3ea8486d3cf154e2aae1816516e4a8 rw,relatime - aufs none rw,si=9b4a7644a824139c -166 15 0:3499 / /var/lib/docker/aufs/mnt/f5ae8fd5a41a337907d16515bc3162525154b59c32314c695ecd092c3b47943d rw,relatime - aufs none rw,si=9b4a7644a824439c -167 15 0:3500 / /var/lib/docker/aufs/mnt/5a430854f2a03a9e5f7cbc9f3fb46a8ebca526a5b3f435236d8295e5998798f5 rw,relatime - aufs none rw,si=9b4a7647fc82439c -168 15 0:3501 / /var/lib/docker/aufs/mnt/eda16901ae4cead35070c39845cbf1e10bd6b8cb0ffa7879ae2d8a186e460f91 rw,relatime - aufs none rw,si=9b4a76441e0df39c -169 15 0:3502 / /var/lib/docker/aufs/mnt/5a593721430c2a51b119ff86a7e06ea2b37e3b4131f8f1344d402b61b0c8d868 rw,relatime - aufs none rw,si=9b4a764248bad39c -170 15 0:3503 / /var/lib/docker/aufs/mnt/d662ad0a30fbfa902e0962108685b9330597e1ee2abb16dc9462eb5a67fdd23f rw,relatime - aufs none rw,si=9b4a764248bae39c -171 15 0:3504 / /var/lib/docker/aufs/mnt/5bc9de5c79812843fb36eee96bef1ddba812407861f572e33242f4ee10da2c15 rw,relatime - aufs none rw,si=9b4a764248ba839c -172 15 0:3505 / /var/lib/docker/aufs/mnt/5e763de8e9b0f7d58d2e12a341e029ab4efb3b99788b175090d8209e971156c1 rw,relatime - aufs none rw,si=9b4a764248baa39c -173 15 0:3506 / /var/lib/docker/aufs/mnt/b4431dc2739936f1df6387e337f5a0c99cf051900c896bd7fd46a870ce61c873 rw,relatime - aufs none rw,si=9b4a76401263539c -174 15 0:3507 / /var/lib/docker/aufs/mnt/5f37830e5a02561ab8c67ea3113137ba69f67a60e41c05cb0e7a0edaa1925b24 rw,relatime - aufs none rw,si=9b4a76401263639c -184 15 0:3508 / /var/lib/docker/aufs/mnt/62ea10b957e6533538a4633a1e1d678502f50ddcdd354b2ca275c54dd7a7793a rw,relatime - aufs none rw,si=9b4a76401263039c -187 15 0:3509 / /var/lib/docker/aufs/mnt/d56ee9d44195fe390e042fda75ec15af5132adb6d5c69468fa8792f4e54a6953 rw,relatime - aufs none rw,si=9b4a76401263239c -188 15 0:3510 / /var/lib/docker/aufs/mnt/6a300930673174549c2b62f36c933f0332a20735978c007c805a301f897146c5 rw,relatime - aufs none rw,si=9b4a76455d4c539c -189 15 0:3511 / /var/lib/docker/aufs/mnt/64496c45c84d348c24d410015456d101601c30cab4d1998c395591caf7e57a70 rw,relatime - aufs none rw,si=9b4a76455d4c639c -190 15 0:3512 / /var/lib/docker/aufs/mnt/65a6a645883fe97a7422cd5e71ebe0bc17c8e6302a5361edf52e89747387e908 rw,relatime - aufs none rw,si=9b4a76455d4c039c -191 15 0:3513 / /var/lib/docker/aufs/mnt/672be40695f7b6e13b0a3ed9fc996c73727dede3481f58155950fcfad57ed616 rw,relatime - aufs none rw,si=9b4a76455d4c239c -192 15 0:3514 / /var/lib/docker/aufs/mnt/d42438acb2bfb2169e1c0d8e917fc824f7c85d336dadb0b0af36dfe0f001b3ba rw,relatime - aufs none rw,si=9b4a7642bfded39c -193 15 0:3515 / /var/lib/docker/aufs/mnt/b48a54abf26d01cb2ddd908b1ed6034d17397c1341bf0eb2b251a3e5b79be854 rw,relatime - aufs none rw,si=9b4a7642bfdee39c -194 15 0:3516 / /var/lib/docker/aufs/mnt/76f27134491f052bfb87f59092126e53ef875d6851990e59195a9da16a9412f8 rw,relatime - aufs none rw,si=9b4a7642bfde839c -195 15 0:3517 / /var/lib/docker/aufs/mnt/6bd626a5462b4f8a8e1cc7d10351326dca97a59b2758e5ea549a4f6350ce8a90 rw,relatime - aufs none rw,si=9b4a7642bfdea39c -196 15 0:3518 / /var/lib/docker/aufs/mnt/f1fe3549dbd6f5ca615e9139d9b53f0c83a3b825565df37628eacc13e70cbd6d rw,relatime - aufs none rw,si=9b4a7642bfdf539c -197 15 0:3519 / /var/lib/docker/aufs/mnt/6d0458c8426a9e93d58d0625737e6122e725c9408488ed9e3e649a9984e15c34 rw,relatime - aufs none rw,si=9b4a7642bfdf639c -198 15 0:3520 / /var/lib/docker/aufs/mnt/6e4c97db83aa82145c9cf2bafc20d500c0b5389643b689e3ae84188c270a48c5 rw,relatime - aufs none rw,si=9b4a7642bfdf039c -199 15 0:3521 / /var/lib/docker/aufs/mnt/eb94d6498f2c5969eaa9fa11ac2934f1ab90ef88e2d002258dca08e5ba74ea27 rw,relatime - aufs none rw,si=9b4a7642bfdf239c -200 15 0:3522 / /var/lib/docker/aufs/mnt/fe3f88f0c511608a2eec5f13a98703aa16e55dbf930309723d8a37101f539fe1 rw,relatime - aufs none rw,si=9b4a7642bfc3539c -201 15 0:3523 / /var/lib/docker/aufs/mnt/6f40c229fb9cad85fabf4b64a2640a5403ec03fe5ac1a57d0609fb8b606b9c83 rw,relatime - aufs none rw,si=9b4a7642bfc3639c -202 15 0:3524 / /var/lib/docker/aufs/mnt/7513e9131f7a8acf58ff15248237feb767c78732ca46e159f4d791e6ef031dbc rw,relatime - aufs none rw,si=9b4a7642bfc3039c -203 15 0:3525 / /var/lib/docker/aufs/mnt/79f48b00aa713cdf809c6bb7c7cb911b66e9a8076c81d6c9d2504139984ea2da rw,relatime - aufs none rw,si=9b4a7642bfc3239c -204 15 0:3526 / /var/lib/docker/aufs/mnt/c3680418350d11358f0a96c676bc5aa74fa00a7c89e629ef5909d3557b060300 rw,relatime - aufs none rw,si=9b4a7642f47cd39c -205 15 0:3527 / /var/lib/docker/aufs/mnt/7a1744dd350d7fcc0cccb6f1757ca4cbe5453f203a5888b0f1014d96ad5a5ef9 rw,relatime - aufs none rw,si=9b4a7642f47ce39c -206 15 0:3528 / /var/lib/docker/aufs/mnt/7fa99662db046be9f03c33c35251afda9ccdc0085636bbba1d90592cec3ff68d rw,relatime - aufs none rw,si=9b4a7642f47c839c -207 15 0:3529 / /var/lib/docker/aufs/mnt/f815021ef20da9c9b056bd1d52d8aaf6e2c0c19f11122fc793eb2b04eb995e35 rw,relatime - aufs none rw,si=9b4a7642f47ca39c -208 15 0:3530 / /var/lib/docker/aufs/mnt/801086ae3110192d601dfcebdba2db92e86ce6b6a9dba6678ea04488e4513669 rw,relatime - aufs none rw,si=9b4a7642dc6dd39c -209 15 0:3531 / /var/lib/docker/aufs/mnt/822ba7db69f21daddda87c01cfbfbf73013fc03a879daf96d16cdde6f9b1fbd6 rw,relatime - aufs none rw,si=9b4a7642dc6de39c -210 15 0:3532 / /var/lib/docker/aufs/mnt/834227c1a950fef8cae3827489129d0dd220541e60c6b731caaa765bf2e6a199 rw,relatime - aufs none rw,si=9b4a7642dc6d839c -211 15 0:3533 / /var/lib/docker/aufs/mnt/83dccbc385299bd1c7cf19326e791b33a544eea7b4cdfb6db70ea94eed4389fb rw,relatime - aufs none rw,si=9b4a7642dc6da39c -212 15 0:3534 / /var/lib/docker/aufs/mnt/f1b8e6f0e7c8928b5dcdab944db89306ebcae3e0b32f9ff40d2daa8329f21600 rw,relatime - aufs none rw,si=9b4a7645a126039c -213 15 0:3535 / /var/lib/docker/aufs/mnt/970efb262c7a020c2404cbcc5b3259efba0d110a786079faeef05bc2952abf3a rw,relatime - aufs none rw,si=9b4a7644c8ed139c -214 15 0:3536 / /var/lib/docker/aufs/mnt/84b6d73af7450f3117a77e15a5ca1255871fea6182cd8e8a7be6bc744be18c2c rw,relatime - aufs none rw,si=9b4a76406559139c -215 15 0:3537 / /var/lib/docker/aufs/mnt/88be2716e026bc681b5e63fe7942068773efbd0b6e901ca7ba441412006a96b6 rw,relatime - aufs none rw,si=9b4a76406559339c -216 15 0:3538 / /var/lib/docker/aufs/mnt/c81939aa166ce50cd8bca5cfbbcc420a78e0318dd5cd7c755209b9166a00a752 rw,relatime - aufs none rw,si=9b4a76406559239c -217 15 0:3539 / /var/lib/docker/aufs/mnt/e0f241645d64b7dc5ff6a8414087cca226be08fb54ce987d1d1f6350c57083aa rw,relatime - aufs none rw,si=9b4a7647cfc0f39c -218 15 0:3540 / /var/lib/docker/aufs/mnt/e10e2bf75234ed51d8a6a4bb39e465404fecbe318e54400d3879cdb2b0679c78 rw,relatime - aufs none rw,si=9b4a7647cfc0939c -219 15 0:3541 / /var/lib/docker/aufs/mnt/8f71d74c8cfc3228b82564aa9f09b2e576cff0083ddfb6aa5cb350346063f080 rw,relatime - aufs none rw,si=9b4a7647cfc0a39c -220 15 0:3542 / /var/lib/docker/aufs/mnt/9159f1eba2aef7f5205cc18d015cda7f5933cd29bba3b1b8aed5ccb5824c69ee rw,relatime - aufs none rw,si=9b4a76468cedd39c -221 15 0:3543 / /var/lib/docker/aufs/mnt/932cad71e652e048e500d9fbb5b8ea4fc9a269d42a3134ce527ceef42a2be56b rw,relatime - aufs none rw,si=9b4a76468cede39c -222 15 0:3544 / /var/lib/docker/aufs/mnt/bf1e1b5f529e8943cc0144ee86dbaaa37885c1ddffcef29537e0078ee7dd316a rw,relatime - aufs none rw,si=9b4a76468ced839c -223 15 0:3545 / /var/lib/docker/aufs/mnt/949d93ecf3322e09f858ce81d5f4b434068ec44ff84c375de03104f7b45ee955 rw,relatime - aufs none rw,si=9b4a76468ceda39c -224 15 0:3546 / /var/lib/docker/aufs/mnt/d65c6087f92dc2a3841b5251d2fe9ca07d4c6e5b021597692479740816e4e2a1 rw,relatime - aufs none rw,si=9b4a7645a126239c -225 15 0:3547 / /var/lib/docker/aufs/mnt/98a0153119d0651c193d053d254f6e16a68345a141baa80c87ae487e9d33f290 rw,relatime - aufs none rw,si=9b4a7640787cf39c -226 15 0:3548 / /var/lib/docker/aufs/mnt/99daf7fe5847c017392f6e59aa9706b3dfdd9e6d1ba11dae0f7fffde0a60b5e5 rw,relatime - aufs none rw,si=9b4a7640787c839c -227 15 0:3549 / /var/lib/docker/aufs/mnt/9ad1f2fe8a5599d4e10c5a6effa7f03d932d4e92ee13149031a372087a359079 rw,relatime - aufs none rw,si=9b4a7640787ca39c -228 15 0:3550 / /var/lib/docker/aufs/mnt/c26d64494da782ddac26f8370d86ac93e7c1666d88a7b99110fc86b35ea6a85d rw,relatime - aufs none rw,si=9b4a7642fc6b539c -229 15 0:3551 / /var/lib/docker/aufs/mnt/a49e4a8275133c230ec640997f35f172312eb0ea5bd2bbe10abf34aae98f30eb rw,relatime - aufs none rw,si=9b4a7642fc6b639c -230 15 0:3552 / /var/lib/docker/aufs/mnt/b5e2740c867ed843025f49d84e8d769de9e8e6039b3c8cb0735b5bf358994bc7 rw,relatime - aufs none rw,si=9b4a7642fc6b039c -231 15 0:3553 / /var/lib/docker/aufs/mnt/a826fdcf3a7039b30570054579b65763db605a314275d7aef31b872c13311b4b rw,relatime - aufs none rw,si=9b4a7642fc6b239c -232 15 0:3554 / /var/lib/docker/aufs/mnt/addf3025babf5e43b5a3f4a0da7ad863dda3c01fb8365c58fd8d28bb61dc11bc rw,relatime - aufs none rw,si=9b4a76407871d39c -233 15 0:3555 / /var/lib/docker/aufs/mnt/c5b6c6813ab3e5ebdc6d22cb2a3d3106a62095f2c298be52b07a3b0fa20ff690 rw,relatime - aufs none rw,si=9b4a76407871e39c -234 15 0:3556 / /var/lib/docker/aufs/mnt/af0609eaaf64e2392060cb46f5a9f3d681a219bb4c651d4f015bf573fbe6c4cf rw,relatime - aufs none rw,si=9b4a76407871839c -235 15 0:3557 / /var/lib/docker/aufs/mnt/e7f20e3c37ecad39cd90a97cd3549466d0d106ce4f0a930b8495442634fa4a1f rw,relatime - aufs none rw,si=9b4a76407871a39c -237 15 0:3559 / /var/lib/docker/aufs/mnt/b57a53d440ffd0c1295804fa68cdde35d2fed5409484627e71b9c37e4249fd5c rw,relatime - aufs none rw,si=9b4a76444445a39c -238 15 0:3560 / /var/lib/docker/aufs/mnt/b5e7d7b8f35e47efbba3d80c5d722f5e7bd43e54c824e54b4a4b351714d36d42 rw,relatime - aufs none rw,si=9b4a7647932d439c -239 15 0:3561 / /var/lib/docker/aufs/mnt/f1b136def157e9465640658f277f3347de593c6ae76412a2e79f7002f091cae2 rw,relatime - aufs none rw,si=9b4a76445abcd39c -240 15 0:3562 / /var/lib/docker/aufs/mnt/b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc rw,relatime - aufs none rw,si=9b4a7644403b339c -241 15 0:3563 / /var/lib/docker/aufs/mnt/b89b140cdbc95063761864e0a23346207fa27ee4c5c63a1ae85c9069a9d9cf1d rw,relatime - aufs none rw,si=9b4a7644aa19739c -242 15 0:3564 / /var/lib/docker/aufs/mnt/bc6a69ed51c07f5228f6b4f161c892e6a949c0e7e86a9c3432049d4c0e5cd298 rw,relatime - aufs none rw,si=9b4a7644aa19139c -243 15 0:3565 / /var/lib/docker/aufs/mnt/be4e2ba3f136933e239f7cf3d136f484fb9004f1fbdfee24a62a2c7b0ab30670 rw,relatime - aufs none rw,si=9b4a7644aa19339c -244 15 0:3566 / /var/lib/docker/aufs/mnt/e04ca1a4a5171e30d20f0c92f90a50b8b6f8600af5459c4b4fb25e42e864dfe1 rw,relatime - aufs none rw,si=9b4a7647932d139c -245 15 0:3567 / /var/lib/docker/aufs/mnt/be61576b31db893129aaffcd3dcb5ce35e49c4b71b30c392a78609a45c7323d8 rw,relatime - aufs none rw,si=9b4a7642d85f739c -246 15 0:3568 / /var/lib/docker/aufs/mnt/dda42c191e56becf672327658ab84fcb563322db3764b91c2fefe4aaef04c624 rw,relatime - aufs none rw,si=9b4a7642d85f139c -247 15 0:3569 / /var/lib/docker/aufs/mnt/c0a7995053330f3d88969247a2e72b07e2dd692133f5668a4a35ea3905561072 rw,relatime - aufs none rw,si=9b4a7642d85f339c -249 15 0:3571 / /var/lib/docker/aufs/mnt/c3594b2e5f08c59ff5ed338a1ba1eceeeb1f7fc5d180068338110c00b1eb8502 rw,relatime - aufs none rw,si=9b4a7642738c739c -250 15 0:3572 / /var/lib/docker/aufs/mnt/c58dce03a0ab0a7588393880379dc3bce9f96ec08ed3f99cf1555260ff0031e8 rw,relatime - aufs none rw,si=9b4a7642738c139c -251 15 0:3573 / /var/lib/docker/aufs/mnt/c73e9f1d109c9d14cb36e1c7489df85649be3911116d76c2fd3648ec8fd94e23 rw,relatime - aufs none rw,si=9b4a7642738c339c -252 15 0:3574 / /var/lib/docker/aufs/mnt/c9eef28c344877cd68aa09e543c0710ab2b305a0ff96dbb859bfa7808c3e8d01 rw,relatime - aufs none rw,si=9b4a7642d85f439c -253 15 0:3575 / /var/lib/docker/aufs/mnt/feb67148f548d70cb7484f2aaad2a86051cd6867a561741a2f13b552457d666e rw,relatime - aufs none rw,si=9b4a76468c55739c -254 15 0:3576 / /var/lib/docker/aufs/mnt/cdf1f96c36d35a96041a896bf398ec0f7dc3b0fb0643612a0f4b6ff96e04e1bb rw,relatime - aufs none rw,si=9b4a76468c55139c -255 15 0:3577 / /var/lib/docker/aufs/mnt/ec6e505872353268451ac4bc034c1df00f3bae4a3ea2261c6e48f7bd5417c1b3 rw,relatime - aufs none rw,si=9b4a76468c55339c -256 15 0:3578 / /var/lib/docker/aufs/mnt/d6dc8aca64efd90e0bc10274001882d0efb310d42ccbf5712b99b169053b8b1a rw,relatime - aufs none rw,si=9b4a7642738c439c -257 15 0:3579 / /var/lib/docker/aufs/mnt/d712594e2ff6eaeb895bfd150d694bd1305fb927e7a186b2dab7df2ea95f8f81 rw,relatime - aufs none rw,si=9b4a76401268f39c -259 15 0:3581 / /var/lib/docker/aufs/mnt/dbfa1174cd78cde2d7410eae442af0b416c4a0e6f87ed4ff1e9f169a0029abc0 rw,relatime - aufs none rw,si=9b4a76401268b39c -260 15 0:3582 / /var/lib/docker/aufs/mnt/e883f5a82316d7856fbe93ee8c0af5a920b7079619dd95c4ffd88bbd309d28dd rw,relatime - aufs none rw,si=9b4a76468c55439c -261 15 0:3583 / /var/lib/docker/aufs/mnt/fdec3eff581c4fc2b09f87befa2fa021f3f2d373bea636a87f1fb5b367d6347a rw,relatime - aufs none rw,si=9b4a7644aa1af39c -262 15 0:3584 / /var/lib/docker/aufs/mnt/ef764e26712184653067ecf7afea18a80854c41331ca0f0ef03e1bacf90a6ffc rw,relatime - aufs none rw,si=9b4a7644aa1a939c -263 15 0:3585 / /var/lib/docker/aufs/mnt/f3176b40c41fce8ce6942936359a2001a6f1b5c1bb40ee224186db0789ec2f76 rw,relatime - aufs none rw,si=9b4a7644aa1ab39c -264 15 0:3586 / /var/lib/docker/aufs/mnt/f5daf06785d3565c6dd18ea7d953d9a8b9606107781e63270fe0514508736e6a rw,relatime - aufs none rw,si=9b4a76401268c39c -58 15 0:3587 / /var/lib/docker/aufs/mnt/cde8c40f6524b7361af4f5ad05bb857dc9ee247c20852ba666195c0739e3a2b8-init rw,relatime - aufs none rw,si=9b4a76444445839c -67 15 0:3588 / /var/lib/docker/aufs/mnt/cde8c40f6524b7361af4f5ad05bb857dc9ee247c20852ba666195c0739e3a2b8 rw,relatime - aufs none rw,si=9b4a7644badd339c -265 15 0:3610 / /var/lib/docker/aufs/mnt/e812472cd2c8c4748d1ef71fac4e77e50d661b9349abe66ce3e23511ed44f414 rw,relatime - aufs none rw,si=9b4a76427937d39c -270 15 0:3615 / /var/lib/docker/aufs/mnt/997636e7c5c9d0d1376a217e295c14c205350b62bc12052804fb5f90abe6f183 rw,relatime - aufs none rw,si=9b4a76406540739c -273 15 0:3618 / /var/lib/docker/aufs/mnt/d5794d080417b6e52e69227c3873e0e4c1ff0d5a845ebe3860ec2f89a47a2a1e rw,relatime - aufs none rw,si=9b4a76454814039c -278 15 0:3623 / /var/lib/docker/aufs/mnt/586bdd48baced671bb19bc4d294ec325f26c55545ae267db426424f157d59c48 rw,relatime - aufs none rw,si=9b4a7644b439f39c -281 15 0:3626 / /var/lib/docker/aufs/mnt/69739d022f89f8586908bbd5edbbdd95ea5256356f177f9ffcc6ef9c0ea752d2 rw,relatime - aufs none rw,si=9b4a7644a0f1b39c -286 15 0:3631 / /var/lib/docker/aufs/mnt/ff28c27d5f894363993622de26d5dd352dba072f219e4691d6498c19bbbc15a9 rw,relatime - aufs none rw,si=9b4a7642265b339c -289 15 0:3634 / /var/lib/docker/aufs/mnt/aa128fe0e64fdede333aa48fd9de39530c91a9244a0f0649a3c411c61e372daa rw,relatime - aufs none rw,si=9b4a764012ada39c -99 15 8:33 / /media/REMOVE\040ME rw,nosuid,nodev,relatime - fuseblk /dev/sdc1 rw,user_id=0,group_id=0,allow_other,blksize=4096` -) - -func TestParseFedoraMountinfo(t *testing.T) { - r := bytes.NewBuffer([]byte(fedoraMountinfo)) - _, err := parseInfoFile(r) - if err != nil { - t.Fatal(err) - } -} - -func TestParseUbuntuMountinfo(t *testing.T) { - r := bytes.NewBuffer([]byte(ubuntuMountInfo)) - _, err := parseInfoFile(r) - if err != nil { - t.Fatal(err) - } -} - -func TestParseGentooMountinfo(t *testing.T) { - r := bytes.NewBuffer([]byte(gentooMountinfo)) - _, err := parseInfoFile(r) - if err != nil { - t.Fatal(err) - } -} - -func TestParseFedoraMountinfoFields(t *testing.T) { - r := bytes.NewBuffer([]byte(fedoraMountinfo)) - infos, err := parseInfoFile(r) - if err != nil { - t.Fatal(err) - } - expectedLength := 58 - if len(infos) != expectedLength { - t.Fatalf("Expected %d entries, got %d", expectedLength, len(infos)) - } - mi := Info{ - ID: 15, - Parent: 35, - Major: 0, - Minor: 3, - Root: "/", - Mountpoint: "/proc", - Opts: "rw,nosuid,nodev,noexec,relatime", - Optional: "shared:5", - Fstype: "proc", - Source: "proc", - VfsOpts: "rw", - } - - if *infos[0] != mi { - t.Fatalf("expected %#v, got %#v", mi, infos[0]) - } -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/mount/sharedsubtree_linux_test.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/mount/sharedsubtree_linux_test.go deleted file mode 100644 index 4a8d22f02..000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/mount/sharedsubtree_linux_test.go +++ /dev/null @@ -1,331 +0,0 @@ -// +build linux - -package mount - -import ( - "os" - "path" - "syscall" - "testing" -) - -// nothing is propagated in or out -func TestSubtreePrivate(t *testing.T) { - tmp := path.Join(os.TempDir(), "mount-tests") - if err := os.MkdirAll(tmp, 0777); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - var ( - sourceDir = path.Join(tmp, "source") - targetDir = path.Join(tmp, "target") - outside1Dir = path.Join(tmp, "outside1") - outside2Dir = path.Join(tmp, "outside2") - - outside1Path = path.Join(outside1Dir, "file.txt") - outside2Path = path.Join(outside2Dir, "file.txt") - outside1CheckPath = path.Join(targetDir, "a", "file.txt") - outside2CheckPath = path.Join(sourceDir, "b", "file.txt") - ) - if err := os.MkdirAll(path.Join(sourceDir, "a"), 0777); err != nil { - t.Fatal(err) - } - if err := os.MkdirAll(path.Join(sourceDir, "b"), 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(targetDir, 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(outside1Dir, 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(outside2Dir, 0777); err != nil { - t.Fatal(err) - } - - if err := createFile(outside1Path); err != nil { - t.Fatal(err) - } - if err := createFile(outside2Path); err != nil { - t.Fatal(err) - } - - // mount the shared directory to a target - if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() - - // next, make the target private - if err := MakePrivate(targetDir); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() - - // mount in an outside path to a mounted path inside the _source_ - if err := Mount(outside1Dir, path.Join(sourceDir, "a"), "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(path.Join(sourceDir, "a")); err != nil { - t.Fatal(err) - } - }() - - // check that this file _does_not_ show in the _target_ - if _, err := os.Stat(outside1CheckPath); err != nil && !os.IsNotExist(err) { - t.Fatal(err) - } else if err == nil { - t.Fatalf("%q should not be visible, but is", outside1CheckPath) - } - - // next mount outside2Dir into the _target_ - if err := Mount(outside2Dir, path.Join(targetDir, "b"), "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(path.Join(targetDir, "b")); err != nil { - t.Fatal(err) - } - }() - - // check that this file _does_not_ show in the _source_ - if _, err := os.Stat(outside2CheckPath); err != nil && !os.IsNotExist(err) { - t.Fatal(err) - } else if err == nil { - t.Fatalf("%q should not be visible, but is", outside2CheckPath) - } -} - -// Testing that when a target is a shared mount, -// then child mounts propagate to the source -func TestSubtreeShared(t *testing.T) { - tmp := path.Join(os.TempDir(), "mount-tests") - if err := os.MkdirAll(tmp, 0777); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - var ( - sourceDir = path.Join(tmp, "source") - targetDir = path.Join(tmp, "target") - outsideDir = path.Join(tmp, "outside") - - outsidePath = path.Join(outsideDir, "file.txt") - sourceCheckPath = path.Join(sourceDir, "a", "file.txt") - ) - - if err := os.MkdirAll(path.Join(sourceDir, "a"), 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(targetDir, 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(outsideDir, 0777); err != nil { - t.Fatal(err) - } - - if err := createFile(outsidePath); err != nil { - t.Fatal(err) - } - - // mount the source as shared - if err := MakeShared(sourceDir); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(sourceDir); err != nil { - t.Fatal(err) - } - }() - - // mount the shared directory to a target - if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() - - // mount in an outside path to a mounted path inside the target - if err := Mount(outsideDir, path.Join(targetDir, "a"), "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(path.Join(targetDir, "a")); err != nil { - t.Fatal(err) - } - }() - - // NOW, check that the file from the outside directory is avaible in the source directory - if _, err := os.Stat(sourceCheckPath); err != nil { - t.Fatal(err) - } -} - -// testing that mounts to a shared source show up in the slave target, -// and that mounts into a slave target do _not_ show up in the shared source -func TestSubtreeSharedSlave(t *testing.T) { - tmp := path.Join(os.TempDir(), "mount-tests") - if err := os.MkdirAll(tmp, 0777); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - var ( - sourceDir = path.Join(tmp, "source") - targetDir = path.Join(tmp, "target") - outside1Dir = path.Join(tmp, "outside1") - outside2Dir = path.Join(tmp, "outside2") - - outside1Path = path.Join(outside1Dir, "file.txt") - outside2Path = path.Join(outside2Dir, "file.txt") - outside1CheckPath = path.Join(targetDir, "a", "file.txt") - outside2CheckPath = path.Join(sourceDir, "b", "file.txt") - ) - if err := os.MkdirAll(path.Join(sourceDir, "a"), 0777); err != nil { - t.Fatal(err) - } - if err := os.MkdirAll(path.Join(sourceDir, "b"), 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(targetDir, 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(outside1Dir, 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(outside2Dir, 0777); err != nil { - t.Fatal(err) - } - - if err := createFile(outside1Path); err != nil { - t.Fatal(err) - } - if err := createFile(outside2Path); err != nil { - t.Fatal(err) - } - - // mount the source as shared - if err := MakeShared(sourceDir); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(sourceDir); err != nil { - t.Fatal(err) - } - }() - - // mount the shared directory to a target - if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() - - // next, make the target slave - if err := MakeSlave(targetDir); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() - - // mount in an outside path to a mounted path inside the _source_ - if err := Mount(outside1Dir, path.Join(sourceDir, "a"), "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(path.Join(sourceDir, "a")); err != nil { - t.Fatal(err) - } - }() - - // check that this file _does_ show in the _target_ - if _, err := os.Stat(outside1CheckPath); err != nil { - t.Fatal(err) - } - - // next mount outside2Dir into the _target_ - if err := Mount(outside2Dir, path.Join(targetDir, "b"), "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(path.Join(targetDir, "b")); err != nil { - t.Fatal(err) - } - }() - - // check that this file _does_not_ show in the _source_ - if _, err := os.Stat(outside2CheckPath); err != nil && !os.IsNotExist(err) { - t.Fatal(err) - } else if err == nil { - t.Fatalf("%q should not be visible, but is", outside2CheckPath) - } -} - -func TestSubtreeUnbindable(t *testing.T) { - tmp := path.Join(os.TempDir(), "mount-tests") - if err := os.MkdirAll(tmp, 0777); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - var ( - sourceDir = path.Join(tmp, "source") - targetDir = path.Join(tmp, "target") - ) - if err := os.MkdirAll(sourceDir, 0777); err != nil { - t.Fatal(err) - } - if err := os.MkdirAll(targetDir, 0777); err != nil { - t.Fatal(err) - } - - // next, make the source unbindable - if err := MakeUnbindable(sourceDir); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(sourceDir); err != nil { - t.Fatal(err) - } - }() - - // then attempt to mount it to target. It should fail - if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil && err != syscall.EINVAL { - t.Fatal(err) - } else if err == nil { - t.Fatalf("%q should not have been bindable", sourceDir) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() -} - -func createFile(path string) error { - f, err := os.Create(path) - if err != nil { - return err - } - f.WriteString("hello world!") - return f.Close() -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/filters/parse.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/filters/parse.go deleted file mode 100644 index df5486d51..000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/filters/parse.go +++ /dev/null @@ -1,116 +0,0 @@ -package filters - -import ( - "encoding/json" - "errors" - "regexp" - "strings" -) - -type Args map[string][]string - -// Parse the argument to the filter flag. Like -// -// `docker ps -f 'created=today' -f 'image.name=ubuntu*'` -// -// If prev map is provided, then it is appended to, and returned. By default a new -// map is created. -func ParseFlag(arg string, prev Args) (Args, error) { - var filters Args = prev - if prev == nil { - filters = Args{} - } - if len(arg) == 0 { - return filters, nil - } - - if !strings.Contains(arg, "=") { - return filters, ErrorBadFormat - } - - f := strings.SplitN(arg, "=", 2) - name := strings.ToLower(strings.TrimSpace(f[0])) - value := strings.TrimSpace(f[1]) - filters[name] = append(filters[name], value) - - return filters, nil -} - -var ErrorBadFormat = errors.New("bad format of filter (expected name=value)") - -// packs the Args into an string for easy transport from client to server -func ToParam(a Args) (string, error) { - // this way we don't URL encode {}, just empty space - if len(a) == 0 { - return "", nil - } - - buf, err := json.Marshal(a) - if err != nil { - return "", err - } - return string(buf), nil -} - -// unpacks the filter Args -func FromParam(p string) (Args, error) { - args := Args{} - if len(p) == 0 { - return args, nil - } - if err := json.NewDecoder(strings.NewReader(p)).Decode(&args); err != nil { - return nil, err - } - return args, nil -} - -func (filters Args) MatchKVList(field string, sources map[string]string) bool { - fieldValues := filters[field] - - //do not filter if there is no filter set or cannot determine filter - if len(fieldValues) == 0 { - return true - } - - if sources == nil || len(sources) == 0 { - return false - } - -outer: - for _, name2match := range fieldValues { - testKV := strings.SplitN(name2match, "=", 2) - - for k, v := range sources { - if len(testKV) == 1 { - if k == testKV[0] { - continue outer - } - } else if k == testKV[0] && v == testKV[1] { - continue outer - } - } - - return false - } - - return true -} - -func (filters Args) Match(field, source string) bool { - fieldValues := filters[field] - - //do not filter if there is no filter set or cannot determine filter - if len(fieldValues) == 0 { - return true - } - for _, name2match := range fieldValues { - match, err := regexp.MatchString(name2match, source) - if err != nil { - continue - } - if match { - return true - } - } - return false -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/filters/parse_test.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/filters/parse_test.go deleted file mode 100644 index a141c33ce..000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/filters/parse_test.go +++ /dev/null @@ -1,218 +0,0 @@ -package filters - -import ( - "sort" - "testing" -) - -func TestParseArgs(t *testing.T) { - // equivalent of `docker ps -f 'created=today' -f 'image.name=ubuntu*' -f 'image.name=*untu'` - flagArgs := []string{ - "created=today", - "image.name=ubuntu*", - "image.name=*untu", - } - var ( - args = Args{} - err error - ) - for i := range flagArgs { - args, err = ParseFlag(flagArgs[i], args) - if err != nil { - t.Errorf("failed to parse %s: %s", flagArgs[i], err) - } - } - if len(args["created"]) != 1 { - t.Errorf("failed to set this arg") - } - if len(args["image.name"]) != 2 { - t.Errorf("the args should have collapsed") - } -} - -func TestParseArgsEdgeCase(t *testing.T) { - var filters Args - args, err := ParseFlag("", filters) - if err != nil { - t.Fatal(err) - } - if args == nil || len(args) != 0 { - t.Fatalf("Expected an empty Args (map), got %v", args) - } - if args, err = ParseFlag("anything", args); err == nil || err != ErrorBadFormat { - t.Fatalf("Expected ErrorBadFormat, got %v", err) - } -} - -func TestToParam(t *testing.T) { - a := Args{ - "created": []string{"today"}, - "image.name": []string{"ubuntu*", "*untu"}, - } - - _, err := ToParam(a) - if err != nil { - t.Errorf("failed to marshal the filters: %s", err) - } -} - -func TestFromParam(t *testing.T) { - invalids := []string{ - "anything", - "['a','list']", - "{'key': 'value'}", - `{"key": "value"}`, - } - valids := map[string]Args{ - `{"key": ["value"]}`: { - "key": {"value"}, - }, - `{"key": ["value1", "value2"]}`: { - "key": {"value1", "value2"}, - }, - `{"key1": ["value1"], "key2": ["value2"]}`: { - "key1": {"value1"}, - "key2": {"value2"}, - }, - } - for _, invalid := range invalids { - if _, err := FromParam(invalid); err == nil { - t.Fatalf("Expected an error with %v, got nothing", invalid) - } - } - for json, expectedArgs := range valids { - args, err := FromParam(json) - if err != nil { - t.Fatal(err) - } - if len(args) != len(expectedArgs) { - t.Fatalf("Expected %v, go %v", expectedArgs, args) - } - for key, expectedValues := range expectedArgs { - values := args[key] - sort.Strings(values) - sort.Strings(expectedValues) - if len(values) != len(expectedValues) { - t.Fatalf("Expected %v, go %v", expectedArgs, args) - } - for index, expectedValue := range expectedValues { - if values[index] != expectedValue { - t.Fatalf("Expected %v, go %v", expectedArgs, args) - } - } - } - } -} - -func TestEmpty(t *testing.T) { - a := Args{} - v, err := ToParam(a) - if err != nil { - t.Errorf("failed to marshal the filters: %s", err) - } - v1, err := FromParam(v) - if err != nil { - t.Errorf("%s", err) - } - if len(a) != len(v1) { - t.Errorf("these should both be empty sets") - } -} - -func TestArgsMatchKVList(t *testing.T) { - // empty sources - args := Args{ - "created": []string{"today"}, - } - if args.MatchKVList("created", map[string]string{}) { - t.Fatalf("Expected false for (%v,created), got true", args) - } - // Not empty sources - sources := map[string]string{ - "key1": "value1", - "key2": "value2", - "key3": "value3", - } - matches := map[*Args]string{ - &Args{}: "field", - &Args{ - "created": []string{"today"}, - "labels": []string{"key1"}, - }: "labels", - &Args{ - "created": []string{"today"}, - "labels": []string{"key1=value1"}, - }: "labels", - } - differs := map[*Args]string{ - &Args{ - "created": []string{"today"}, - }: "created", - &Args{ - "created": []string{"today"}, - "labels": []string{"key4"}, - }: "labels", - &Args{ - "created": []string{"today"}, - "labels": []string{"key1=value3"}, - }: "labels", - } - for args, field := range matches { - if args.MatchKVList(field, sources) != true { - t.Fatalf("Expected true for %v on %v, got false", sources, args) - } - } - for args, field := range differs { - if args.MatchKVList(field, sources) != false { - t.Fatalf("Expected false for %v on %v, got true", sources, args) - } - } -} - -func TestArgsMatch(t *testing.T) { - source := "today" - matches := map[*Args]string{ - &Args{}: "field", - &Args{ - "created": []string{"today"}, - "labels": []string{"key1"}, - }: "today", - &Args{ - "created": []string{"to*"}, - }: "created", - &Args{ - "created": []string{"to(.*)"}, - }: "created", - &Args{ - "created": []string{"tod"}, - }: "created", - &Args{ - "created": []string{"anything", "to*"}, - }: "created", - } - differs := map[*Args]string{ - &Args{ - "created": []string{"tomorrow"}, - }: "created", - &Args{ - "created": []string{"to(day"}, - }: "created", - &Args{ - "created": []string{"tom(.*)"}, - }: "created", - &Args{ - "created": []string{"today1"}, - "labels": []string{"today"}, - }: "created", - } - for args, field := range matches { - if args.Match(field, source) != true { - t.Fatalf("Expected true for %v on %v, got false", source, args) - } - } - for args, field := range differs { - if args.Match(field, source) != false { - t.Fatalf("Expected false for %v on %v, got true", source, args) - } - } -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go deleted file mode 100644 index 5f7930684..000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go +++ /dev/null @@ -1,95 +0,0 @@ -// +build !windows - -package kernel - -import ( - "bytes" - "errors" - "fmt" -) - -type KernelVersionInfo struct { - Kernel int - Major int - Minor int - Flavor string -} - -func (k *KernelVersionInfo) String() string { - return fmt.Sprintf("%d.%d.%d%s", k.Kernel, k.Major, k.Minor, k.Flavor) -} - -// Compare two KernelVersionInfo struct. -// Returns -1 if a < b, 0 if a == b, 1 it a > b -func CompareKernelVersion(a, b *KernelVersionInfo) int { - if a.Kernel < b.Kernel { - return -1 - } else if a.Kernel > b.Kernel { - return 1 - } - - if a.Major < b.Major { - return -1 - } else if a.Major > b.Major { - return 1 - } - - if a.Minor < b.Minor { - return -1 - } else if a.Minor > b.Minor { - return 1 - } - - return 0 -} - -func GetKernelVersion() (*KernelVersionInfo, error) { - var ( - err error - ) - - uts, err := uname() - if err != nil { - return nil, err - } - - release := make([]byte, len(uts.Release)) - - i := 0 - for _, c := range uts.Release { - release[i] = byte(c) - i++ - } - - // Remove the \x00 from the release for Atoi to parse correctly - release = release[:bytes.IndexByte(release, 0)] - - return ParseRelease(string(release)) -} - -func ParseRelease(release string) (*KernelVersionInfo, error) { - var ( - kernel, major, minor, parsed int - flavor, partial string - ) - - // Ignore error from Sscanf to allow an empty flavor. Instead, just - // make sure we got all the version numbers. - parsed, _ = fmt.Sscanf(release, "%d.%d%s", &kernel, &major, &partial) - if parsed < 2 { - return nil, errors.New("Can't parse kernel version " + release) - } - - // sometimes we have 3.12.25-gentoo, but sometimes we just have 3.12-1-amd64 - parsed, _ = fmt.Sscanf(partial, ".%d%s", &minor, &flavor) - if parsed < 1 { - flavor = partial - } - - return &KernelVersionInfo{ - Kernel: kernel, - Major: major, - Minor: minor, - Flavor: flavor, - }, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_test.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_test.go deleted file mode 100644 index 7f40939cc..000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package kernel - -import ( - "fmt" - "testing" -) - -func assertParseRelease(t *testing.T, release string, b *KernelVersionInfo, result int) { - var ( - a *KernelVersionInfo - ) - a, _ = ParseRelease(release) - - if r := CompareKernelVersion(a, b); r != result { - t.Fatalf("Unexpected kernel version comparison result for (%v,%v). Found %d, expected %d", release, b, r, result) - } - if a.Flavor != b.Flavor { - t.Fatalf("Unexpected parsed kernel flavor. Found %s, expected %s", a.Flavor, b.Flavor) - } -} - -func TestParseRelease(t *testing.T) { - assertParseRelease(t, "3.8.0", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, 0) - assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0) - assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0) - assertParseRelease(t, "3.8.0-19-generic", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "-19-generic"}, 0) - assertParseRelease(t, "3.12.8tag", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 8, Flavor: "tag"}, 0) - assertParseRelease(t, "3.12-1-amd64", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 0, Flavor: "-1-amd64"}, 0) - assertParseRelease(t, "3.8.0", &KernelVersionInfo{Kernel: 4, Major: 8, Minor: 0}, -1) - // Errors - invalids := []string{ - "3", - "a", - "a.a", - "a.a.a-a", - } - for _, invalid := range invalids { - expectedMessage := fmt.Sprintf("Can't parse kernel version %v", invalid) - if _, err := ParseRelease(invalid); err == nil || err.Error() != expectedMessage { - - } - } -} - -func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) { - if r := CompareKernelVersion(a, b); r != result { - t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result) - } -} - -func TestCompareKernelVersion(t *testing.T) { - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - 0) - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0}, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - -1) - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - &KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0}, - 1) - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - 0) - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5}, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - 1) - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20}, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - -1) - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 3, Major: 7, Minor: 20}, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - -1) - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 20}, - &KernelVersionInfo{Kernel: 3, Major: 7, Minor: 0}, - 1) - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 20}, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - 1) - assertKernelVersion(t, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, - &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 20}, - -1) -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_windows.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_windows.go deleted file mode 100644 index 399d63e5f..000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_windows.go +++ /dev/null @@ -1,65 +0,0 @@ -package kernel - -import ( - "fmt" - "syscall" - "unsafe" -) - -type KernelVersionInfo struct { - kvi string - major int - minor int - build int -} - -func (k *KernelVersionInfo) String() string { - return fmt.Sprintf("%d.%d %d (%s)", k.major, k.minor, k.build, k.kvi) -} - -func GetKernelVersion() (*KernelVersionInfo, error) { - - var ( - h syscall.Handle - dwVersion uint32 - err error - ) - - KVI := &KernelVersionInfo{"Unknown", 0, 0, 0} - - if err = syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, - syscall.StringToUTF16Ptr(`SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\`), - 0, - syscall.KEY_READ, - &h); err != nil { - return KVI, err - } - defer syscall.RegCloseKey(h) - - var buf [1 << 10]uint16 - var typ uint32 - n := uint32(len(buf) * 2) // api expects array of bytes, not uint16 - - if err = syscall.RegQueryValueEx(h, - syscall.StringToUTF16Ptr("BuildLabEx"), - nil, - &typ, - (*byte)(unsafe.Pointer(&buf[0])), - &n); err != nil { - return KVI, err - } - - KVI.kvi = syscall.UTF16ToString(buf[:]) - - // Important - docker.exe MUST be manifested for this API to return - // the correct information. - if dwVersion, err = syscall.GetVersion(); err != nil { - return KVI, err - } - - KVI.major = int(dwVersion & 0xFF) - KVI.minor = int((dwVersion & 0XFF00) >> 8) - KVI.build = int((dwVersion & 0xFFFF0000) >> 16) - - return KVI, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_linux.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_linux.go deleted file mode 100644 index 8ca814c1f..000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_linux.go +++ /dev/null @@ -1,16 +0,0 @@ -package kernel - -import ( - "syscall" -) - -type Utsname syscall.Utsname - -func uname() (*syscall.Utsname, error) { - uts := &syscall.Utsname{} - - if err := syscall.Uname(uts); err != nil { - return nil, err - } - return uts, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_unsupported.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_unsupported.go deleted file mode 100644 index 00c542258..000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_unsupported.go +++ /dev/null @@ -1,15 +0,0 @@ -// +build !linux - -package kernel - -import ( - "errors" -) - -type Utsname struct { - Release [65]byte -} - -func uname() (*Utsname, error) { - return nil, errors.New("Kernel version detection is available only on linux") -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_linux.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_linux.go deleted file mode 100644 index af185f9f6..000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_linux.go +++ /dev/null @@ -1,40 +0,0 @@ -package operatingsystem - -import ( - "bytes" - "errors" - "io/ioutil" -) - -var ( - // file to use to detect if the daemon is running in a container - proc1Cgroup = "/proc/1/cgroup" - - // file to check to determine Operating System - etcOsRelease = "/etc/os-release" -) - -func GetOperatingSystem() (string, error) { - b, err := ioutil.ReadFile(etcOsRelease) - if err != nil { - return "", err - } - if i := bytes.Index(b, []byte("PRETTY_NAME")); i >= 0 { - b = b[i+13:] - return string(b[:bytes.IndexByte(b, '"')]), nil - } - return "", errors.New("PRETTY_NAME not found") -} - -func IsContainerized() (bool, error) { - b, err := ioutil.ReadFile(proc1Cgroup) - if err != nil { - return false, err - } - for _, line := range bytes.Split(b, []byte{'\n'}) { - if len(line) > 0 && !bytes.HasSuffix(line, []byte{'/'}) { - return true, nil - } - } - return false, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_test.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_test.go deleted file mode 100644 index b7d54cbb1..000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_test.go +++ /dev/null @@ -1,124 +0,0 @@ -package operatingsystem - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" -) - -func TestGetOperatingSystem(t *testing.T) { - var ( - backup = etcOsRelease - ubuntuTrusty = []byte(`NAME="Ubuntu" -VERSION="14.04, Trusty Tahr" -ID=ubuntu -ID_LIKE=debian -PRETTY_NAME="Ubuntu 14.04 LTS" -VERSION_ID="14.04" -HOME_URL="http://www.ubuntu.com/" -SUPPORT_URL="http://help.ubuntu.com/" -BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`) - gentoo = []byte(`NAME=Gentoo -ID=gentoo -PRETTY_NAME="Gentoo/Linux" -ANSI_COLOR="1;32" -HOME_URL="http://www.gentoo.org/" -SUPPORT_URL="http://www.gentoo.org/main/en/support.xml" -BUG_REPORT_URL="https://bugs.gentoo.org/" -`) - noPrettyName = []byte(`NAME="Ubuntu" -VERSION="14.04, Trusty Tahr" -ID=ubuntu -ID_LIKE=debian -VERSION_ID="14.04" -HOME_URL="http://www.ubuntu.com/" -SUPPORT_URL="http://help.ubuntu.com/" -BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`) - ) - - dir := os.TempDir() - etcOsRelease = filepath.Join(dir, "etcOsRelease") - - defer func() { - os.Remove(etcOsRelease) - etcOsRelease = backup - }() - - for expect, osRelease := range map[string][]byte{ - "Ubuntu 14.04 LTS": ubuntuTrusty, - "Gentoo/Linux": gentoo, - "": noPrettyName, - } { - if err := ioutil.WriteFile(etcOsRelease, osRelease, 0600); err != nil { - t.Fatalf("failed to write to %s: %v", etcOsRelease, err) - } - s, err := GetOperatingSystem() - if s != expect { - if expect == "" { - t.Fatalf("Expected error 'PRETTY_NAME not found', but got %v", err) - } else { - t.Fatalf("Expected '%s', but got '%s'. Err=%v", expect, s, err) - } - } - } -} - -func TestIsContainerized(t *testing.T) { - var ( - backup = proc1Cgroup - nonContainerizedProc1Cgroup = []byte(`14:name=systemd:/ -13:hugetlb:/ -12:net_prio:/ -11:perf_event:/ -10:bfqio:/ -9:blkio:/ -8:net_cls:/ -7:freezer:/ -6:devices:/ -5:memory:/ -4:cpuacct:/ -3:cpu:/ -2:cpuset:/ -`) - containerizedProc1Cgroup = []byte(`9:perf_event:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d -8:blkio:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d -7:net_cls:/ -6:freezer:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d -5:devices:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d -4:memory:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d -3:cpuacct:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d -2:cpu:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d -1:cpuset:/`) - ) - - dir := os.TempDir() - proc1Cgroup = filepath.Join(dir, "proc1Cgroup") - - defer func() { - os.Remove(proc1Cgroup) - proc1Cgroup = backup - }() - - if err := ioutil.WriteFile(proc1Cgroup, nonContainerizedProc1Cgroup, 0600); err != nil { - t.Fatalf("failed to write to %s: %v", proc1Cgroup, err) - } - inContainer, err := IsContainerized() - if err != nil { - t.Fatal(err) - } - if inContainer { - t.Fatal("Wrongly assuming containerized") - } - - if err := ioutil.WriteFile(proc1Cgroup, containerizedProc1Cgroup, 0600); err != nil { - t.Fatalf("failed to write to %s: %v", proc1Cgroup, err) - } - inContainer, err = IsContainerized() - if err != nil { - t.Fatal(err) - } - if !inContainer { - t.Fatal("Wrongly assuming non-containerized") - } -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_windows.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_windows.go deleted file mode 100644 index c843c6f84..000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_windows.go +++ /dev/null @@ -1,47 +0,0 @@ -package operatingsystem - -import ( - "syscall" - "unsafe" -) - -// See https://code.google.com/p/go/source/browse/src/pkg/mime/type_windows.go?r=d14520ac25bf6940785aabb71f5be453a286f58c -// for a similar sample - -func GetOperatingSystem() (string, error) { - - var h syscall.Handle - - // Default return value - ret := "Unknown Operating System" - - if err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, - syscall.StringToUTF16Ptr(`SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\`), - 0, - syscall.KEY_READ, - &h); err != nil { - return ret, err - } - defer syscall.RegCloseKey(h) - - var buf [1 << 10]uint16 - var typ uint32 - n := uint32(len(buf) * 2) // api expects array of bytes, not uint16 - - if err := syscall.RegQueryValueEx(h, - syscall.StringToUTF16Ptr("ProductName"), - nil, - &typ, - (*byte)(unsafe.Pointer(&buf[0])), - &n); err != nil { - return ret, err - } - ret = syscall.UTF16ToString(buf[:]) - - return ret, nil -} - -// No-op on Windows -func IsContainerized() (bool, error) { - return false, nil -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/parsers_test.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/parsers_test.go deleted file mode 100644 index a64e6b946..000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/parsers_test.go +++ /dev/null @@ -1,210 +0,0 @@ -package parsers - -import ( - "strings" - "testing" -) - -func TestParseHost(t *testing.T) { - var ( - defaultHttpHost = "127.0.0.1" - defaultUnix = "/var/run/docker.sock" - ) - invalids := map[string]string{ - "0.0.0.0": "Invalid bind address format: 0.0.0.0", - "tcp://": "Invalid proto, expected tcp: ", - "tcp:a.b.c.d": "Invalid bind address format: tcp:a.b.c.d", - "tcp:a.b.c.d/path": "Invalid bind address format: tcp:a.b.c.d/path", - "udp://127.0.0.1": "Invalid bind address format: udp://127.0.0.1", - "udp://127.0.0.1:2375": "Invalid bind address format: udp://127.0.0.1:2375", - } - valids := map[string]string{ - "0.0.0.1:5555": "tcp://0.0.0.1:5555", - "0.0.0.1:5555/path": "tcp://0.0.0.1:5555/path", - ":6666": "tcp://127.0.0.1:6666", - ":6666/path": "tcp://127.0.0.1:6666/path", - "tcp://:7777": "tcp://127.0.0.1:7777", - "tcp://:7777/path": "tcp://127.0.0.1:7777/path", - "": "unix:///var/run/docker.sock", - "unix:///run/docker.sock": "unix:///run/docker.sock", - "unix://": "unix:///var/run/docker.sock", - "fd://": "fd://", - "fd://something": "fd://something", - } - for invalidAddr, expectedError := range invalids { - if addr, err := ParseHost(defaultHttpHost, defaultUnix, invalidAddr); err == nil || err.Error() != expectedError { - t.Errorf("tcp %v address expected error %v return, got %s and addr %v", invalidAddr, expectedError, err, addr) - } - } - for validAddr, expectedAddr := range valids { - if addr, err := ParseHost(defaultHttpHost, defaultUnix, validAddr); err != nil || addr != expectedAddr { - t.Errorf("%v -> expected %v, got %v", validAddr, expectedAddr, addr) - } - } -} - -func TestParseInvalidUnixAddrInvalid(t *testing.T) { - if _, err := ParseUnixAddr("unix://tcp://127.0.0.1", "unix:///var/run/docker.sock"); err == nil || err.Error() != "Invalid proto, expected unix: tcp://127.0.0.1" { - t.Fatalf("Expected an error, got %v", err) - } -} - -func TestParseRepositoryTag(t *testing.T) { - if repo, tag := ParseRepositoryTag("root"); repo != "root" || tag != "" { - t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "root", "", repo, tag) - } - if repo, tag := ParseRepositoryTag("root:tag"); repo != "root" || tag != "tag" { - t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "root", "tag", repo, tag) - } - if repo, digest := ParseRepositoryTag("root@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); repo != "root" || digest != "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" { - t.Errorf("Expected repo: '%s' and digest: '%s', got '%s' and '%s'", "root", "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", repo, digest) - } - if repo, tag := ParseRepositoryTag("user/repo"); repo != "user/repo" || tag != "" { - t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "user/repo", "", repo, tag) - } - if repo, tag := ParseRepositoryTag("user/repo:tag"); repo != "user/repo" || tag != "tag" { - t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "user/repo", "tag", repo, tag) - } - if repo, digest := ParseRepositoryTag("user/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); repo != "user/repo" || digest != "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" { - t.Errorf("Expected repo: '%s' and digest: '%s', got '%s' and '%s'", "user/repo", "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", repo, digest) - } - if repo, tag := ParseRepositoryTag("url:5000/repo"); repo != "url:5000/repo" || tag != "" { - t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "url:5000/repo", "", repo, tag) - } - if repo, tag := ParseRepositoryTag("url:5000/repo:tag"); repo != "url:5000/repo" || tag != "tag" { - t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "url:5000/repo", "tag", repo, tag) - } - if repo, digest := ParseRepositoryTag("url:5000/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); repo != "url:5000/repo" || digest != "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" { - t.Errorf("Expected repo: '%s' and digest: '%s', got '%s' and '%s'", "url:5000/repo", "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", repo, digest) - } -} - -func TestParsePortMapping(t *testing.T) { - if _, err := PartParser("ip:public:private", "192.168.1.1:80"); err == nil { - t.Fatalf("Expected an error, got %v", err) - } - data, err := PartParser("ip:public:private", "192.168.1.1:80:8080") - if err != nil { - t.Fatal(err) - } - - if len(data) != 3 { - t.FailNow() - } - if data["ip"] != "192.168.1.1" { - t.Fail() - } - if data["public"] != "80" { - t.Fail() - } - if data["private"] != "8080" { - t.Fail() - } -} - -func TestParseKeyValueOpt(t *testing.T) { - invalids := map[string]string{ - "": "Unable to parse key/value option: ", - "key": "Unable to parse key/value option: key", - } - for invalid, expectedError := range invalids { - if _, _, err := ParseKeyValueOpt(invalid); err == nil || err.Error() != expectedError { - t.Fatalf("Expected error %v for %v, got %v", expectedError, invalid, err) - } - } - valids := map[string][]string{ - "key=value": {"key", "value"}, - " key = value ": {"key", "value"}, - "key=value1=value2": {"key", "value1=value2"}, - " key = value1 = value2 ": {"key", "value1 = value2"}, - } - for valid, expectedKeyValue := range valids { - key, value, err := ParseKeyValueOpt(valid) - if err != nil { - t.Fatal(err) - } - if key != expectedKeyValue[0] || value != expectedKeyValue[1] { - t.Fatalf("Expected {%v: %v} got {%v: %v}", expectedKeyValue[0], expectedKeyValue[1], key, value) - } - } -} - -func TestParsePortRange(t *testing.T) { - if start, end, err := ParsePortRange("8000-8080"); err != nil || start != 8000 || end != 8080 { - t.Fatalf("Error: %s or Expecting {start,end} values {8000,8080} but found {%d,%d}.", err, start, end) - } -} - -func TestParsePortRangeEmpty(t *testing.T) { - if _, _, err := ParsePortRange(""); err == nil || err.Error() != "Empty string specified for ports." { - t.Fatalf("Expected error 'Empty string specified for ports.', got %v", err) - } -} - -func TestParsePortRangeWithNoRange(t *testing.T) { - start, end, err := ParsePortRange("8080") - if err != nil { - t.Fatal(err) - } - if start != 8080 || end != 8080 { - t.Fatalf("Expected start and end to be the same and equal to 8080, but were %v and %v", start, end) - } -} - -func TestParsePortRangeIncorrectRange(t *testing.T) { - if _, _, err := ParsePortRange("9000-8080"); err == nil || !strings.Contains(err.Error(), "Invalid range specified for the Port") { - t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err) - } -} - -func TestParsePortRangeIncorrectEndRange(t *testing.T) { - if _, _, err := ParsePortRange("8000-a"); err == nil || !strings.Contains(err.Error(), "invalid syntax") { - t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err) - } - - if _, _, err := ParsePortRange("8000-30a"); err == nil || !strings.Contains(err.Error(), "invalid syntax") { - t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err) - } -} - -func TestParsePortRangeIncorrectStartRange(t *testing.T) { - if _, _, err := ParsePortRange("a-8000"); err == nil || !strings.Contains(err.Error(), "invalid syntax") { - t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err) - } - - if _, _, err := ParsePortRange("30a-8000"); err == nil || !strings.Contains(err.Error(), "invalid syntax") { - t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err) - } -} - -func TestParseLink(t *testing.T) { - name, alias, err := ParseLink("name:alias") - if err != nil { - t.Fatalf("Expected not to error out on a valid name:alias format but got: %v", err) - } - if name != "name" { - t.Fatalf("Link name should have been name, got %s instead", name) - } - if alias != "alias" { - t.Fatalf("Link alias should have been alias, got %s instead", alias) - } - // short format definition - name, alias, err = ParseLink("name") - if err != nil { - t.Fatalf("Expected not to error out on a valid name only format but got: %v", err) - } - if name != "name" { - t.Fatalf("Link name should have been name, got %s instead", name) - } - if alias != "name" { - t.Fatalf("Link alias should have been name, got %s instead", alias) - } - // empty string link definition is not allowed - if _, _, err := ParseLink(""); err == nil || !strings.Contains(err.Error(), "empty string specified for links") { - t.Fatalf("Expected error 'empty string specified for links' but got: %v", err) - } - // more than two colons are not allowed - if _, _, err := ParseLink("link:alias:wrong"); err == nil || !strings.Contains(err.Error(), "bad format for links: link:alias:wrong") { - t.Fatalf("Expected error 'bad format for links: link:alias:wrong' but got: %v", err) - } -} diff --git a/Godeps/_workspace/src/github.com/docker/go-units/duration_test.go b/Godeps/_workspace/src/github.com/docker/go-units/duration_test.go deleted file mode 100644 index 63baa515b..000000000 --- a/Godeps/_workspace/src/github.com/docker/go-units/duration_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package units - -import ( - "fmt" - "testing" - "time" -) - -func ExampleHumanDuration() { - fmt.Println(HumanDuration(450 * time.Millisecond)) - fmt.Println(HumanDuration(47 * time.Second)) - fmt.Println(HumanDuration(1 * time.Minute)) - fmt.Println(HumanDuration(3 * time.Minute)) - fmt.Println(HumanDuration(35 * time.Minute)) - fmt.Println(HumanDuration(35*time.Minute + 40*time.Second)) - fmt.Println(HumanDuration(1 * time.Hour)) - fmt.Println(HumanDuration(1*time.Hour + 45*time.Minute)) - fmt.Println(HumanDuration(3 * time.Hour)) - fmt.Println(HumanDuration(3*time.Hour + 59*time.Minute)) - fmt.Println(HumanDuration(3*time.Hour + 60*time.Minute)) - fmt.Println(HumanDuration(24 * time.Hour)) - fmt.Println(HumanDuration(24*time.Hour + 12*time.Hour)) - fmt.Println(HumanDuration(2 * 24 * time.Hour)) - fmt.Println(HumanDuration(7 * 24 * time.Hour)) - fmt.Println(HumanDuration(13*24*time.Hour + 5*time.Hour)) - fmt.Println(HumanDuration(2 * 7 * 24 * time.Hour)) - fmt.Println(HumanDuration(2*7*24*time.Hour + 4*24*time.Hour)) - fmt.Println(HumanDuration(3 * 7 * 24 * time.Hour)) - fmt.Println(HumanDuration(4 * 7 * 24 * time.Hour)) - fmt.Println(HumanDuration(4*7*24*time.Hour + 3*24*time.Hour)) - fmt.Println(HumanDuration(1 * 30 * 24 * time.Hour)) - fmt.Println(HumanDuration(1*30*24*time.Hour + 2*7*24*time.Hour)) - fmt.Println(HumanDuration(2 * 30 * 24 * time.Hour)) - fmt.Println(HumanDuration(3*30*24*time.Hour + 1*7*24*time.Hour)) - fmt.Println(HumanDuration(5*30*24*time.Hour + 2*7*24*time.Hour)) - fmt.Println(HumanDuration(13 * 30 * 24 * time.Hour)) - fmt.Println(HumanDuration(23 * 30 * 24 * time.Hour)) - fmt.Println(HumanDuration(24 * 30 * 24 * time.Hour)) - fmt.Println(HumanDuration(24*30*24*time.Hour + 2*7*24*time.Hour)) - fmt.Println(HumanDuration(3*365*24*time.Hour + 2*30*24*time.Hour)) -} - -func TestHumanDuration(t *testing.T) { - // Useful duration abstractions - day := 24 * time.Hour - week := 7 * day - month := 30 * day - year := 365 * day - - assertEquals(t, "Less than a second", HumanDuration(450*time.Millisecond)) - assertEquals(t, "47 seconds", HumanDuration(47*time.Second)) - assertEquals(t, "About a minute", HumanDuration(1*time.Minute)) - assertEquals(t, "3 minutes", HumanDuration(3*time.Minute)) - assertEquals(t, "35 minutes", HumanDuration(35*time.Minute)) - assertEquals(t, "35 minutes", HumanDuration(35*time.Minute+40*time.Second)) - assertEquals(t, "About an hour", HumanDuration(1*time.Hour)) - assertEquals(t, "About an hour", HumanDuration(1*time.Hour+45*time.Minute)) - assertEquals(t, "3 hours", HumanDuration(3*time.Hour)) - assertEquals(t, "3 hours", HumanDuration(3*time.Hour+59*time.Minute)) - assertEquals(t, "4 hours", HumanDuration(3*time.Hour+60*time.Minute)) - assertEquals(t, "24 hours", HumanDuration(24*time.Hour)) - assertEquals(t, "36 hours", HumanDuration(1*day+12*time.Hour)) - assertEquals(t, "2 days", HumanDuration(2*day)) - assertEquals(t, "7 days", HumanDuration(7*day)) - assertEquals(t, "13 days", HumanDuration(13*day+5*time.Hour)) - assertEquals(t, "2 weeks", HumanDuration(2*week)) - assertEquals(t, "2 weeks", HumanDuration(2*week+4*day)) - assertEquals(t, "3 weeks", HumanDuration(3*week)) - assertEquals(t, "4 weeks", HumanDuration(4*week)) - assertEquals(t, "4 weeks", HumanDuration(4*week+3*day)) - assertEquals(t, "4 weeks", HumanDuration(1*month)) - assertEquals(t, "6 weeks", HumanDuration(1*month+2*week)) - assertEquals(t, "8 weeks", HumanDuration(2*month)) - assertEquals(t, "3 months", HumanDuration(3*month+1*week)) - assertEquals(t, "5 months", HumanDuration(5*month+2*week)) - assertEquals(t, "13 months", HumanDuration(13*month)) - assertEquals(t, "23 months", HumanDuration(23*month)) - assertEquals(t, "24 months", HumanDuration(24*month)) - assertEquals(t, "2 years", HumanDuration(24*month+2*week)) - assertEquals(t, "3 years", HumanDuration(3*year+2*month)) -} diff --git a/Godeps/_workspace/src/github.com/docker/go-units/size_test.go b/Godeps/_workspace/src/github.com/docker/go-units/size_test.go deleted file mode 100644 index a968f5c0d..000000000 --- a/Godeps/_workspace/src/github.com/docker/go-units/size_test.go +++ /dev/null @@ -1,160 +0,0 @@ -package units - -import ( - "fmt" - "reflect" - "runtime" - "strings" - "testing" -) - -func ExampleBytesSize() { - fmt.Println(BytesSize(1024)) - fmt.Println(BytesSize(1024 * 1024)) - fmt.Println(BytesSize(1048576)) - fmt.Println(BytesSize(2 * MiB)) - fmt.Println(BytesSize(3.42 * GiB)) - fmt.Println(BytesSize(5.372 * TiB)) - fmt.Println(BytesSize(2.22 * PiB)) -} - -func ExampleHumanSize() { - fmt.Println(HumanSize(1000)) - fmt.Println(HumanSize(1024)) - fmt.Println(HumanSize(1000000)) - fmt.Println(HumanSize(1048576)) - fmt.Println(HumanSize(2 * MB)) - fmt.Println(HumanSize(float64(3.42 * GB))) - fmt.Println(HumanSize(float64(5.372 * TB))) - fmt.Println(HumanSize(float64(2.22 * PB))) -} - -func ExampleFromHumanSize() { - fmt.Println(FromHumanSize("32")) - fmt.Println(FromHumanSize("32b")) - fmt.Println(FromHumanSize("32B")) - fmt.Println(FromHumanSize("32k")) - fmt.Println(FromHumanSize("32K")) - fmt.Println(FromHumanSize("32kb")) - fmt.Println(FromHumanSize("32Kb")) - fmt.Println(FromHumanSize("32Mb")) - fmt.Println(FromHumanSize("32Gb")) - fmt.Println(FromHumanSize("32Tb")) - fmt.Println(FromHumanSize("32Pb")) -} - -func ExampleRAMInBytes() { - fmt.Println(RAMInBytes("32")) - fmt.Println(RAMInBytes("32b")) - fmt.Println(RAMInBytes("32B")) - fmt.Println(RAMInBytes("32k")) - fmt.Println(RAMInBytes("32K")) - fmt.Println(RAMInBytes("32kb")) - fmt.Println(RAMInBytes("32Kb")) - fmt.Println(RAMInBytes("32Mb")) - fmt.Println(RAMInBytes("32Gb")) - fmt.Println(RAMInBytes("32Tb")) - fmt.Println(RAMInBytes("32Pb")) - fmt.Println(RAMInBytes("32PB")) - fmt.Println(RAMInBytes("32P")) -} - -func TestBytesSize(t *testing.T) { - assertEquals(t, "1 KiB", BytesSize(1024)) - assertEquals(t, "1 MiB", BytesSize(1024*1024)) - assertEquals(t, "1 MiB", BytesSize(1048576)) - assertEquals(t, "2 MiB", BytesSize(2*MiB)) - assertEquals(t, "3.42 GiB", BytesSize(3.42*GiB)) - assertEquals(t, "5.372 TiB", BytesSize(5.372*TiB)) - assertEquals(t, "2.22 PiB", BytesSize(2.22*PiB)) -} - -func TestHumanSize(t *testing.T) { - assertEquals(t, "1 kB", HumanSize(1000)) - assertEquals(t, "1.024 kB", HumanSize(1024)) - assertEquals(t, "1 MB", HumanSize(1000000)) - assertEquals(t, "1.049 MB", HumanSize(1048576)) - assertEquals(t, "2 MB", HumanSize(2*MB)) - assertEquals(t, "3.42 GB", HumanSize(float64(3.42*GB))) - assertEquals(t, "5.372 TB", HumanSize(float64(5.372*TB))) - assertEquals(t, "2.22 PB", HumanSize(float64(2.22*PB))) -} - -func TestFromHumanSize(t *testing.T) { - assertSuccessEquals(t, 32, FromHumanSize, "32") - assertSuccessEquals(t, 32, FromHumanSize, "32b") - assertSuccessEquals(t, 32, FromHumanSize, "32B") - assertSuccessEquals(t, 32*KB, FromHumanSize, "32k") - assertSuccessEquals(t, 32*KB, FromHumanSize, "32K") - assertSuccessEquals(t, 32*KB, FromHumanSize, "32kb") - assertSuccessEquals(t, 32*KB, FromHumanSize, "32Kb") - assertSuccessEquals(t, 32*MB, FromHumanSize, "32Mb") - assertSuccessEquals(t, 32*GB, FromHumanSize, "32Gb") - assertSuccessEquals(t, 32*TB, FromHumanSize, "32Tb") - assertSuccessEquals(t, 32*PB, FromHumanSize, "32Pb") - - assertError(t, FromHumanSize, "") - assertError(t, FromHumanSize, "hello") - assertError(t, FromHumanSize, "-32") - assertError(t, FromHumanSize, "32.3") - assertError(t, FromHumanSize, " 32 ") - assertError(t, FromHumanSize, "32.3Kb") - assertError(t, FromHumanSize, "32 mb") - assertError(t, FromHumanSize, "32m b") - assertError(t, FromHumanSize, "32bm") -} - -func TestRAMInBytes(t *testing.T) { - assertSuccessEquals(t, 32, RAMInBytes, "32") - assertSuccessEquals(t, 32, RAMInBytes, "32b") - assertSuccessEquals(t, 32, RAMInBytes, "32B") - assertSuccessEquals(t, 32*KiB, RAMInBytes, "32k") - assertSuccessEquals(t, 32*KiB, RAMInBytes, "32K") - assertSuccessEquals(t, 32*KiB, RAMInBytes, "32kb") - assertSuccessEquals(t, 32*KiB, RAMInBytes, "32Kb") - assertSuccessEquals(t, 32*MiB, RAMInBytes, "32Mb") - assertSuccessEquals(t, 32*GiB, RAMInBytes, "32Gb") - assertSuccessEquals(t, 32*TiB, RAMInBytes, "32Tb") - assertSuccessEquals(t, 32*PiB, RAMInBytes, "32Pb") - assertSuccessEquals(t, 32*PiB, RAMInBytes, "32PB") - assertSuccessEquals(t, 32*PiB, RAMInBytes, "32P") - - assertError(t, RAMInBytes, "") - assertError(t, RAMInBytes, "hello") - assertError(t, RAMInBytes, "-32") - assertError(t, RAMInBytes, "32.3") - assertError(t, RAMInBytes, " 32 ") - assertError(t, RAMInBytes, "32.3Kb") - assertError(t, RAMInBytes, "32 mb") - assertError(t, RAMInBytes, "32m b") - assertError(t, RAMInBytes, "32bm") -} - -func assertEquals(t *testing.T, expected, actual interface{}) { - if expected != actual { - t.Errorf("Expected '%v' but got '%v'", expected, actual) - } -} - -// func that maps to the parse function signatures as testing abstraction -type parseFn func(string) (int64, error) - -// Define 'String()' for pretty-print -func (fn parseFn) String() string { - fnName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() - return fnName[strings.LastIndex(fnName, ".")+1:] -} - -func assertSuccessEquals(t *testing.T, expected int64, fn parseFn, arg string) { - res, err := fn(arg) - if err != nil || res != expected { - t.Errorf("%s(\"%s\") -> expected '%d' but got '%d' with error '%v'", fn, arg, expected, res, err) - } -} - -func assertError(t *testing.T, fn parseFn, arg string) { - res, err := fn(arg) - if err == nil && res != -1 { - t.Errorf("%s(\"%s\") -> expected error but got '%d'", fn, arg, res) - } -} diff --git a/Godeps/_workspace/src/github.com/docker/go-units/ulimit_test.go b/Godeps/_workspace/src/github.com/docker/go-units/ulimit_test.go deleted file mode 100644 index 3e7f10fc2..000000000 --- a/Godeps/_workspace/src/github.com/docker/go-units/ulimit_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package units - -import ( - "fmt" - "strconv" - "testing" -) - -func ExampleParseUlimit() { - fmt.Println(ParseUlimit("nofile=512:1024")) - fmt.Println(ParseUlimit("nofile=1024")) - fmt.Println(ParseUlimit("cpu=2:4")) - fmt.Println(ParseUlimit("cpu=6")) -} - -func TestParseUlimitValid(t *testing.T) { - u1 := &Ulimit{"nofile", 1024, 512} - if u2, _ := ParseUlimit("nofile=512:1024"); *u1 != *u2 { - t.Fatalf("expected %q, but got %q", u1, u2) - } -} - -func TestParseUlimitInvalidLimitType(t *testing.T) { - if _, err := ParseUlimit("notarealtype=1024:1024"); err == nil { - t.Fatalf("expected error on invalid ulimit type") - } -} - -func TestParseUlimitBadFormat(t *testing.T) { - if _, err := ParseUlimit("nofile:1024:1024"); err == nil { - t.Fatal("expected error on bad syntax") - } - - if _, err := ParseUlimit("nofile"); err == nil { - t.Fatal("expected error on bad syntax") - } - - if _, err := ParseUlimit("nofile="); err == nil { - t.Fatal("expected error on bad syntax") - } - if _, err := ParseUlimit("nofile=:"); err == nil { - t.Fatal("expected error on bad syntax") - } - if _, err := ParseUlimit("nofile=:1024"); err == nil { - t.Fatal("expected error on bad syntax") - } -} - -func TestParseUlimitHardLessThanSoft(t *testing.T) { - if _, err := ParseUlimit("nofile=1024:1"); err == nil { - t.Fatal("expected error on hard limit less than soft limit") - } -} - -func TestParseUlimitInvalidValueType(t *testing.T) { - if _, err := ParseUlimit("nofile=asdf"); err == nil { - t.Fatal("expected error on bad value type, but got no error") - } else if _, ok := err.(*strconv.NumError); !ok { - t.Fatalf("expected error on bad value type, but got `%s`", err) - } - - if _, err := ParseUlimit("nofile=1024:asdf"); err == nil { - t.Fatal("expected error on bad value type, but got no error") - } else if _, ok := err.(*strconv.NumError); !ok { - t.Fatalf("expected error on bad value type, but got `%s`", err) - } -} - -func TestUlimitStringOutput(t *testing.T) { - u := &Ulimit{"nofile", 1024, 512} - if s := u.String(); s != "nofile=512:1024" { - t.Fatal("expected String to return nofile=512:1024, but got", s) - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/bench_curly_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/bench_curly_test.go deleted file mode 100644 index db6a1a752..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/bench_curly_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package restful - -import ( - "fmt" - "net/http" - "net/http/httptest" - "testing" -) - -func setupCurly(container *Container) []string { - wsCount := 26 - rtCount := 26 - urisCurly := []string{} - - container.Router(CurlyRouter{}) - for i := 0; i < wsCount; i++ { - root := fmt.Sprintf("/%s/{%s}/", string(i+97), string(i+97)) - ws := new(WebService).Path(root) - for j := 0; j < rtCount; j++ { - sub := fmt.Sprintf("/%s2/{%s2}", string(j+97), string(j+97)) - ws.Route(ws.GET(sub).Consumes("application/xml").Produces("application/xml").To(echoCurly)) - } - container.Add(ws) - for _, each := range ws.Routes() { - urisCurly = append(urisCurly, "http://bench.com"+each.Path) - } - } - return urisCurly -} - -func echoCurly(req *Request, resp *Response) {} - -func BenchmarkManyCurly(b *testing.B) { - container := NewContainer() - urisCurly := setupCurly(container) - b.ResetTimer() - for t := 0; t < b.N; t++ { - for r := 0; r < 1000; r++ { - for _, each := range urisCurly { - sendNoReturnTo(each, container, t) - } - } - } -} - -func sendNoReturnTo(address string, container *Container, t int) { - httpRequest, _ := http.NewRequest("GET", address, nil) - httpRequest.Header.Set("Accept", "application/xml") - httpWriter := httptest.NewRecorder() - container.dispatch(httpWriter, httpRequest) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/bench_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/bench_test.go deleted file mode 100644 index 3e77c2d29..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/bench_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package restful - -import ( - "fmt" - "io" - "testing" -) - -var uris = []string{} - -func setup(container *Container) { - wsCount := 26 - rtCount := 26 - - for i := 0; i < wsCount; i++ { - root := fmt.Sprintf("/%s/{%s}/", string(i+97), string(i+97)) - ws := new(WebService).Path(root) - for j := 0; j < rtCount; j++ { - sub := fmt.Sprintf("/%s2/{%s2}", string(j+97), string(j+97)) - ws.Route(ws.GET(sub).To(echo)) - } - container.Add(ws) - for _, each := range ws.Routes() { - uris = append(uris, "http://bench.com"+each.Path) - } - } -} - -func echo(req *Request, resp *Response) { - io.WriteString(resp.ResponseWriter, "echo") -} - -func BenchmarkMany(b *testing.B) { - container := NewContainer() - setup(container) - b.ResetTimer() - for t := 0; t < b.N; t++ { - for _, each := range uris { - // println(each) - sendItTo(each, container) - } - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/compress_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/compress_test.go deleted file mode 100644 index 84a93c3fc..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/compress_test.go +++ /dev/null @@ -1,127 +0,0 @@ -package restful - -import ( - "bytes" - "compress/gzip" - "compress/zlib" - "io" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" -) - -// go test -v -test.run TestGzip ...restful -func TestGzip(t *testing.T) { - EnableContentEncoding = true - httpRequest, _ := http.NewRequest("GET", "/test", nil) - httpRequest.Header.Set("Accept-Encoding", "gzip,deflate") - httpWriter := httptest.NewRecorder() - wanted, encoding := wantsCompressedResponse(httpRequest) - if !wanted { - t.Fatal("should accept gzip") - } - if encoding != "gzip" { - t.Fatal("expected gzip") - } - c, err := NewCompressingResponseWriter(httpWriter, encoding) - if err != nil { - t.Fatal(err.Error()) - } - c.Write([]byte("Hello World")) - c.Close() - if httpWriter.Header().Get("Content-Encoding") != "gzip" { - t.Fatal("Missing gzip header") - } - reader, err := gzip.NewReader(httpWriter.Body) - if err != nil { - t.Fatal(err.Error()) - } - data, err := ioutil.ReadAll(reader) - if err != nil { - t.Fatal(err.Error()) - } - if got, want := string(data), "Hello World"; got != want { - t.Errorf("got %v want %v", got, want) - } -} - -func TestDeflate(t *testing.T) { - EnableContentEncoding = true - httpRequest, _ := http.NewRequest("GET", "/test", nil) - httpRequest.Header.Set("Accept-Encoding", "deflate,gzip") - httpWriter := httptest.NewRecorder() - wanted, encoding := wantsCompressedResponse(httpRequest) - if !wanted { - t.Fatal("should accept deflate") - } - if encoding != "deflate" { - t.Fatal("expected deflate") - } - c, err := NewCompressingResponseWriter(httpWriter, encoding) - if err != nil { - t.Fatal(err.Error()) - } - c.Write([]byte("Hello World")) - c.Close() - if httpWriter.Header().Get("Content-Encoding") != "deflate" { - t.Fatal("Missing deflate header") - } - reader, err := zlib.NewReader(httpWriter.Body) - if err != nil { - t.Fatal(err.Error()) - } - data, err := ioutil.ReadAll(reader) - if err != nil { - t.Fatal(err.Error()) - } - if got, want := string(data), "Hello World"; got != want { - t.Errorf("got %v want %v", got, want) - } -} - -func TestGzipDecompressRequestBody(t *testing.T) { - b := new(bytes.Buffer) - w := newGzipWriter() - w.Reset(b) - io.WriteString(w, `{"msg":"hi"}`) - w.Flush() - w.Close() - - req := new(Request) - httpRequest, _ := http.NewRequest("GET", "/", bytes.NewReader(b.Bytes())) - httpRequest.Header.Set("Content-Type", "application/json") - httpRequest.Header.Set("Content-Encoding", "gzip") - req.Request = httpRequest - - doCacheReadEntityBytes = false - doc := make(map[string]interface{}) - req.ReadEntity(&doc) - - if got, want := doc["msg"], "hi"; got != want { - t.Errorf("got %v want %v", got, want) - } -} - -func TestZlibDecompressRequestBody(t *testing.T) { - b := new(bytes.Buffer) - w := newZlibWriter() - w.Reset(b) - io.WriteString(w, `{"msg":"hi"}`) - w.Flush() - w.Close() - - req := new(Request) - httpRequest, _ := http.NewRequest("GET", "/", bytes.NewReader(b.Bytes())) - httpRequest.Header.Set("Content-Type", "application/json") - httpRequest.Header.Set("Content-Encoding", "deflate") - req.Request = httpRequest - - doCacheReadEntityBytes = false - doc := make(map[string]interface{}) - req.ReadEntity(&doc) - - if got, want := doc["msg"], "hi"; got != want { - t.Errorf("got %v want %v", got, want) - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/container_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/container_test.go deleted file mode 100644 index dd2552c37..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/container_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package restful - -import ( - "net/http" - "net/http/httptest" - "testing" -) - -// go test -v -test.run TestContainer_computeAllowedMethods ...restful -func TestContainer_computeAllowedMethods(t *testing.T) { - wc := NewContainer() - ws1 := new(WebService).Path("/users") - ws1.Route(ws1.GET("{i}").To(dummy)) - ws1.Route(ws1.POST("{i}").To(dummy)) - wc.Add(ws1) - httpRequest, _ := http.NewRequest("GET", "http://api.his.com/users/1", nil) - rreq := Request{Request: httpRequest} - m := wc.computeAllowedMethods(&rreq) - if len(m) != 2 { - t.Errorf("got %d expected 2 methods, %v", len(m), m) - } -} - -func TestContainer_HandleWithFilter(t *testing.T) { - prefilterCalled := false - postfilterCalled := false - httpHandlerCalled := false - - wc := NewContainer() - wc.Filter(func(request *Request, response *Response, chain *FilterChain) { - prefilterCalled = true - chain.ProcessFilter(request, response) - }) - wc.HandleWithFilter("/", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - httpHandlerCalled = true - w.Write([]byte("ok")) - })) - wc.Filter(func(request *Request, response *Response, chain *FilterChain) { - postfilterCalled = true - chain.ProcessFilter(request, response) - }) - - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/", nil) - wc.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("unexpected code %d", recorder.Code) - } - if recorder.Body.String() != "ok" { - t.Errorf("unexpected body %s", recorder.Body.String()) - } - if !prefilterCalled { - t.Errorf("filter added before calling HandleWithFilter wasn't called") - } - if !postfilterCalled { - t.Errorf("filter added after calling HandleWithFilter wasn't called") - } - if !httpHandlerCalled { - t.Errorf("handler added by calling HandleWithFilter wasn't called") - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/cors_filter_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/cors_filter_test.go deleted file mode 100644 index 9b4723089..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/cors_filter_test.go +++ /dev/null @@ -1,125 +0,0 @@ -package restful - -import ( - "net/http" - "net/http/httptest" - "testing" -) - -// go test -v -test.run TestCORSFilter_Preflight ...restful -// http://www.html5rocks.com/en/tutorials/cors/#toc-handling-a-not-so-simple-request -func TestCORSFilter_Preflight(t *testing.T) { - tearDown() - ws := new(WebService) - ws.Route(ws.PUT("/cors").To(dummy)) - Add(ws) - - cors := CrossOriginResourceSharing{ - ExposeHeaders: []string{"X-Custom-Header"}, - AllowedHeaders: []string{"X-Custom-Header", "X-Additional-Header"}, - CookiesAllowed: true, - Container: DefaultContainer} - Filter(cors.Filter) - - // Preflight - httpRequest, _ := http.NewRequest("OPTIONS", "http://api.alice.com/cors", nil) - httpRequest.Method = "OPTIONS" - httpRequest.Header.Set(HEADER_Origin, "http://api.bob.com") - httpRequest.Header.Set(HEADER_AccessControlRequestMethod, "PUT") - httpRequest.Header.Set(HEADER_AccessControlRequestHeaders, "X-Custom-Header, X-Additional-Header") - - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - - actual := httpWriter.Header().Get(HEADER_AccessControlAllowOrigin) - if "http://api.bob.com" != actual { - t.Fatal("expected: http://api.bob.com but got:" + actual) - } - actual = httpWriter.Header().Get(HEADER_AccessControlAllowMethods) - if "PUT" != actual { - t.Fatal("expected: PUT but got:" + actual) - } - actual = httpWriter.Header().Get(HEADER_AccessControlAllowHeaders) - if "X-Custom-Header, X-Additional-Header" != actual { - t.Fatal("expected: X-Custom-Header, X-Additional-Header but got:" + actual) - } - - if !cors.isOriginAllowed("somewhere") { - t.Fatal("origin expected to be allowed") - } - cors.AllowedDomains = []string{"overthere.com"} - if cors.isOriginAllowed("somewhere") { - t.Fatal("origin [somewhere] expected NOT to be allowed") - } - if !cors.isOriginAllowed("overthere.com") { - t.Fatal("origin [overthere] expected to be allowed") - } - -} - -// go test -v -test.run TestCORSFilter_Actual ...restful -// http://www.html5rocks.com/en/tutorials/cors/#toc-handling-a-not-so-simple-request -func TestCORSFilter_Actual(t *testing.T) { - tearDown() - ws := new(WebService) - ws.Route(ws.PUT("/cors").To(dummy)) - Add(ws) - - cors := CrossOriginResourceSharing{ - ExposeHeaders: []string{"X-Custom-Header"}, - AllowedHeaders: []string{"X-Custom-Header", "X-Additional-Header"}, - CookiesAllowed: true, - Container: DefaultContainer} - Filter(cors.Filter) - - // Actual - httpRequest, _ := http.NewRequest("PUT", "http://api.alice.com/cors", nil) - httpRequest.Header.Set(HEADER_Origin, "http://api.bob.com") - httpRequest.Header.Set("X-Custom-Header", "value") - - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - actual := httpWriter.Header().Get(HEADER_AccessControlAllowOrigin) - if "http://api.bob.com" != actual { - t.Fatal("expected: http://api.bob.com but got:" + actual) - } - if httpWriter.Body.String() != "dummy" { - t.Fatal("expected: dummy but got:" + httpWriter.Body.String()) - } -} - -var allowedDomainInput = []struct { - domains []string - origin string - accepted bool -}{ - {[]string{}, "http://anything.com", true}, -} - -// go test -v -test.run TestCORSFilter_AllowedDomains ...restful -func TestCORSFilter_AllowedDomains(t *testing.T) { - for _, each := range allowedDomainInput { - tearDown() - ws := new(WebService) - ws.Route(ws.PUT("/cors").To(dummy)) - Add(ws) - - cors := CrossOriginResourceSharing{ - AllowedDomains: each.domains, - CookiesAllowed: true, - Container: DefaultContainer} - Filter(cors.Filter) - - httpRequest, _ := http.NewRequest("PUT", "http://api.his.com/cors", nil) - httpRequest.Header.Set(HEADER_Origin, each.origin) - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - actual := httpWriter.Header().Get(HEADER_AccessControlAllowOrigin) - if actual != each.origin && each.accepted { - t.Fatal("expected to be accepted") - } - if actual == each.origin && !each.accepted { - t.Fatal("did not expect to be accepted") - } - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/curly_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/curly_test.go deleted file mode 100644 index 31d66dcbd..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/curly_test.go +++ /dev/null @@ -1,231 +0,0 @@ -package restful - -import ( - "io" - "net/http" - "testing" -) - -var requestPaths = []struct { - // url with path (1) is handled by service with root (2) and remainder has value final (3) - path, root string -}{ - {"/", "/"}, - {"/p", "/p"}, - {"/p/x", "/p/{q}"}, - {"/q/x", "/q"}, - {"/p/x/", "/p/{q}"}, - {"/p/x/y", "/p/{q}"}, - {"/q/x/y", "/q"}, - {"/z/q", "/{p}/q"}, - {"/a/b/c/q", "/"}, -} - -// go test -v -test.run TestCurlyDetectWebService ...restful -func TestCurlyDetectWebService(t *testing.T) { - ws1 := new(WebService).Path("/") - ws2 := new(WebService).Path("/p") - ws3 := new(WebService).Path("/q") - ws4 := new(WebService).Path("/p/q") - ws5 := new(WebService).Path("/p/{q}") - ws7 := new(WebService).Path("/{p}/q") - var wss = []*WebService{ws1, ws2, ws3, ws4, ws5, ws7} - - for _, each := range wss { - t.Logf("path=%s,toks=%v\n", each.pathExpr.Source, each.pathExpr.tokens) - } - - router := CurlyRouter{} - - ok := true - for i, fixture := range requestPaths { - requestTokens := tokenizePath(fixture.path) - who := router.detectWebService(requestTokens, wss) - if who != nil && who.RootPath() != fixture.root { - t.Logf("[line:%v] Unexpected dispatcher, expected:%v, actual:%v", i, fixture.root, who.RootPath()) - ok = false - } - } - if !ok { - t.Fail() - } -} - -var serviceDetects = []struct { - path string - found bool - root string -}{ - {"/a/b", true, "/{p}/{q}/{r}"}, - {"/p/q", true, "/p/q"}, - {"/q/p", true, "/q"}, - {"/", true, "/"}, - {"/p/q/r", true, "/p/q"}, -} - -// go test -v -test.run Test_detectWebService ...restful -func Test_detectWebService(t *testing.T) { - router := CurlyRouter{} - ws1 := new(WebService).Path("/") - ws2 := new(WebService).Path("/p") - ws3 := new(WebService).Path("/q") - ws4 := new(WebService).Path("/p/q") - ws5 := new(WebService).Path("/p/{q}") - ws6 := new(WebService).Path("/p/{q}/") - ws7 := new(WebService).Path("/{p}/q") - ws8 := new(WebService).Path("/{p}/{q}/{r}") - var wss = []*WebService{ws8, ws7, ws6, ws5, ws4, ws3, ws2, ws1} - for _, fix := range serviceDetects { - requestPath := fix.path - requestTokens := tokenizePath(requestPath) - for _, ws := range wss { - serviceTokens := ws.pathExpr.tokens - matches, score := router.computeWebserviceScore(requestTokens, serviceTokens) - t.Logf("req=%s,toks:%v,ws=%s,toks:%v,score=%d,matches=%v", requestPath, requestTokens, ws.RootPath(), serviceTokens, score, matches) - } - best := router.detectWebService(requestTokens, wss) - if best != nil { - if fix.found { - t.Logf("best=%s", best.RootPath()) - } else { - t.Fatalf("should have found:%s", fix.root) - } - } - } -} - -var routeMatchers = []struct { - route string - path string - matches bool - paramCount int - staticCount int -}{ - // route, request-path - {"/a", "/a", true, 0, 1}, - {"/a", "/b", false, 0, 0}, - {"/a", "/b", false, 0, 0}, - {"/a/{b}/c/", "/a/2/c", true, 1, 2}, - {"/{a}/{b}/{c}/", "/a/b", false, 0, 0}, - {"/{x:*}", "/", false, 0, 0}, - {"/{x:*}", "/a", true, 1, 0}, - {"/{x:*}", "/a/b", true, 1, 0}, - {"/a/{x:*}", "/a/b", true, 1, 1}, - {"/a/{x:[A-Z][A-Z]}", "/a/ZX", true, 1, 1}, - {"/basepath/{resource:*}", "/basepath/some/other/location/test.xml", true, 1, 1}, -} - -// clear && go test -v -test.run Test_matchesRouteByPathTokens ...restful -func Test_matchesRouteByPathTokens(t *testing.T) { - router := CurlyRouter{} - for i, each := range routeMatchers { - routeToks := tokenizePath(each.route) - reqToks := tokenizePath(each.path) - matches, pCount, sCount := router.matchesRouteByPathTokens(routeToks, reqToks) - if matches != each.matches { - t.Fatalf("[%d] unexpected matches outcome route:%s, path:%s, matches:%v", i, each.route, each.path, matches) - } - if pCount != each.paramCount { - t.Fatalf("[%d] unexpected paramCount got:%d want:%d ", i, pCount, each.paramCount) - } - if sCount != each.staticCount { - t.Fatalf("[%d] unexpected staticCount got:%d want:%d ", i, sCount, each.staticCount) - } - } -} - -// clear && go test -v -test.run TestExtractParameters_Wildcard1 ...restful -func TestExtractParameters_Wildcard1(t *testing.T) { - params := doExtractParams("/fixed/{var:*}", 2, "/fixed/remainder", t) - if params["var"] != "remainder" { - t.Errorf("parameter mismatch var: %s", params["var"]) - } -} - -// clear && go test -v -test.run TestExtractParameters_Wildcard2 ...restful -func TestExtractParameters_Wildcard2(t *testing.T) { - params := doExtractParams("/fixed/{var:*}", 2, "/fixed/remain/der", t) - if params["var"] != "remain/der" { - t.Errorf("parameter mismatch var: %s", params["var"]) - } -} - -// clear && go test -v -test.run TestExtractParameters_Wildcard3 ...restful -func TestExtractParameters_Wildcard3(t *testing.T) { - params := doExtractParams("/static/{var:*}", 2, "/static/test/sub/hi.html", t) - if params["var"] != "test/sub/hi.html" { - t.Errorf("parameter mismatch var: %s", params["var"]) - } -} - -// clear && go test -v -test.run TestCurly_ISSUE_34 ...restful -func TestCurly_ISSUE_34(t *testing.T) { - ws1 := new(WebService).Path("/") - ws1.Route(ws1.GET("/{type}/{id}").To(curlyDummy)) - ws1.Route(ws1.GET("/network/{id}").To(curlyDummy)) - routes := CurlyRouter{}.selectRoutes(ws1, tokenizePath("/network/12")) - if len(routes) != 2 { - t.Fatal("expected 2 routes") - } - if routes[0].Path != "/network/{id}" { - t.Error("first is", routes[0].Path) - } -} - -// clear && go test -v -test.run TestCurly_ISSUE_34_2 ...restful -func TestCurly_ISSUE_34_2(t *testing.T) { - ws1 := new(WebService) - ws1.Route(ws1.GET("/network/{id}").To(curlyDummy)) - ws1.Route(ws1.GET("/{type}/{id}").To(curlyDummy)) - routes := CurlyRouter{}.selectRoutes(ws1, tokenizePath("/network/12")) - if len(routes) != 2 { - t.Fatal("expected 2 routes") - } - if routes[0].Path != "/network/{id}" { - t.Error("first is", routes[0].Path) - } -} - -// clear && go test -v -test.run TestCurly_JsonHtml ...restful -func TestCurly_JsonHtml(t *testing.T) { - ws1 := new(WebService) - ws1.Path("/") - ws1.Route(ws1.GET("/some.html").To(curlyDummy).Consumes("*/*").Produces("text/html")) - req, _ := http.NewRequest("GET", "/some.html", nil) - req.Header.Set("Accept", "application/json") - _, route, err := CurlyRouter{}.SelectRoute([]*WebService{ws1}, req) - if err == nil { - t.Error("error expected") - } - if route != nil { - t.Error("no route expected") - } -} - -// go test -v -test.run TestCurly_ISSUE_137 ...restful -func TestCurly_ISSUE_137(t *testing.T) { - ws1 := new(WebService) - ws1.Route(ws1.GET("/hello").To(curlyDummy)) - ws1.Path("/") - req, _ := http.NewRequest("GET", "/", nil) - _, route, _ := CurlyRouter{}.SelectRoute([]*WebService{ws1}, req) - t.Log(route) - if route != nil { - t.Error("no route expected") - } -} - -// go test -v -test.run TestCurly_ISSUE_137_2 ...restful -func TestCurly_ISSUE_137_2(t *testing.T) { - ws1 := new(WebService) - ws1.Route(ws1.GET("/hello").To(curlyDummy)) - ws1.Path("/") - req, _ := http.NewRequest("GET", "/hello/bob", nil) - _, route, _ := CurlyRouter{}.SelectRoute([]*WebService{ws1}, req) - t.Log(route) - if route != nil { - t.Errorf("no route expected, got %v", route) - } -} - -func curlyDummy(req *Request, resp *Response) { io.WriteString(resp.ResponseWriter, "curlyDummy") } diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/doc_examples_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/doc_examples_test.go deleted file mode 100644 index 0af636e55..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/doc_examples_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package restful - -import "net/http" - -func ExampleOPTIONSFilter() { - // Install the OPTIONS filter on the default Container - Filter(OPTIONSFilter()) -} -func ExampleContainer_OPTIONSFilter() { - // Install the OPTIONS filter on a Container - myContainer := new(Container) - myContainer.Filter(myContainer.OPTIONSFilter) -} - -func ExampleContainer() { - // The Default container of go-restful uses the http.DefaultServeMux. - // You can create your own Container using restful.NewContainer() and create a new http.Server for that particular container - - ws := new(WebService) - wsContainer := NewContainer() - wsContainer.Add(ws) - server := &http.Server{Addr: ":8080", Handler: wsContainer} - server.ListenAndServe() -} - -func ExampleCrossOriginResourceSharing() { - // To install this filter on the Default Container use: - cors := CrossOriginResourceSharing{ExposeHeaders: []string{"X-My-Header"}, CookiesAllowed: false, Container: DefaultContainer} - Filter(cors.Filter) -} - -func ExampleServiceError() { - resp := new(Response) - resp.WriteEntity(NewError(http.StatusBadRequest, "Non-integer {id} path parameter")) -} - -func ExampleBoundedCachedCompressors() { - // Register a compressor provider (gzip/deflate read/write) that uses - // a bounded cache with a maximum of 20 writers and 20 readers. - SetCompressorProvider(NewBoundedCachedCompressors(20, 20)) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/entity_accessors_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/entity_accessors_test.go deleted file mode 100644 index 943093ae0..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/entity_accessors_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package restful - -import ( - "bytes" - "fmt" - "io" - "net/http" - "net/http/httptest" - "reflect" - "testing" -) - -type keyvalue struct { - readCalled bool - writeCalled bool -} - -func (kv *keyvalue) Read(req *Request, v interface{}) error { - //t := reflect.TypeOf(v) - //rv := reflect.ValueOf(v) - kv.readCalled = true - return nil -} - -func (kv *keyvalue) Write(resp *Response, status int, v interface{}) error { - t := reflect.TypeOf(v) - rv := reflect.ValueOf(v) - for ix := 0; ix < t.NumField(); ix++ { - sf := t.Field(ix) - io.WriteString(resp, sf.Name) - io.WriteString(resp, "=") - io.WriteString(resp, fmt.Sprintf("%v\n", rv.Field(ix).Interface())) - } - kv.writeCalled = true - return nil -} - -// go test -v -test.run TestKeyValueEncoding ...restful -func TestKeyValueEncoding(t *testing.T) { - type Book struct { - Title string - Author string - PublishedYear int - } - kv := new(keyvalue) - RegisterEntityAccessor("application/kv", kv) - b := Book{"Singing for Dummies", "john doe", 2015} - - // Write - httpWriter := httptest.NewRecorder() - // Accept Produces - resp := Response{httpWriter, "application/kv,*/*;q=0.8", []string{"application/kv"}, 0, 0, true, nil} - resp.WriteEntity(b) - t.Log(string(httpWriter.Body.Bytes())) - if !kv.writeCalled { - t.Error("Write never called") - } - - // Read - bodyReader := bytes.NewReader(httpWriter.Body.Bytes()) - httpRequest, _ := http.NewRequest("GET", "/test", bodyReader) - httpRequest.Header.Set("Content-Type", "application/kv; charset=UTF-8") - request := NewRequest(httpRequest) - var bb Book - request.ReadEntity(&bb) - if !kv.readCalled { - t.Error("Read never called") - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/.goconvey b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/.goconvey deleted file mode 100644 index 8485e986e..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/.goconvey +++ /dev/null @@ -1 +0,0 @@ -ignore \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/.goconvey b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/.goconvey deleted file mode 100644 index 8485e986e..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/.goconvey +++ /dev/null @@ -1 +0,0 @@ -ignore \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/app.yaml b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/app.yaml deleted file mode 100644 index 362db6b07..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/app.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# -# Include your application ID here -# -application: -version: 1 -runtime: go -api_version: go1 - -handlers: -# -# Regex for all swagger files to make as static content. -# You should create the folder static/swagger and copy -# swagger-ui into it. -# -- url: /apidocs/(.*?)/(.*\.(js|html|css)) - static_files: static/swagger/\1/\2 - upload: static/swagger/(.*?)/(.*\.(js|html|css)) - -- url: /.* - script: _go_app diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/datastore/.goconvey b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/datastore/.goconvey deleted file mode 100644 index 8485e986e..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/datastore/.goconvey +++ /dev/null @@ -1 +0,0 @@ -ignore \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/datastore/app.yaml b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/datastore/app.yaml deleted file mode 100644 index 1ac9dca28..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/datastore/app.yaml +++ /dev/null @@ -1,18 +0,0 @@ -application: -version: 1 -runtime: go -api_version: go1 - -handlers: -# Regex for all swagger files to make as static content. -# You should create the folder static/swagger and copy -# swagger-ui into it. -# -- url: /apidocs/(.*?)/(.*\.(js|html|css)) - static_files: static/swagger/\1/\2 - upload: static/swagger/(.*?)/(.*\.(js|html|css)) - -# Catch all. -- url: /.* - script: _go_app - login: required diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/datastore/main.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/datastore/main.go deleted file mode 100644 index 9f9c78d1f..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/datastore/main.go +++ /dev/null @@ -1,266 +0,0 @@ -package main - -import ( - "github.com/emicklei/go-restful" - "github.com/emicklei/go-restful/swagger" - "google.golang.com/appengine" - "google.golang.com/appengine/datastore" - "google.golang.com/appengine/user" - "net/http" - "time" -) - -// This example demonstrates a reasonably complete suite of RESTful operations backed -// by DataStore on Google App Engine. - -// Our simple example struct. -type Profile struct { - LastModified time.Time `json:"-" xml:"-"` - Email string `json:"-" xml:"-"` - FirstName string `json:"first_name" xml:"first-name"` - NickName string `json:"nick_name" xml:"nick-name"` - LastName string `json:"last_name" xml:"last-name"` -} - -type ProfileApi struct { - Path string -} - -func gaeUrl() string { - if appengine.IsDevAppServer() { - return "http://localhost:8080" - } else { - // Include your URL on App Engine here. - // I found no way to get AppID without appengine.Context and this always - // based on a http.Request. - return "http://federatedservices.appspot.com" - } -} - -func init() { - u := ProfileApi{Path: "/profiles"} - u.register() - - // Optionally, you can install the Swagger Service which provides a nice Web UI on your REST API - // You need to download the Swagger HTML5 assets and change the FilePath location in the config below. - // Open .appspot.com/apidocs and enter - // Place the Swagger UI files into a folder called static/swagger if you wish to use Swagger - // http://.appspot.com/apidocs.json in the api input field. - // For testing, you can use http://localhost:8080/apidocs.json - config := swagger.Config{ - // You control what services are visible - WebServices: restful.RegisteredWebServices(), - WebServicesUrl: gaeUrl(), - ApiPath: "/apidocs.json", - - // Optionally, specifiy where the UI is located - SwaggerPath: "/apidocs/", - - // GAE support static content which is configured in your app.yaml. - // This example expect the swagger-ui in static/swagger so you should place it there :) - SwaggerFilePath: "static/swagger"} - swagger.InstallSwaggerService(config) -} - -func (u ProfileApi) register() { - ws := new(restful.WebService) - - ws. - Path(u.Path). - // You can specify consumes and produces per route as well. - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON, restful.MIME_XML) - - ws.Route(ws.POST("").To(u.insert). - // Swagger documentation. - Doc("insert a new profile"). - Param(ws.BodyParameter("Profile", "representation of a profile").DataType("main.Profile")). - Reads(Profile{})) - - ws.Route(ws.GET("/{profile-id}").To(u.read). - // Swagger documentation. - Doc("read a profile"). - Param(ws.PathParameter("profile-id", "identifier for a profile").DataType("string")). - Writes(Profile{})) - - ws.Route(ws.PUT("/{profile-id}").To(u.update). - // Swagger documentation. - Doc("update an existing profile"). - Param(ws.PathParameter("profile-id", "identifier for a profile").DataType("string")). - Param(ws.BodyParameter("Profile", "representation of a profile").DataType("main.Profile")). - Reads(Profile{})) - - ws.Route(ws.DELETE("/{profile-id}").To(u.remove). - // Swagger documentation. - Doc("remove a profile"). - Param(ws.PathParameter("profile-id", "identifier for a profile").DataType("string"))) - - restful.Add(ws) -} - -// POST http://localhost:8080/profiles -// {"first_name": "Ivan", "nick_name": "Socks", "last_name": "Hawkes"} -// -func (u *ProfileApi) insert(r *restful.Request, w *restful.Response) { - c := appengine.NewContext(r.Request) - - // Marshall the entity from the request into a struct. - p := new(Profile) - err := r.ReadEntity(&p) - if err != nil { - w.WriteError(http.StatusNotAcceptable, err) - return - } - - // Ensure we start with a sensible value for this field. - p.LastModified = time.Now() - - // The profile belongs to this user. - p.Email = user.Current(c).String() - - k, err := datastore.Put(c, datastore.NewIncompleteKey(c, "profiles", nil), p) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - // Let them know the location of the newly created resource. - // TODO: Use a safe Url path append function. - w.AddHeader("Location", u.Path+"/"+k.Encode()) - - // Return the resultant entity. - w.WriteHeader(http.StatusCreated) - w.WriteEntity(p) -} - -// GET http://localhost:8080/profiles/ahdkZXZ-ZmVkZXJhdGlvbi1zZXJ2aWNlc3IVCxIIcHJvZmlsZXMYgICAgICAgAoM -// -func (u ProfileApi) read(r *restful.Request, w *restful.Response) { - c := appengine.NewContext(r.Request) - - // Decode the request parameter to determine the key for the entity. - k, err := datastore.DecodeKey(r.PathParameter("profile-id")) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - // Retrieve the entity from the datastore. - p := Profile{} - if err := datastore.Get(c, k, &p); err != nil { - if err.Error() == "datastore: no such entity" { - http.Error(w, err.Error(), http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - return - } - - // Check we own the profile before allowing them to view it. - // Optionally, return a 404 instead to help prevent guessing ids. - // TODO: Allow admins access. - if p.Email != user.Current(c).String() { - http.Error(w, "You do not have access to this resource", http.StatusForbidden) - return - } - - w.WriteEntity(p) -} - -// PUT http://localhost:8080/profiles/ahdkZXZ-ZmVkZXJhdGlvbi1zZXJ2aWNlc3IVCxIIcHJvZmlsZXMYgICAgICAgAoM -// {"first_name": "Ivan", "nick_name": "Socks", "last_name": "Hawkes"} -// -func (u *ProfileApi) update(r *restful.Request, w *restful.Response) { - c := appengine.NewContext(r.Request) - - // Decode the request parameter to determine the key for the entity. - k, err := datastore.DecodeKey(r.PathParameter("profile-id")) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - // Marshall the entity from the request into a struct. - p := new(Profile) - err = r.ReadEntity(&p) - if err != nil { - w.WriteError(http.StatusNotAcceptable, err) - return - } - - // Retrieve the old entity from the datastore. - old := Profile{} - if err := datastore.Get(c, k, &old); err != nil { - if err.Error() == "datastore: no such entity" { - http.Error(w, err.Error(), http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - return - } - - // Check we own the profile before allowing them to update it. - // Optionally, return a 404 instead to help prevent guessing ids. - // TODO: Allow admins access. - if old.Email != user.Current(c).String() { - http.Error(w, "You do not have access to this resource", http.StatusForbidden) - return - } - - // Since the whole entity is re-written, we need to assign any invariant fields again - // e.g. the owner of the entity. - p.Email = user.Current(c).String() - - // Keep track of the last modification date. - p.LastModified = time.Now() - - // Attempt to overwrite the old entity. - _, err = datastore.Put(c, k, p) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - // Let them know it succeeded. - w.WriteHeader(http.StatusNoContent) -} - -// DELETE http://localhost:8080/profiles/ahdkZXZ-ZmVkZXJhdGlvbi1zZXJ2aWNlc3IVCxIIcHJvZmlsZXMYgICAgICAgAoM -// -func (u *ProfileApi) remove(r *restful.Request, w *restful.Response) { - c := appengine.NewContext(r.Request) - - // Decode the request parameter to determine the key for the entity. - k, err := datastore.DecodeKey(r.PathParameter("profile-id")) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - // Retrieve the old entity from the datastore. - old := Profile{} - if err := datastore.Get(c, k, &old); err != nil { - if err.Error() == "datastore: no such entity" { - http.Error(w, err.Error(), http.StatusNotFound) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - return - } - - // Check we own the profile before allowing them to delete it. - // Optionally, return a 404 instead to help prevent guessing ids. - // TODO: Allow admins access. - if old.Email != user.Current(c).String() { - http.Error(w, "You do not have access to this resource", http.StatusForbidden) - return - } - - // Delete the entity. - if err := datastore.Delete(c, k); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - - // Success notification. - w.WriteHeader(http.StatusNoContent) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/restful-appstats-integration.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/restful-appstats-integration.go deleted file mode 100644 index b3261eeb9..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/restful-appstats-integration.go +++ /dev/null @@ -1,13 +0,0 @@ -package main - -import ( - "github.com/mjibson/appstats" -) - - -func stats(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - c := appstats.NewContext(req.Request) - chain.ProcessFilter(req, resp) - c.Stats.Status = resp.StatusCode() - c.Save() -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/restful-user-service.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/restful-user-service.go deleted file mode 100644 index 0e8830181..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/restful-user-service.go +++ /dev/null @@ -1,161 +0,0 @@ -package main - -import ( - "github.com/emicklei/go-restful" - "github.com/emicklei/go-restful/swagger" - "google.golang.com/appengine" - "google.golang.com/appengine/memcache" - "net/http" -) - -// This example is functionally the same as ../restful-user-service.go -// but it`s supposed to run on Goole App Engine (GAE) -// -// contributed by ivanhawkes - -type User struct { - Id, Name string -} - -type UserService struct { - // normally one would use DAO (data access object) - // but in this example we simple use memcache. -} - -func (u UserService) Register() { - ws := new(restful.WebService) - - ws. - Path("/users"). - Consumes(restful.MIME_XML, restful.MIME_JSON). - Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well - - ws.Route(ws.GET("/{user-id}").To(u.findUser). - // docs - Doc("get a user"). - Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")). - Writes(User{})) // on the response - - ws.Route(ws.PATCH("").To(u.updateUser). - // docs - Doc("update a user"). - Reads(User{})) // from the request - - ws.Route(ws.PUT("/{user-id}").To(u.createUser). - // docs - Doc("create a user"). - Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")). - Reads(User{})) // from the request - - ws.Route(ws.DELETE("/{user-id}").To(u.removeUser). - // docs - Doc("delete a user"). - Param(ws.PathParameter("user-id", "identifier of the user").DataType("string"))) - - restful.Add(ws) -} - -// GET http://localhost:8080/users/1 -// -func (u UserService) findUser(request *restful.Request, response *restful.Response) { - c := appengine.NewContext(request.Request) - id := request.PathParameter("user-id") - usr := new(User) - _, err := memcache.Gob.Get(c, id, &usr) - if err != nil || len(usr.Id) == 0 { - response.WriteErrorString(http.StatusNotFound, "User could not be found.") - } else { - response.WriteEntity(usr) - } -} - -// PATCH http://localhost:8080/users -// 1Melissa Raspberry -// -func (u *UserService) updateUser(request *restful.Request, response *restful.Response) { - c := appengine.NewContext(request.Request) - usr := new(User) - err := request.ReadEntity(&usr) - if err == nil { - item := &memcache.Item{ - Key: usr.Id, - Object: &usr, - } - err = memcache.Gob.Set(c, item) - if err != nil { - response.WriteError(http.StatusInternalServerError, err) - return - } - response.WriteEntity(usr) - } else { - response.WriteError(http.StatusInternalServerError, err) - } -} - -// PUT http://localhost:8080/users/1 -// 1Melissa -// -func (u *UserService) createUser(request *restful.Request, response *restful.Response) { - c := appengine.NewContext(request.Request) - usr := User{Id: request.PathParameter("user-id")} - err := request.ReadEntity(&usr) - if err == nil { - item := &memcache.Item{ - Key: usr.Id, - Object: &usr, - } - err = memcache.Gob.Add(c, item) - if err != nil { - response.WriteError(http.StatusInternalServerError, err) - return - } - response.WriteHeader(http.StatusCreated) - response.WriteEntity(usr) - } else { - response.WriteError(http.StatusInternalServerError, err) - } -} - -// DELETE http://localhost:8080/users/1 -// -func (u *UserService) removeUser(request *restful.Request, response *restful.Response) { - c := appengine.NewContext(request.Request) - id := request.PathParameter("user-id") - err := memcache.Delete(c, id) - if err != nil { - response.WriteError(http.StatusInternalServerError, err) - } -} - -func getGaeURL() string { - if appengine.IsDevAppServer() { - return "http://localhost:8080" - } else { - /** - * Include your URL on App Engine here. - * I found no way to get AppID without appengine.Context and this always - * based on a http.Request. - */ - return "http://.appspot.com" - } -} - -func init() { - u := UserService{} - u.Register() - - // Optionally, you can install the Swagger Service which provides a nice Web UI on your REST API - // You need to download the Swagger HTML5 assets and change the FilePath location in the config below. - // Open .appspot.com/apidocs and enter http://.appspot.com/apidocs.json in the api input field. - config := swagger.Config{ - WebServices: restful.RegisteredWebServices(), // you control what services are visible - WebServicesUrl: getGaeURL(), - ApiPath: "/apidocs.json", - - // Optionally, specifiy where the UI is located - SwaggerPath: "/apidocs/", - // GAE support static content which is configured in your app.yaml. - // This example expect the swagger-ui in static/swagger so you should place it there :) - SwaggerFilePath: "static/swagger"} - swagger.InstallSwaggerService(config) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/home.html b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/home.html deleted file mode 100644 index e5d49b42c..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/home.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - -

{{.Text}}

- - \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-CORS-filter.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-CORS-filter.go deleted file mode 100644 index 346aa1b37..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-CORS-filter.go +++ /dev/null @@ -1,67 +0,0 @@ -package main - -import ( - "io" - "log" - "net/http" - - "github.com/emicklei/go-restful" -) - -// Cross-origin resource sharing (CORS) is a mechanism that allows JavaScript on a web page -// to make XMLHttpRequests to another domain, not the domain the JavaScript originated from. -// -// http://en.wikipedia.org/wiki/Cross-origin_resource_sharing -// http://enable-cors.org/server.html -// -// GET http://localhost:8080/users -// -// GET http://localhost:8080/users/1 -// -// PUT http://localhost:8080/users/1 -// -// DELETE http://localhost:8080/users/1 -// -// OPTIONS http://localhost:8080/users/1 with Header "Origin" set to some domain and - -type UserResource struct{} - -func (u UserResource) RegisterTo(container *restful.Container) { - ws := new(restful.WebService) - ws. - Path("/users"). - Consumes("*/*"). - Produces("*/*") - - ws.Route(ws.GET("/{user-id}").To(u.nop)) - ws.Route(ws.POST("").To(u.nop)) - ws.Route(ws.PUT("/{user-id}").To(u.nop)) - ws.Route(ws.DELETE("/{user-id}").To(u.nop)) - - container.Add(ws) -} - -func (u UserResource) nop(request *restful.Request, response *restful.Response) { - io.WriteString(response.ResponseWriter, "this would be a normal response") -} - -func main() { - wsContainer := restful.NewContainer() - u := UserResource{} - u.RegisterTo(wsContainer) - - // Add container filter to enable CORS - cors := restful.CrossOriginResourceSharing{ - ExposeHeaders: []string{"X-My-Header"}, - AllowedHeaders: []string{"Content-Type", "Accept"}, - CookiesAllowed: false, - Container: wsContainer} - wsContainer.Filter(cors.Filter) - - // Add container filter to respond to OPTIONS - wsContainer.Filter(wsContainer.OPTIONSFilter) - - log.Printf("start listening on localhost:8080") - server := &http.Server{Addr: ":8080", Handler: wsContainer} - log.Fatal(server.ListenAndServe()) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-NCSA-logging.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-NCSA-logging.go deleted file mode 100644 index 0cda50d34..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-NCSA-logging.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "github.com/emicklei/go-restful" - "io" - "log" - "net/http" - "os" - "strings" - "time" -) - -// This example shows how to create a filter that produces log lines -// according to the Common Log Format, also known as the NCSA standard. -// -// kindly contributed by leehambley -// -// GET http://localhost:8080/ping - -var logger *log.Logger = log.New(os.Stdout, "", 0) - -func NCSACommonLogFormatLogger() restful.FilterFunction { - return func(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - var username = "-" - if req.Request.URL.User != nil { - if name := req.Request.URL.User.Username(); name != "" { - username = name - } - } - chain.ProcessFilter(req, resp) - logger.Printf("%s - %s [%s] \"%s %s %s\" %d %d", - strings.Split(req.Request.RemoteAddr, ":")[0], - username, - time.Now().Format("02/Jan/2006:15:04:05 -0700"), - req.Request.Method, - req.Request.URL.RequestURI(), - req.Request.Proto, - resp.StatusCode(), - resp.ContentLength(), - ) - } -} - -func main() { - ws := new(restful.WebService) - ws.Filter(NCSACommonLogFormatLogger()) - ws.Route(ws.GET("/ping").To(hello)) - restful.Add(ws) - http.ListenAndServe(":8080", nil) -} - -func hello(req *restful.Request, resp *restful.Response) { - io.WriteString(resp, "pong") -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-basic-authentication.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-basic-authentication.go deleted file mode 100644 index 5dd3067e9..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-basic-authentication.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "github.com/emicklei/go-restful" - "io" - "net/http" -) - -// This example shows how to create a (Route) Filter that performs Basic Authentication on the Http request. -// -// GET http://localhost:8080/secret -// and use admin,admin for the credentials - -func main() { - ws := new(restful.WebService) - ws.Route(ws.GET("/secret").Filter(basicAuthenticate).To(secret)) - restful.Add(ws) - http.ListenAndServe(":8080", nil) -} - -func basicAuthenticate(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - encoded := req.Request.Header.Get("Authorization") - // usr/pwd = admin/admin - // real code does some decoding - if len(encoded) == 0 || "Basic YWRtaW46YWRtaW4=" != encoded { - resp.AddHeader("WWW-Authenticate", "Basic realm=Protected Area") - resp.WriteErrorString(401, "401: Not Authorized") - return - } - chain.ProcessFilter(req, resp) -} - -func secret(req *restful.Request, resp *restful.Response) { - io.WriteString(resp, "42") -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-cpuprofiler-service.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-cpuprofiler-service.go deleted file mode 100644 index 9148213cf..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-cpuprofiler-service.go +++ /dev/null @@ -1,65 +0,0 @@ -package main - -import ( - "github.com/emicklei/go-restful" - "io" - "log" - "os" - "runtime/pprof" -) - -// ProfilingService is a WebService that can start/stop a CPU profile and write results to a file -// GET /{rootPath}/start will activate CPU profiling -// GET /{rootPath}/stop will stop profiling -// -// NewProfileService("/profiler", "ace.prof").AddWebServiceTo(restful.DefaultContainer) -// -type ProfilingService struct { - rootPath string // the base (root) of the service, e.g. /profiler - cpuprofile string // the output filename to write profile results, e.g. myservice.prof - cpufile *os.File // if not nil, then profiling is active -} - -func NewProfileService(rootPath string, outputFilename string) *ProfilingService { - ps := new(ProfilingService) - ps.rootPath = rootPath - ps.cpuprofile = outputFilename - return ps -} - -// Add this ProfileService to a restful Container -func (p ProfilingService) AddWebServiceTo(container *restful.Container) { - ws := new(restful.WebService) - ws.Path(p.rootPath).Consumes("*/*").Produces(restful.MIME_JSON) - ws.Route(ws.GET("/start").To(p.startProfiler)) - ws.Route(ws.GET("/stop").To(p.stopProfiler)) - container.Add(ws) -} - -func (p *ProfilingService) startProfiler(req *restful.Request, resp *restful.Response) { - if p.cpufile != nil { - io.WriteString(resp.ResponseWriter, "[restful] CPU profiling already running") - return // error? - } - cpufile, err := os.Create(p.cpuprofile) - if err != nil { - log.Fatal(err) - } - // remember for close - p.cpufile = cpufile - pprof.StartCPUProfile(cpufile) - io.WriteString(resp.ResponseWriter, "[restful] CPU profiling started, writing on:"+p.cpuprofile) -} - -func (p *ProfilingService) stopProfiler(req *restful.Request, resp *restful.Response) { - if p.cpufile == nil { - io.WriteString(resp.ResponseWriter, "[restful] CPU profiling not active") - return // error? - } - pprof.StopCPUProfile() - p.cpufile.Close() - p.cpufile = nil - io.WriteString(resp.ResponseWriter, "[restful] CPU profiling stopped, closing:"+p.cpuprofile) -} - -func main() {} // exists for example compilation only diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-curly-router.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-curly-router.go deleted file mode 100644 index 1b95dd02b..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-curly-router.go +++ /dev/null @@ -1,107 +0,0 @@ -package main - -import ( - "github.com/emicklei/go-restful" - "log" - "net/http" -) - -// This example has the same service definition as restful-user-resource -// but uses a different router (CurlyRouter) that does not use regular expressions -// -// POST http://localhost:8080/users -// 1Melissa Raspberry -// -// GET http://localhost:8080/users/1 -// -// PUT http://localhost:8080/users/1 -// 1Melissa -// -// DELETE http://localhost:8080/users/1 -// - -type User struct { - Id, Name string -} - -type UserResource struct { - // normally one would use DAO (data access object) - users map[string]User -} - -func (u UserResource) Register(container *restful.Container) { - ws := new(restful.WebService) - ws. - Path("/users"). - Consumes(restful.MIME_XML, restful.MIME_JSON). - Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well - - ws.Route(ws.GET("/{user-id}").To(u.findUser)) - ws.Route(ws.POST("").To(u.updateUser)) - ws.Route(ws.PUT("/{user-id}").To(u.createUser)) - ws.Route(ws.DELETE("/{user-id}").To(u.removeUser)) - - container.Add(ws) -} - -// GET http://localhost:8080/users/1 -// -func (u UserResource) findUser(request *restful.Request, response *restful.Response) { - id := request.PathParameter("user-id") - usr := u.users[id] - if len(usr.Id) == 0 { - response.AddHeader("Content-Type", "text/plain") - response.WriteErrorString(http.StatusNotFound, "User could not be found.") - } else { - response.WriteEntity(usr) - } -} - -// POST http://localhost:8080/users -// 1Melissa Raspberry -// -func (u *UserResource) updateUser(request *restful.Request, response *restful.Response) { - usr := new(User) - err := request.ReadEntity(&usr) - if err == nil { - u.users[usr.Id] = *usr - response.WriteEntity(usr) - } else { - response.AddHeader("Content-Type", "text/plain") - response.WriteErrorString(http.StatusInternalServerError, err.Error()) - } -} - -// PUT http://localhost:8080/users/1 -// 1Melissa -// -func (u *UserResource) createUser(request *restful.Request, response *restful.Response) { - usr := User{Id: request.PathParameter("user-id")} - err := request.ReadEntity(&usr) - if err == nil { - u.users[usr.Id] = usr - response.WriteHeader(http.StatusCreated) - response.WriteEntity(usr) - } else { - response.AddHeader("Content-Type", "text/plain") - response.WriteErrorString(http.StatusInternalServerError, err.Error()) - } -} - -// DELETE http://localhost:8080/users/1 -// -func (u *UserResource) removeUser(request *restful.Request, response *restful.Response) { - id := request.PathParameter("user-id") - delete(u.users, id) -} - -func main() { - wsContainer := restful.NewContainer() - wsContainer.Router(restful.CurlyRouter{}) - u := UserResource{map[string]User{}} - u.Register(wsContainer) - - log.Printf("start listening on localhost:8080") - server := &http.Server{Addr: ":8080", Handler: wsContainer} - log.Fatal(server.ListenAndServe()) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-encoding-filter.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-encoding-filter.go deleted file mode 100644 index 6094c4909..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-encoding-filter.go +++ /dev/null @@ -1,61 +0,0 @@ -package main - -import ( - "github.com/emicklei/go-restful" - "log" - "net/http" -) - -type User struct { - Id, Name string -} - -type UserList struct { - Users []User -} - -// -// This example shows how to use the CompressingResponseWriter by a Filter -// such that encoding can be enabled per WebService or per Route (instead of per container) -// Using restful.DefaultContainer.EnableContentEncoding(true) will encode all responses served by WebServices in the DefaultContainer. -// -// Set Accept-Encoding to gzip or deflate -// GET http://localhost:8080/users/42 -// and look at the response headers - -func main() { - restful.Add(NewUserService()) - log.Printf("start listening on localhost:8080") - log.Fatal(http.ListenAndServe(":8080", nil)) -} - -func NewUserService() *restful.WebService { - ws := new(restful.WebService) - ws. - Path("/users"). - Consumes(restful.MIME_XML, restful.MIME_JSON). - Produces(restful.MIME_JSON, restful.MIME_XML) - - // install a response encoding filter - ws.Route(ws.GET("/{user-id}").Filter(encodingFilter).To(findUser)) - return ws -} - -// Route Filter (defines FilterFunction) -func encodingFilter(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - log.Printf("[encoding-filter] %s,%s\n", req.Request.Method, req.Request.URL) - // wrap responseWriter into a compressing one - compress, _ := restful.NewCompressingResponseWriter(resp.ResponseWriter, restful.ENCODING_GZIP) - resp.ResponseWriter = compress - defer func() { - compress.Close() - }() - chain.ProcessFilter(req, resp) -} - -// GET http://localhost:8080/users/42 -// -func findUser(request *restful.Request, response *restful.Response) { - log.Printf("findUser") - response.WriteEntity(User{"42", "Gandalf"}) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-filters.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-filters.go deleted file mode 100644 index 47e1146a0..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-filters.go +++ /dev/null @@ -1,114 +0,0 @@ -package main - -import ( - "github.com/emicklei/go-restful" - "log" - "net/http" - "time" -) - -type User struct { - Id, Name string -} - -type UserList struct { - Users []User -} - -// This example show how to create and use the three different Filters (Container,WebService and Route) -// When applied to the restful.DefaultContainer, we refer to them as a global filter. -// -// GET http://locahost:8080/users/42 -// and see the logging per filter (try repeating this request) - -func main() { - // install a global (=DefaultContainer) filter (processed before any webservice in the DefaultContainer) - restful.Filter(globalLogging) - - restful.Add(NewUserService()) - log.Printf("start listening on localhost:8080") - log.Fatal(http.ListenAndServe(":8080", nil)) -} - -func NewUserService() *restful.WebService { - ws := new(restful.WebService) - ws. - Path("/users"). - Consumes(restful.MIME_XML, restful.MIME_JSON). - Produces(restful.MIME_JSON, restful.MIME_XML) - - // install a webservice filter (processed before any route) - ws.Filter(webserviceLogging).Filter(measureTime) - - // install a counter filter - ws.Route(ws.GET("").Filter(NewCountFilter().routeCounter).To(getAllUsers)) - - // install 2 chained route filters (processed before calling findUser) - ws.Route(ws.GET("/{user-id}").Filter(routeLogging).Filter(NewCountFilter().routeCounter).To(findUser)) - return ws -} - -// Global Filter -func globalLogging(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - log.Printf("[global-filter (logger)] %s,%s\n", req.Request.Method, req.Request.URL) - chain.ProcessFilter(req, resp) -} - -// WebService Filter -func webserviceLogging(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - log.Printf("[webservice-filter (logger)] %s,%s\n", req.Request.Method, req.Request.URL) - chain.ProcessFilter(req, resp) -} - -// WebService (post-process) Filter (as a struct that defines a FilterFunction) -func measureTime(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - now := time.Now() - chain.ProcessFilter(req, resp) - log.Printf("[webservice-filter (timer)] %v\n", time.Now().Sub(now)) -} - -// Route Filter (defines FilterFunction) -func routeLogging(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - log.Printf("[route-filter (logger)] %s,%s\n", req.Request.Method, req.Request.URL) - chain.ProcessFilter(req, resp) -} - -// Route Filter (as a struct that defines a FilterFunction) -// CountFilter implements a FilterFunction for counting requests. -type CountFilter struct { - count int - counter chan int // for go-routine safe count increments -} - -// NewCountFilter creates and initializes a new CountFilter. -func NewCountFilter() *CountFilter { - c := new(CountFilter) - c.counter = make(chan int) - go func() { - for { - c.count += <-c.counter - } - }() - return c -} - -// routeCounter increments the count of the filter (through a channel) -func (c *CountFilter) routeCounter(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - c.counter <- 1 - log.Printf("[route-filter (counter)] count:%d", c.count) - chain.ProcessFilter(req, resp) -} - -// GET http://localhost:8080/users -// -func getAllUsers(request *restful.Request, response *restful.Response) { - log.Printf("getAllUsers") - response.WriteEntity(UserList{[]User{User{"42", "Gandalf"}, User{"3.14", "Pi"}}}) -} - -// GET http://localhost:8080/users/42 -// -func findUser(request *restful.Request, response *restful.Response) { - log.Printf("findUser") - response.WriteEntity(User{"42", "Gandalf"}) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-form-handling.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-form-handling.go deleted file mode 100644 index a83db4492..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-form-handling.go +++ /dev/null @@ -1,62 +0,0 @@ -package main - -import ( - "fmt" - "github.com/emicklei/go-restful" - "github.com/gorilla/schema" - "io" - "net/http" -) - -// This example shows how to handle a POST of a HTML form that uses the standard x-www-form-urlencoded content-type. -// It uses the gorilla web tool kit schema package to decode the form data into a struct. -// -// GET http://localhost:8080/profiles -// - -type Profile struct { - Name string - Age int -} - -var decoder *schema.Decoder - -func main() { - decoder = schema.NewDecoder() - ws := new(restful.WebService) - ws.Route(ws.POST("/profiles").Consumes("application/x-www-form-urlencoded").To(postAdddress)) - ws.Route(ws.GET("/profiles").To(addresssForm)) - restful.Add(ws) - http.ListenAndServe(":8080", nil) -} - -func postAdddress(req *restful.Request, resp *restful.Response) { - err := req.Request.ParseForm() - if err != nil { - resp.WriteErrorString(http.StatusBadRequest, err.Error()) - return - } - p := new(Profile) - err = decoder.Decode(p, req.Request.PostForm) - if err != nil { - resp.WriteErrorString(http.StatusBadRequest, err.Error()) - return - } - io.WriteString(resp.ResponseWriter, fmt.Sprintf("Name=%s, Age=%d", p.Name, p.Age)) -} - -func addresssForm(req *restful.Request, resp *restful.Response) { - io.WriteString(resp.ResponseWriter, - ` - -

Enter Profile

-
- - - - - -
- - `) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-hello-world.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-hello-world.go deleted file mode 100644 index a21c2a69c..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-hello-world.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "github.com/emicklei/go-restful" - "io" - "net/http" -) - -// This example shows the minimal code needed to get a restful.WebService working. -// -// GET http://localhost:8080/hello - -func main() { - ws := new(restful.WebService) - ws.Route(ws.GET("/hello").To(hello)) - restful.Add(ws) - http.ListenAndServe(":8080", nil) -} - -func hello(req *restful.Request, resp *restful.Response) { - io.WriteString(resp, "world") -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-html-template.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-html-template.go deleted file mode 100644 index de51c5919..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-html-template.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "log" - "net/http" - "text/template" - - "github.com/emicklei/go-restful" -) - -// This example shows how to serve a HTML page using the standard Go template engine. -// -// GET http://localhost:8080/ - -func main() { - ws := new(restful.WebService) - ws.Route(ws.GET("/").To(home)) - restful.Add(ws) - print("open browser on http://localhost:8080/\n") - http.ListenAndServe(":8080", nil) -} - -type Message struct { - Text string -} - -func home(req *restful.Request, resp *restful.Response) { - p := &Message{"restful-html-template demo"} - // you might want to cache compiled templates - t, err := template.ParseFiles("home.html") - if err != nil { - log.Fatalf("Template gave: %s", err) - } - t.Execute(resp.ResponseWriter, p) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-multi-containers.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-multi-containers.go deleted file mode 100644 index 3f1650b36..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-multi-containers.go +++ /dev/null @@ -1,43 +0,0 @@ -package main - -import ( - "github.com/emicklei/go-restful" - "io" - "log" - "net/http" -) - -// This example shows how to have a program with 2 WebServices containers -// each having a http server listening on its own port. -// -// The first "hello" is added to the restful.DefaultContainer (and uses DefaultServeMux) -// For the second "hello", a new container and ServeMux is created -// and requires a new http.Server with the container being the Handler. -// This first server is spawn in its own go-routine such that the program proceeds to create the second. -// -// GET http://localhost:8080/hello -// GET http://localhost:8081/hello - -func main() { - ws := new(restful.WebService) - ws.Route(ws.GET("/hello").To(hello)) - restful.Add(ws) - go func() { - http.ListenAndServe(":8080", nil) - }() - - container2 := restful.NewContainer() - ws2 := new(restful.WebService) - ws2.Route(ws2.GET("/hello").To(hello2)) - container2.Add(ws2) - server := &http.Server{Addr: ":8081", Handler: container2} - log.Fatal(server.ListenAndServe()) -} - -func hello(req *restful.Request, resp *restful.Response) { - io.WriteString(resp, "default world") -} - -func hello2(req *restful.Request, resp *restful.Response) { - io.WriteString(resp, "second world") -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-options-filter.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-options-filter.go deleted file mode 100644 index 73dc3cfe5..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-options-filter.go +++ /dev/null @@ -1,51 +0,0 @@ -package main - -import ( - "github.com/emicklei/go-restful" - "io" - "log" - "net/http" -) - -// This example shows how to use the OPTIONSFilter on a Container -// -// OPTIONS http://localhost:8080/users -// -// OPTIONS http://localhost:8080/users/1 - -type UserResource struct{} - -func (u UserResource) RegisterTo(container *restful.Container) { - ws := new(restful.WebService) - ws. - Path("/users"). - Consumes("*/*"). - Produces("*/*") - - ws.Route(ws.GET("/{user-id}").To(u.nop)) - ws.Route(ws.POST("").To(u.nop)) - ws.Route(ws.PUT("/{user-id}").To(u.nop)) - ws.Route(ws.DELETE("/{user-id}").To(u.nop)) - - container.Add(ws) -} - -func (u UserResource) nop(request *restful.Request, response *restful.Response) { - io.WriteString(response.ResponseWriter, "this would be a normal response") -} - -func main() { - wsContainer := restful.NewContainer() - u := UserResource{} - u.RegisterTo(wsContainer) - - // Add container filter to respond to OPTIONS - wsContainer.Filter(wsContainer.OPTIONSFilter) - - // For use on the default container, you can write - // restful.Filter(restful.OPTIONSFilter()) - - log.Printf("start listening on localhost:8080") - server := &http.Server{Addr: ":8080", Handler: wsContainer} - log.Fatal(server.ListenAndServe()) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-path-tail.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-path-tail.go deleted file mode 100644 index 8488a232c..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-path-tail.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "io" - "net/http" - . "github.com/emicklei/go-restful" -) - -// This example shows how to a Route that matches the "tail" of a path. -// Requires the use of a CurlyRouter and the star "*" path parameter pattern. -// -// GET http://localhost:8080/basepath/some/other/location/test.xml - -func main() { - DefaultContainer.Router(CurlyRouter{}) - ws := new(WebService) - ws.Route(ws.GET("/basepath/{resource:*}").To(staticFromPathParam)) - Add(ws) - - println("[go-restful] serve path tails from http://localhost:8080/basepath") - http.ListenAndServe(":8080", nil) -} - -func staticFromPathParam(req *Request, resp *Response) { - io.WriteString(resp, "Tail="+req.PathParameter("resource")) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-pre-post-filters.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-pre-post-filters.go deleted file mode 100644 index 0b55f1493..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-pre-post-filters.go +++ /dev/null @@ -1,98 +0,0 @@ -package main - -import ( - "github.com/emicklei/go-restful" - "io" - "log" - "net/http" -) - -// This example shows how the different types of filters are called in the request-response flow. -// The call chain is logged on the console when sending an http request. -// -// GET http://localhost:8080/1 -// GET http://localhost:8080/2 - -var indentLevel int - -func container_filter_A(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - log.Printf("url path:%v\n", req.Request.URL) - trace("container_filter_A: before", 1) - chain.ProcessFilter(req, resp) - trace("container_filter_A: after", -1) -} - -func container_filter_B(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - trace("container_filter_B: before", 1) - chain.ProcessFilter(req, resp) - trace("container_filter_B: after", -1) -} - -func service_filter_A(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - trace("service_filter_A: before", 1) - chain.ProcessFilter(req, resp) - trace("service_filter_A: after", -1) -} - -func service_filter_B(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - trace("service_filter_B: before", 1) - chain.ProcessFilter(req, resp) - trace("service_filter_B: after", -1) -} - -func route_filter_A(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - trace("route_filter_A: before", 1) - chain.ProcessFilter(req, resp) - trace("route_filter_A: after", -1) -} - -func route_filter_B(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - trace("route_filter_B: before", 1) - chain.ProcessFilter(req, resp) - trace("route_filter_B: after", -1) -} - -func trace(what string, delta int) { - indented := what - if delta < 0 { - indentLevel += delta - } - for t := 0; t < indentLevel; t++ { - indented = "." + indented - } - log.Printf("%s", indented) - if delta > 0 { - indentLevel += delta - } -} - -func main() { - restful.Filter(container_filter_A) - restful.Filter(container_filter_B) - - ws1 := new(restful.WebService) - ws1.Path("/1") - ws1.Filter(service_filter_A) - ws1.Filter(service_filter_B) - ws1.Route(ws1.GET("").To(doit1).Filter(route_filter_A).Filter(route_filter_B)) - - ws2 := new(restful.WebService) - ws2.Path("/2") - ws2.Filter(service_filter_A) - ws2.Filter(service_filter_B) - ws2.Route(ws2.GET("").To(doit2).Filter(route_filter_A).Filter(route_filter_B)) - - restful.Add(ws1) - restful.Add(ws2) - - log.Print("go-restful example listing on http://localhost:8080/1 and http://localhost:8080/2") - log.Fatal(http.ListenAndServe(":8080", nil)) -} - -func doit1(req *restful.Request, resp *restful.Response) { - io.WriteString(resp, "nothing to see in 1") -} - -func doit2(req *restful.Request, resp *restful.Response) { - io.WriteString(resp, "nothing to see in 2") -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-resource-functions.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-resource-functions.go deleted file mode 100644 index fb1012a02..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-resource-functions.go +++ /dev/null @@ -1,63 +0,0 @@ -package main - -import ( - "github.com/emicklei/go-restful" - "log" - "net/http" -) - -// This example shows how to use methods as RouteFunctions for WebServices. -// The ProductResource has a Register() method that creates and initializes -// a WebService to expose its methods as REST operations. -// The WebService is added to the restful.DefaultContainer. -// A ProductResource is typically created using some data access object. -// -// GET http://localhost:8080/products/1 -// POST http://localhost:8080/products -// 1The First - -type Product struct { - Id, Title string -} - -type ProductResource struct { - // typically reference a DAO (data-access-object) -} - -func (p ProductResource) getOne(req *restful.Request, resp *restful.Response) { - id := req.PathParameter("id") - log.Println("getting product with id:" + id) - resp.WriteEntity(Product{Id: id, Title: "test"}) -} - -func (p ProductResource) postOne(req *restful.Request, resp *restful.Response) { - updatedProduct := new(Product) - err := req.ReadEntity(updatedProduct) - if err != nil { // bad request - resp.WriteErrorString(http.StatusBadRequest, err.Error()) - return - } - log.Println("updating product with id:" + updatedProduct.Id) -} - -func (p ProductResource) Register() { - ws := new(restful.WebService) - ws.Path("/products") - ws.Consumes(restful.MIME_XML) - ws.Produces(restful.MIME_XML) - - ws.Route(ws.GET("/{id}").To(p.getOne). - Doc("get the product by its id"). - Param(ws.PathParameter("id", "identifier of the product").DataType("string"))) - - ws.Route(ws.POST("").To(p.postOne). - Doc("update or create a product"). - Param(ws.BodyParameter("Product", "a Product (XML)").DataType("main.Product"))) - - restful.Add(ws) -} - -func main() { - ProductResource{}.Register() - http.ListenAndServe(":8080", nil) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-route_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-route_test.go deleted file mode 100644 index 20c366bf9..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-route_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -import ( - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/emicklei/go-restful" -) - -var ( - Result string -) - -func TestRouteExtractParameter(t *testing.T) { - // setup service - ws := new(restful.WebService) - ws.Consumes(restful.MIME_XML) - ws.Route(ws.GET("/test/{param}").To(DummyHandler)) - restful.Add(ws) - - // setup request + writer - bodyReader := strings.NewReader("42") - httpRequest, _ := http.NewRequest("GET", "/test/THIS", bodyReader) - httpRequest.Header.Set("Content-Type", restful.MIME_XML) - httpWriter := httptest.NewRecorder() - - // run - restful.DefaultContainer.ServeHTTP(httpWriter, httpRequest) - - if Result != "THIS" { - t.Fatalf("Result is actually: %s", Result) - } -} - -func DummyHandler(rq *restful.Request, rp *restful.Response) { - Result = rq.PathParameter("param") -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-routefunction_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-routefunction_test.go deleted file mode 100644 index 957c05550..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-routefunction_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/emicklei/go-restful" -) - -// This example show how to test one particular RouteFunction (getIt) -// It uses the httptest.ResponseRecorder to capture output - -func getIt(req *restful.Request, resp *restful.Response) { - resp.WriteHeader(204) -} - -func TestCallFunction(t *testing.T) { - httpReq, _ := http.NewRequest("GET", "/", nil) - req := restful.NewRequest(httpReq) - - recorder := new(httptest.ResponseRecorder) - resp := restful.NewResponse(recorder) - - getIt(req, resp) - if recorder.Code != 204 { - t.Fatalf("Missing or wrong status code:%d", recorder.Code) - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-serve-static.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-serve-static.go deleted file mode 100644 index 8cb7848c1..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-serve-static.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "path" - - "github.com/emicklei/go-restful" -) - -// This example shows how to define methods that serve static files -// It uses the standard http.ServeFile method -// -// GET http://localhost:8080/static/test.xml -// GET http://localhost:8080/static/ -// -// GET http://localhost:8080/static?resource=subdir/test.xml - -var rootdir = "/tmp" - -func main() { - restful.DefaultContainer.Router(restful.CurlyRouter{}) - - ws := new(restful.WebService) - ws.Route(ws.GET("/static/{subpath:*}").To(staticFromPathParam)) - ws.Route(ws.GET("/static").To(staticFromQueryParam)) - restful.Add(ws) - - println("[go-restful] serving files on http://localhost:8080/static from local /tmp") - http.ListenAndServe(":8080", nil) -} - -func staticFromPathParam(req *restful.Request, resp *restful.Response) { - actual := path.Join(rootdir, req.PathParameter("subpath")) - fmt.Printf("serving %s ... (from %s)\n", actual, req.PathParameter("subpath")) - http.ServeFile( - resp.ResponseWriter, - req.Request, - actual) -} - -func staticFromQueryParam(req *restful.Request, resp *restful.Response) { - http.ServeFile( - resp.ResponseWriter, - req.Request, - path.Join(rootdir, req.QueryParameter("resource"))) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-swagger.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-swagger.go deleted file mode 100644 index 7746b5b07..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-swagger.go +++ /dev/null @@ -1,61 +0,0 @@ -package main - -import ( - "log" - "net/http" - - "github.com/emicklei/go-restful" - "github.com/emicklei/go-restful/swagger" -) - -type Book struct { - Title string - Author string -} - -func main() { - ws := new(restful.WebService) - ws.Path("/books") - ws.Consumes(restful.MIME_JSON, restful.MIME_XML) - ws.Produces(restful.MIME_JSON, restful.MIME_XML) - restful.Add(ws) - - ws.Route(ws.GET("/{medium}").To(noop). - Doc("Search all books"). - Param(ws.PathParameter("medium", "digital or paperback").DataType("string")). - Param(ws.QueryParameter("language", "en,nl,de").DataType("string")). - Param(ws.HeaderParameter("If-Modified-Since", "last known timestamp").DataType("datetime")). - Do(returns200, returns500)) - - ws.Route(ws.PUT("/{medium}").To(noop). - Doc("Add a new book"). - Param(ws.PathParameter("medium", "digital or paperback").DataType("string")). - Reads(Book{})) - - // You can install the Swagger Service which provides a nice Web UI on your REST API - // You need to download the Swagger HTML5 assets and change the FilePath location in the config below. - // Open http://localhost:8080/apidocs and enter http://localhost:8080/apidocs.json in the api input field. - config := swagger.Config{ - WebServices: restful.DefaultContainer.RegisteredWebServices(), // you control what services are visible - WebServicesUrl: "http://localhost:8080", - ApiPath: "/apidocs.json", - - // Optionally, specifiy where the UI is located - SwaggerPath: "/apidocs/", - SwaggerFilePath: "/Users/emicklei/xProjects/swagger-ui/dist"} - swagger.RegisterSwaggerService(config, restful.DefaultContainer) - - log.Printf("start listening on localhost:8080") - server := &http.Server{Addr: ":8080", Handler: restful.DefaultContainer} - log.Fatal(server.ListenAndServe()) -} - -func noop(req *restful.Request, resp *restful.Response) {} - -func returns200(b *restful.RouteBuilder) { - b.Returns(http.StatusOK, "OK", Book{}) -} - -func returns500(b *restful.RouteBuilder) { - b.Returns(http.StatusInternalServerError, "Bummer, something went wrong", nil) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-user-resource.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-user-resource.go deleted file mode 100644 index 6b860dc20..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-user-resource.go +++ /dev/null @@ -1,152 +0,0 @@ -package main - -import ( - "log" - "net/http" - "strconv" - - "github.com/emicklei/go-restful" - "github.com/emicklei/go-restful/swagger" -) - -// This example show a complete (GET,PUT,POST,DELETE) conventional example of -// a REST Resource including documentation to be served by e.g. a Swagger UI -// It is recommended to create a Resource struct (UserResource) that can encapsulate -// an object that provide domain access (a DAO) -// It has a Register method including the complete Route mapping to methods together -// with all the appropriate documentation -// -// POST http://localhost:8080/users -// 1Melissa Raspberry -// -// GET http://localhost:8080/users/1 -// -// PUT http://localhost:8080/users/1 -// 1Melissa -// -// DELETE http://localhost:8080/users/1 -// - -type User struct { - Id, Name string -} - -type UserResource struct { - // normally one would use DAO (data access object) - users map[string]User -} - -func (u UserResource) Register(container *restful.Container) { - ws := new(restful.WebService) - ws. - Path("/users"). - Doc("Manage Users"). - Consumes(restful.MIME_XML, restful.MIME_JSON). - Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well - - ws.Route(ws.GET("/{user-id}").To(u.findUser). - // docs - Doc("get a user"). - Operation("findUser"). - Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")). - Writes(User{})) // on the response - - ws.Route(ws.PUT("/{user-id}").To(u.updateUser). - // docs - Doc("update a user"). - Operation("updateUser"). - Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")). - ReturnsError(409, "duplicate user-id", nil). - Reads(User{})) // from the request - - ws.Route(ws.POST("").To(u.createUser). - // docs - Doc("create a user"). - Operation("createUser"). - Reads(User{})) // from the request - - ws.Route(ws.DELETE("/{user-id}").To(u.removeUser). - // docs - Doc("delete a user"). - Operation("removeUser"). - Param(ws.PathParameter("user-id", "identifier of the user").DataType("string"))) - - container.Add(ws) -} - -// GET http://localhost:8080/users/1 -// -func (u UserResource) findUser(request *restful.Request, response *restful.Response) { - id := request.PathParameter("user-id") - usr := u.users[id] - if len(usr.Id) == 0 { - response.AddHeader("Content-Type", "text/plain") - response.WriteErrorString(http.StatusNotFound, "404: User could not be found.") - return - } - response.WriteEntity(usr) -} - -// POST http://localhost:8080/users -// Melissa -// -func (u *UserResource) createUser(request *restful.Request, response *restful.Response) { - usr := new(User) - err := request.ReadEntity(usr) - if err != nil { - response.AddHeader("Content-Type", "text/plain") - response.WriteErrorString(http.StatusInternalServerError, err.Error()) - return - } - usr.Id = strconv.Itoa(len(u.users) + 1) // simple id generation - u.users[usr.Id] = *usr - response.WriteHeaderAndEntity(http.StatusCreated, usr) -} - -// PUT http://localhost:8080/users/1 -// 1Melissa Raspberry -// -func (u *UserResource) updateUser(request *restful.Request, response *restful.Response) { - usr := new(User) - err := request.ReadEntity(&usr) - if err != nil { - response.AddHeader("Content-Type", "text/plain") - response.WriteErrorString(http.StatusInternalServerError, err.Error()) - return - } - u.users[usr.Id] = *usr - response.WriteEntity(usr) -} - -// DELETE http://localhost:8080/users/1 -// -func (u *UserResource) removeUser(request *restful.Request, response *restful.Response) { - id := request.PathParameter("user-id") - delete(u.users, id) -} - -func main() { - // to see what happens in the package, uncomment the following - //restful.TraceLogger(log.New(os.Stdout, "[restful] ", log.LstdFlags|log.Lshortfile)) - - wsContainer := restful.NewContainer() - u := UserResource{map[string]User{}} - u.Register(wsContainer) - - // Optionally, you can install the Swagger Service which provides a nice Web UI on your REST API - // You need to download the Swagger HTML5 assets and change the FilePath location in the config below. - // Open http://localhost:8080/apidocs and enter http://localhost:8080/apidocs.json in the api input field. - config := swagger.Config{ - WebServices: wsContainer.RegisteredWebServices(), // you control what services are visible - WebServicesUrl: "http://localhost:8080", - ApiPath: "/apidocs.json", - - // Optionally, specifiy where the UI is located - SwaggerPath: "/apidocs/", - SwaggerFilePath: "/Users/emicklei/xProjects/swagger-ui/dist"} - swagger.RegisterSwaggerService(config, wsContainer) - - log.Printf("start listening on localhost:8080") - server := &http.Server{Addr: ":8080", Handler: wsContainer} - log.Fatal(server.ListenAndServe()) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-user-service.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-user-service.go deleted file mode 100644 index 77c678ce4..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-user-service.go +++ /dev/null @@ -1,137 +0,0 @@ -package main - -import ( - "log" - "net/http" - - "github.com/emicklei/go-restful" - "github.com/emicklei/go-restful/swagger" -) - -// This example is functionally the same as the example in restful-user-resource.go -// with the only difference that is served using the restful.DefaultContainer - -type User struct { - Id, Name string -} - -type UserService struct { - // normally one would use DAO (data access object) - users map[string]User -} - -func (u UserService) Register() { - ws := new(restful.WebService) - ws. - Path("/users"). - Consumes(restful.MIME_XML, restful.MIME_JSON). - Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well - - ws.Route(ws.GET("/").To(u.findAllUsers). - // docs - Doc("get all users"). - Operation("findAllUsers"). - Returns(200, "OK", []User{})) - - ws.Route(ws.GET("/{user-id}").To(u.findUser). - // docs - Doc("get a user"). - Operation("findUser"). - Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")). - Writes(User{})) // on the response - - ws.Route(ws.PUT("/{user-id}").To(u.updateUser). - // docs - Doc("update a user"). - Operation("updateUser"). - Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")). - Reads(User{})) // from the request - - ws.Route(ws.PUT("").To(u.createUser). - // docs - Doc("create a user"). - Operation("createUser"). - Reads(User{})) // from the request - - ws.Route(ws.DELETE("/{user-id}").To(u.removeUser). - // docs - Doc("delete a user"). - Operation("removeUser"). - Param(ws.PathParameter("user-id", "identifier of the user").DataType("string"))) - - restful.Add(ws) -} - -// GET http://localhost:8080/users -// -func (u UserService) findAllUsers(request *restful.Request, response *restful.Response) { - response.WriteEntity(u.users) -} - -// GET http://localhost:8080/users/1 -// -func (u UserService) findUser(request *restful.Request, response *restful.Response) { - id := request.PathParameter("user-id") - usr := u.users[id] - if len(usr.Id) == 0 { - response.WriteErrorString(http.StatusNotFound, "User could not be found.") - } else { - response.WriteEntity(usr) - } -} - -// PUT http://localhost:8080/users/1 -// 1Melissa Raspberry -// -func (u *UserService) updateUser(request *restful.Request, response *restful.Response) { - usr := new(User) - err := request.ReadEntity(&usr) - if err == nil { - u.users[usr.Id] = *usr - response.WriteEntity(usr) - } else { - response.WriteError(http.StatusInternalServerError, err) - } -} - -// PUT http://localhost:8080/users/1 -// 1Melissa -// -func (u *UserService) createUser(request *restful.Request, response *restful.Response) { - usr := User{Id: request.PathParameter("user-id")} - err := request.ReadEntity(&usr) - if err == nil { - u.users[usr.Id] = usr - response.WriteHeaderAndEntity(http.StatusCreated, usr) - } else { - response.WriteError(http.StatusInternalServerError, err) - } -} - -// DELETE http://localhost:8080/users/1 -// -func (u *UserService) removeUser(request *restful.Request, response *restful.Response) { - id := request.PathParameter("user-id") - delete(u.users, id) -} - -func main() { - u := UserService{map[string]User{}} - u.Register() - - // Optionally, you can install the Swagger Service which provides a nice Web UI on your REST API - // You need to download the Swagger HTML5 assets and change the FilePath location in the config below. - // Open http://localhost:8080/apidocs and enter http://localhost:8080/apidocs.json in the api input field. - config := swagger.Config{ - WebServices: restful.RegisteredWebServices(), // you control what services are visible - WebServicesUrl: "http://localhost:8080", - ApiPath: "/apidocs.json", - - // Optionally, specifiy where the UI is located - SwaggerPath: "/apidocs/", - SwaggerFilePath: "/Users/emicklei/Projects/swagger-ui/dist"} - swagger.InstallSwaggerService(config) - - log.Printf("start listening on localhost:8080") - log.Fatal(http.ListenAndServe(":8080", nil)) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/filter_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/filter_test.go deleted file mode 100644 index fadfb570f..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/filter_test.go +++ /dev/null @@ -1,141 +0,0 @@ -package restful - -import ( - "io" - "net/http" - "net/http/httptest" - "testing" -) - -func setupServices(addGlobalFilter bool, addServiceFilter bool, addRouteFilter bool) { - if addGlobalFilter { - Filter(globalFilter) - } - Add(newTestService(addServiceFilter, addRouteFilter)) -} - -func tearDown() { - DefaultContainer.webServices = []*WebService{} - DefaultContainer.isRegisteredOnRoot = true // this allows for setupServices multiple times - DefaultContainer.containerFilters = []FilterFunction{} -} - -func newTestService(addServiceFilter bool, addRouteFilter bool) *WebService { - ws := new(WebService).Path("") - if addServiceFilter { - ws.Filter(serviceFilter) - } - rb := ws.GET("/foo").To(foo) - if addRouteFilter { - rb.Filter(routeFilter) - } - ws.Route(rb) - ws.Route(ws.GET("/bar").To(bar)) - return ws -} - -func foo(req *Request, resp *Response) { - io.WriteString(resp.ResponseWriter, "foo") -} - -func bar(req *Request, resp *Response) { - io.WriteString(resp.ResponseWriter, "bar") -} - -func fail(req *Request, resp *Response) { - http.Error(resp.ResponseWriter, "something failed", http.StatusInternalServerError) -} - -func globalFilter(req *Request, resp *Response, chain *FilterChain) { - io.WriteString(resp.ResponseWriter, "global-") - chain.ProcessFilter(req, resp) -} - -func serviceFilter(req *Request, resp *Response, chain *FilterChain) { - io.WriteString(resp.ResponseWriter, "service-") - chain.ProcessFilter(req, resp) -} - -func routeFilter(req *Request, resp *Response, chain *FilterChain) { - io.WriteString(resp.ResponseWriter, "route-") - chain.ProcessFilter(req, resp) -} - -func TestNoFilter(t *testing.T) { - tearDown() - setupServices(false, false, false) - actual := sendIt("http://example.com/foo") - if "foo" != actual { - t.Fatal("expected: foo but got:" + actual) - } -} - -func TestGlobalFilter(t *testing.T) { - tearDown() - setupServices(true, false, false) - actual := sendIt("http://example.com/foo") - if "global-foo" != actual { - t.Fatal("expected: global-foo but got:" + actual) - } -} - -func TestWebServiceFilter(t *testing.T) { - tearDown() - setupServices(true, true, false) - actual := sendIt("http://example.com/foo") - if "global-service-foo" != actual { - t.Fatal("expected: global-service-foo but got:" + actual) - } -} - -func TestRouteFilter(t *testing.T) { - tearDown() - setupServices(true, true, true) - actual := sendIt("http://example.com/foo") - if "global-service-route-foo" != actual { - t.Fatal("expected: global-service-route-foo but got:" + actual) - } -} - -func TestRouteFilterOnly(t *testing.T) { - tearDown() - setupServices(false, false, true) - actual := sendIt("http://example.com/foo") - if "route-foo" != actual { - t.Fatal("expected: route-foo but got:" + actual) - } -} - -func TestBar(t *testing.T) { - tearDown() - setupServices(false, true, false) - actual := sendIt("http://example.com/bar") - if "service-bar" != actual { - t.Fatal("expected: service-bar but got:" + actual) - } -} - -func TestAllFiltersBar(t *testing.T) { - tearDown() - setupServices(true, true, true) - actual := sendIt("http://example.com/bar") - if "global-service-bar" != actual { - t.Fatal("expected: global-service-bar but got:" + actual) - } -} - -func sendIt(address string) string { - httpRequest, _ := http.NewRequest("GET", address, nil) - httpRequest.Header.Set("Accept", "*/*") - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - return httpWriter.Body.String() -} - -func sendItTo(address string, container *Container) string { - httpRequest, _ := http.NewRequest("GET", address, nil) - httpRequest.Header.Set("Accept", "*/*") - httpWriter := httptest.NewRecorder() - container.dispatch(httpWriter, httpRequest) - return httpWriter.Body.String() -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/jsr311_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/jsr311_test.go deleted file mode 100644 index 3e79a6def..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/jsr311_test.go +++ /dev/null @@ -1,212 +0,0 @@ -package restful - -import ( - "io" - "sort" - "testing" -) - -// -// Step 1 tests -// -var paths = []struct { - // url with path (1) is handled by service with root (2) and last capturing group has value final (3) - path, root, final string -}{ - {"/", "/", "/"}, - {"/p", "/p", ""}, - {"/p/x", "/p/{q}", ""}, - {"/q/x", "/q", "/x"}, - {"/p/x/", "/p/{q}", "/"}, - {"/p/x/y", "/p/{q}", "/y"}, - {"/q/x/y", "/q", "/x/y"}, - {"/z/q", "/{p}/q", ""}, - {"/a/b/c/q", "/", "/a/b/c/q"}, -} - -func TestDetectDispatcher(t *testing.T) { - ws1 := new(WebService).Path("/") - ws2 := new(WebService).Path("/p") - ws3 := new(WebService).Path("/q") - ws4 := new(WebService).Path("/p/q") - ws5 := new(WebService).Path("/p/{q}") - ws6 := new(WebService).Path("/p/{q}/") - ws7 := new(WebService).Path("/{p}/q") - var dispatchers = []*WebService{ws1, ws2, ws3, ws4, ws5, ws6, ws7} - - wc := NewContainer() - for _, each := range dispatchers { - wc.Add(each) - } - - router := RouterJSR311{} - - ok := true - for i, fixture := range paths { - who, final, err := router.detectDispatcher(fixture.path, dispatchers) - if err != nil { - t.Logf("error in detection:%v", err) - ok = false - } - if who.RootPath() != fixture.root { - t.Logf("[line:%v] Unexpected dispatcher, expected:%v, actual:%v", i, fixture.root, who.RootPath()) - ok = false - } - if final != fixture.final { - t.Logf("[line:%v] Unexpected final, expected:%v, actual:%v", i, fixture.final, final) - ok = false - } - } - if !ok { - t.Fail() - } -} - -// -// Step 2 tests -// - -// go test -v -test.run TestISSUE_179 ...restful -func TestISSUE_179(t *testing.T) { - ws1 := new(WebService) - ws1.Route(ws1.GET("/v1/category/{param:*}").To(dummy)) - routes := RouterJSR311{}.selectRoutes(ws1, "/v1/category/sub/sub") - t.Logf("%v", routes) -} - -// go test -v -test.run TestISSUE_30 ...restful -func TestISSUE_30(t *testing.T) { - ws1 := new(WebService).Path("/users") - ws1.Route(ws1.GET("/{id}").To(dummy)) - ws1.Route(ws1.POST("/login").To(dummy)) - routes := RouterJSR311{}.selectRoutes(ws1, "/login") - if len(routes) != 2 { - t.Fatal("expected 2 routes") - } - if routes[0].Path != "/users/login" { - t.Error("first is", routes[0].Path) - t.Logf("routes:%v", routes) - } -} - -// go test -v -test.run TestISSUE_34 ...restful -func TestISSUE_34(t *testing.T) { - ws1 := new(WebService).Path("/") - ws1.Route(ws1.GET("/{type}/{id}").To(dummy)) - ws1.Route(ws1.GET("/network/{id}").To(dummy)) - routes := RouterJSR311{}.selectRoutes(ws1, "/network/12") - if len(routes) != 2 { - t.Fatal("expected 2 routes") - } - if routes[0].Path != "/network/{id}" { - t.Error("first is", routes[0].Path) - t.Logf("routes:%v", routes) - } -} - -// go test -v -test.run TestISSUE_34_2 ...restful -func TestISSUE_34_2(t *testing.T) { - ws1 := new(WebService).Path("/") - // change the registration order - ws1.Route(ws1.GET("/network/{id}").To(dummy)) - ws1.Route(ws1.GET("/{type}/{id}").To(dummy)) - routes := RouterJSR311{}.selectRoutes(ws1, "/network/12") - if len(routes) != 2 { - t.Fatal("expected 2 routes") - } - if routes[0].Path != "/network/{id}" { - t.Error("first is", routes[0].Path) - } -} - -// go test -v -test.run TestISSUE_137 ...restful -func TestISSUE_137(t *testing.T) { - ws1 := new(WebService) - ws1.Route(ws1.GET("/hello").To(dummy)) - routes := RouterJSR311{}.selectRoutes(ws1, "/") - t.Log(routes) - if len(routes) > 0 { - t.Error("no route expected") - } -} - -func TestSelectRoutesSlash(t *testing.T) { - ws1 := new(WebService).Path("/") - ws1.Route(ws1.GET("").To(dummy)) - ws1.Route(ws1.GET("/").To(dummy)) - ws1.Route(ws1.GET("/u").To(dummy)) - ws1.Route(ws1.POST("/u").To(dummy)) - ws1.Route(ws1.POST("/u/v").To(dummy)) - ws1.Route(ws1.POST("/u/{w}").To(dummy)) - ws1.Route(ws1.POST("/u/{w}/z").To(dummy)) - routes := RouterJSR311{}.selectRoutes(ws1, "/u") - checkRoutesContains(routes, "/u", t) - checkRoutesContainsNo(routes, "/u/v", t) - checkRoutesContainsNo(routes, "/", t) - checkRoutesContainsNo(routes, "/u/{w}/z", t) -} -func TestSelectRoutesU(t *testing.T) { - ws1 := new(WebService).Path("/u") - ws1.Route(ws1.GET("").To(dummy)) - ws1.Route(ws1.GET("/").To(dummy)) - ws1.Route(ws1.GET("/v").To(dummy)) - ws1.Route(ws1.POST("/{w}").To(dummy)) - ws1.Route(ws1.POST("/{w}/z").To(dummy)) // so full path = /u/{w}/z - routes := RouterJSR311{}.selectRoutes(ws1, "/v") // test against /u/v - checkRoutesContains(routes, "/u/{w}", t) -} - -func TestSelectRoutesUsers1(t *testing.T) { - ws1 := new(WebService).Path("/users") - ws1.Route(ws1.POST("").To(dummy)) - ws1.Route(ws1.POST("/").To(dummy)) - ws1.Route(ws1.PUT("/{id}").To(dummy)) - routes := RouterJSR311{}.selectRoutes(ws1, "/1") - checkRoutesContains(routes, "/users/{id}", t) -} -func checkRoutesContains(routes []Route, path string, t *testing.T) { - if !containsRoutePath(routes, path, t) { - for _, r := range routes { - t.Logf("route %v %v", r.Method, r.Path) - } - t.Fatalf("routes should include [%v]:", path) - } -} -func checkRoutesContainsNo(routes []Route, path string, t *testing.T) { - if containsRoutePath(routes, path, t) { - for _, r := range routes { - t.Logf("route %v %v", r.Method, r.Path) - } - t.Fatalf("routes should not include [%v]:", path) - } -} -func containsRoutePath(routes []Route, path string, t *testing.T) bool { - for _, each := range routes { - if each.Path == path { - return true - } - } - return false -} - -// go test -v -test.run TestSortableRouteCandidates ...restful -func TestSortableRouteCandidates(t *testing.T) { - fixture := &sortableRouteCandidates{} - r1 := routeCandidate{matchesCount: 0, literalCount: 0, nonDefaultCount: 0} - r2 := routeCandidate{matchesCount: 0, literalCount: 0, nonDefaultCount: 1} - r3 := routeCandidate{matchesCount: 0, literalCount: 1, nonDefaultCount: 1} - r4 := routeCandidate{matchesCount: 1, literalCount: 1, nonDefaultCount: 0} - r5 := routeCandidate{matchesCount: 1, literalCount: 0, nonDefaultCount: 0} - fixture.candidates = append(fixture.candidates, r5, r4, r3, r2, r1) - sort.Sort(sort.Reverse(fixture)) - first := fixture.candidates[0] - if first.matchesCount != 1 && first.literalCount != 1 && first.nonDefaultCount != 0 { - t.Fatal("expected r4") - } - last := fixture.candidates[len(fixture.candidates)-1] - if last.matchesCount != 0 && last.literalCount != 0 && last.nonDefaultCount != 0 { - t.Fatal("expected r1") - } -} - -func dummy(req *Request, resp *Response) { io.WriteString(resp.ResponseWriter, "dummy") } diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/options_filter_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/options_filter_test.go deleted file mode 100644 index f0fceb834..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/options_filter_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package restful - -import ( - "net/http" - "net/http/httptest" - "testing" -) - -// go test -v -test.run TestOptionsFilter ...restful -func TestOptionsFilter(t *testing.T) { - tearDown() - ws := new(WebService) - ws.Route(ws.GET("/candy/{kind}").To(dummy)) - ws.Route(ws.DELETE("/candy/{kind}").To(dummy)) - ws.Route(ws.POST("/candies").To(dummy)) - Add(ws) - Filter(OPTIONSFilter()) - - httpRequest, _ := http.NewRequest("OPTIONS", "http://here.io/candy/gum", nil) - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - actual := httpWriter.Header().Get(HEADER_Allow) - if "GET,DELETE" != actual { - t.Fatal("expected: GET,DELETE but got:" + actual) - } - - httpRequest, _ = http.NewRequest("OPTIONS", "http://here.io/candies", nil) - httpWriter = httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - actual = httpWriter.Header().Get(HEADER_Allow) - if "POST" != actual { - t.Fatal("expected: POST but got:" + actual) - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/path_expression_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/path_expression_test.go deleted file mode 100644 index 334fcef73..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/path_expression_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package restful - -import "testing" - -var tempregexs = []struct { - template, regex string - literalCount, varCount int -}{ - {"", "^(/.*)?$", 0, 0}, - {"/a/{b}/c/", "^/a/([^/]+?)/c(/.*)?$", 2, 1}, - {"/{a}/{b}/{c-d-e}/", "^/([^/]+?)/([^/]+?)/([^/]+?)(/.*)?$", 0, 3}, - {"/{p}/abcde", "^/([^/]+?)/abcde(/.*)?$", 5, 1}, - {"/a/{b:*}", "^/a/(.*)(/.*)?$", 1, 1}, - {"/a/{b:[a-z]+}", "^/a/([a-z]+)(/.*)?$", 1, 1}, -} - -func TestTemplateToRegularExpression(t *testing.T) { - ok := true - for i, fixture := range tempregexs { - actual, lCount, vCount, _ := templateToRegularExpression(fixture.template) - if actual != fixture.regex { - t.Logf("regex mismatch, expected:%v , actual:%v, line:%v\n", fixture.regex, actual, i) // 11 = where the data starts - ok = false - } - if lCount != fixture.literalCount { - t.Logf("literal count mismatch, expected:%v , actual:%v, line:%v\n", fixture.literalCount, lCount, i) - ok = false - } - if vCount != fixture.varCount { - t.Logf("variable count mismatch, expected:%v , actual:%v, line:%v\n", fixture.varCount, vCount, i) - ok = false - } - } - if !ok { - t.Fatal("one or more expression did not match") - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/request_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/request_test.go deleted file mode 100644 index 72f078f92..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/request_test.go +++ /dev/null @@ -1,204 +0,0 @@ -package restful - -import ( - "encoding/json" - "net/http" - "net/url" - "strconv" - "strings" - "testing" -) - -func TestQueryParameter(t *testing.T) { - hreq := http.Request{Method: "GET"} - hreq.URL, _ = url.Parse("http://www.google.com/search?q=foo&q=bar") - rreq := Request{Request: &hreq} - if rreq.QueryParameter("q") != "foo" { - t.Errorf("q!=foo %#v", rreq) - } -} - -type Anything map[string]interface{} - -type Number struct { - ValueFloat float64 - ValueInt int64 -} - -type Sample struct { - Value string -} - -func TestReadEntityXmlCached(t *testing.T) { - SetCacheReadEntity(true) - bodyReader := strings.NewReader("42") - httpRequest, _ := http.NewRequest("GET", "/test", bodyReader) - httpRequest.Header.Set("Content-Type", "application/xml") - request := &Request{Request: httpRequest} - sam := new(Sample) - request.ReadEntity(sam) - if sam.Value != "42" { - t.Fatal("read failed") - } - if request.bodyContent == nil { - t.Fatal("no expected cached bytes found") - } -} - -func TestReadEntityXmlNonCached(t *testing.T) { - SetCacheReadEntity(false) - bodyReader := strings.NewReader("42") - httpRequest, _ := http.NewRequest("GET", "/test", bodyReader) - httpRequest.Header.Set("Content-Type", "application/xml") - request := &Request{Request: httpRequest} - sam := new(Sample) - request.ReadEntity(sam) - if sam.Value != "42" { - t.Fatal("read failed") - } - if request.bodyContent != nil { - t.Fatal("unexpected cached bytes found") - } -} - -func TestReadEntityJson(t *testing.T) { - bodyReader := strings.NewReader(`{"Value" : "42"}`) - httpRequest, _ := http.NewRequest("GET", "/test", bodyReader) - httpRequest.Header.Set("Content-Type", "application/json") - request := &Request{Request: httpRequest} - sam := new(Sample) - request.ReadEntity(sam) - if sam.Value != "42" { - t.Fatal("read failed") - } -} - -func TestReadEntityJsonCharset(t *testing.T) { - bodyReader := strings.NewReader(`{"Value" : "42"}`) - httpRequest, _ := http.NewRequest("GET", "/test", bodyReader) - httpRequest.Header.Set("Content-Type", "application/json; charset=UTF-8") - request := NewRequest(httpRequest) - sam := new(Sample) - request.ReadEntity(sam) - if sam.Value != "42" { - t.Fatal("read failed") - } -} - -func TestReadEntityJsonNumber(t *testing.T) { - SetCacheReadEntity(true) - bodyReader := strings.NewReader(`{"Value" : 4899710515899924123}`) - httpRequest, _ := http.NewRequest("GET", "/test", bodyReader) - httpRequest.Header.Set("Content-Type", "application/json") - request := &Request{Request: httpRequest} - any := make(Anything) - request.ReadEntity(&any) - number, ok := any["Value"].(json.Number) - if !ok { - t.Fatal("read failed") - } - vint, err := number.Int64() - if err != nil { - t.Fatal("convert failed") - } - if vint != 4899710515899924123 { - t.Fatal("read failed") - } - vfloat, err := number.Float64() - if err != nil { - t.Fatal("convert failed") - } - // match the default behaviour - vstring := strconv.FormatFloat(vfloat, 'e', 15, 64) - if vstring != "4.899710515899924e+18" { - t.Fatal("convert float64 failed") - } -} - -func TestReadEntityJsonNumberNonCached(t *testing.T) { - SetCacheReadEntity(false) - bodyReader := strings.NewReader(`{"Value" : 4899710515899924123}`) - httpRequest, _ := http.NewRequest("GET", "/test", bodyReader) - httpRequest.Header.Set("Content-Type", "application/json") - request := &Request{Request: httpRequest} - any := make(Anything) - request.ReadEntity(&any) - number, ok := any["Value"].(json.Number) - if !ok { - t.Fatal("read failed") - } - vint, err := number.Int64() - if err != nil { - t.Fatal("convert failed") - } - if vint != 4899710515899924123 { - t.Fatal("read failed") - } - vfloat, err := number.Float64() - if err != nil { - t.Fatal("convert failed") - } - // match the default behaviour - vstring := strconv.FormatFloat(vfloat, 'e', 15, 64) - if vstring != "4.899710515899924e+18" { - t.Fatal("convert float64 failed") - } -} - -func TestReadEntityJsonLong(t *testing.T) { - bodyReader := strings.NewReader(`{"ValueFloat" : 4899710515899924123, "ValueInt": 4899710515899924123}`) - httpRequest, _ := http.NewRequest("GET", "/test", bodyReader) - httpRequest.Header.Set("Content-Type", "application/json") - request := &Request{Request: httpRequest} - number := new(Number) - request.ReadEntity(&number) - if number.ValueInt != 4899710515899924123 { - t.Fatal("read failed") - } - // match the default behaviour - vstring := strconv.FormatFloat(number.ValueFloat, 'e', 15, 64) - if vstring != "4.899710515899924e+18" { - t.Fatal("convert float64 failed") - } -} - -func TestBodyParameter(t *testing.T) { - bodyReader := strings.NewReader(`value1=42&value2=43`) - httpRequest, _ := http.NewRequest("POST", "/test?value1=44", bodyReader) // POST and PUT body parameters take precedence over URL query string - httpRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") - request := NewRequest(httpRequest) - v1, err := request.BodyParameter("value1") - if err != nil { - t.Error(err) - } - v2, err := request.BodyParameter("value2") - if err != nil { - t.Error(err) - } - if v1 != "42" || v2 != "43" { - t.Fatal("read failed") - } -} - -func TestReadEntityUnkown(t *testing.T) { - bodyReader := strings.NewReader("?") - httpRequest, _ := http.NewRequest("GET", "/test", bodyReader) - httpRequest.Header.Set("Content-Type", "application/rubbish") - request := NewRequest(httpRequest) - sam := new(Sample) - err := request.ReadEntity(sam) - if err == nil { - t.Fatal("read should be in error") - } -} - -func TestSetAttribute(t *testing.T) { - bodyReader := strings.NewReader("?") - httpRequest, _ := http.NewRequest("GET", "/test", bodyReader) - request := NewRequest(httpRequest) - request.SetAttribute("go", "there") - there := request.Attribute("go") - if there != "there" { - t.Fatalf("missing request attribute:%v", there) - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/response_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/response_test.go deleted file mode 100644 index c8354f8ae..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/response_test.go +++ /dev/null @@ -1,204 +0,0 @@ -package restful - -import ( - "errors" - "net/http" - "net/http/httptest" - "strings" - "testing" -) - -func TestWriteHeader(t *testing.T) { - httpWriter := httptest.NewRecorder() - resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil} - resp.WriteHeader(123) - if resp.StatusCode() != 123 { - t.Errorf("Unexpected status code:%d", resp.StatusCode()) - } -} - -func TestNoWriteHeader(t *testing.T) { - httpWriter := httptest.NewRecorder() - resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil} - if resp.StatusCode() != http.StatusOK { - t.Errorf("Unexpected status code:%d", resp.StatusCode()) - } -} - -type food struct { - Kind string -} - -// go test -v -test.run TestMeasureContentLengthXml ...restful -func TestMeasureContentLengthXml(t *testing.T) { - httpWriter := httptest.NewRecorder() - resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil} - resp.WriteAsXml(food{"apple"}) - if resp.ContentLength() != 76 { - t.Errorf("Incorrect measured length:%d", resp.ContentLength()) - } -} - -// go test -v -test.run TestMeasureContentLengthJson ...restful -func TestMeasureContentLengthJson(t *testing.T) { - httpWriter := httptest.NewRecorder() - resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil} - resp.WriteAsJson(food{"apple"}) - if resp.ContentLength() != 22 { - t.Errorf("Incorrect measured length:%d", resp.ContentLength()) - } -} - -// go test -v -test.run TestMeasureContentLengthJsonNotPretty ...restful -func TestMeasureContentLengthJsonNotPretty(t *testing.T) { - httpWriter := httptest.NewRecorder() - resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, false, nil} - resp.WriteAsJson(food{"apple"}) - if resp.ContentLength() != 17 { // 16+1 using the Encoder directly yields another /n - t.Errorf("Incorrect measured length:%d", resp.ContentLength()) - } -} - -// go test -v -test.run TestMeasureContentLengthWriteErrorString ...restful -func TestMeasureContentLengthWriteErrorString(t *testing.T) { - httpWriter := httptest.NewRecorder() - resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil} - resp.WriteErrorString(404, "Invalid") - if resp.ContentLength() != len("Invalid") { - t.Errorf("Incorrect measured length:%d", resp.ContentLength()) - } -} - -// go test -v -test.run TestStatusIsPassedToResponse ...restful -func TestStatusIsPassedToResponse(t *testing.T) { - for _, each := range []struct { - write, read int - }{ - {write: 204, read: 204}, - {write: 304, read: 304}, - {write: 200, read: 200}, - {write: 400, read: 400}, - } { - httpWriter := httptest.NewRecorder() - resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil} - resp.WriteHeader(each.write) - if got, want := httpWriter.Code, each.read; got != want { - t.Errorf("got %v want %v", got, want) - } - } -} - -// go test -v -test.run TestStatusCreatedAndContentTypeJson_Issue54 ...restful -func TestStatusCreatedAndContentTypeJson_Issue54(t *testing.T) { - httpWriter := httptest.NewRecorder() - resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true, nil} - resp.WriteHeader(201) - resp.WriteAsJson(food{"Juicy"}) - if httpWriter.HeaderMap.Get("Content-Type") != "application/json" { - t.Errorf("Expected content type json but got:%d", httpWriter.HeaderMap.Get("Content-Type")) - } - if httpWriter.Code != 201 { - t.Errorf("Expected status 201 but got:%d", httpWriter.Code) - } -} - -type errorOnWriteRecorder struct { - *httptest.ResponseRecorder -} - -func (e errorOnWriteRecorder) Write(bytes []byte) (int, error) { - return 0, errors.New("fail") -} - -// go test -v -test.run TestLastWriteErrorCaught ...restful -func TestLastWriteErrorCaught(t *testing.T) { - httpWriter := errorOnWriteRecorder{httptest.NewRecorder()} - resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true, nil} - err := resp.WriteAsJson(food{"Juicy"}) - if err.Error() != "fail" { - t.Errorf("Unexpected error message:%v", err) - } -} - -// go test -v -test.run TestAcceptStarStar_Issue83 ...restful -func TestAcceptStarStar_Issue83(t *testing.T) { - httpWriter := httptest.NewRecorder() - // Accept Produces - resp := Response{httpWriter, "application/bogus,*/*;q=0.8", []string{"application/json"}, 0, 0, true, nil} - resp.WriteEntity(food{"Juicy"}) - ct := httpWriter.Header().Get("Content-Type") - if "application/json" != ct { - t.Errorf("Unexpected content type:%s", ct) - } -} - -// go test -v -test.run TestAcceptSkipStarStar_Issue83 ...restful -func TestAcceptSkipStarStar_Issue83(t *testing.T) { - httpWriter := httptest.NewRecorder() - // Accept Produces - resp := Response{httpWriter, " application/xml ,*/* ; q=0.8", []string{"application/json", "application/xml"}, 0, 0, true, nil} - resp.WriteEntity(food{"Juicy"}) - ct := httpWriter.Header().Get("Content-Type") - if "application/xml" != ct { - t.Errorf("Unexpected content type:%s", ct) - } -} - -// go test -v -test.run TestAcceptXmlBeforeStarStar_Issue83 ...restful -func TestAcceptXmlBeforeStarStar_Issue83(t *testing.T) { - httpWriter := httptest.NewRecorder() - // Accept Produces - resp := Response{httpWriter, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", []string{"application/json"}, 0, 0, true, nil} - resp.WriteEntity(food{"Juicy"}) - ct := httpWriter.Header().Get("Content-Type") - if "application/json" != ct { - t.Errorf("Unexpected content type:%s", ct) - } -} - -// go test -v -test.run TestWriteHeaderNoContent_Issue124 ...restful -func TestWriteHeaderNoContent_Issue124(t *testing.T) { - httpWriter := httptest.NewRecorder() - resp := Response{httpWriter, "text/plain", []string{"text/plain"}, 0, 0, true, nil} - resp.WriteHeader(http.StatusNoContent) - if httpWriter.Code != http.StatusNoContent { - t.Errorf("got %d want %d", httpWriter.Code, http.StatusNoContent) - } -} - -// go test -v -test.run TestStatusCreatedAndContentTypeJson_Issue163 ...restful -func TestStatusCreatedAndContentTypeJson_Issue163(t *testing.T) { - httpWriter := httptest.NewRecorder() - resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true, nil} - resp.WriteHeader(http.StatusNotModified) - if httpWriter.Code != http.StatusNotModified { - t.Errorf("Got %d want %d", httpWriter.Code, http.StatusNotModified) - } -} - -func TestWriteHeaderAndEntity_Issue235(t *testing.T) { - httpWriter := httptest.NewRecorder() - resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true, nil} - var pong = struct { - Foo string `json:"foo"` - }{Foo: "123"} - resp.WriteHeaderAndEntity(404, pong) - if httpWriter.Code != http.StatusNotFound { - t.Errorf("got %d want %d", httpWriter.Code, http.StatusNoContent) - } - if got, want := httpWriter.Header().Get("Content-Type"), "application/json"; got != want { - t.Errorf("got %v want %v", got, want) - } - if !strings.HasPrefix(httpWriter.Body.String(), "{") { - t.Errorf("expected pong struct in json:%s", httpWriter.Body.String()) - } -} - -func TestWriteEntityNotAcceptable(t *testing.T) { - httpWriter := httptest.NewRecorder() - resp := Response{httpWriter, "application/bogus", []string{"application/json"}, 0, 0, true, nil} - resp.WriteEntity("done") - if httpWriter.Code != http.StatusNotAcceptable { - t.Errorf("got %d want %d", httpWriter.Code, http.StatusNotAcceptable) - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/route_builder_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/route_builder_test.go deleted file mode 100644 index 56dbe02e4..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/route_builder_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package restful - -import ( - "testing" -) - -func TestRouteBuilder_PathParameter(t *testing.T) { - p := &Parameter{&ParameterData{Name: "name", Description: "desc"}} - p.AllowMultiple(true) - p.DataType("int") - p.Required(true) - values := map[string]string{"a": "b"} - p.AllowableValues(values) - p.bePath() - - b := new(RouteBuilder) - b.function = dummy - b.Param(p) - r := b.Build() - if !r.ParameterDocs[0].Data().AllowMultiple { - t.Error("AllowMultiple invalid") - } - if r.ParameterDocs[0].Data().DataType != "int" { - t.Error("dataType invalid") - } - if !r.ParameterDocs[0].Data().Required { - t.Error("required invalid") - } - if r.ParameterDocs[0].Data().Kind != PathParameterKind { - t.Error("kind invalid") - } - if r.ParameterDocs[0].Data().AllowableValues["a"] != "b" { - t.Error("allowableValues invalid") - } - if b.ParameterNamed("name") == nil { - t.Error("access to parameter failed") - } -} - -func TestRouteBuilder(t *testing.T) { - json := "application/json" - b := new(RouteBuilder) - b.To(dummy) - b.Path("/routes").Method("HEAD").Consumes(json).Produces(json) - r := b.Build() - if r.Path != "/routes" { - t.Error("path invalid") - } - if r.Produces[0] != json { - t.Error("produces invalid") - } - if r.Consumes[0] != json { - t.Error("consumes invalid") - } - if r.Operation != "dummy" { - t.Error("Operation not set") - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/route_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/route_test.go deleted file mode 100644 index 6a104aff8..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/route_test.go +++ /dev/null @@ -1,127 +0,0 @@ -package restful - -import ( - "testing" -) - -// accept should match produces -func TestMatchesAcceptPlainTextWhenProducePlainTextAsLast(t *testing.T) { - r := Route{Produces: []string{"application/json", "text/plain"}} - if !r.matchesAccept("text/plain") { - t.Errorf("accept should match text/plain") - } -} - -// accept should match produces -func TestMatchesAcceptStar(t *testing.T) { - r := Route{Produces: []string{"application/xml"}} - if !r.matchesAccept("*/*") { - t.Errorf("accept should match star") - } -} - -// accept should match produces -func TestMatchesAcceptIE(t *testing.T) { - r := Route{Produces: []string{"application/xml"}} - if !r.matchesAccept("text/html, application/xhtml+xml, */*") { - t.Errorf("accept should match star") - } -} - -// accept should match produces -func TestMatchesAcceptXml(t *testing.T) { - r := Route{Produces: []string{"application/xml"}} - if r.matchesAccept("application/json") { - t.Errorf("accept should not match json") - } - if !r.matchesAccept("application/xml") { - t.Errorf("accept should match xml") - } -} - -// accept should match produces -func TestMatchesAcceptAny(t *testing.T) { - r := Route{Produces: []string{"*/*"}} - if !r.matchesAccept("application/json") { - t.Errorf("accept should match json") - } - if !r.matchesAccept("application/xml") { - t.Errorf("accept should match xml") - } -} - -// content type should match consumes -func TestMatchesContentTypeXml(t *testing.T) { - r := Route{Consumes: []string{"application/xml"}} - if r.matchesContentType("application/json") { - t.Errorf("accept should not match json") - } - if !r.matchesContentType("application/xml") { - t.Errorf("accept should match xml") - } -} - -// content type should match consumes -func TestMatchesContentTypeCharsetInformation(t *testing.T) { - r := Route{Consumes: []string{"application/json"}} - if !r.matchesContentType("application/json; charset=UTF-8") { - t.Errorf("matchesContentType should ignore charset information") - } -} - -func TestMatchesPath_OneParam(t *testing.T) { - params := doExtractParams("/from/{source}", 2, "/from/here", t) - if params["source"] != "here" { - t.Errorf("parameter mismatch here") - } -} - -func TestMatchesPath_Slash(t *testing.T) { - params := doExtractParams("/", 0, "/", t) - if len(params) != 0 { - t.Errorf("expected empty parameters") - } -} - -func TestMatchesPath_SlashNonVar(t *testing.T) { - params := doExtractParams("/any", 1, "/any", t) - if len(params) != 0 { - t.Errorf("expected empty parameters") - } -} - -func TestMatchesPath_TwoVars(t *testing.T) { - params := doExtractParams("/from/{source}/to/{destination}", 4, "/from/AMS/to/NY", t) - if params["source"] != "AMS" { - t.Errorf("parameter mismatch AMS") - } -} - -func TestMatchesPath_VarOnFront(t *testing.T) { - params := doExtractParams("{what}/from/{source}/", 3, "who/from/SOS/", t) - if params["source"] != "SOS" { - t.Errorf("parameter mismatch SOS") - } -} - -func TestExtractParameters_EmptyValue(t *testing.T) { - params := doExtractParams("/fixed/{var}", 2, "/fixed/", t) - if params["var"] != "" { - t.Errorf("parameter mismatch var") - } -} - -func TestTokenizePath(t *testing.T) { - if len(tokenizePath("/")) != 0 { - t.Errorf("not empty path tokens") - } -} - -func doExtractParams(routePath string, size int, urlPath string, t *testing.T) map[string]string { - r := Route{Path: routePath} - r.postBuild() - if len(r.pathParts) != size { - t.Fatalf("len not %v %v, but %v", size, r.pathParts, len(r.pathParts)) - } - return r.extractParameters(urlPath) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_builder_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_builder_test.go deleted file mode 100644 index fa3a1a453..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_builder_test.go +++ /dev/null @@ -1,1142 +0,0 @@ -package swagger - -import ( - "net" - "testing" - "time" -) - -type YesNo bool - -func (y YesNo) MarshalJSON() ([]byte, error) { - if y { - return []byte("yes"), nil - } - return []byte("no"), nil -} - -// clear && go test -v -test.run TestRef_Issue190 ...swagger -func TestRef_Issue190(t *testing.T) { - type User struct { - items []string - } - testJsonFromStruct(t, User{}, `{ - "swagger.User": { - "id": "swagger.User", - "required": [ - "items" - ], - "properties": { - "items": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - }`) -} - -// clear && go test -v -test.run TestCustomMarshaller_Issue96 ...swagger -func TestCustomMarshaller_Issue96(t *testing.T) { - type Vote struct { - What YesNo - } - testJsonFromStruct(t, Vote{}, `{ - "swagger.Vote": { - "id": "swagger.Vote", - "required": [ - "What" - ], - "properties": { - "What": { - "type": "string" - } - } - } - }`) -} - -// clear && go test -v -test.run TestPrimitiveTypes ...swagger -func TestPrimitiveTypes(t *testing.T) { - type Prims struct { - f float64 - t time.Time - } - testJsonFromStruct(t, Prims{}, `{ - "swagger.Prims": { - "id": "swagger.Prims", - "required": [ - "f", - "t" - ], - "properties": { - "f": { - "type": "number", - "format": "double" - }, - "t": { - "type": "string", - "format": "date-time" - } - } - } - }`) -} - -// clear && go test -v -test.run TestPrimitivePtrTypes ...swagger -func TestPrimitivePtrTypes(t *testing.T) { - type Prims struct { - f *float64 - t *time.Time - b *bool - s *string - i *int - } - testJsonFromStruct(t, Prims{}, `{ - "swagger.Prims": { - "id": "swagger.Prims", - "required": [ - "f", - "t", - "b", - "s", - "i" - ], - "properties": { - "b": { - "type": "boolean" - }, - "f": { - "type": "number", - "format": "double" - }, - "i": { - "type": "integer", - "format": "int32" - }, - "s": { - "type": "string" - }, - "t": { - "type": "string", - "format": "date-time" - } - } - } - }`) -} - -// clear && go test -v -test.run TestS1 ...swagger -func TestS1(t *testing.T) { - type S1 struct { - Id string - } - testJsonFromStruct(t, S1{}, `{ - "swagger.S1": { - "id": "swagger.S1", - "required": [ - "Id" - ], - "properties": { - "Id": { - "type": "string" - } - } - } - }`) -} - -// clear && go test -v -test.run TestS2 ...swagger -func TestS2(t *testing.T) { - type S2 struct { - Ids []string - } - testJsonFromStruct(t, S2{}, `{ - "swagger.S2": { - "id": "swagger.S2", - "required": [ - "Ids" - ], - "properties": { - "Ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - }`) -} - -// clear && go test -v -test.run TestS3 ...swagger -func TestS3(t *testing.T) { - type NestedS3 struct { - Id string - } - type S3 struct { - Nested NestedS3 - } - testJsonFromStruct(t, S3{}, `{ - "swagger.NestedS3": { - "id": "swagger.NestedS3", - "required": [ - "Id" - ], - "properties": { - "Id": { - "type": "string" - } - } - }, - "swagger.S3": { - "id": "swagger.S3", - "required": [ - "Nested" - ], - "properties": { - "Nested": { - "$ref": "swagger.NestedS3" - } - } - } - }`) -} - -type sample struct { - id string `swagger:"required"` // TODO - items []item - rootItem item `json:"root" description:"root desc"` -} - -type item struct { - itemName string `json:"name"` -} - -// clear && go test -v -test.run TestSampleToModelAsJson ...swagger -func TestSampleToModelAsJson(t *testing.T) { - testJsonFromStruct(t, sample{items: []item{}}, `{ - "swagger.item": { - "id": "swagger.item", - "required": [ - "name" - ], - "properties": { - "name": { - "type": "string" - } - } - }, - "swagger.sample": { - "id": "swagger.sample", - "required": [ - "id", - "items", - "root" - ], - "properties": { - "id": { - "type": "string" - }, - "items": { - "type": "array", - "items": { - "$ref": "swagger.item" - } - }, - "root": { - "$ref": "swagger.item", - "description": "root desc" - } - } - } - }`) -} - -func TestJsonTags(t *testing.T) { - type X struct { - A string - B string `json:"-"` - C int `json:",string"` - D int `json:","` - } - - expected := `{ - "swagger.X": { - "id": "swagger.X", - "required": [ - "A", - "C", - "D" - ], - "properties": { - "A": { - "type": "string" - }, - "C": { - "type": "string" - }, - "D": { - "type": "integer", - "format": "int32" - } - } - } - }` - - testJsonFromStruct(t, X{}, expected) -} - -func TestJsonTagOmitempty(t *testing.T) { - type X struct { - A int `json:",omitempty"` - B int `json:"C,omitempty"` - } - - expected := `{ - "swagger.X": { - "id": "swagger.X", - "properties": { - "A": { - "type": "integer", - "format": "int32" - }, - "C": { - "type": "integer", - "format": "int32" - } - } - } - }` - - testJsonFromStruct(t, X{}, expected) -} - -func TestJsonTagName(t *testing.T) { - type X struct { - A string `json:"B"` - } - - expected := `{ - "swagger.X": { - "id": "swagger.X", - "required": [ - "B" - ], - "properties": { - "B": { - "type": "string" - } - } - } - }` - - testJsonFromStruct(t, X{}, expected) -} - -func TestAnonymousStruct(t *testing.T) { - type X struct { - A struct { - B int - } - } - - expected := `{ - "swagger.X": { - "id": "swagger.X", - "required": [ - "A" - ], - "properties": { - "A": { - "$ref": "swagger.X.A" - } - } - }, - "swagger.X.A": { - "id": "swagger.X.A", - "required": [ - "B" - ], - "properties": { - "B": { - "type": "integer", - "format": "int32" - } - } - } - }` - - testJsonFromStruct(t, X{}, expected) -} - -func TestAnonymousPtrStruct(t *testing.T) { - type X struct { - A *struct { - B int - } - } - - expected := `{ - "swagger.X": { - "id": "swagger.X", - "required": [ - "A" - ], - "properties": { - "A": { - "$ref": "swagger.X.A" - } - } - }, - "swagger.X.A": { - "id": "swagger.X.A", - "required": [ - "B" - ], - "properties": { - "B": { - "type": "integer", - "format": "int32" - } - } - } - }` - - testJsonFromStruct(t, X{}, expected) -} - -func TestAnonymousArrayStruct(t *testing.T) { - type X struct { - A []struct { - B int - } - } - - expected := `{ - "swagger.X": { - "id": "swagger.X", - "required": [ - "A" - ], - "properties": { - "A": { - "type": "array", - "items": { - "$ref": "swagger.X.A" - } - } - } - }, - "swagger.X.A": { - "id": "swagger.X.A", - "required": [ - "B" - ], - "properties": { - "B": { - "type": "integer", - "format": "int32" - } - } - } - }` - - testJsonFromStruct(t, X{}, expected) -} - -func TestAnonymousPtrArrayStruct(t *testing.T) { - type X struct { - A *[]struct { - B int - } - } - - expected := `{ - "swagger.X": { - "id": "swagger.X", - "required": [ - "A" - ], - "properties": { - "A": { - "type": "array", - "items": { - "$ref": "swagger.X.A" - } - } - } - }, - "swagger.X.A": { - "id": "swagger.X.A", - "required": [ - "B" - ], - "properties": { - "B": { - "type": "integer", - "format": "int32" - } - } - } - }` - - testJsonFromStruct(t, X{}, expected) -} - -// go test -v -test.run TestEmbeddedStruct_Issue98 ...swagger -func TestEmbeddedStruct_Issue98(t *testing.T) { - type Y struct { - A int - } - type X struct { - Y - } - testJsonFromStruct(t, X{}, `{ - "swagger.X": { - "id": "swagger.X", - "required": [ - "A" - ], - "properties": { - "A": { - "type": "integer", - "format": "int32" - } - } - } - }`) -} - -type Dataset struct { - Names []string -} - -// clear && go test -v -test.run TestIssue85 ...swagger -func TestIssue85(t *testing.T) { - anon := struct{ Datasets []Dataset }{} - testJsonFromStruct(t, anon, `{ - "struct { Datasets ||swagger.Dataset }": { - "id": "struct { Datasets ||swagger.Dataset }", - "required": [ - "Datasets" - ], - "properties": { - "Datasets": { - "type": "array", - "items": { - "$ref": "swagger.Dataset" - } - } - } - }, - "swagger.Dataset": { - "id": "swagger.Dataset", - "required": [ - "Names" - ], - "properties": { - "Names": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - }`) -} - -type File struct { - History []File - HistoryPtrs []*File -} - -// go test -v -test.run TestRecursiveStructure ...swagger -func TestRecursiveStructure(t *testing.T) { - testJsonFromStruct(t, File{}, `{ - "swagger.File": { - "id": "swagger.File", - "required": [ - "History", - "HistoryPtrs" - ], - "properties": { - "History": { - "type": "array", - "items": { - "$ref": "swagger.File" - } - }, - "HistoryPtrs": { - "type": "array", - "items": { - "$ref": "swagger.File" - } - } - } - } - }`) -} - -type A1 struct { - B struct { - Id int - Comment string `json:"comment,omitempty"` - } -} - -// go test -v -test.run TestEmbeddedStructA1 ...swagger -func TestEmbeddedStructA1(t *testing.T) { - testJsonFromStruct(t, A1{}, `{ - "swagger.A1": { - "id": "swagger.A1", - "required": [ - "B" - ], - "properties": { - "B": { - "$ref": "swagger.A1.B" - } - } - }, - "swagger.A1.B": { - "id": "swagger.A1.B", - "required": [ - "Id" - ], - "properties": { - "Id": { - "type": "integer", - "format": "int32" - }, - "comment": { - "type": "string" - } - } - } - }`) -} - -type A2 struct { - C -} -type C struct { - Id int `json:"B"` - Comment string `json:"comment,omitempty"` - Secure bool `json:"secure"` -} - -// go test -v -test.run TestEmbeddedStructA2 ...swagger -func TestEmbeddedStructA2(t *testing.T) { - testJsonFromStruct(t, A2{}, `{ - "swagger.A2": { - "id": "swagger.A2", - "required": [ - "B", - "secure" - ], - "properties": { - "B": { - "type": "integer", - "format": "int32" - }, - "comment": { - "type": "string" - }, - "secure": { - "type": "boolean" - } - } - } - }`) -} - -type A3 struct { - B D -} - -type D struct { - Id int -} - -// clear && go test -v -test.run TestStructA3 ...swagger -func TestStructA3(t *testing.T) { - testJsonFromStruct(t, A3{}, `{ - "swagger.A3": { - "id": "swagger.A3", - "required": [ - "B" - ], - "properties": { - "B": { - "$ref": "swagger.D" - } - } - }, - "swagger.D": { - "id": "swagger.D", - "required": [ - "Id" - ], - "properties": { - "Id": { - "type": "integer", - "format": "int32" - } - } - } - }`) -} - -type A4 struct { - D "json:,inline" -} - -// clear && go test -v -test.run TestStructA4 ...swagger -func TestEmbeddedStructA4(t *testing.T) { - testJsonFromStruct(t, A4{}, `{ - "swagger.A4": { - "id": "swagger.A4", - "required": [ - "Id" - ], - "properties": { - "Id": { - "type": "integer", - "format": "int32" - } - } - } - }`) -} - -type A5 struct { - D `json:"d"` -} - -// clear && go test -v -test.run TestStructA5 ...swagger -func TestEmbeddedStructA5(t *testing.T) { - testJsonFromStruct(t, A5{}, `{ - "swagger.A5": { - "id": "swagger.A5", - "required": [ - "d" - ], - "properties": { - "d": { - "$ref": "swagger.D" - } - } - }, - "swagger.D": { - "id": "swagger.D", - "required": [ - "Id" - ], - "properties": { - "Id": { - "type": "integer", - "format": "int32" - } - } - } - }`) -} - -type D2 struct { - id int - D []D -} - -type A6 struct { - D2 "json:,inline" -} - -// clear && go test -v -test.run TestStructA4 ...swagger -func TestEmbeddedStructA6(t *testing.T) { - testJsonFromStruct(t, A6{}, `{ - "swagger.A6": { - "id": "swagger.A6", - "required": [ - "id", - "D" - ], - "properties": { - "D": { - "type": "array", - "items": { - "$ref": "swagger.D" - } - }, - "id": { - "type": "integer", - "format": "int32" - } - } - }, - "swagger.D": { - "id": "swagger.D", - "required": [ - "Id" - ], - "properties": { - "Id": { - "type": "integer", - "format": "int32" - } - } - } - }`) -} - -type ObjectId []byte - -type Region struct { - Id ObjectId `bson:"_id" json:"id"` - Name string `bson:"name" json:"name"` - Type string `bson:"type" json:"type"` -} - -// clear && go test -v -test.run TestRegion_Issue113 ...swagger -func TestRegion_Issue113(t *testing.T) { - testJsonFromStruct(t, []Region{}, `{ - "integer": { - "id": "integer", - "properties": {} - }, - "swagger.Region": { - "id": "swagger.Region", - "required": [ - "id", - "name", - "type" - ], - "properties": { - "id": { - "type": "array", - "items": { - "$ref": "integer" - } - }, - "name": { - "type": "string" - }, - "type": { - "type": "string" - } - } - }, - "||swagger.Region": { - "id": "||swagger.Region", - "properties": {} - } - }`) -} - -// clear && go test -v -test.run TestIssue158 ...swagger -func TestIssue158(t *testing.T) { - type Address struct { - Country string `json:"country,omitempty"` - } - - type Customer struct { - Name string `json:"name"` - Address Address `json:"address"` - } - expected := `{ - "swagger.Address": { - "id": "swagger.Address", - "properties": { - "country": { - "type": "string" - } - } - }, - "swagger.Customer": { - "id": "swagger.Customer", - "required": [ - "name", - "address" - ], - "properties": { - "address": { - "$ref": "swagger.Address" - }, - "name": { - "type": "string" - } - } - } - }` - testJsonFromStruct(t, Customer{}, expected) -} - -func TestSlices(t *testing.T) { - type Address struct { - Country string `json:"country,omitempty"` - } - expected := `{ - "swagger.Address": { - "id": "swagger.Address", - "properties": { - "country": { - "type": "string" - } - } - }, - "swagger.Customer": { - "id": "swagger.Customer", - "required": [ - "name", - "addresses" - ], - "properties": { - "addresses": { - "type": "array", - "items": { - "$ref": "swagger.Address" - } - }, - "name": { - "type": "string" - } - } - } - }` - // both slices (with pointer value and with type value) should have equal swagger representation - { - type Customer struct { - Name string `json:"name"` - Addresses []Address `json:"addresses"` - } - testJsonFromStruct(t, Customer{}, expected) - } - { - type Customer struct { - Name string `json:"name"` - Addresses []*Address `json:"addresses"` - } - testJsonFromStruct(t, Customer{}, expected) - } - -} - -type Name struct { - Value string -} - -func (n Name) PostBuildModel(m *Model) *Model { - m.Description = "titles must be upcase" - return m -} - -type TOC struct { - Titles []Name -} - -type Discography struct { - Title Name - TOC -} - -// clear && go test -v -test.run TestEmbeddedStructPull204 ...swagger -func TestEmbeddedStructPull204(t *testing.T) { - b := Discography{} - testJsonFromStruct(t, b, ` -{ - "swagger.Discography": { - "id": "swagger.Discography", - "required": [ - "Title", - "Titles" - ], - "properties": { - "Title": { - "$ref": "swagger.Name" - }, - "Titles": { - "type": "array", - "items": { - "$ref": "swagger.Name" - } - } - } - }, - "swagger.Name": { - "id": "swagger.Name", - "required": [ - "Value" - ], - "properties": { - "Value": { - "type": "string" - } - } - } - } -`) -} - -type AddressWithMethod struct { - Country string `json:"country,omitempty"` - PostCode int `json:"postcode,omitempty"` -} - -func (AddressWithMethod) SwaggerDoc() map[string]string { - return map[string]string{ - "": "Address doc", - "country": "Country doc", - "postcode": "PostCode doc", - } -} - -func TestDocInMethodSwaggerDoc(t *testing.T) { - expected := `{ - "swagger.AddressWithMethod": { - "id": "swagger.AddressWithMethod", - "description": "Address doc", - "properties": { - "country": { - "type": "string", - "description": "Country doc" - }, - "postcode": { - "type": "integer", - "format": "int32", - "description": "PostCode doc" - } - } - } - }` - testJsonFromStruct(t, AddressWithMethod{}, expected) -} - -type RefDesc struct { - f1 *int64 `description:"desc"` -} - -func TestPtrDescription(t *testing.T) { - b := RefDesc{} - expected := `{ - "swagger.RefDesc": { - "id": "swagger.RefDesc", - "required": [ - "f1" - ], - "properties": { - "f1": { - "type": "integer", - "format": "int64", - "description": "desc" - } - } - } - }` - testJsonFromStruct(t, b, expected) -} - -type A struct { - B `json:",inline"` - C1 `json:"metadata,omitempty"` -} - -type B struct { - SB string -} - -type C1 struct { - SC string -} - -func (A) SwaggerDoc() map[string]string { - return map[string]string{ - "": "A struct", - "B": "B field", // We should not get anything from this - "metadata": "C1 field", - } -} - -func (B) SwaggerDoc() map[string]string { - return map[string]string{ - "": "B struct", - "SB": "SB field", - } -} - -func (C1) SwaggerDoc() map[string]string { - return map[string]string{ - "": "C1 struct", - "SC": "SC field", - } -} - -func TestNestedStructDescription(t *testing.T) { - expected := ` -{ - "swagger.A": { - "id": "swagger.A", - "description": "A struct", - "required": [ - "SB" - ], - "properties": { - "SB": { - "type": "string", - "description": "SB field" - }, - "metadata": { - "$ref": "swagger.C1", - "description": "C1 field" - } - } - }, - "swagger.C1": { - "id": "swagger.C1", - "description": "C1 struct", - "required": [ - "SC" - ], - "properties": { - "SC": { - "type": "string", - "description": "SC field" - } - } - } - } -` - testJsonFromStruct(t, A{}, expected) -} - -// This tests a primitive with type overrides in the struct tags -type FakeInt int -type E struct { - Id FakeInt `type:"integer"` - IP net.IP `type:"string"` -} - -func TestOverridenTypeTagE1(t *testing.T) { - expected := ` -{ - "swagger.E": { - "id": "swagger.E", - "required": [ - "Id", - "IP" - ], - "properties": { - "Id": { - "type": "integer" - }, - "IP": { - "type": "string" - } - } - } - } -` - testJsonFromStruct(t, E{}, expected) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_list_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_list_test.go deleted file mode 100644 index 9a9ab919b..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_list_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package swagger - -import ( - "encoding/json" - "testing" -) - -func TestModelList(t *testing.T) { - m := Model{} - m.Id = "m" - l := ModelList{} - l.Put("m", m) - k, ok := l.At("m") - if !ok { - t.Error("want model back") - } - if got, want := k.Id, "m"; got != want { - t.Errorf("got %v want %v", got, want) - } -} - -func TestModelList_Marshal(t *testing.T) { - l := ModelList{} - m := Model{Id: "myid"} - l.Put("myid", m) - data, err := json.Marshal(l) - if err != nil { - t.Error(err) - } - if got, want := string(data), `{"myid":{"id":"myid","properties":{}}}`; got != want { - t.Errorf("got %v want %v", got, want) - } -} - -func TestModelList_Unmarshal(t *testing.T) { - data := `{"myid":{"id":"myid","properties":{}}}` - l := ModelList{} - if err := json.Unmarshal([]byte(data), &l); err != nil { - t.Error(err) - } - m, ok := l.At("myid") - if !ok { - t.Error("expected myid") - } - if got, want := m.Id, "myid"; got != want { - t.Errorf("got %v want %v", got, want) - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_ext_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_ext_test.go deleted file mode 100644 index eff0d59d3..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_ext_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package swagger - -import ( - "net" - "testing" -) - -// clear && go test -v -test.run TestThatExtraTagsAreReadIntoModel ...swagger -func TestThatExtraTagsAreReadIntoModel(t *testing.T) { - type fakeint int - type Anything struct { - Name string `description:"name" modelDescription:"a test"` - Size int `minimum:"0" maximum:"10"` - Stati string `enum:"off|on" default:"on" modelDescription:"more description"` - ID string `unique:"true"` - FakeInt fakeint `type:"integer"` - IP net.IP `type:"string"` - Password string - } - m := modelsFromStruct(Anything{}) - props, _ := m.At("swagger.Anything") - p1, _ := props.Properties.At("Name") - if got, want := p1.Description, "name"; got != want { - t.Errorf("got %v want %v", got, want) - } - p2, _ := props.Properties.At("Size") - if got, want := p2.Minimum, "0"; got != want { - t.Errorf("got %v want %v", got, want) - } - if got, want := p2.Maximum, "10"; got != want { - t.Errorf("got %v want %v", got, want) - } - p3, _ := props.Properties.At("Stati") - if got, want := p3.Enum[0], "off"; got != want { - t.Errorf("got %v want %v", got, want) - } - if got, want := p3.Enum[1], "on"; got != want { - t.Errorf("got %v want %v", got, want) - } - p4, _ := props.Properties.At("ID") - if got, want := *p4.UniqueItems, true; got != want { - t.Errorf("got %v want %v", got, want) - } - p5, _ := props.Properties.At("Password") - if got, want := *p5.Type, "string"; got != want { - t.Errorf("got %v want %v", got, want) - } - p6, _ := props.Properties.At("FakeInt") - if got, want := *p6.Type, "integer"; got != want { - t.Errorf("got %v want %v", got, want) - } - p7, _ := props.Properties.At("IP") - if got, want := *p7.Type, "string"; got != want { - t.Errorf("got %v want %v", got, want) - } - - if got, want := props.Description, "a test\nmore description"; got != want { - t.Errorf("got %v want %v", got, want) - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_list_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_list_test.go deleted file mode 100644 index 2833ad8fd..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_list_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package swagger - -import ( - "encoding/json" - "testing" -) - -func TestModelPropertyList(t *testing.T) { - l := ModelPropertyList{} - p := ModelProperty{Description: "d"} - l.Put("p", p) - q, ok := l.At("p") - if !ok { - t.Error("expected p") - } - if got, want := q.Description, "d"; got != want { - t.Errorf("got %v want %v", got, want) - } -} - -func TestModelPropertyList_Marshal(t *testing.T) { - l := ModelPropertyList{} - p := ModelProperty{Description: "d"} - l.Put("p", p) - data, err := json.Marshal(l) - if err != nil { - t.Error(err) - } - if got, want := string(data), `{"p":{"description":"d"}}`; got != want { - t.Errorf("got %v want %v", got, want) - } -} - -func TestModelPropertyList_Unmarshal(t *testing.T) { - data := `{"p":{"description":"d"}}` - l := ModelPropertyList{} - if err := json.Unmarshal([]byte(data), &l); err != nil { - t.Error(err) - } - m, ok := l.At("p") - if !ok { - t.Error("expected p") - } - if got, want := m.Description, "d"; got != want { - t.Errorf("got %v want %v", got, want) - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/ordered_route_map_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/ordered_route_map_test.go deleted file mode 100644 index 964e7da05..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/ordered_route_map_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package swagger - -import ( - "testing" - - "github.com/emicklei/go-restful" -) - -// go test -v -test.run TestOrderedRouteMap ...swagger -func TestOrderedRouteMap(t *testing.T) { - m := newOrderedRouteMap() - r1 := restful.Route{Path: "/r1"} - r2 := restful.Route{Path: "/r2"} - m.Add("a", r1) - m.Add("b", r2) - m.Add("b", r1) - m.Add("d", r2) - m.Add("c", r2) - order := "" - m.Do(func(k string, routes []restful.Route) { - order += k - if len(routes) == 0 { - t.Fail() - } - }) - if order != "abdc" { - t.Fail() - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/postbuild_model_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/postbuild_model_test.go deleted file mode 100644 index 3e20d2f5b..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/postbuild_model_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package swagger - -import "testing" - -type Boat struct { - Length int `json:"-"` // on default, this makes the fields not required - Weight int `json:"-"` -} - -// PostBuildModel is from swagger.ModelBuildable -func (b Boat) PostBuildModel(m *Model) *Model { - // override required - m.Required = []string{"Length", "Weight"} - - // add model property (just to test is can be added; is this a real usecase?) - extraType := "string" - m.Properties.Put("extra", ModelProperty{ - Description: "extra description", - DataTypeFields: DataTypeFields{ - Type: &extraType, - }, - }) - return m -} - -func TestCustomPostModelBuilde(t *testing.T) { - testJsonFromStruct(t, Boat{}, `{ - "swagger.Boat": { - "id": "swagger.Boat", - "required": [ - "Length", - "Weight" - ], - "properties": { - "extra": { - "type": "string", - "description": "extra description" - } - } - } -}`) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_test.go deleted file mode 100644 index 78e2d43cc..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_test.go +++ /dev/null @@ -1,284 +0,0 @@ -package swagger - -import ( - "encoding/json" - "testing" - - "github.com/emicklei/go-restful" - "github.com/emicklei/go-restful/swagger/test_package" -) - -func TestInfoStruct_Issue231(t *testing.T) { - config := Config{ - Info: Info{ - Title: "Title", - Description: "Description", - TermsOfServiceUrl: "http://example.com", - Contact: "example@example.com", - License: "License", - LicenseUrl: "http://example.com/license.txt", - }, - } - sws := newSwaggerService(config) - str, err := json.MarshalIndent(sws.produceListing(), "", " ") - if err != nil { - t.Fatal(err) - } - compareJson(t, string(str), ` - { - "apiVersion": "", - "swaggerVersion": "1.2", - "apis": null, - "info": { - "title": "Title", - "description": "Description", - "termsOfServiceUrl": "http://example.com", - "contact": "example@example.com", - "license": "License", - "licenseUrl": "http://example.com/license.txt" - } - } - `) -} - -// go test -v -test.run TestThatMultiplePathsOnRootAreHandled ...swagger -func TestThatMultiplePathsOnRootAreHandled(t *testing.T) { - ws1 := new(restful.WebService) - ws1.Route(ws1.GET("/_ping").To(dummy)) - ws1.Route(ws1.GET("/version").To(dummy)) - - cfg := Config{ - WebServicesUrl: "http://here.com", - ApiPath: "/apipath", - WebServices: []*restful.WebService{ws1}, - } - sws := newSwaggerService(cfg) - decl := sws.composeDeclaration(ws1, "/") - if got, want := len(decl.Apis), 2; got != want { - t.Errorf("got %v want %v", got, want) - } -} - -func TestWriteSamples(t *testing.T) { - ws1 := new(restful.WebService) - ws1.Route(ws1.GET("/object").To(dummy).Writes(test_package.TestStruct{})) - ws1.Route(ws1.GET("/array").To(dummy).Writes([]test_package.TestStruct{})) - ws1.Route(ws1.GET("/object_and_array").To(dummy).Writes(struct{ Abc test_package.TestStruct }{})) - - cfg := Config{ - WebServicesUrl: "http://here.com", - ApiPath: "/apipath", - WebServices: []*restful.WebService{ws1}, - } - sws := newSwaggerService(cfg) - - decl := sws.composeDeclaration(ws1, "/") - - str, err := json.MarshalIndent(decl.Apis, "", " ") - if err != nil { - t.Fatal(err) - } - - compareJson(t, string(str), ` - [ - { - "path": "/object", - "description": "", - "operations": [ - { - "type": "test_package.TestStruct", - "method": "GET", - "nickname": "dummy", - "parameters": [] - } - ] - }, - { - "path": "/array", - "description": "", - "operations": [ - { - "type": "array", - "items": { - "$ref": "test_package.TestStruct" - }, - "method": "GET", - "nickname": "dummy", - "parameters": [] - } - ] - }, - { - "path": "/object_and_array", - "description": "", - "operations": [ - { - "type": "struct { Abc test_package.TestStruct }", - "method": "GET", - "nickname": "dummy", - "parameters": [] - } - ] - } - ]`) - - str, err = json.MarshalIndent(decl.Models, "", " ") - if err != nil { - t.Fatal(err) - } - compareJson(t, string(str), ` - { - "test_package.TestStruct": { - "id": "test_package.TestStruct", - "required": [ - "TestField" - ], - "properties": { - "TestField": { - "type": "string" - } - } - }, - "||test_package.TestStruct": { - "id": "||test_package.TestStruct", - "properties": {} - }, - "struct { Abc test_package.TestStruct }": { - "id": "struct { Abc test_package.TestStruct }", - "required": [ - "Abc" - ], - "properties": { - "Abc": { - "$ref": "test_package.TestStruct" - } - } - } - }`) -} - -// go test -v -test.run TestServiceToApi ...swagger -func TestServiceToApi(t *testing.T) { - ws := new(restful.WebService) - ws.Path("/tests") - ws.Consumes(restful.MIME_JSON) - ws.Produces(restful.MIME_XML) - ws.Route(ws.GET("/a").To(dummy).Writes(sample{})) - ws.Route(ws.PUT("/b").To(dummy).Writes(sample{})) - ws.Route(ws.POST("/c").To(dummy).Writes(sample{})) - ws.Route(ws.DELETE("/d").To(dummy).Writes(sample{})) - - ws.Route(ws.GET("/d").To(dummy).Writes(sample{})) - ws.Route(ws.PUT("/c").To(dummy).Writes(sample{})) - ws.Route(ws.POST("/b").To(dummy).Writes(sample{})) - ws.Route(ws.DELETE("/a").To(dummy).Writes(sample{})) - ws.ApiVersion("1.2.3") - cfg := Config{ - WebServicesUrl: "http://here.com", - ApiPath: "/apipath", - WebServices: []*restful.WebService{ws}, - PostBuildHandler: func(in *ApiDeclarationList) {}, - } - sws := newSwaggerService(cfg) - decl := sws.composeDeclaration(ws, "/tests") - // checks - if decl.ApiVersion != "1.2.3" { - t.Errorf("got %v want %v", decl.ApiVersion, "1.2.3") - } - if decl.BasePath != "http://here.com" { - t.Errorf("got %v want %v", decl.BasePath, "http://here.com") - } - if len(decl.Apis) != 4 { - t.Errorf("got %v want %v", len(decl.Apis), 4) - } - pathOrder := "" - for _, each := range decl.Apis { - pathOrder += each.Path - for _, other := range each.Operations { - pathOrder += other.Method - } - } - - if pathOrder != "/tests/aGETDELETE/tests/bPUTPOST/tests/cPOSTPUT/tests/dDELETEGET" { - t.Errorf("got %v want %v", pathOrder, "see test source") - } -} - -func dummy(i *restful.Request, o *restful.Response) {} - -// go test -v -test.run TestIssue78 ...swagger -type Response struct { - Code int - Users *[]User - Items *[]TestItem -} -type User struct { - Id, Name string -} -type TestItem struct { - Id, Name string -} - -// clear && go test -v -test.run TestComposeResponseMessages ...swagger -func TestComposeResponseMessages(t *testing.T) { - responseErrors := map[int]restful.ResponseError{} - responseErrors[400] = restful.ResponseError{Code: 400, Message: "Bad Request", Model: TestItem{}} - route := restful.Route{ResponseErrors: responseErrors} - decl := new(ApiDeclaration) - decl.Models = ModelList{} - msgs := composeResponseMessages(route, decl) - if msgs[0].ResponseModel != "swagger.TestItem" { - t.Errorf("got %s want swagger.TestItem", msgs[0].ResponseModel) - } -} - -// clear && go test -v -test.run TestComposeResponseMessageArray ...swagger -func TestComposeResponseMessageArray(t *testing.T) { - responseErrors := map[int]restful.ResponseError{} - responseErrors[400] = restful.ResponseError{Code: 400, Message: "Bad Request", Model: []TestItem{}} - route := restful.Route{ResponseErrors: responseErrors} - decl := new(ApiDeclaration) - decl.Models = ModelList{} - msgs := composeResponseMessages(route, decl) - if msgs[0].ResponseModel != "array[swagger.TestItem]" { - t.Errorf("got %s want swagger.TestItem", msgs[0].ResponseModel) - } -} - -func TestIssue78(t *testing.T) { - sws := newSwaggerService(Config{}) - models := new(ModelList) - sws.addModelFromSampleTo(&Operation{}, true, Response{Items: &[]TestItem{}}, models) - model, ok := models.At("swagger.Response") - if !ok { - t.Fatal("missing response model") - } - if "swagger.Response" != model.Id { - t.Fatal("wrong model id:" + model.Id) - } - code, ok := model.Properties.At("Code") - if !ok { - t.Fatal("missing code") - } - if "integer" != *code.Type { - t.Fatal("wrong code type:" + *code.Type) - } - items, ok := model.Properties.At("Items") - if !ok { - t.Fatal("missing items") - } - if "array" != *items.Type { - t.Fatal("wrong items type:" + *items.Type) - } - items_items := items.Items - if items_items == nil { - t.Fatal("missing items->items") - } - ref := items_items.Ref - if ref == nil { - t.Fatal("missing $ref") - } - if *ref != "swagger.TestItem" { - t.Fatal("wrong $ref:" + *ref) - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/test_package/struct.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/test_package/struct.go deleted file mode 100644 index b9a6f9308..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/test_package/struct.go +++ /dev/null @@ -1,5 +0,0 @@ -package test_package - -type TestStruct struct { - TestField string -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/utils_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/utils_test.go deleted file mode 100644 index dab871032..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/utils_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package swagger - -import ( - "bytes" - "encoding/json" - "fmt" - "reflect" - "strings" - "testing" -) - -func testJsonFromStruct(t *testing.T, sample interface{}, expectedJson string) bool { - m := modelsFromStruct(sample) - data, _ := json.MarshalIndent(m, " ", " ") - return compareJson(t, string(data), expectedJson) -} - -func modelsFromStruct(sample interface{}) *ModelList { - models := new(ModelList) - builder := modelBuilder{models} - builder.addModelFrom(sample) - return models -} - -func compareJson(t *testing.T, actualJsonAsString string, expectedJsonAsString string) bool { - success := false - var actualMap map[string]interface{} - json.Unmarshal([]byte(actualJsonAsString), &actualMap) - var expectedMap map[string]interface{} - err := json.Unmarshal([]byte(expectedJsonAsString), &expectedMap) - if err != nil { - var actualArray []interface{} - json.Unmarshal([]byte(actualJsonAsString), &actualArray) - var expectedArray []interface{} - err := json.Unmarshal([]byte(expectedJsonAsString), &expectedArray) - success = reflect.DeepEqual(actualArray, expectedArray) - if err != nil { - t.Fatalf("Unparsable expected JSON: %s", err) - } - } else { - success = reflect.DeepEqual(actualMap, expectedMap) - } - if !success { - t.Log("---- expected -----") - t.Log(withLineNumbers(expectedJsonAsString)) - t.Log("---- actual -----") - t.Log(withLineNumbers(actualJsonAsString)) - t.Log("---- raw -----") - t.Log(actualJsonAsString) - t.Error("there are differences") - return false - } - return true -} - -func indexOfNonMatchingLine(actual, expected string) int { - a := strings.Split(actual, "\n") - e := strings.Split(expected, "\n") - size := len(a) - if len(e) < len(a) { - size = len(e) - } - for i := 0; i < size; i++ { - if a[i] != e[i] { - return i - } - } - return -1 -} - -func withLineNumbers(content string) string { - var buffer bytes.Buffer - lines := strings.Split(content, "\n") - for i, each := range lines { - buffer.WriteString(fmt.Sprintf("%d:%s\n", i, each)) - } - return buffer.String() -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/tracer_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/tracer_test.go deleted file mode 100644 index 60c1e9fc0..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/tracer_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package restful - -import "testing" - -// Use like this: -// -// TraceLogger(testLogger{t}) -type testLogger struct { - t *testing.T -} - -func (l testLogger) Print(v ...interface{}) { - l.t.Log(v...) -} - -func (l testLogger) Printf(format string, v ...interface{}) { - l.t.Logf(format, v...) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/web_service_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/web_service_test.go deleted file mode 100644 index 469890434..000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/web_service_test.go +++ /dev/null @@ -1,254 +0,0 @@ -package restful - -import ( - "net/http" - "net/http/httptest" - "testing" -) - -const ( - pathGetFriends = "/get/{userId}/friends" -) - -func TestParameter(t *testing.T) { - p := &Parameter{&ParameterData{Name: "name", Description: "desc"}} - p.AllowMultiple(true) - p.DataType("int") - p.Required(true) - values := map[string]string{"a": "b"} - p.AllowableValues(values) - p.bePath() - - ws := new(WebService) - ws.Param(p) - if ws.pathParameters[0].Data().Name != "name" { - t.Error("path parameter (or name) invalid") - } -} -func TestWebService_CanCreateParameterKinds(t *testing.T) { - ws := new(WebService) - if ws.BodyParameter("b", "b").Kind() != BodyParameterKind { - t.Error("body parameter expected") - } - if ws.PathParameter("p", "p").Kind() != PathParameterKind { - t.Error("path parameter expected") - } - if ws.QueryParameter("q", "q").Kind() != QueryParameterKind { - t.Error("query parameter expected") - } -} - -func TestCapturePanic(t *testing.T) { - tearDown() - Add(newPanicingService()) - httpRequest, _ := http.NewRequest("GET", "http://here.com/fire", nil) - httpRequest.Header.Set("Accept", "*/*") - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - if 500 != httpWriter.Code { - t.Error("500 expected on fire") - } -} - -func TestCapturePanicWithEncoded(t *testing.T) { - tearDown() - Add(newPanicingService()) - DefaultContainer.EnableContentEncoding(true) - httpRequest, _ := http.NewRequest("GET", "http://here.com/fire", nil) - httpRequest.Header.Set("Accept", "*/*") - httpRequest.Header.Set("Accept-Encoding", "gzip") - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - if 500 != httpWriter.Code { - t.Error("500 expected on fire, got", httpWriter.Code) - } -} - -func TestNotFound(t *testing.T) { - tearDown() - httpRequest, _ := http.NewRequest("GET", "http://here.com/missing", nil) - httpRequest.Header.Set("Accept", "*/*") - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - if 404 != httpWriter.Code { - t.Error("404 expected on missing") - } -} - -func TestMethodNotAllowed(t *testing.T) { - tearDown() - Add(newGetOnlyService()) - httpRequest, _ := http.NewRequest("POST", "http://here.com/get", nil) - httpRequest.Header.Set("Accept", "*/*") - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - if 405 != httpWriter.Code { - t.Error("405 expected method not allowed") - } -} - -func TestSelectedRoutePath_Issue100(t *testing.T) { - tearDown() - Add(newSelectedRouteTestingService()) - httpRequest, _ := http.NewRequest("GET", "http://here.com/get/232452/friends", nil) - httpRequest.Header.Set("Accept", "*/*") - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - if http.StatusOK != httpWriter.Code { - t.Error(http.StatusOK, "expected,", httpWriter.Code, "received.") - } -} - -func TestContentType415_Issue170(t *testing.T) { - tearDown() - Add(newGetOnlyJsonOnlyService()) - httpRequest, _ := http.NewRequest("GET", "http://here.com/get", nil) - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - if 200 != httpWriter.Code { - t.Errorf("Expected 200, got %d", httpWriter.Code) - } -} - -func TestContentType415_POST_Issue170(t *testing.T) { - tearDown() - Add(newPostOnlyJsonOnlyService()) - httpRequest, _ := http.NewRequest("POST", "http://here.com/post", nil) - httpRequest.Header.Set("Content-Type", "application/json") - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - if 200 != httpWriter.Code { - t.Errorf("Expected 200, got %d", httpWriter.Code) - } -} - -// go test -v -test.run TestContentType406PlainJson ...restful -func TestContentType406PlainJson(t *testing.T) { - tearDown() - TraceLogger(testLogger{t}) - Add(newGetPlainTextOrJsonService()) - httpRequest, _ := http.NewRequest("GET", "http://here.com/get", nil) - httpRequest.Header.Set("Accept", "text/plain") - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - if got, want := httpWriter.Code, 200; got != want { - t.Errorf("got %v, want %v", got, want) - } -} - -func TestRemoveRoute(t *testing.T) { - tearDown() - TraceLogger(testLogger{t}) - ws := newGetPlainTextOrJsonService() - Add(ws) - httpRequest, _ := http.NewRequest("GET", "http://here.com/get", nil) - httpRequest.Header.Set("Accept", "text/plain") - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - if got, want := httpWriter.Code, 200; got != want { - t.Errorf("got %v, want %v", got, want) - } - - // dynamic apis are disabled, should error and do nothing - if err := ws.RemoveRoute("/get", "GET"); err == nil { - t.Error("unexpected non-error") - } - - httpWriter = httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - if got, want := httpWriter.Code, 200; got != want { - t.Errorf("got %v, want %v", got, want) - } - - ws.SetDynamicRoutes(true) - if err := ws.RemoveRoute("/get", "GET"); err != nil { - t.Errorf("unexpected error %v", err) - } - - httpWriter = httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - if got, want := httpWriter.Code, 404; got != want { - t.Errorf("got %v, want %v", got, want) - } -} - -// go test -v -test.run TestContentTypeOctet_Issue170 ...restful -func TestContentTypeOctet_Issue170(t *testing.T) { - tearDown() - Add(newGetConsumingOctetStreamService()) - // with content-type - httpRequest, _ := http.NewRequest("GET", "http://here.com/get", nil) - httpRequest.Header.Set("Content-Type", MIME_OCTET) - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - if 200 != httpWriter.Code { - t.Errorf("Expected 200, got %d", httpWriter.Code) - } - // without content-type - httpRequest, _ = http.NewRequest("GET", "http://here.com/get", nil) - httpWriter = httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - if 200 != httpWriter.Code { - t.Errorf("Expected 200, got %d", httpWriter.Code) - } -} - -func newPanicingService() *WebService { - ws := new(WebService).Path("") - ws.Route(ws.GET("/fire").To(doPanic)) - return ws -} - -func newGetOnlyService() *WebService { - ws := new(WebService).Path("") - ws.Route(ws.GET("/get").To(doPanic)) - return ws -} - -func newPostOnlyJsonOnlyService() *WebService { - ws := new(WebService).Path("") - ws.Consumes("application/json") - ws.Route(ws.POST("/post").To(doNothing)) - return ws -} - -func newGetOnlyJsonOnlyService() *WebService { - ws := new(WebService).Path("") - ws.Consumes("application/json") - ws.Route(ws.GET("/get").To(doNothing)) - return ws -} - -func newGetPlainTextOrJsonService() *WebService { - ws := new(WebService).Path("") - ws.Produces("text/plain", "application/json") - ws.Route(ws.GET("/get").To(doNothing)) - return ws -} - -func newGetConsumingOctetStreamService() *WebService { - ws := new(WebService).Path("") - ws.Consumes("application/octet-stream") - ws.Route(ws.GET("/get").To(doNothing)) - return ws -} - -func newSelectedRouteTestingService() *WebService { - ws := new(WebService).Path("") - ws.Route(ws.GET(pathGetFriends).To(selectedRouteChecker)) - return ws -} - -func selectedRouteChecker(req *Request, resp *Response) { - if req.SelectedRoutePath() != pathGetFriends { - resp.InternalServerError() - } -} - -func doPanic(req *Request, resp *Response) { - println("lightning...") - panic("fire") -} - -func doNothing(req *Request, resp *Response) { -} diff --git a/Godeps/_workspace/src/github.com/evanphx/json-patch/merge_test.go b/Godeps/_workspace/src/github.com/evanphx/json-patch/merge_test.go deleted file mode 100644 index e9e68ceef..000000000 --- a/Godeps/_workspace/src/github.com/evanphx/json-patch/merge_test.go +++ /dev/null @@ -1,322 +0,0 @@ -package jsonpatch - -import ( - "strings" - "testing" -) - -func mergePatch(doc, patch string) string { - out, err := MergePatch([]byte(doc), []byte(patch)) - - if err != nil { - panic(err) - } - - return string(out) -} - -func TestMergePatchReplaceKey(t *testing.T) { - doc := `{ "title": "hello" }` - pat := `{ "title": "goodbye" }` - - res := mergePatch(doc, pat) - - if !compareJSON(pat, res) { - t.Fatalf("Key was not replaced") - } -} - -func TestMergePatchIgnoresOtherValues(t *testing.T) { - doc := `{ "title": "hello", "age": 18 }` - pat := `{ "title": "goodbye" }` - - res := mergePatch(doc, pat) - - exp := `{ "title": "goodbye", "age": 18 }` - - if !compareJSON(exp, res) { - t.Fatalf("Key was not replaced") - } -} - -func TestMergePatchNilDoc(t *testing.T) { - doc := `{ "title": null }` - pat := `{ "title": {"foo": "bar"} }` - - res := mergePatch(doc, pat) - - exp := `{ "title": {"foo": "bar"} }` - - if !compareJSON(exp, res) { - t.Fatalf("Key was not replaced") - } -} - -func TestMergePatchRecursesIntoObjects(t *testing.T) { - doc := `{ "person": { "title": "hello", "age": 18 } }` - pat := `{ "person": { "title": "goodbye" } }` - - res := mergePatch(doc, pat) - - exp := `{ "person": { "title": "goodbye", "age": 18 } }` - - if !compareJSON(exp, res) { - t.Fatalf("Key was not replaced") - } -} - -type nonObjectCases struct { - doc, pat, res string -} - -func TestMergePatchReplacesNonObjectsWholesale(t *testing.T) { - a1 := `[1]` - a2 := `[2]` - o1 := `{ "a": 1 }` - o2 := `{ "a": 2 }` - o3 := `{ "a": 1, "b": 1 }` - o4 := `{ "a": 2, "b": 1 }` - - cases := []nonObjectCases{ - {a1, a2, a2}, - {o1, a2, a2}, - {a1, o1, o1}, - {o3, o2, o4}, - } - - for _, c := range cases { - act := mergePatch(c.doc, c.pat) - - if !compareJSON(c.res, act) { - t.Errorf("whole object replacement failed") - } - } -} - -func TestMergePatchReturnsErrorOnBadJSON(t *testing.T) { - _, err := MergePatch([]byte(`[[[[`), []byte(`1`)) - - if err == nil { - t.Errorf("Did not return an error for bad json: %s", err) - } - - _, err = MergePatch([]byte(`1`), []byte(`[[[[`)) - - if err == nil { - t.Errorf("Did not return an error for bad json: %s", err) - } -} - -var rfcTests = []struct { - target string - patch string - expected string -}{ - // test cases from https://tools.ietf.org/html/rfc7386#appendix-A - {target: `{"a":"b"}`, patch: `{"a":"c"}`, expected: `{"a":"c"}`}, - {target: `{"a":"b"}`, patch: `{"b":"c"}`, expected: `{"a":"b","b":"c"}`}, - {target: `{"a":"b"}`, patch: `{"a":null}`, expected: `{}`}, - {target: `{"a":"b","b":"c"}`, patch: `{"a":null}`, expected: `{"b":"c"}`}, - {target: `{"a":["b"]}`, patch: `{"a":"c"}`, expected: `{"a":"c"}`}, - {target: `{"a":"c"}`, patch: `{"a":["b"]}`, expected: `{"a":["b"]}`}, - {target: `{"a":{"b": "c"}}`, patch: `{"a": {"b": "d","c": null}}`, expected: `{"a":{"b":"d"}}`}, - {target: `{"a":[{"b":"c"}]}`, patch: `{"a":[1]}`, expected: `{"a":[1]}`}, - {target: `["a","b"]`, patch: `["c","d"]`, expected: `["c","d"]`}, - {target: `{"a":"b"}`, patch: `["c"]`, expected: `["c"]`}, - // {target: `{"a":"foo"}`, patch: `null`, expected: `null`}, - // {target: `{"a":"foo"}`, patch: `"bar"`, expected: `"bar"`}, - {target: `{"e":null}`, patch: `{"a":1}`, expected: `{"a":1,"e":null}`}, - {target: `[1,2]`, patch: `{"a":"b","c":null}`, expected: `{"a":"b"}`}, - {target: `{}`, patch: `{"a":{"bb":{"ccc":null}}}`, expected: `{"a":{"bb":{}}}`}, -} - -func TestMergePatchRFCCases(t *testing.T) { - for i, c := range rfcTests { - out := mergePatch(c.target, c.patch) - - if !compareJSON(out, c.expected) { - t.Errorf("case[%d], patch '%s' did not apply properly to '%s'. expected:\n'%s'\ngot:\n'%s'", i, c.patch, c.target, c.expected, out) - } - } -} - -var rfcFailTests = ` - {"a":"foo"} | null - {"a":"foo"} | "bar" -` - -func TestMergePatchFailRFCCases(t *testing.T) { - tests := strings.Split(rfcFailTests, "\n") - - for _, c := range tests { - if strings.TrimSpace(c) == "" { - continue - } - - parts := strings.SplitN(c, "|", 2) - - doc := strings.TrimSpace(parts[0]) - pat := strings.TrimSpace(parts[1]) - - out, err := MergePatch([]byte(doc), []byte(pat)) - - if err != errBadJSONPatch { - t.Errorf("error not returned properly: %s, %s", err, string(out)) - } - } - -} - -func TestMergeReplaceKey(t *testing.T) { - doc := `{ "title": "hello", "nested": {"one": 1, "two": 2} }` - pat := `{ "title": "goodbye", "nested": {"one": 2, "two": 2} }` - - exp := `{ "title": "goodbye", "nested": {"one": 2} }` - - res, err := CreateMergePatch([]byte(doc), []byte(pat)) - - if err != nil { - t.Errorf("Unexpected error: %s, %s", err, string(res)) - } - - if !compareJSON(exp, string(res)) { - t.Fatalf("Key was not replaced") - } -} - -func TestMergeGetArray(t *testing.T) { - doc := `{ "title": "hello", "array": ["one", "two"], "notmatch": [1, 2, 3] }` - pat := `{ "title": "hello", "array": ["one", "two", "three"], "notmatch": [1, 2, 3] }` - - exp := `{ "array": ["one", "two", "three"] }` - - res, err := CreateMergePatch([]byte(doc), []byte(pat)) - - if err != nil { - t.Errorf("Unexpected error: %s, %s", err, string(res)) - } - - if !compareJSON(exp, string(res)) { - t.Fatalf("Array was not added") - } -} - -func TestMergeGetObjArray(t *testing.T) { - doc := `{ "title": "hello", "array": [{"banana": true}, {"evil": false}], "notmatch": [{"one":1}, {"two":2}, {"three":3}] }` - pat := `{ "title": "hello", "array": [{"banana": false}, {"evil": true}], "notmatch": [{"one":1}, {"two":2}, {"three":3}] }` - - exp := `{ "array": [{"banana": false}, {"evil": true}] }` - - res, err := CreateMergePatch([]byte(doc), []byte(pat)) - - if err != nil { - t.Errorf("Unexpected error: %s, %s", err, string(res)) - } - - if !compareJSON(exp, string(res)) { - t.Fatalf("Object array was not added") - } -} - -func TestMergeDeleteKey(t *testing.T) { - doc := `{ "title": "hello", "nested": {"one": 1, "two": 2} }` - pat := `{ "title": "hello", "nested": {"one": 1} }` - - exp := `{"nested":{"two":null}}` - - res, err := CreateMergePatch([]byte(doc), []byte(pat)) - - if err != nil { - t.Errorf("Unexpected error: %s, %s", err, string(res)) - } - - // We cannot use "compareJSON", since Equals does not report a difference if the value is null - if exp != string(res) { - t.Fatalf("Key was not removed") - } -} - -func TestMergeEmptyArray(t *testing.T) { - doc := `{ "array": null }` - pat := `{ "array": [] }` - - exp := `{"array":[]}` - - res, err := CreateMergePatch([]byte(doc), []byte(pat)) - - if err != nil { - t.Errorf("Unexpected error: %s, %s", err, string(res)) - } - - // We cannot use "compareJSON", since Equals does not report a difference if the value is null - if exp != string(res) { - t.Fatalf("Key was not removed") - } -} - -func TestMergeObjArray(t *testing.T) { - doc := `{ "array": [ {"a": {"b": 2}}, {"a": {"b": 3}} ]}` - exp := `{}` - - res, err := CreateMergePatch([]byte(doc), []byte(doc)) - - if err != nil { - t.Errorf("Unexpected error: %s, %s", err, string(res)) - } - - // We cannot use "compareJSON", since Equals does not report a difference if the value is null - if exp != string(res) { - t.Fatalf("Array was not empty, was " + string(res)) - } -} - -func TestMergeComplexMatch(t *testing.T) { - doc := `{"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4], "nested": {"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4]} }` - empty := `{}` - res, err := CreateMergePatch([]byte(doc), []byte(doc)) - - if err != nil { - t.Errorf("Unexpected error: %s, %s", err, string(res)) - } - - // We cannot use "compareJSON", since Equals does not report a difference if the value is null - if empty != string(res) { - t.Fatalf("Did not get empty result, was:%s", string(res)) - } -} - -func TestMergeComplexAddAll(t *testing.T) { - doc := `{"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4], "nested": {"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4]} }` - empty := `{}` - res, err := CreateMergePatch([]byte(empty), []byte(doc)) - - if err != nil { - t.Errorf("Unexpected error: %s, %s", err, string(res)) - } - - if !compareJSON(doc, string(res)) { - t.Fatalf("Did not get everything as, it was:\n%s", string(res)) - } -} - -func TestMergeComplexRemoveAll(t *testing.T) { - doc := `{"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4], "nested": {"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4]} }` - exp := `{"a":null,"f":null,"hello":null,"i":null,"n":null,"nested":null,"pi":null,"t":null}` - empty := `{}` - res, err := CreateMergePatch([]byte(doc), []byte(empty)) - - if err != nil { - t.Errorf("Unexpected error: %s, %s", err, string(res)) - } - - if exp != string(res) { - t.Fatalf("Did not get result, was:%s", string(res)) - } - - // FIXME: Crashes if using compareJSON like this: - /* - if !compareJSON(doc, string(res)) { - t.Fatalf("Did not get everything as, it was:\n%s", string(res)) - } - */ -} diff --git a/Godeps/_workspace/src/github.com/evanphx/json-patch/patch_test.go b/Godeps/_workspace/src/github.com/evanphx/json-patch/patch_test.go deleted file mode 100644 index 493c4f165..000000000 --- a/Godeps/_workspace/src/github.com/evanphx/json-patch/patch_test.go +++ /dev/null @@ -1,234 +0,0 @@ -package jsonpatch - -import ( - "bytes" - "encoding/json" - "fmt" - "reflect" - "testing" -) - -func reformatJSON(j string) string { - buf := new(bytes.Buffer) - - json.Indent(buf, []byte(j), "", " ") - - return buf.String() -} - -func compareJSON(a, b string) bool { - // return Equal([]byte(a), []byte(b)) - - var obj_a, obj_b map[string]interface{} - json.Unmarshal([]byte(a), &obj_a) - json.Unmarshal([]byte(b), &obj_b) - - // fmt.Printf("Comparing %#v\nagainst %#v\n", obj_a, obj_b) - return reflect.DeepEqual(obj_a, obj_b) -} - -func applyPatch(doc, patch string) (string, error) { - obj, err := DecodePatch([]byte(patch)) - - if err != nil { - panic(err) - } - - out, err := obj.Apply([]byte(doc)) - - if err != nil { - return "", err - } - - return string(out), nil -} - -type Case struct { - doc, patch, result string -} - -var Cases = []Case{ - { - `{ "foo": "bar"}`, - `[ - { "op": "add", "path": "/baz", "value": "qux" } - ]`, - `{ - "baz": "qux", - "foo": "bar" - }`, - }, - { - `{ "foo": [ "bar", "baz" ] }`, - `[ - { "op": "add", "path": "/foo/1", "value": "qux" } - ]`, - `{ "foo": [ "bar", "qux", "baz" ] }`, - }, - { - `{ "baz": "qux", "foo": "bar" }`, - `[ { "op": "remove", "path": "/baz" } ]`, - `{ "foo": "bar" }`, - }, - { - `{ "foo": [ "bar", "qux", "baz" ] }`, - `[ { "op": "remove", "path": "/foo/1" } ]`, - `{ "foo": [ "bar", "baz" ] }`, - }, - { - `{ "baz": "qux", "foo": "bar" }`, - `[ { "op": "replace", "path": "/baz", "value": "boo" } ]`, - `{ "baz": "boo", "foo": "bar" }`, - }, - { - `{ - "foo": { - "bar": "baz", - "waldo": "fred" - }, - "qux": { - "corge": "grault" - } - }`, - `[ { "op": "move", "from": "/foo/waldo", "path": "/qux/thud" } ]`, - `{ - "foo": { - "bar": "baz" - }, - "qux": { - "corge": "grault", - "thud": "fred" - } - }`, - }, - { - `{ "foo": [ "all", "grass", "cows", "eat" ] }`, - `[ { "op": "move", "from": "/foo/1", "path": "/foo/3" } ]`, - `{ "foo": [ "all", "cows", "eat", "grass" ] }`, - }, - { - `{ "foo": "bar" }`, - `[ { "op": "add", "path": "/child", "value": { "grandchild": { } } } ]`, - `{ "foo": "bar", "child": { "grandchild": { } } }`, - }, - { - `{ "foo": ["bar"] }`, - `[ { "op": "add", "path": "/foo/-", "value": ["abc", "def"] } ]`, - `{ "foo": ["bar", ["abc", "def"]] }`, - }, - { - `{ "foo": "bar", "qux": { "baz": 1, "bar": null } }`, - `[ { "op": "remove", "path": "/qux/bar" } ]`, - `{ "foo": "bar", "qux": { "baz": 1 } }`, - }, -} - -type BadCase struct { - doc, patch string -} - -var MutationTestCases = []BadCase{ - { - `{ "foo": "bar", "qux": { "baz": 1, "bar": null } }`, - `[ { "op": "remove", "path": "/qux/bar" } ]`, - }, -} - -var BadCases = []BadCase{ - { - `{ "foo": "bar" }`, - `[ { "op": "add", "path": "/baz/bat", "value": "qux" } ]`, - }, -} - -func TestAllCases(t *testing.T) { - for _, c := range Cases { - out, err := applyPatch(c.doc, c.patch) - - if err != nil { - t.Errorf("Unable to apply patch: %s", err) - } - - if !compareJSON(out, c.result) { - t.Errorf("Patch did not apply. Expected:\n%s\n\nActual:\n%s", - reformatJSON(c.result), reformatJSON(out)) - } - } - - for _, c := range MutationTestCases { - out, err := applyPatch(c.doc, c.patch) - - if err != nil { - t.Errorf("Unable to apply patch: %s", err) - } - - if compareJSON(out, c.doc) { - t.Errorf("Patch did not apply. Original:\n%s\n\nPatched:\n%s", - reformatJSON(c.doc), reformatJSON(out)) - } - } - - for _, c := range BadCases { - _, err := applyPatch(c.doc, c.patch) - - if err == nil { - t.Errorf("Patch should have failed to apply but it did not") - } - } -} - -type TestCase struct { - doc, patch string - result bool - failedPath string -} - -var TestCases = []TestCase{ - { - `{ - "baz": "qux", - "foo": [ "a", 2, "c" ] - }`, - `[ - { "op": "test", "path": "/baz", "value": "qux" }, - { "op": "test", "path": "/foo/1", "value": 2 } - ]`, - true, - "", - }, - { - `{ "baz": "qux" }`, - `[ { "op": "test", "path": "/baz", "value": "bar" } ]`, - false, - "/baz", - }, - { - `{ - "baz": "qux", - "foo": ["a", 2, "c"] - }`, - `[ - { "op": "test", "path": "/baz", "value": "qux" }, - { "op": "test", "path": "/foo/1", "value": "c" } - ]`, - false, - "/foo/1", - }, -} - -func TestAllTest(t *testing.T) { - for _, c := range TestCases { - _, err := applyPatch(c.doc, c.patch) - - if c.result && err != nil { - t.Errorf("Testing failed when it should have passed: %s", err) - } else if !c.result && err == nil { - t.Errorf("Testing passed when it should have faild: %s", err) - } else if !c.result { - expected := fmt.Sprintf("Testing value %s failed", c.failedPath) - if err.Error() != expected { - t.Errorf("Testing failed as expected but invalid message: expected [%s], got [%s]", expected, err) - } - } - } -} diff --git a/Godeps/_workspace/src/github.com/fatih/structs/field.go b/Godeps/_workspace/src/github.com/fatih/structs/field.go index 1178b0ca6..566f5497e 100644 --- a/Godeps/_workspace/src/github.com/fatih/structs/field.go +++ b/Godeps/_workspace/src/github.com/fatih/structs/field.go @@ -41,7 +41,7 @@ func (f *Field) IsExported() bool { return f.field.PkgPath == "" } -// IsZero returns true if the given field is not initalized (has a zero value). +// IsZero returns true if the given field is not initialized (has a zero value). // It panics if the field is not exported. func (f *Field) IsZero() bool { zero := reflect.Zero(f.value.Type()).Interface() @@ -60,8 +60,8 @@ func (f *Field) Kind() reflect.Kind { return f.value.Kind() } -// Set sets the field to given value v. It retuns an error if the field is not -// settable (not addresable or not exported) or if the given value's type +// Set sets the field to given value v. It returns an error if the field is not +// settable (not addressable or not exported) or if the given value's type // doesn't match the fields type. func (f *Field) Set(val interface{}) error { // we can't set unexported fields, so be sure this field is exported diff --git a/Godeps/_workspace/src/github.com/fatih/structs/field_test.go b/Godeps/_workspace/src/github.com/fatih/structs/field_test.go deleted file mode 100644 index b77e95120..000000000 --- a/Godeps/_workspace/src/github.com/fatih/structs/field_test.go +++ /dev/null @@ -1,383 +0,0 @@ -package structs - -import ( - "reflect" - "testing" -) - -// A test struct that defines all cases -type Foo struct { - A string - B int `structs:"y"` - C bool `json:"c"` - d string // not exported - E *Baz - x string `xml:"x"` // not exported, with tag - Y []string - Z map[string]interface{} - *Bar // embedded -} - -type Baz struct { - A string - B int -} - -type Bar struct { - E string - F int - g []string -} - -func newStruct() *Struct { - b := &Bar{ - E: "example", - F: 2, - g: []string{"zeynep", "fatih"}, - } - - // B and x is not initialized for testing - f := &Foo{ - A: "gopher", - C: true, - d: "small", - E: nil, - Y: []string{"example"}, - Z: nil, - } - f.Bar = b - - return New(f) -} - -func TestField_Set(t *testing.T) { - s := newStruct() - - f := s.Field("A") - err := f.Set("fatih") - if err != nil { - t.Error(err) - } - - if f.Value().(string) != "fatih" { - t.Errorf("Setted value is wrong: %s want: %s", f.Value().(string), "fatih") - } - - f = s.Field("Y") - err = f.Set([]string{"override", "with", "this"}) - if err != nil { - t.Error(err) - } - - sliceLen := len(f.Value().([]string)) - if sliceLen != 3 { - t.Errorf("Setted values slice length is wrong: %d, want: %d", sliceLen, 3) - } - - f = s.Field("C") - err = f.Set(false) - if err != nil { - t.Error(err) - } - - if f.Value().(bool) { - t.Errorf("Setted value is wrong: %t want: %t", f.Value().(bool), false) - } - - // let's pass a different type - f = s.Field("A") - err = f.Set(123) // Field A is of type string, but we are going to pass an integer - if err == nil { - t.Error("Setting a field's value with a different type than the field's type should return an error") - } - - // old value should be still there :) - if f.Value().(string) != "fatih" { - t.Errorf("Setted value is wrong: %s want: %s", f.Value().(string), "fatih") - } - - // let's access an unexported field, which should give an error - f = s.Field("d") - err = f.Set("large") - if err != errNotExported { - t.Error(err) - } - - // let's set a pointer to struct - b := &Bar{ - E: "gopher", - F: 2, - } - - f = s.Field("Bar") - err = f.Set(b) - if err != nil { - t.Error(err) - } - - baz := &Baz{ - A: "helloWorld", - B: 42, - } - - f = s.Field("E") - err = f.Set(baz) - if err != nil { - t.Error(err) - } - - ba := s.Field("E").Value().(*Baz) - - if ba.A != "helloWorld" { - t.Errorf("could not set baz. Got: %s Want: helloWorld", ba.A) - } -} - -func TestField_Zero(t *testing.T) { - s := newStruct() - - f := s.Field("A") - err := f.Zero() - if err != nil { - t.Error(err) - } - - if f.Value().(string) != "" { - t.Errorf("Zeroed value is wrong: %s want: %s", f.Value().(string), "") - } - - f = s.Field("Y") - err = f.Zero() - if err != nil { - t.Error(err) - } - - sliceLen := len(f.Value().([]string)) - if sliceLen != 0 { - t.Errorf("Zeroed values slice length is wrong: %d, want: %d", sliceLen, 0) - } - - f = s.Field("C") - err = f.Zero() - if err != nil { - t.Error(err) - } - - if f.Value().(bool) { - t.Errorf("Zeroed value is wrong: %t want: %t", f.Value().(bool), false) - } - - // let's access an unexported field, which should give an error - f = s.Field("d") - err = f.Zero() - if err != errNotExported { - t.Error(err) - } - - f = s.Field("Bar") - err = f.Zero() - if err != nil { - t.Error(err) - } - - f = s.Field("E") - err = f.Zero() - if err != nil { - t.Error(err) - } - - v := s.Field("E").value - if !v.IsNil() { - t.Errorf("could not set baz. Got: %s Want: ", v.Interface()) - } -} - -func TestField(t *testing.T) { - s := newStruct() - - defer func() { - err := recover() - if err == nil { - t.Error("Retrieveing a non existing field from the struct should panic") - } - }() - - _ = s.Field("no-field") -} - -func TestField_Kind(t *testing.T) { - s := newStruct() - - f := s.Field("A") - if f.Kind() != reflect.String { - t.Errorf("Field A has wrong kind: %s want: %s", f.Kind(), reflect.String) - } - - f = s.Field("B") - if f.Kind() != reflect.Int { - t.Errorf("Field B has wrong kind: %s want: %s", f.Kind(), reflect.Int) - } - - // unexported - f = s.Field("d") - if f.Kind() != reflect.String { - t.Errorf("Field d has wrong kind: %s want: %s", f.Kind(), reflect.String) - } -} - -func TestField_Tag(t *testing.T) { - s := newStruct() - - v := s.Field("B").Tag("json") - if v != "" { - t.Errorf("Field's tag value of a non existing tag should return empty, got: %s", v) - } - - v = s.Field("C").Tag("json") - if v != "c" { - t.Errorf("Field's tag value of the existing field C should return 'c', got: %s", v) - } - - v = s.Field("d").Tag("json") - if v != "" { - t.Errorf("Field's tag value of a non exported field should return empty, got: %s", v) - } - - v = s.Field("x").Tag("xml") - if v != "x" { - t.Errorf("Field's tag value of a non exported field with a tag should return 'x', got: %s", v) - } - - v = s.Field("A").Tag("json") - if v != "" { - t.Errorf("Field's tag value of a existing field without a tag should return empty, got: %s", v) - } -} - -func TestField_Value(t *testing.T) { - s := newStruct() - - v := s.Field("A").Value() - val, ok := v.(string) - if !ok { - t.Errorf("Field's value of a A should be string") - } - - if val != "gopher" { - t.Errorf("Field's value of a existing tag should return 'gopher', got: %s", val) - } - - defer func() { - err := recover() - if err == nil { - t.Error("Value of a non exported field from the field should panic") - } - }() - - // should panic - _ = s.Field("d").Value() -} - -func TestField_IsEmbedded(t *testing.T) { - s := newStruct() - - if !s.Field("Bar").IsEmbedded() { - t.Errorf("Fields 'Bar' field is an embedded field") - } - - if s.Field("d").IsEmbedded() { - t.Errorf("Fields 'd' field is not an embedded field") - } -} - -func TestField_IsExported(t *testing.T) { - s := newStruct() - - if !s.Field("Bar").IsExported() { - t.Errorf("Fields 'Bar' field is an exported field") - } - - if !s.Field("A").IsExported() { - t.Errorf("Fields 'A' field is an exported field") - } - - if s.Field("d").IsExported() { - t.Errorf("Fields 'd' field is not an exported field") - } -} - -func TestField_IsZero(t *testing.T) { - s := newStruct() - - if s.Field("A").IsZero() { - t.Errorf("Fields 'A' field is an initialized field") - } - - if !s.Field("B").IsZero() { - t.Errorf("Fields 'B' field is not an initialized field") - } -} - -func TestField_Name(t *testing.T) { - s := newStruct() - - if s.Field("A").Name() != "A" { - t.Errorf("Fields 'A' field should have the name 'A'") - } -} - -func TestField_Field(t *testing.T) { - s := newStruct() - - e := s.Field("Bar").Field("E") - - val, ok := e.Value().(string) - if !ok { - t.Error("The value of the field 'e' inside 'Bar' struct should be string") - } - - if val != "example" { - t.Errorf("The value of 'e' should be 'example, got: %s", val) - } - - defer func() { - err := recover() - if err == nil { - t.Error("Field of a non existing nested struct should panic") - } - }() - - _ = s.Field("Bar").Field("e") -} - -func TestField_Fields(t *testing.T) { - s := newStruct() - fields := s.Field("Bar").Fields() - - if len(fields) != 3 { - t.Errorf("We expect 3 fields in embedded struct, was: %d", len(fields)) - } -} - -func TestField_FieldOk(t *testing.T) { - s := newStruct() - - b, ok := s.FieldOk("Bar") - if !ok { - t.Error("The field 'Bar' should exists.") - } - - e, ok := b.FieldOk("E") - if !ok { - t.Error("The field 'E' should exists.") - } - - val, ok := e.Value().(string) - if !ok { - t.Error("The value of the field 'e' inside 'Bar' struct should be string") - } - - if val != "example" { - t.Errorf("The value of 'e' should be 'example, got: %s", val) - } -} diff --git a/Godeps/_workspace/src/github.com/fatih/structs/structs.go b/Godeps/_workspace/src/github.com/fatih/structs/structs.go index a0b77e67d..7ac88369f 100644 --- a/Godeps/_workspace/src/github.com/fatih/structs/structs.go +++ b/Godeps/_workspace/src/github.com/fatih/structs/structs.go @@ -1,7 +1,11 @@ // Package structs contains various utilities functions to work with structs. package structs -import "reflect" +import ( + "fmt" + + "reflect" +) var ( // DefaultTagName is the default tag name for struct fields which provides @@ -42,6 +46,12 @@ func New(s interface{}) *Struct { // // Field is ignored by this package. // Field bool `structs:"-"` // +// A tag value with the content of "string" uses the stringer to get the value. Example: +// +// // The value will be output of Animal's String() func. +// // Map will panic if Animal does not implement String(). +// Field *Animal `structs:"field,string"` +// // A tag value with the option of "omitnested" stops iterating further if the type // is a struct. Example: // @@ -94,11 +104,24 @@ func (s *Struct) Map() map[string]interface{} { // map[string]interface{} too n := New(val.Interface()) n.TagName = s.TagName - finalVal = n.Map() + m := n.Map() + if len(m) == 0 { + finalVal = val.Interface() + } else { + finalVal = m + } } else { finalVal = val.Interface() } + if tagOpts.Has("string") { + s, ok := val.Interface().(fmt.Stringer) + if ok { + out[name] = s.String() + } + continue + } + out[name] = finalVal } @@ -148,6 +171,14 @@ func (s *Struct) Values() []interface{} { } } + if tagOpts.Has("string") { + s, ok := val.Interface().(fmt.Stringer) + if ok { + t = append(t, s.String()) + } + continue + } + if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") { // look out for embedded structs, and convert them to a // []interface{} to be added to the final values slice diff --git a/Godeps/_workspace/src/github.com/fatih/structs/structs_example_test.go b/Godeps/_workspace/src/github.com/fatih/structs/structs_example_test.go deleted file mode 100644 index 32bb82937..000000000 --- a/Godeps/_workspace/src/github.com/fatih/structs/structs_example_test.go +++ /dev/null @@ -1,351 +0,0 @@ -package structs - -import ( - "fmt" - "time" -) - -func ExampleNew() { - type Server struct { - Name string - ID int32 - Enabled bool - } - - server := &Server{ - Name: "Arslan", - ID: 123456, - Enabled: true, - } - - s := New(server) - - fmt.Printf("Name : %v\n", s.Name()) - fmt.Printf("Values : %v\n", s.Values()) - fmt.Printf("Value of ID : %v\n", s.Field("ID").Value()) - // Output: - // Name : Server - // Values : [Arslan 123456 true] - // Value of ID : 123456 - -} - -func ExampleMap() { - type Server struct { - Name string - ID int32 - Enabled bool - } - - s := &Server{ - Name: "Arslan", - ID: 123456, - Enabled: true, - } - - m := Map(s) - - fmt.Printf("%#v\n", m["Name"]) - fmt.Printf("%#v\n", m["ID"]) - fmt.Printf("%#v\n", m["Enabled"]) - // Output: - // "Arslan" - // 123456 - // true - -} - -func ExampleMap_tags() { - // Custom tags can change the map keys instead of using the fields name - type Server struct { - Name string `structs:"server_name"` - ID int32 `structs:"server_id"` - Enabled bool `structs:"enabled"` - } - - s := &Server{ - Name: "Zeynep", - ID: 789012, - } - - m := Map(s) - - // access them by the custom tags defined above - fmt.Printf("%#v\n", m["server_name"]) - fmt.Printf("%#v\n", m["server_id"]) - fmt.Printf("%#v\n", m["enabled"]) - // Output: - // "Zeynep" - // 789012 - // false - -} - -func ExampleMap_nested() { - // By default field with struct types are processed too. We can stop - // processing them via "omitnested" tag option. - type Server struct { - Name string `structs:"server_name"` - ID int32 `structs:"server_id"` - Time time.Time `structs:"time,omitnested"` // do not convert to map[string]interface{} - } - - const shortForm = "2006-Jan-02" - t, _ := time.Parse("2006-Jan-02", "2013-Feb-03") - - s := &Server{ - Name: "Zeynep", - ID: 789012, - Time: t, - } - - m := Map(s) - - // access them by the custom tags defined above - fmt.Printf("%v\n", m["server_name"]) - fmt.Printf("%v\n", m["server_id"]) - fmt.Printf("%v\n", m["time"].(time.Time)) - // Output: - // Zeynep - // 789012 - // 2013-02-03 00:00:00 +0000 UTC -} - -func ExampleMap_omitEmpty() { - // By default field with struct types of zero values are processed too. We - // can stop processing them via "omitempty" tag option. - type Server struct { - Name string `structs:",omitempty"` - ID int32 `structs:"server_id,omitempty"` - Location string - } - - // Only add location - s := &Server{ - Location: "Tokyo", - } - - m := Map(s) - - // map contains only the Location field - fmt.Printf("%v\n", m) - // Output: - // map[Location:Tokyo] -} - -func ExampleValues() { - type Server struct { - Name string - ID int32 - Enabled bool - } - - s := &Server{ - Name: "Fatih", - ID: 135790, - Enabled: false, - } - - m := Values(s) - - fmt.Printf("Values: %+v\n", m) - // Output: - // Values: [Fatih 135790 false] -} - -func ExampleValues_omitEmpty() { - // By default field with struct types of zero values are processed too. We - // can stop processing them via "omitempty" tag option. - type Server struct { - Name string `structs:",omitempty"` - ID int32 `structs:"server_id,omitempty"` - Location string - } - - // Only add location - s := &Server{ - Location: "Ankara", - } - - m := Values(s) - - // values contains only the Location field - fmt.Printf("Values: %+v\n", m) - // Output: - // Values: [Ankara] -} - -func ExampleValues_tags() { - type Location struct { - City string - Country string - } - - type Server struct { - Name string - ID int32 - Enabled bool - Location Location `structs:"-"` // values from location are not included anymore - } - - s := &Server{ - Name: "Fatih", - ID: 135790, - Enabled: false, - Location: Location{City: "Ankara", Country: "Turkey"}, - } - - // Let get all values from the struct s. Note that we don't include values - // from the Location field - m := Values(s) - - fmt.Printf("Values: %+v\n", m) - // Output: - // Values: [Fatih 135790 false] -} - -func ExampleFields() { - type Access struct { - Name string - LastAccessed time.Time - Number int - } - - s := &Access{ - Name: "Fatih", - LastAccessed: time.Now(), - Number: 1234567, - } - - fields := Fields(s) - - for i, field := range fields { - fmt.Printf("[%d] %+v\n", i, field.Name()) - } - - // Output: - // [0] Name - // [1] LastAccessed - // [2] Number -} - -func ExampleFields_nested() { - type Person struct { - Name string - Number int - } - - type Access struct { - Person Person - HasPermission bool - LastAccessed time.Time - } - - s := &Access{ - Person: Person{Name: "fatih", Number: 1234567}, - LastAccessed: time.Now(), - HasPermission: true, - } - - // Let's get all fields from the struct s. - fields := Fields(s) - - for _, field := range fields { - if field.Name() == "Person" { - fmt.Printf("Access.Person.Name: %+v\n", field.Field("Name").Value()) - } - } - - // Output: - // Access.Person.Name: fatih -} - -func ExampleField() { - type Person struct { - Name string - Number int - } - - type Access struct { - Person Person - HasPermission bool - LastAccessed time.Time - } - - access := &Access{ - Person: Person{Name: "fatih", Number: 1234567}, - LastAccessed: time.Now(), - HasPermission: true, - } - - // Create a new Struct type - s := New(access) - - // Get the Field type for "Person" field - p := s.Field("Person") - - // Get the underlying "Name field" and print the value of it - name := p.Field("Name") - - fmt.Printf("Value of Person.Access.Name: %+v\n", name.Value()) - - // Output: - // Value of Person.Access.Name: fatih - -} - -func ExampleIsZero() { - type Server struct { - Name string - ID int32 - Enabled bool - } - - // Nothing is initalized - a := &Server{} - isZeroA := IsZero(a) - - // Name and Enabled is initialized, but not ID - b := &Server{ - Name: "Golang", - Enabled: true, - } - isZeroB := IsZero(b) - - fmt.Printf("%#v\n", isZeroA) - fmt.Printf("%#v\n", isZeroB) - // Output: - // true - // false -} - -func ExampleHasZero() { - // Let's define an Access struct. Note that the "Enabled" field is not - // going to be checked because we added the "structs" tag to the field. - type Access struct { - Name string - LastAccessed time.Time - Number int - Enabled bool `structs:"-"` - } - - // Name and Number is not initialized. - a := &Access{ - LastAccessed: time.Now(), - } - hasZeroA := HasZero(a) - - // Name and Number is initialized. - b := &Access{ - Name: "Fatih", - LastAccessed: time.Now(), - Number: 12345, - } - hasZeroB := HasZero(b) - - fmt.Printf("%#v\n", hasZeroA) - fmt.Printf("%#v\n", hasZeroB) - // Output: - // true - // false -} diff --git a/Godeps/_workspace/src/github.com/fatih/structs/structs_test.go b/Godeps/_workspace/src/github.com/fatih/structs/structs_test.go deleted file mode 100644 index 14e3de72f..000000000 --- a/Godeps/_workspace/src/github.com/fatih/structs/structs_test.go +++ /dev/null @@ -1,898 +0,0 @@ -package structs - -import ( - "fmt" - "reflect" - "testing" - "time" -) - -func TestMapNonStruct(t *testing.T) { - foo := []string{"foo"} - - defer func() { - err := recover() - if err == nil { - t.Error("Passing a non struct into Map should panic") - } - }() - - // this should panic. We are going to recover and and test it - _ = Map(foo) -} - -func TestStructIndexes(t *testing.T) { - type C struct { - something int - Props map[string]interface{} - } - - defer func() { - err := recover() - if err != nil { - fmt.Printf("err %+v\n", err) - t.Error("Using mixed indexes should not panic") - } - }() - - // They should not panic - _ = Map(&C{}) - _ = Fields(&C{}) - _ = Values(&C{}) - _ = IsZero(&C{}) - _ = HasZero(&C{}) -} - -func TestMap(t *testing.T) { - var T = struct { - A string - B int - C bool - }{ - A: "a-value", - B: 2, - C: true, - } - - a := Map(T) - - if typ := reflect.TypeOf(a).Kind(); typ != reflect.Map { - t.Errorf("Map should return a map type, got: %v", typ) - } - - // we have three fields - if len(a) != 3 { - t.Errorf("Map should return a map of len 3, got: %d", len(a)) - } - - inMap := func(val interface{}) bool { - for _, v := range a { - if reflect.DeepEqual(v, val) { - return true - } - } - - return false - } - - for _, val := range []interface{}{"a-value", 2, true} { - if !inMap(val) { - t.Errorf("Map should have the value %v", val) - } - } - -} - -func TestMap_Tag(t *testing.T) { - var T = struct { - A string `structs:"x"` - B int `structs:"y"` - C bool `structs:"z"` - }{ - A: "a-value", - B: 2, - C: true, - } - - a := Map(T) - - inMap := func(key interface{}) bool { - for k := range a { - if reflect.DeepEqual(k, key) { - return true - } - } - return false - } - - for _, key := range []string{"x", "y", "z"} { - if !inMap(key) { - t.Errorf("Map should have the key %v", key) - } - } - -} - -func TestMap_CustomTag(t *testing.T) { - var T = struct { - A string `json:"x"` - B int `json:"y"` - C bool `json:"z"` - D struct { - E string `json:"jkl"` - } `json:"nested"` - }{ - A: "a-value", - B: 2, - C: true, - } - T.D.E = "e-value" - - s := New(T) - s.TagName = "json" - - a := s.Map() - - inMap := func(key interface{}) bool { - for k := range a { - if reflect.DeepEqual(k, key) { - return true - } - } - return false - } - - for _, key := range []string{"x", "y", "z"} { - if !inMap(key) { - t.Errorf("Map should have the key %v", key) - } - } - - nested, ok := a["nested"].(map[string]interface{}) - if !ok { - t.Fatalf("Map should contain the D field that is tagged as 'nested'") - } - - e, ok := nested["jkl"].(string) - if !ok { - t.Fatalf("Map should contain the D.E field that is tagged as 'jkl'") - } - - if e != "e-value" { - t.Errorf("D.E field should be equal to 'e-value', got: '%v'", e) - } - -} - -func TestMap_MultipleCustomTag(t *testing.T) { - var A = struct { - X string `aa:"ax"` - }{"a_value"} - - aStruct := New(A) - aStruct.TagName = "aa" - - var B = struct { - X string `bb:"bx"` - }{"b_value"} - - bStruct := New(B) - bStruct.TagName = "bb" - - a, b := aStruct.Map(), bStruct.Map() - if !reflect.DeepEqual(a, map[string]interface{}{"ax": "a_value"}) { - t.Error("Map should have field ax with value a_value") - } - - if !reflect.DeepEqual(b, map[string]interface{}{"bx": "b_value"}) { - t.Error("Map should have field bx with value b_value") - } -} - -func TestMap_OmitEmpty(t *testing.T) { - type A struct { - Name string - Value string `structs:",omitempty"` - Time time.Time `structs:",omitempty"` - } - a := A{} - - m := Map(a) - - _, ok := m["Value"].(map[string]interface{}) - if ok { - t.Error("Map should not contain the Value field that is tagged as omitempty") - } - - _, ok = m["Time"].(map[string]interface{}) - if ok { - t.Error("Map should not contain the Time field that is tagged as omitempty") - } -} - -func TestMap_OmitNested(t *testing.T) { - type A struct { - Name string - Value string - Time time.Time `structs:",omitnested"` - } - a := A{Time: time.Now()} - - type B struct { - Desc string - A A - } - b := &B{A: a} - - m := Map(b) - - in, ok := m["A"].(map[string]interface{}) - if !ok { - t.Error("Map nested structs is not available in the map") - } - - // should not happen - if _, ok := in["Time"].(map[string]interface{}); ok { - t.Error("Map nested struct should omit recursiving parsing of Time") - } - - if _, ok := in["Time"].(time.Time); !ok { - t.Error("Map nested struct should stop parsing of Time at is current value") - } -} - -func TestMap_Nested(t *testing.T) { - type A struct { - Name string - } - a := &A{Name: "example"} - - type B struct { - A *A - } - b := &B{A: a} - - m := Map(b) - - if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map { - t.Errorf("Map should return a map type, got: %v", typ) - } - - in, ok := m["A"].(map[string]interface{}) - if !ok { - t.Error("Map nested structs is not available in the map") - } - - if name := in["Name"].(string); name != "example" { - t.Errorf("Map nested struct's name field should give example, got: %s", name) - } -} - -func TestMap_Anonymous(t *testing.T) { - type A struct { - Name string - } - a := &A{Name: "example"} - - type B struct { - *A - } - b := &B{} - b.A = a - - m := Map(b) - - if typ := reflect.TypeOf(m).Kind(); typ != reflect.Map { - t.Errorf("Map should return a map type, got: %v", typ) - } - - in, ok := m["A"].(map[string]interface{}) - if !ok { - t.Error("Embedded structs is not available in the map") - } - - if name := in["Name"].(string); name != "example" { - t.Errorf("Embedded A struct's Name field should give example, got: %s", name) - } -} - -func TestStruct(t *testing.T) { - var T = struct{}{} - - if !IsStruct(T) { - t.Errorf("T should be a struct, got: %T", T) - } - - if !IsStruct(&T) { - t.Errorf("T should be a struct, got: %T", T) - } - -} - -func TestValues(t *testing.T) { - var T = struct { - A string - B int - C bool - }{ - A: "a-value", - B: 2, - C: true, - } - - s := Values(T) - - if typ := reflect.TypeOf(s).Kind(); typ != reflect.Slice { - t.Errorf("Values should return a slice type, got: %v", typ) - } - - inSlice := func(val interface{}) bool { - for _, v := range s { - if reflect.DeepEqual(v, val) { - return true - } - } - return false - } - - for _, val := range []interface{}{"a-value", 2, true} { - if !inSlice(val) { - t.Errorf("Values should have the value %v", val) - } - } -} - -func TestValues_OmitEmpty(t *testing.T) { - type A struct { - Name string - Value int `structs:",omitempty"` - } - - a := A{Name: "example"} - s := Values(a) - - if len(s) != 1 { - t.Errorf("Values of omitted empty fields should be not counted") - } - - if s[0].(string) != "example" { - t.Errorf("Values of omitted empty fields should left the value example") - } -} - -func TestValues_OmitNested(t *testing.T) { - type A struct { - Name string - Value int - } - - a := A{ - Name: "example", - Value: 123, - } - - type B struct { - A A `structs:",omitnested"` - C int - } - b := &B{A: a, C: 123} - - s := Values(b) - - if len(s) != 2 { - t.Errorf("Values of omitted nested struct should be not counted") - } - - inSlice := func(val interface{}) bool { - for _, v := range s { - if reflect.DeepEqual(v, val) { - return true - } - } - return false - } - - for _, val := range []interface{}{123, a} { - if !inSlice(val) { - t.Errorf("Values should have the value %v", val) - } - } -} - -func TestValues_Nested(t *testing.T) { - type A struct { - Name string - } - a := A{Name: "example"} - - type B struct { - A A - C int - } - b := &B{A: a, C: 123} - - s := Values(b) - - inSlice := func(val interface{}) bool { - for _, v := range s { - if reflect.DeepEqual(v, val) { - return true - } - } - return false - } - - for _, val := range []interface{}{"example", 123} { - if !inSlice(val) { - t.Errorf("Values should have the value %v", val) - } - } -} - -func TestValues_Anonymous(t *testing.T) { - type A struct { - Name string - } - a := A{Name: "example"} - - type B struct { - A - C int - } - b := &B{C: 123} - b.A = a - - s := Values(b) - - inSlice := func(val interface{}) bool { - for _, v := range s { - if reflect.DeepEqual(v, val) { - return true - } - } - return false - } - - for _, val := range []interface{}{"example", 123} { - if !inSlice(val) { - t.Errorf("Values should have the value %v", val) - } - } -} - -func TestNames(t *testing.T) { - var T = struct { - A string - B int - C bool - }{ - A: "a-value", - B: 2, - C: true, - } - - s := Names(T) - - if len(s) != 3 { - t.Errorf("Names should return a slice of len 3, got: %d", len(s)) - } - - inSlice := func(val string) bool { - for _, v := range s { - if reflect.DeepEqual(v, val) { - return true - } - } - return false - } - - for _, val := range []string{"A", "B", "C"} { - if !inSlice(val) { - t.Errorf("Names should have the value %v", val) - } - } -} - -func TestFields(t *testing.T) { - var T = struct { - A string - B int - C bool - }{ - A: "a-value", - B: 2, - C: true, - } - - s := Fields(T) - - if len(s) != 3 { - t.Errorf("Fields should return a slice of len 3, got: %d", len(s)) - } - - inSlice := func(val string) bool { - for _, v := range s { - if reflect.DeepEqual(v.Name(), val) { - return true - } - } - return false - } - - for _, val := range []string{"A", "B", "C"} { - if !inSlice(val) { - t.Errorf("Fields should have the value %v", val) - } - } -} - -func TestFields_OmitNested(t *testing.T) { - type A struct { - Name string - Enabled bool - } - a := A{Name: "example"} - - type B struct { - A A - C int - Value string `structs:"-"` - Number int - } - b := &B{A: a, C: 123} - - s := Fields(b) - - if len(s) != 3 { - t.Errorf("Fields should omit nested struct. Expecting 2 got: %d", len(s)) - } - - inSlice := func(val interface{}) bool { - for _, v := range s { - if reflect.DeepEqual(v.Name(), val) { - return true - } - } - return false - } - - for _, val := range []interface{}{"A", "C"} { - if !inSlice(val) { - t.Errorf("Fields should have the value %v", val) - } - } -} - -func TestFields_Anonymous(t *testing.T) { - type A struct { - Name string - } - a := A{Name: "example"} - - type B struct { - A - C int - } - b := &B{C: 123} - b.A = a - - s := Fields(b) - - inSlice := func(val interface{}) bool { - for _, v := range s { - if reflect.DeepEqual(v.Name(), val) { - return true - } - } - return false - } - - for _, val := range []interface{}{"A", "C"} { - if !inSlice(val) { - t.Errorf("Fields should have the value %v", val) - } - } -} - -func TestIsZero(t *testing.T) { - var T = struct { - A string - B int - C bool `structs:"-"` - D []string - }{} - - ok := IsZero(T) - if !ok { - t.Error("IsZero should return true because none of the fields are initialized.") - } - - var X = struct { - A string - F *bool - }{ - A: "a-value", - } - - ok = IsZero(X) - if ok { - t.Error("IsZero should return false because A is initialized") - } - - var Y = struct { - A string - B int - }{ - A: "a-value", - B: 123, - } - - ok = IsZero(Y) - if ok { - t.Error("IsZero should return false because A and B is initialized") - } -} - -func TestIsZero_OmitNested(t *testing.T) { - type A struct { - Name string - D string - } - a := A{Name: "example"} - - type B struct { - A A `structs:",omitnested"` - C int - } - b := &B{A: a, C: 123} - - ok := IsZero(b) - if ok { - t.Error("IsZero should return false because A, B and C are initialized") - } - - aZero := A{} - bZero := &B{A: aZero} - - ok = IsZero(bZero) - if !ok { - t.Error("IsZero should return true because neither A nor B is initialized") - } - -} - -func TestIsZero_Nested(t *testing.T) { - type A struct { - Name string - D string - } - a := A{Name: "example"} - - type B struct { - A A - C int - } - b := &B{A: a, C: 123} - - ok := IsZero(b) - if ok { - t.Error("IsZero should return false because A, B and C are initialized") - } - - aZero := A{} - bZero := &B{A: aZero} - - ok = IsZero(bZero) - if !ok { - t.Error("IsZero should return true because neither A nor B is initialized") - } - -} - -func TestIsZero_Anonymous(t *testing.T) { - type A struct { - Name string - D string - } - a := A{Name: "example"} - - type B struct { - A - C int - } - b := &B{C: 123} - b.A = a - - ok := IsZero(b) - if ok { - t.Error("IsZero should return false because A, B and C are initialized") - } - - aZero := A{} - bZero := &B{} - bZero.A = aZero - - ok = IsZero(bZero) - if !ok { - t.Error("IsZero should return true because neither A nor B is initialized") - } -} - -func TestHasZero(t *testing.T) { - var T = struct { - A string - B int - C bool `structs:"-"` - D []string - }{ - A: "a-value", - B: 2, - } - - ok := HasZero(T) - if !ok { - t.Error("HasZero should return true because A and B are initialized.") - } - - var X = struct { - A string - F *bool - }{ - A: "a-value", - } - - ok = HasZero(X) - if !ok { - t.Error("HasZero should return true because A is initialized") - } - - var Y = struct { - A string - B int - }{ - A: "a-value", - B: 123, - } - - ok = HasZero(Y) - if ok { - t.Error("HasZero should return false because A and B is initialized") - } -} - -func TestHasZero_OmitNested(t *testing.T) { - type A struct { - Name string - D string - } - a := A{Name: "example"} - - type B struct { - A A `structs:",omitnested"` - C int - } - b := &B{A: a, C: 123} - - // Because the Field A inside B is omitted HasZero should return false - // because it will stop iterating deeper andnot going to lookup for D - ok := HasZero(b) - if ok { - t.Error("HasZero should return false because A and C are initialized") - } -} - -func TestHasZero_Nested(t *testing.T) { - type A struct { - Name string - D string - } - a := A{Name: "example"} - - type B struct { - A A - C int - } - b := &B{A: a, C: 123} - - ok := HasZero(b) - if !ok { - t.Error("HasZero should return true because D is not initialized") - } -} - -func TestHasZero_Anonymous(t *testing.T) { - type A struct { - Name string - D string - } - a := A{Name: "example"} - - type B struct { - A - C int - } - b := &B{C: 123} - b.A = a - - ok := HasZero(b) - if !ok { - t.Error("HasZero should return false because D is not initialized") - } -} - -func TestName(t *testing.T) { - type Foo struct { - A string - B bool - } - f := &Foo{} - - n := Name(f) - if n != "Foo" { - t.Errorf("Name should return Foo, got: %s", n) - } - - unnamed := struct{ Name string }{Name: "Cihangir"} - m := Name(unnamed) - if m != "" { - t.Errorf("Name should return empty string for unnamed struct, got: %s", n) - } - - defer func() { - err := recover() - if err == nil { - t.Error("Name should panic if a non struct is passed") - } - }() - - Name([]string{}) -} - -func TestNestedNilPointer(t *testing.T) { - type Collar struct { - Engraving string - } - - type Dog struct { - Name string - Collar *Collar - } - - type Person struct { - Name string - Dog *Dog - } - - person := &Person{ - Name: "John", - } - - personWithDog := &Person{ - Name: "Ron", - Dog: &Dog{ - Name: "Rover", - }, - } - - personWithDogWithCollar := &Person{ - Name: "Kon", - Dog: &Dog{ - Name: "Ruffles", - Collar: &Collar{ - Engraving: "If lost, call Kon", - }, - }, - } - - defer func() { - err := recover() - if err != nil { - fmt.Printf("err %+v\n", err) - t.Error("Internal nil pointer should not panic") - } - }() - - _ = Map(person) // Panics - _ = Map(personWithDog) // Panics - _ = Map(personWithDogWithCollar) // Doesn't panic -} diff --git a/Godeps/_workspace/src/github.com/fatih/structs/tags_test.go b/Godeps/_workspace/src/github.com/fatih/structs/tags_test.go deleted file mode 100644 index 5d12724f1..000000000 --- a/Godeps/_workspace/src/github.com/fatih/structs/tags_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package structs - -import "testing" - -func TestParseTag_Name(t *testing.T) { - tags := []struct { - tag string - has bool - }{ - {"", false}, - {"name", true}, - {"name,opt", true}, - {"name , opt, opt2", false}, // has a single whitespace - {", opt, opt2", false}, - } - - for _, tag := range tags { - name, _ := parseTag(tag.tag) - - if (name != "name") && tag.has { - t.Errorf("Parse tag should return name: %#v", tag) - } - } -} - -func TestParseTag_Opts(t *testing.T) { - tags := []struct { - opts string - has bool - }{ - {"name", false}, - {"name,opt", true}, - {"name , opt, opt2", false}, // has a single whitespace - {",opt, opt2", true}, - {", opt3, opt4", false}, - } - - // search for "opt" - for _, tag := range tags { - _, opts := parseTag(tag.opts) - - if opts.Has("opt") != tag.has { - t.Errorf("Tag opts should have opt: %#v", tag) - } - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml index 6b9cd96ae..4a6a01fb7 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml @@ -4,7 +4,7 @@ go: - 1.3.3 - 1.4.2 - 1.5.3 - - 1.6beta2 + - 1.6rc2 - tip env: - GOARCH=amd64 DOCKER_VERSION=1.7.1 @@ -13,6 +13,8 @@ env: - GOARCH=386 DOCKER_VERSION=1.8.3 - GOARCH=amd64 DOCKER_VERSION=1.9.1 - GOARCH=386 DOCKER_VERSION=1.9.1 + - GOARCH=amd64 DOCKER_VERSION=1.10.0 + - GOARCH=386 DOCKER_VERSION=1.10.0 install: - make prepare_docker script: diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS index 988849612..0c42ae344 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS @@ -50,6 +50,7 @@ Flavia Missi Francisco Souza Grégoire Delattre Guillermo Álvarez Fernández +Harry Zhang He Simei Ivan Mikushin James Bardin @@ -111,6 +112,7 @@ Summer Mousa Sunjin Lee Tarsis Azevedo Tim Schindler +Timothy St. Clair Tobi Knaup Tom Wilkie Tonic diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/Makefile b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/Makefile index 205d8f3c2..7a94eaa1e 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/Makefile +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/Makefile @@ -41,7 +41,7 @@ prepare_docker: sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main" | sudo tee /etc/apt/sources.list.d/docker.list sudo apt-get update - sudo apt-get install docker-engine=$(DOCKER_VERSION)-0~$(shell lsb_release -cs) -y --force-yes + sudo apt-get install docker-engine=$(DOCKER_VERSION)-0~$(shell lsb_release -cs) -y --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" pretest: lint vet fmtcheck diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/auth_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/auth_test.go deleted file mode 100644 index e53b17601..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/auth_test.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2015 go-dockerclient 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 docker - -import ( - "encoding/base64" - "fmt" - "net/http" - "strings" - "testing" -) - -func TestAuthLegacyConfig(t *testing.T) { - auth := base64.StdEncoding.EncodeToString([]byte("user:pa:ss")) - read := strings.NewReader(fmt.Sprintf(`{"docker.io":{"auth":"%s","email":"user@example.com"}}`, auth)) - ac, err := NewAuthConfigurations(read) - if err != nil { - t.Error(err) - } - c, ok := ac.Configs["docker.io"] - if !ok { - t.Error("NewAuthConfigurations: Expected Configs to contain docker.io") - } - if got, want := c.Email, "user@example.com"; got != want { - t.Errorf(`AuthConfigurations.Configs["docker.io"].Email: wrong result. Want %q. Got %q`, want, got) - } - if got, want := c.Username, "user"; got != want { - t.Errorf(`AuthConfigurations.Configs["docker.io"].Username: wrong result. Want %q. Got %q`, want, got) - } - if got, want := c.Password, "pa:ss"; got != want { - t.Errorf(`AuthConfigurations.Configs["docker.io"].Password: wrong result. Want %q. Got %q`, want, got) - } - if got, want := c.ServerAddress, "docker.io"; got != want { - t.Errorf(`AuthConfigurations.Configs["docker.io"].ServerAddress: wrong result. Want %q. Got %q`, want, got) - } -} - -func TestAuthBadConfig(t *testing.T) { - auth := base64.StdEncoding.EncodeToString([]byte("userpass")) - read := strings.NewReader(fmt.Sprintf(`{"docker.io":{"auth":"%s","email":"user@example.com"}}`, auth)) - ac, err := NewAuthConfigurations(read) - if err != ErrCannotParseDockercfg { - t.Errorf("Incorrect error returned %v\n", err) - } - if ac != nil { - t.Errorf("Invalid auth configuration returned, should be nil %v\n", ac) - } -} - -func TestAuthConfig(t *testing.T) { - auth := base64.StdEncoding.EncodeToString([]byte("user:pass")) - read := strings.NewReader(fmt.Sprintf(`{"auths":{"docker.io":{"auth":"%s","email":"user@example.com"}}}`, auth)) - ac, err := NewAuthConfigurations(read) - if err != nil { - t.Error(err) - } - c, ok := ac.Configs["docker.io"] - if !ok { - t.Error("NewAuthConfigurations: Expected Configs to contain docker.io") - } - if got, want := c.Email, "user@example.com"; got != want { - t.Errorf(`AuthConfigurations.Configs["docker.io"].Email: wrong result. Want %q. Got %q`, want, got) - } - if got, want := c.Username, "user"; got != want { - t.Errorf(`AuthConfigurations.Configs["docker.io"].Username: wrong result. Want %q. Got %q`, want, got) - } - if got, want := c.Password, "pass"; got != want { - t.Errorf(`AuthConfigurations.Configs["docker.io"].Password: wrong result. Want %q. Got %q`, want, got) - } - if got, want := c.ServerAddress, "docker.io"; got != want { - t.Errorf(`AuthConfigurations.Configs["docker.io"].ServerAddress: wrong result. Want %q. Got %q`, want, got) - } -} - -func TestAuthCheck(t *testing.T) { - fakeRT := &FakeRoundTripper{status: http.StatusOK} - client := newTestClient(fakeRT) - if err := client.AuthCheck(nil); err == nil { - t.Fatalf("expected error on nil auth config") - } - // test good auth - if err := client.AuthCheck(&AuthConfiguration{}); err != nil { - t.Fatal(err) - } - *fakeRT = FakeRoundTripper{status: http.StatusUnauthorized} - if err := client.AuthCheck(&AuthConfiguration{}); err == nil { - t.Fatal("expected failure from unauthorized auth") - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/build_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/build_test.go deleted file mode 100644 index c9640f205..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/build_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package docker - -import ( - "bytes" - "io" - "io/ioutil" - "net/http" - "os" - "reflect" - "testing" - - "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive" -) - -func TestBuildImageMultipleContextsError(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - var buf bytes.Buffer - opts := BuildImageOptions{ - Name: "testImage", - NoCache: true, - SuppressOutput: true, - RmTmpContainer: true, - ForceRmTmpContainer: true, - InputStream: &buf, - OutputStream: &buf, - ContextDir: "testing/data", - } - err := client.BuildImage(opts) - if err != ErrMultipleContexts { - t.Errorf("BuildImage: providing both InputStream and ContextDir should produce an error") - } -} - -func TestBuildImageContextDirDockerignoreParsing(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - - if err := os.Symlink("doesnotexist", "testing/data/symlink"); err != nil { - t.Errorf("error creating symlink on demand: %s", err) - } - defer func() { - if err := os.Remove("testing/data/symlink"); err != nil { - t.Errorf("error removing symlink on demand: %s", err) - } - }() - - var buf bytes.Buffer - opts := BuildImageOptions{ - Name: "testImage", - NoCache: true, - SuppressOutput: true, - RmTmpContainer: true, - ForceRmTmpContainer: true, - OutputStream: &buf, - ContextDir: "testing/data", - } - err := client.BuildImage(opts) - if err != nil { - t.Fatal(err) - } - reqBody := fakeRT.requests[0].Body - tmpdir, err := unpackBodyTarball(reqBody) - if err != nil { - t.Fatal(err) - } - - defer func() { - if err := os.RemoveAll(tmpdir); err != nil { - t.Fatal(err) - } - }() - - files, err := ioutil.ReadDir(tmpdir) - if err != nil { - t.Fatal(err) - } - - foundFiles := []string{} - for _, file := range files { - foundFiles = append(foundFiles, file.Name()) - } - - expectedFiles := []string{ - ".dockerignore", - "Dockerfile", - "barfile", - "ca.pem", - "cert.pem", - "key.pem", - "server.pem", - "serverkey.pem", - "symlink", - } - - if !reflect.DeepEqual(expectedFiles, foundFiles) { - t.Errorf( - "BuildImage: incorrect files sent in tarball to docker server\nexpected %+v, found %+v", - expectedFiles, foundFiles, - ) - } -} - -func TestBuildImageSendXRegistryConfig(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - var buf bytes.Buffer - opts := BuildImageOptions{ - Name: "testImage", - NoCache: true, - SuppressOutput: true, - RmTmpContainer: true, - ForceRmTmpContainer: true, - OutputStream: &buf, - ContextDir: "testing/data", - AuthConfigs: AuthConfigurations{ - Configs: map[string]AuthConfiguration{ - "quay.io": { - Username: "foo", - Password: "bar", - Email: "baz", - ServerAddress: "quay.io", - }, - }, - }, - } - - encodedConfig := "eyJjb25maWdzIjp7InF1YXkuaW8iOnsidXNlcm5hbWUiOiJmb28iLCJwYXNzd29yZCI6ImJhciIsImVtYWlsIjoiYmF6Iiwic2VydmVyYWRkcmVzcyI6InF1YXkuaW8ifX19Cg==" - - if err := client.BuildImage(opts); err != nil { - t.Fatal(err) - } - - xRegistryConfig := fakeRT.requests[0].Header["X-Registry-Config"][0] - if xRegistryConfig != encodedConfig { - t.Errorf( - "BuildImage: X-Registry-Config not set currectly: expected %q, got %q", - encodedConfig, - xRegistryConfig, - ) - } -} - -func unpackBodyTarball(req io.ReadCloser) (tmpdir string, err error) { - tmpdir, err = ioutil.TempDir("", "go-dockerclient-test") - if err != nil { - return - } - err = archive.Untar(req, tmpdir, &archive.TarOptions{ - Compression: archive.Uncompressed, - NoLchown: true, - }) - return -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/change_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/change_test.go deleted file mode 100644 index 9418b183c..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/change_test.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2014 go-dockerclient 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 docker - -import "testing" - -func TestChangeString(t *testing.T) { - var tests = []struct { - change Change - expected string - }{ - {Change{"/etc/passwd", ChangeModify}, "C /etc/passwd"}, - {Change{"/etc/passwd", ChangeAdd}, "A /etc/passwd"}, - {Change{"/etc/passwd", ChangeDelete}, "D /etc/passwd"}, - {Change{"/etc/passwd", 33}, " /etc/passwd"}, - } - for _, tt := range tests { - if got := tt.change.String(); got != tt.expected { - t.Errorf("Change.String(): want %q. Got %q.", tt.expected, got) - } - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go index 3a7d755f5..114fb87b7 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go @@ -60,7 +60,8 @@ func NewAPIVersion(input string) (APIVersion, error) { if !strings.Contains(input, ".") { return nil, fmt.Errorf("Unable to parse version %q", input) } - arr := strings.Split(input, ".") + raw := strings.Split(input, "-") + arr := strings.Split(raw[0], ".") ret := make(APIVersion, len(arr)) var err error for i, val := range arr { @@ -586,7 +587,7 @@ func (c *Client) hijack(method, path string, hijackOptions hijackOptions) (Close if err != nil { return nil, err } - req.Header.Set("Content-Type", "plain/text") + req.Header.Set("Content-Type", "application/json") req.Header.Set("Connection", "Upgrade") req.Header.Set("Upgrade", "tcp") protocol := c.endpointURL.Scheme diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client_test.go deleted file mode 100644 index 721352757..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client_test.go +++ /dev/null @@ -1,518 +0,0 @@ -// Copyright 2015 go-dockerclient 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 docker - -import ( - "bytes" - "fmt" - "io/ioutil" - "net" - "net/http" - "net/url" - "os" - "path/filepath" - "reflect" - "strconv" - "strings" - "testing" - "time" - - "github.com/fsouza/go-dockerclient/external/github.com/hashicorp/go-cleanhttp" -) - -func TestNewAPIClient(t *testing.T) { - endpoint := "http://localhost:4243" - client, err := NewClient(endpoint) - if err != nil { - t.Fatal(err) - } - if client.endpoint != endpoint { - t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint) - } - // test unix socket endpoints - endpoint = "unix:///var/run/docker.sock" - client, err = NewClient(endpoint) - if err != nil { - t.Fatal(err) - } - if client.endpoint != endpoint { - t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint) - } - if !client.SkipServerVersionCheck { - t.Error("Expected SkipServerVersionCheck to be true, got false") - } - if client.requestedAPIVersion != nil { - t.Errorf("Expected requestedAPIVersion to be nil, got %#v.", client.requestedAPIVersion) - } -} - -func newTLSClient(endpoint string) (*Client, error) { - return NewTLSClient(endpoint, - "testing/data/cert.pem", - "testing/data/key.pem", - "testing/data/ca.pem") -} - -func TestNewTSLAPIClient(t *testing.T) { - endpoint := "https://localhost:4243" - client, err := newTLSClient(endpoint) - if err != nil { - t.Fatal(err) - } - if client.endpoint != endpoint { - t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint) - } - if !client.SkipServerVersionCheck { - t.Error("Expected SkipServerVersionCheck to be true, got false") - } - if client.requestedAPIVersion != nil { - t.Errorf("Expected requestedAPIVersion to be nil, got %#v.", client.requestedAPIVersion) - } -} - -func TestNewVersionedClient(t *testing.T) { - endpoint := "http://localhost:4243" - client, err := NewVersionedClient(endpoint, "1.12") - if err != nil { - t.Fatal(err) - } - if client.endpoint != endpoint { - t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint) - } - if reqVersion := client.requestedAPIVersion.String(); reqVersion != "1.12" { - t.Errorf("Wrong requestAPIVersion. Want %q. Got %q.", "1.12", reqVersion) - } - if client.SkipServerVersionCheck { - t.Error("Expected SkipServerVersionCheck to be false, got true") - } -} - -func TestNewVersionedClientFromEnv(t *testing.T) { - endpoint := "tcp://localhost:2376" - endpointURL := "http://localhost:2376" - os.Setenv("DOCKER_HOST", endpoint) - os.Setenv("DOCKER_TLS_VERIFY", "") - client, err := NewVersionedClientFromEnv("1.12") - if err != nil { - t.Fatal(err) - } - if client.endpoint != endpoint { - t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint) - } - if client.endpointURL.String() != endpointURL { - t.Errorf("Expected endpointURL %s. Got %s.", endpoint, client.endpoint) - } - if reqVersion := client.requestedAPIVersion.String(); reqVersion != "1.12" { - t.Errorf("Wrong requestAPIVersion. Want %q. Got %q.", "1.12", reqVersion) - } - if client.SkipServerVersionCheck { - t.Error("Expected SkipServerVersionCheck to be false, got true") - } -} - -func TestNewVersionedClientFromEnvTLS(t *testing.T) { - endpoint := "tcp://localhost:2376" - endpointURL := "https://localhost:2376" - base, _ := os.Getwd() - os.Setenv("DOCKER_CERT_PATH", filepath.Join(base, "/testing/data/")) - os.Setenv("DOCKER_HOST", endpoint) - os.Setenv("DOCKER_TLS_VERIFY", "1") - client, err := NewVersionedClientFromEnv("1.12") - if err != nil { - t.Fatal(err) - } - if client.endpoint != endpoint { - t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint) - } - if client.endpointURL.String() != endpointURL { - t.Errorf("Expected endpointURL %s. Got %s.", endpoint, client.endpoint) - } - if reqVersion := client.requestedAPIVersion.String(); reqVersion != "1.12" { - t.Errorf("Wrong requestAPIVersion. Want %q. Got %q.", "1.12", reqVersion) - } - if client.SkipServerVersionCheck { - t.Error("Expected SkipServerVersionCheck to be false, got true") - } -} - -func TestNewTLSVersionedClient(t *testing.T) { - certPath := "testing/data/cert.pem" - keyPath := "testing/data/key.pem" - caPath := "testing/data/ca.pem" - endpoint := "https://localhost:4243" - client, err := NewVersionedTLSClient(endpoint, certPath, keyPath, caPath, "1.14") - if err != nil { - t.Fatal(err) - } - if client.endpoint != endpoint { - t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint) - } - if reqVersion := client.requestedAPIVersion.String(); reqVersion != "1.14" { - t.Errorf("Wrong requestAPIVersion. Want %q. Got %q.", "1.14", reqVersion) - } - if client.SkipServerVersionCheck { - t.Error("Expected SkipServerVersionCheck to be false, got true") - } -} - -func TestNewTLSVersionedClientInvalidCA(t *testing.T) { - certPath := "testing/data/cert.pem" - keyPath := "testing/data/key.pem" - caPath := "testing/data/key.pem" - endpoint := "https://localhost:4243" - _, err := NewVersionedTLSClient(endpoint, certPath, keyPath, caPath, "1.14") - if err == nil { - t.Errorf("Expected invalid ca at %s", caPath) - } -} - -func TestNewClientInvalidEndpoint(t *testing.T) { - cases := []string{ - "htp://localhost:3243", "http://localhost:a", - "", "http://localhost:8080:8383", "http://localhost:65536", - "https://localhost:-20", - } - for _, c := range cases { - client, err := NewClient(c) - if client != nil { - t.Errorf("Want client for invalid endpoint, got %#v.", client) - } - if !reflect.DeepEqual(err, ErrInvalidEndpoint) { - t.Errorf("NewClient(%q): Got invalid error for invalid endpoint. Want %#v. Got %#v.", c, ErrInvalidEndpoint, err) - } - } -} - -func TestNewClientNoSchemeEndpoint(t *testing.T) { - cases := []string{"localhost", "localhost:8080"} - for _, c := range cases { - client, err := NewClient(c) - if client == nil { - t.Errorf("Want client for scheme-less endpoint, got ") - } - if err != nil { - t.Errorf("Got unexpected error scheme-less endpoint: %q", err) - } - } -} - -func TestNewTLSClient(t *testing.T) { - var tests = []struct { - endpoint string - expected string - }{ - {"tcp://localhost:2376", "https"}, - {"tcp://localhost:2375", "https"}, - {"tcp://localhost:4000", "https"}, - {"http://localhost:4000", "https"}, - } - for _, tt := range tests { - client, err := newTLSClient(tt.endpoint) - if err != nil { - t.Error(err) - } - got := client.endpointURL.Scheme - if got != tt.expected { - t.Errorf("endpointURL.Scheme: Got %s. Want %s.", got, tt.expected) - } - } -} - -func TestEndpoint(t *testing.T) { - client, err := NewVersionedClient("http://localhost:4243", "1.12") - if err != nil { - t.Fatal(err) - } - if endpoint := client.Endpoint(); endpoint != client.endpoint { - t.Errorf("Client.Endpoint(): want %q. Got %q", client.endpoint, endpoint) - } -} - -func TestGetURL(t *testing.T) { - var tests = []struct { - endpoint string - path string - expected string - }{ - {"http://localhost:4243/", "/", "http://localhost:4243/"}, - {"http://localhost:4243", "/", "http://localhost:4243/"}, - {"http://localhost:4243", "/containers/ps", "http://localhost:4243/containers/ps"}, - {"tcp://localhost:4243", "/containers/ps", "http://localhost:4243/containers/ps"}, - {"http://localhost:4243/////", "/", "http://localhost:4243/"}, - {"unix:///var/run/docker.socket", "/containers", "/containers"}, - } - for _, tt := range tests { - client, _ := NewClient(tt.endpoint) - client.endpoint = tt.endpoint - client.SkipServerVersionCheck = true - got := client.getURL(tt.path) - if got != tt.expected { - t.Errorf("getURL(%q): Got %s. Want %s.", tt.path, got, tt.expected) - } - } -} - -func TestGetFakeUnixURL(t *testing.T) { - var tests = []struct { - endpoint string - path string - expected string - }{ - {"unix://var/run/docker.sock", "/", "http://unix.sock/"}, - {"unix://var/run/docker.socket", "/", "http://unix.sock/"}, - {"unix://var/run/docker.sock", "/containers/ps", "http://unix.sock/containers/ps"}, - } - for _, tt := range tests { - client, _ := NewClient(tt.endpoint) - client.endpoint = tt.endpoint - client.SkipServerVersionCheck = true - got := client.getFakeUnixURL(tt.path) - if got != tt.expected { - t.Errorf("getURL(%q): Got %s. Want %s.", tt.path, got, tt.expected) - } - } -} - -func TestError(t *testing.T) { - fakeBody := ioutil.NopCloser(bytes.NewBufferString("bad parameter")) - resp := &http.Response{ - StatusCode: 400, - Body: fakeBody, - } - err := newError(resp) - expected := Error{Status: 400, Message: "bad parameter"} - if !reflect.DeepEqual(expected, *err) { - t.Errorf("Wrong error type. Want %#v. Got %#v.", expected, *err) - } - message := "API error (400): bad parameter" - if err.Error() != message { - t.Errorf("Wrong error message. Want %q. Got %q.", message, err.Error()) - } -} - -func TestQueryString(t *testing.T) { - v := float32(2.4) - f32QueryString := fmt.Sprintf("w=%s&x=10&y=10.35", strconv.FormatFloat(float64(v), 'f', -1, 64)) - jsonPerson := url.QueryEscape(`{"Name":"gopher","age":4}`) - var tests = []struct { - input interface{} - want string - }{ - {&ListContainersOptions{All: true}, "all=1"}, - {ListContainersOptions{All: true}, "all=1"}, - {ListContainersOptions{Before: "something"}, "before=something"}, - {ListContainersOptions{Before: "something", Since: "other"}, "before=something&since=other"}, - {ListContainersOptions{Filters: map[string][]string{"status": {"paused", "running"}}}, "filters=%7B%22status%22%3A%5B%22paused%22%2C%22running%22%5D%7D"}, - {dumb{X: 10, Y: 10.35000}, "x=10&y=10.35"}, - {dumb{W: v, X: 10, Y: 10.35000}, f32QueryString}, - {dumb{X: 10, Y: 10.35000, Z: 10}, "x=10&y=10.35&zee=10"}, - {dumb{v: 4, X: 10, Y: 10.35000}, "x=10&y=10.35"}, - {dumb{T: 10, Y: 10.35000}, "y=10.35"}, - {dumb{Person: &person{Name: "gopher", Age: 4}}, "p=" + jsonPerson}, - {nil, ""}, - {10, ""}, - {"not_a_struct", ""}, - } - for _, tt := range tests { - got := queryString(tt.input) - if got != tt.want { - t.Errorf("queryString(%v). Want %q. Got %q.", tt.input, tt.want, got) - } - } -} - -func TestNewAPIVersionFailures(t *testing.T) { - var tests = []struct { - input string - expectedError string - }{ - {"1-0", `Unable to parse version "1-0"`}, - {"1.0-beta", `Unable to parse version "1.0-beta": "0-beta" is not an integer`}, - } - for _, tt := range tests { - v, err := NewAPIVersion(tt.input) - if v != nil { - t.Errorf("Expected version, got %v.", v) - } - if err.Error() != tt.expectedError { - t.Errorf("NewAPIVersion(%q): wrong error. Want %q. Got %q", tt.input, tt.expectedError, err.Error()) - } - } -} - -func TestAPIVersions(t *testing.T) { - var tests = []struct { - a string - b string - expectedALessThanB bool - expectedALessThanOrEqualToB bool - expectedAGreaterThanB bool - expectedAGreaterThanOrEqualToB bool - }{ - {"1.11", "1.11", false, true, false, true}, - {"1.10", "1.11", true, true, false, false}, - {"1.11", "1.10", false, false, true, true}, - - {"1.9", "1.11", true, true, false, false}, - {"1.11", "1.9", false, false, true, true}, - - {"1.1.1", "1.1", false, false, true, true}, - {"1.1", "1.1.1", true, true, false, false}, - - {"2.1", "1.1.1", false, false, true, true}, - {"2.1", "1.3.1", false, false, true, true}, - {"1.1.1", "2.1", true, true, false, false}, - {"1.3.1", "2.1", true, true, false, false}, - } - - for _, tt := range tests { - a, _ := NewAPIVersion(tt.a) - b, _ := NewAPIVersion(tt.b) - - if tt.expectedALessThanB && !a.LessThan(b) { - t.Errorf("Expected %#v < %#v", a, b) - } - if tt.expectedALessThanOrEqualToB && !a.LessThanOrEqualTo(b) { - t.Errorf("Expected %#v <= %#v", a, b) - } - if tt.expectedAGreaterThanB && !a.GreaterThan(b) { - t.Errorf("Expected %#v > %#v", a, b) - } - if tt.expectedAGreaterThanOrEqualToB && !a.GreaterThanOrEqualTo(b) { - t.Errorf("Expected %#v >= %#v", a, b) - } - } -} - -func TestPing(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - err := client.Ping() - if err != nil { - t.Fatal(err) - } -} - -func TestPingFailing(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusInternalServerError} - client := newTestClient(fakeRT) - err := client.Ping() - if err == nil { - t.Fatal("Expected non nil error, got nil") - } - expectedErrMsg := "API error (500): " - if err.Error() != expectedErrMsg { - t.Fatalf("Expected error to be %q, got: %q", expectedErrMsg, err.Error()) - } -} - -func TestPingFailingWrongStatus(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusAccepted} - client := newTestClient(fakeRT) - err := client.Ping() - if err == nil { - t.Fatal("Expected non nil error, got nil") - } - expectedErrMsg := "API error (202): " - if err.Error() != expectedErrMsg { - t.Fatalf("Expected error to be %q, got: %q", expectedErrMsg, err.Error()) - } -} - -func TestPingErrorWithUnixSocket(t *testing.T) { - go func() { - li, err := net.Listen("unix", "/tmp/echo.sock") - if err != nil { - t.Fatal(err) - } - defer li.Close() - if err != nil { - t.Fatalf("Expected to get listener, but failed: %#v", err) - } - - fd, err := li.Accept() - if err != nil { - t.Fatalf("Expected to accept connection, but failed: %#v", err) - } - - buf := make([]byte, 512) - nr, err := fd.Read(buf) - - // Create invalid response message to trigger error. - data := buf[0:nr] - for i := 0; i < 10; i++ { - data[i] = 63 - } - - _, err = fd.Write(data) - if err != nil { - t.Fatalf("Expected to write to socket, but failed: %#v", err) - } - - return - }() - - // Wait for unix socket to listen - time.Sleep(10 * time.Millisecond) - - endpoint := "unix:///tmp/echo.sock" - u, _ := parseEndpoint(endpoint, false) - client := Client{ - HTTPClient: cleanhttp.DefaultClient(), - Dialer: &net.Dialer{}, - endpoint: endpoint, - endpointURL: u, - SkipServerVersionCheck: true, - } - - err := client.Ping() - if err == nil { - t.Fatal("Expected non nil error, got nil") - } -} - -type FakeRoundTripper struct { - message string - status int - header map[string]string - requests []*http.Request -} - -func (rt *FakeRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { - body := strings.NewReader(rt.message) - rt.requests = append(rt.requests, r) - res := &http.Response{ - StatusCode: rt.status, - Body: ioutil.NopCloser(body), - Header: make(http.Header), - } - for k, v := range rt.header { - res.Header.Set(k, v) - } - return res, nil -} - -func (rt *FakeRoundTripper) Reset() { - rt.requests = nil -} - -type person struct { - Name string - Age int `json:"age"` -} - -type dumb struct { - T int `qs:"-"` - v int - W float32 - X int - Y float64 - Z int `qs:"zee"` - Person *person `qs:"p"` -} - -type fakeEndpointURL struct { - Scheme string -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go index 0baf01f97..317814b90 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go @@ -510,6 +510,7 @@ type HostConfig struct { BlkioWeight int64 `json:"BlkioWeight,omitempty" yaml:"BlkioWeight"` Ulimits []ULimit `json:"Ulimits,omitempty" yaml:"Ulimits,omitempty"` VolumeDriver string `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty"` + OomScoreAdj int `json:"OomScoreAdj,omitempty" yaml:"OomScoreAdj,omitempty"` } // StartContainer starts a container, returning an error in case of failure. @@ -638,6 +639,7 @@ func (c *Client) TopContainer(id string, psArgs string) (TopResult, error) { // See https://goo.gl/GNmLHb for more details. type Stats struct { Read time.Time `json:"read,omitempty" yaml:"read,omitempty"` + Network NetworkStats `json:"network,omitempty" yaml:"network,omitempty"` Networks map[string]NetworkStats `json:"networks,omitempty" yaml:"networks,omitempty"` MemoryStats struct { Stats struct { @@ -670,6 +672,8 @@ type Stats struct { Pgfault uint64 `json:"pgfault,omitempty" yaml:"pgfault,omitempty"` InactiveFile uint64 `json:"inactive_file,omitempty" yaml:"inactive_file,omitempty"` TotalPgpgin uint64 `json:"total_pgpgin,omitempty" yaml:"total_pgpgin,omitempty"` + HierarchicalMemswLimit uint64 `json:"hierarchical_memsw_limit,omitempty" yaml:"hierarchical_memsw_limit,omitempty"` + Swap uint64 `json:"swap,omitempty" yaml:"swap,omitempty"` } `json:"stats,omitempty" yaml:"stats,omitempty"` MaxUsage uint64 `json:"max_usage,omitempty" yaml:"max_usage,omitempty"` Usage uint64 `json:"usage,omitempty" yaml:"usage,omitempty"` diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container_test.go deleted file mode 100644 index 3e09c5f63..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container_test.go +++ /dev/null @@ -1,2248 +0,0 @@ -// Copyright 2015 go-dockerclient 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 docker - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "net" - "net/http" - "net/http/httptest" - "net/url" - "os" - "reflect" - "regexp" - "runtime" - "strconv" - "strings" - "testing" - "time" - - "github.com/fsouza/go-dockerclient/external/github.com/hashicorp/go-cleanhttp" -) - -func TestStateString(t *testing.T) { - started := time.Now().Add(-3 * time.Hour) - var tests = []struct { - input State - expected string - }{ - {State{Running: true, Paused: true}, "^paused$"}, - {State{Running: true, StartedAt: started}, "^Up 3h.*$"}, - {State{Running: false, ExitCode: 7}, "^Exit 7$"}, - } - for _, tt := range tests { - re := regexp.MustCompile(tt.expected) - if got := tt.input.String(); !re.MatchString(got) { - t.Errorf("State.String(): wrong result. Want %q. Got %q.", tt.expected, got) - } - } -} - -func TestListContainers(t *testing.T) { - jsonContainers := `[ - { - "Id": "8dfafdbc3a40", - "Image": "base:latest", - "Command": "echo 1", - "Created": 1367854155, - "Ports":[{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}], - "Status": "Exit 0" - }, - { - "Id": "9cd87474be90", - "Image": "base:latest", - "Command": "echo 222222", - "Created": 1367854155, - "Ports":[{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}], - "Status": "Exit 0" - }, - { - "Id": "3176a2479c92", - "Image": "base:latest", - "Command": "echo 3333333333333333", - "Created": 1367854154, - "Ports":[{"PrivatePort": 2221, "PublicPort": 3331, "Type": "tcp"}], - "Status": "Exit 0" - }, - { - "Id": "4cb07b47f9fb", - "Image": "base:latest", - "Command": "echo 444444444444444444444444444444444", - "Ports":[{"PrivatePort": 2223, "PublicPort": 3332, "Type": "tcp"}], - "Created": 1367854152, - "Status": "Exit 0" - } -]` - var expected []APIContainers - err := json.Unmarshal([]byte(jsonContainers), &expected) - if err != nil { - t.Fatal(err) - } - client := newTestClient(&FakeRoundTripper{message: jsonContainers, status: http.StatusOK}) - containers, err := client.ListContainers(ListContainersOptions{}) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(containers, expected) { - t.Errorf("ListContainers: Expected %#v. Got %#v.", expected, containers) - } -} - -func TestListContainersParams(t *testing.T) { - var tests = []struct { - input ListContainersOptions - params map[string][]string - }{ - {ListContainersOptions{}, map[string][]string{}}, - {ListContainersOptions{All: true}, map[string][]string{"all": {"1"}}}, - {ListContainersOptions{All: true, Limit: 10}, map[string][]string{"all": {"1"}, "limit": {"10"}}}, - { - ListContainersOptions{All: true, Limit: 10, Since: "adf9983", Before: "abdeef"}, - map[string][]string{"all": {"1"}, "limit": {"10"}, "since": {"adf9983"}, "before": {"abdeef"}}, - }, - { - ListContainersOptions{Filters: map[string][]string{"status": {"paused", "running"}}}, - map[string][]string{"filters": {"{\"status\":[\"paused\",\"running\"]}"}}, - }, - { - ListContainersOptions{All: true, Filters: map[string][]string{"exited": {"0"}, "status": {"exited"}}}, - map[string][]string{"all": {"1"}, "filters": {"{\"exited\":[\"0\"],\"status\":[\"exited\"]}"}}, - }, - } - fakeRT := &FakeRoundTripper{message: "[]", status: http.StatusOK} - client := newTestClient(fakeRT) - u, _ := url.Parse(client.getURL("/containers/json")) - for _, tt := range tests { - if _, err := client.ListContainers(tt.input); err != nil { - t.Error(err) - } - got := map[string][]string(fakeRT.requests[0].URL.Query()) - if !reflect.DeepEqual(got, tt.params) { - t.Errorf("Expected %#v, got %#v.", tt.params, got) - } - if path := fakeRT.requests[0].URL.Path; path != u.Path { - t.Errorf("Wrong path on request. Want %q. Got %q.", u.Path, path) - } - if meth := fakeRT.requests[0].Method; meth != "GET" { - t.Errorf("Wrong HTTP method. Want GET. Got %s.", meth) - } - fakeRT.Reset() - } -} - -func TestListContainersFailure(t *testing.T) { - var tests = []struct { - status int - message string - }{ - {400, "bad parameter"}, - {500, "internal server error"}, - } - for _, tt := range tests { - client := newTestClient(&FakeRoundTripper{message: tt.message, status: tt.status}) - expected := Error{Status: tt.status, Message: tt.message} - containers, err := client.ListContainers(ListContainersOptions{}) - if !reflect.DeepEqual(expected, *err.(*Error)) { - t.Errorf("Wrong error in ListContainers. Want %#v. Got %#v.", expected, err) - } - if len(containers) > 0 { - t.Errorf("ListContainers failure. Expected empty list. Got %#v.", containers) - } - } -} - -func TestInspectContainer(t *testing.T) { - jsonContainer := `{ - "Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2", - "AppArmorProfile": "Profile", - "Created": "2013-05-07T14:51:42.087658+02:00", - "Path": "date", - "Args": [], - "Config": { - "Hostname": "4fa6e0f0c678", - "User": "", - "Memory": 17179869184, - "MemorySwap": 34359738368, - "AttachStdin": false, - "AttachStdout": true, - "AttachStderr": true, - "PortSpecs": null, - "Tty": false, - "OpenStdin": false, - "StdinOnce": false, - "Env": null, - "Cmd": [ - "date" - ], - "Image": "base", - "Volumes": {}, - "VolumesFrom": "", - "SecurityOpt": [ - "label:user:USER" - ], - "Ulimits": [ - { "Name": "nofile", "Soft": 1024, "Hard": 2048 } - ] - }, - "State": { - "Running": false, - "Pid": 0, - "ExitCode": 0, - "StartedAt": "2013-05-07T14:51:42.087658+02:00", - "Ghost": false - }, - "Node": { - "ID": "4I4E:QR4I:Z733:QEZK:5X44:Q4T7:W2DD:JRDY:KB2O:PODO:Z5SR:XRB6", - "IP": "192.168.99.105", - "Addra": "192.168.99.105:2376", - "Name": "node-01", - "Cpus": 4, - "Memory": 1048436736, - "Labels": { - "executiondriver": "native-0.2", - "kernelversion": "3.18.5-tinycore64", - "operatingsystem": "Boot2Docker 1.5.0 (TCL 5.4); master : a66bce5 - Tue Feb 10 23:31:27 UTC 2015", - "provider": "virtualbox", - "storagedriver": "aufs" - } - }, - "Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", - "NetworkSettings": { - "IpAddress": "", - "IpPrefixLen": 0, - "Gateway": "", - "Bridge": "", - "PortMapping": null - }, - "SysInitPath": "/home/kitty/go/src/github.com/dotcloud/docker/bin/docker", - "ResolvConfPath": "/etc/resolv.conf", - "Volumes": {}, - "HostConfig": { - "Binds": null, - "ContainerIDFile": "", - "LxcConf": [], - "Privileged": false, - "PortBindings": { - "80/tcp": [ - { - "HostIp": "0.0.0.0", - "HostPort": "49153" - } - ] - }, - "Links": null, - "PublishAllPorts": false, - "CgroupParent": "/mesos", - "Memory": 17179869184, - "MemorySwap": 34359738368, - "GroupAdd": ["fake", "12345"] - } -}` - var expected Container - err := json.Unmarshal([]byte(jsonContainer), &expected) - if err != nil { - t.Fatal(err) - } - fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK} - client := newTestClient(fakeRT) - id := "4fa6e0f0c678" - container, err := client.InspectContainer(id) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(*container, expected) { - t.Errorf("InspectContainer(%q): Expected %#v. Got %#v.", id, expected, container) - } - expectedURL, _ := url.Parse(client.getURL("/containers/4fa6e0f0c678/json")) - if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path { - t.Errorf("InspectContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath) - } -} - -func TestInspectContainerNetwork(t *testing.T) { - jsonContainer := `{ - "Id": "81e1bbe20b5508349e1c804eb08b7b6ca8366751dbea9f578b3ea0773fa66c1c", - "Created": "2015-11-12T14:54:04.791485659Z", - "Path": "consul-template", - "Args": [ - "-config=/tmp/haproxy.json", - "-consul=192.168.99.120:8500" - ], - "State": { - "Status": "running", - "Running": true, - "Paused": false, - "Restarting": false, - "OOMKilled": false, - "Dead": false, - "Pid": 3196, - "ExitCode": 0, - "Error": "", - "StartedAt": "2015-11-12T14:54:05.026747471Z", - "FinishedAt": "0001-01-01T00:00:00Z" - }, - "Image": "4921c5917fc117df3dec32f4c1976635dc6c56ccd3336fe1db3477f950e78bf7", - "ResolvConfPath": "/mnt/sda1/var/lib/docker/containers/81e1bbe20b5508349e1c804eb08b7b6ca8366751dbea9f578b3ea0773fa66c1c/resolv.conf", - "HostnamePath": "/mnt/sda1/var/lib/docker/containers/81e1bbe20b5508349e1c804eb08b7b6ca8366751dbea9f578b3ea0773fa66c1c/hostname", - "HostsPath": "/mnt/sda1/var/lib/docker/containers/81e1bbe20b5508349e1c804eb08b7b6ca8366751dbea9f578b3ea0773fa66c1c/hosts", - "LogPath": "/mnt/sda1/var/lib/docker/containers/81e1bbe20b5508349e1c804eb08b7b6ca8366751dbea9f578b3ea0773fa66c1c/81e1bbe20b5508349e1c804eb08b7b6ca8366751dbea9f578b3ea0773fa66c1c-json.log", - "Node": { - "ID": "AUIB:LFOT:3LSF:SCFS:OYDQ:NLXD:JZNE:4INI:3DRC:ZFBB:GWCY:DWJK", - "IP": "192.168.99.121", - "Addr": "192.168.99.121:2376", - "Name": "swl-demo1", - "Cpus": 1, - "Memory": 2099945472, - "Labels": { - "executiondriver": "native-0.2", - "kernelversion": "4.1.12-boot2docker", - "operatingsystem": "Boot2Docker 1.9.0 (TCL 6.4); master : 16e4a2a - Tue Nov 3 19:49:22 UTC 2015", - "provider": "virtualbox", - "storagedriver": "aufs" - } - }, - "Name": "/docker-proxy.swl-demo1", - "RestartCount": 0, - "Driver": "aufs", - "ExecDriver": "native-0.2", - "MountLabel": "", - "ProcessLabel": "", - "AppArmorProfile": "", - "ExecIDs": null, - "HostConfig": { - "Binds": null, - "ContainerIDFile": "", - "LxcConf": [], - "Memory": 0, - "MemoryReservation": 0, - "MemorySwap": 0, - "KernelMemory": 0, - "CpuShares": 0, - "CpuPeriod": 0, - "CpusetCpus": "", - "CpusetMems": "", - "CpuQuota": 0, - "BlkioWeight": 0, - "OomKillDisable": false, - "MemorySwappiness": -1, - "Privileged": false, - "PortBindings": { - "443/tcp": [ - { - "HostIp": "", - "HostPort": "443" - } - ] - }, - "Links": null, - "PublishAllPorts": false, - "Dns": null, - "DnsOptions": null, - "DnsSearch": null, - "ExtraHosts": null, - "VolumesFrom": null, - "Devices": [], - "NetworkMode": "swl-net", - "IpcMode": "", - "PidMode": "", - "UTSMode": "", - "CapAdd": null, - "CapDrop": null, - "GroupAdd": null, - "RestartPolicy": { - "Name": "no", - "MaximumRetryCount": 0 - }, - "SecurityOpt": null, - "ReadonlyRootfs": false, - "Ulimits": null, - "LogConfig": { - "Type": "json-file", - "Config": {} - }, - "CgroupParent": "", - "ConsoleSize": [ - 0, - 0 - ], - "VolumeDriver": "" - }, - "GraphDriver": { - "Name": "aufs", - "Data": null - }, - "Mounts": [], - "Config": { - "Hostname": "81e1bbe20b55", - "Domainname": "", - "User": "", - "AttachStdin": false, - "AttachStdout": false, - "AttachStderr": false, - "ExposedPorts": { - "443/tcp": {} - }, - "Tty": false, - "OpenStdin": false, - "StdinOnce": false, - "Env": [ - "DOMAIN=local.auto", - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "CONSUL_TEMPLATE_VERSION=0.11.1" - ], - "Cmd": [ - "-consul=192.168.99.120:8500" - ], - "Image": "docker-proxy:latest", - "Volumes": null, - "WorkingDir": "", - "Entrypoint": [ - "consul-template", - "-config=/tmp/haproxy.json" - ], - "OnBuild": null, - "Labels": {}, - "StopSignal": "SIGTERM" - }, - "NetworkSettings": { - "Bridge": "", - "SandboxID": "c6b903dc5c1a96113a22dbc44709e30194079bd2d262eea1eb4f38d85821f6e1", - "HairpinMode": false, - "LinkLocalIPv6Address": "", - "LinkLocalIPv6PrefixLen": 0, - "Ports": { - "443/tcp": [ - { - "HostIp": "192.168.99.121", - "HostPort": "443" - } - ] - }, - "SandboxKey": "/var/run/docker/netns/c6b903dc5c1a", - "SecondaryIPAddresses": null, - "SecondaryIPv6Addresses": null, - "EndpointID": "", - "Gateway": "", - "GlobalIPv6Address": "", - "GlobalIPv6PrefixLen": 0, - "IPAddress": "", - "IPPrefixLen": 0, - "IPv6Gateway": "", - "MacAddress": "", - "Networks": { - "swl-net": { - "EndpointID": "683e3092275782a53c3b0968cc7e3a10f23264022ded9cb20490902f96fc5981", - "Gateway": "", - "IPAddress": "10.0.0.3", - "IPPrefixLen": 24, - "IPv6Gateway": "", - "GlobalIPv6Address": "", - "GlobalIPv6PrefixLen": 0, - "MacAddress": "02:42:0a:00:00:03" - } - } - } -}` - - fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK} - client := newTestClient(fakeRT) - id := "81e1bbe20b55" - exp := "10.0.0.3" - - container, err := client.InspectContainer(id) - if err != nil { - t.Fatal(err) - } - - s := reflect.Indirect(reflect.ValueOf(container.NetworkSettings)) - networks := s.FieldByName("Networks") - if networks.IsValid() { - var ip string - for _, net := range networks.MapKeys() { - if net.Interface().(string) == container.HostConfig.NetworkMode { - ip = networks.MapIndex(net).FieldByName("IPAddress").Interface().(string) - t.Logf("%s %v", net, ip) - } - } - if ip != exp { - t.Errorf("InspectContainerNetworks(%q): Expected %#v. Got %#v.", id, exp, ip) - } - } else { - t.Errorf("InspectContainerNetworks(%q): No method Networks for NetworkSettings", id) - } - -} - -func TestInspectContainerNegativeSwap(t *testing.T) { - jsonContainer := `{ - "Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2", - "Created": "2013-05-07T14:51:42.087658+02:00", - "Path": "date", - "Args": [], - "Config": { - "Hostname": "4fa6e0f0c678", - "User": "", - "Memory": 17179869184, - "MemorySwap": -1, - "AttachStdin": false, - "AttachStdout": true, - "AttachStderr": true, - "PortSpecs": null, - "Tty": false, - "OpenStdin": false, - "StdinOnce": false, - "Env": null, - "Cmd": [ - "date" - ], - "Image": "base", - "Volumes": {}, - "VolumesFrom": "" - }, - "State": { - "Running": false, - "Pid": 0, - "ExitCode": 0, - "StartedAt": "2013-05-07T14:51:42.087658+02:00", - "Ghost": false - }, - "Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", - "NetworkSettings": { - "IpAddress": "", - "IpPrefixLen": 0, - "Gateway": "", - "Bridge": "", - "PortMapping": null - }, - "SysInitPath": "/home/kitty/go/src/github.com/dotcloud/docker/bin/docker", - "ResolvConfPath": "/etc/resolv.conf", - "Volumes": {}, - "HostConfig": { - "Binds": null, - "ContainerIDFile": "", - "LxcConf": [], - "Privileged": false, - "PortBindings": { - "80/tcp": [ - { - "HostIp": "0.0.0.0", - "HostPort": "49153" - } - ] - }, - "Links": null, - "PublishAllPorts": false - } -}` - var expected Container - err := json.Unmarshal([]byte(jsonContainer), &expected) - if err != nil { - t.Fatal(err) - } - fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK} - client := newTestClient(fakeRT) - id := "4fa6e0f0c678" - container, err := client.InspectContainer(id) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(*container, expected) { - t.Errorf("InspectContainer(%q): Expected %#v. Got %#v.", id, expected, container) - } - expectedURL, _ := url.Parse(client.getURL("/containers/4fa6e0f0c678/json")) - if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path { - t.Errorf("InspectContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath) - } -} - -func TestInspectContainerFailure(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "server error", status: 500}) - expected := Error{Status: 500, Message: "server error"} - container, err := client.InspectContainer("abe033") - if container != nil { - t.Errorf("InspectContainer: Expected container, got %#v", container) - } - if !reflect.DeepEqual(expected, *err.(*Error)) { - t.Errorf("InspectContainer: Wrong error information. Want %#v. Got %#v.", expected, err) - } -} - -func TestInspectContainerNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such container", status: 404}) - container, err := client.InspectContainer("abe033") - if container != nil { - t.Errorf("InspectContainer: Expected container, got %#v", container) - } - expected := &NoSuchContainer{ID: "abe033"} - if !reflect.DeepEqual(err, expected) { - t.Errorf("InspectContainer: Wrong error information. Want %#v. Got %#v.", expected, err) - } -} - -func TestContainerChanges(t *testing.T) { - jsonChanges := `[ - { - "Path":"/dev", - "Kind":0 - }, - { - "Path":"/dev/kmsg", - "Kind":1 - }, - { - "Path":"/test", - "Kind":1 - } -]` - var expected []Change - err := json.Unmarshal([]byte(jsonChanges), &expected) - if err != nil { - t.Fatal(err) - } - fakeRT := &FakeRoundTripper{message: jsonChanges, status: http.StatusOK} - client := newTestClient(fakeRT) - id := "4fa6e0f0c678" - changes, err := client.ContainerChanges(id) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(changes, expected) { - t.Errorf("ContainerChanges(%q): Expected %#v. Got %#v.", id, expected, changes) - } - expectedURL, _ := url.Parse(client.getURL("/containers/4fa6e0f0c678/changes")) - if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path { - t.Errorf("ContainerChanges(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath) - } -} - -func TestContainerChangesFailure(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "server error", status: 500}) - expected := Error{Status: 500, Message: "server error"} - changes, err := client.ContainerChanges("abe033") - if changes != nil { - t.Errorf("ContainerChanges: Expected changes, got %#v", changes) - } - if !reflect.DeepEqual(expected, *err.(*Error)) { - t.Errorf("ContainerChanges: Wrong error information. Want %#v. Got %#v.", expected, err) - } -} - -func TestContainerChangesNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such container", status: 404}) - changes, err := client.ContainerChanges("abe033") - if changes != nil { - t.Errorf("ContainerChanges: Expected changes, got %#v", changes) - } - expected := &NoSuchContainer{ID: "abe033"} - if !reflect.DeepEqual(err, expected) { - t.Errorf("ContainerChanges: Wrong error information. Want %#v. Got %#v.", expected, err) - } -} - -func TestCreateContainer(t *testing.T) { - jsonContainer := `{ - "Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2", - "Warnings": [] -}` - var expected Container - err := json.Unmarshal([]byte(jsonContainer), &expected) - if err != nil { - t.Fatal(err) - } - fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK} - client := newTestClient(fakeRT) - config := Config{AttachStdout: true, AttachStdin: true} - opts := CreateContainerOptions{Name: "TestCreateContainer", Config: &config} - container, err := client.CreateContainer(opts) - if err != nil { - t.Fatal(err) - } - id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - if container.ID != id { - t.Errorf("CreateContainer: wrong ID. Want %q. Got %q.", id, container.ID) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("CreateContainer: wrong HTTP method. Want %q. Got %q.", "POST", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/containers/create")) - if gotPath := req.URL.Path; gotPath != expectedURL.Path { - t.Errorf("CreateContainer: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath) - } - var gotBody Config - err = json.NewDecoder(req.Body).Decode(&gotBody) - if err != nil { - t.Fatal(err) - } -} - -func TestCreateContainerImageNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "No such image", status: http.StatusNotFound}) - config := Config{AttachStdout: true, AttachStdin: true} - container, err := client.CreateContainer(CreateContainerOptions{Config: &config}) - if container != nil { - t.Errorf("CreateContainer: expected container, got %#v.", container) - } - if !reflect.DeepEqual(err, ErrNoSuchImage) { - t.Errorf("CreateContainer: Wrong error type. Want %#v. Got %#v.", ErrNoSuchImage, err) - } -} - -func TestCreateContainerDuplicateName(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "No such image", status: http.StatusConflict}) - config := Config{AttachStdout: true, AttachStdin: true} - container, err := client.CreateContainer(CreateContainerOptions{Config: &config}) - if container != nil { - t.Errorf("CreateContainer: expected container, got %#v.", container) - } - if err != ErrContainerAlreadyExists { - t.Errorf("CreateContainer: Wrong error type. Want %#v. Got %#v.", ErrContainerAlreadyExists, err) - } -} - -func TestCreateContainerWithHostConfig(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "{}", status: http.StatusOK} - client := newTestClient(fakeRT) - config := Config{} - hostConfig := HostConfig{PublishAllPorts: true} - opts := CreateContainerOptions{Name: "TestCreateContainerWithHostConfig", Config: &config, HostConfig: &hostConfig} - _, err := client.CreateContainer(opts) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - var gotBody map[string]interface{} - err = json.NewDecoder(req.Body).Decode(&gotBody) - if err != nil { - t.Fatal(err) - } - if _, ok := gotBody["HostConfig"]; !ok { - t.Errorf("CreateContainer: wrong body. HostConfig was not serialized") - } -} - -func TestStartContainer(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - err := client.StartContainer(id, &HostConfig{}) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("StartContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/start")) - if gotPath := req.URL.Path; gotPath != expectedURL.Path { - t.Errorf("StartContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath) - } - expectedContentType := "application/json" - if contentType := req.Header.Get("Content-Type"); contentType != expectedContentType { - t.Errorf("StartContainer(%q): Wrong content-type in request. Want %q. Got %q.", id, expectedContentType, contentType) - } -} - -func TestStartContainerNilHostConfig(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - err := client.StartContainer(id, nil) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("StartContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/start")) - if gotPath := req.URL.Path; gotPath != expectedURL.Path { - t.Errorf("StartContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath) - } - expectedContentType := "application/json" - if contentType := req.Header.Get("Content-Type"); contentType != expectedContentType { - t.Errorf("StartContainer(%q): Wrong content-type in request. Want %q. Got %q.", id, expectedContentType, contentType) - } - var buf [4]byte - req.Body.Read(buf[:]) - if string(buf[:]) != "null" { - t.Errorf("Startcontainer(%q): Wrong body. Want null. Got %s", id, buf[:]) - } -} - -func TestStartContainerNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound}) - err := client.StartContainer("a2344", &HostConfig{}) - expected := &NoSuchContainer{ID: "a2344", Err: err.(*NoSuchContainer).Err} - if !reflect.DeepEqual(err, expected) { - t.Errorf("StartContainer: Wrong error returned. Want %#v. Got %#v.", expected, err) - } -} - -func TestStartContainerAlreadyRunning(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "container already running", status: http.StatusNotModified}) - err := client.StartContainer("a2334", &HostConfig{}) - expected := &ContainerAlreadyRunning{ID: "a2334"} - if !reflect.DeepEqual(err, expected) { - t.Errorf("StartContainer: Wrong error returned. Want %#v. Got %#v.", expected, err) - } -} - -func TestStopContainer(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent} - client := newTestClient(fakeRT) - id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - err := client.StopContainer(id, 10) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("StopContainer(%q, 10): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/stop")) - if gotPath := req.URL.Path; gotPath != expectedURL.Path { - t.Errorf("StopContainer(%q, 10): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath) - } -} - -func TestStopContainerNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound}) - err := client.StopContainer("a2334", 10) - expected := &NoSuchContainer{ID: "a2334"} - if !reflect.DeepEqual(err, expected) { - t.Errorf("StopContainer: Wrong error returned. Want %#v. Got %#v.", expected, err) - } -} - -func TestStopContainerNotRunning(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "container not running", status: http.StatusNotModified}) - err := client.StopContainer("a2334", 10) - expected := &ContainerNotRunning{ID: "a2334"} - if !reflect.DeepEqual(err, expected) { - t.Errorf("StopContainer: Wrong error returned. Want %#v. Got %#v.", expected, err) - } -} - -func TestRestartContainer(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent} - client := newTestClient(fakeRT) - id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - err := client.RestartContainer(id, 10) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("RestartContainer(%q, 10): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/restart")) - if gotPath := req.URL.Path; gotPath != expectedURL.Path { - t.Errorf("RestartContainer(%q, 10): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath) - } -} - -func TestRestartContainerNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound}) - err := client.RestartContainer("a2334", 10) - expected := &NoSuchContainer{ID: "a2334"} - if !reflect.DeepEqual(err, expected) { - t.Errorf("RestartContainer: Wrong error returned. Want %#v. Got %#v.", expected, err) - } -} - -func TestPauseContainer(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent} - client := newTestClient(fakeRT) - id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - err := client.PauseContainer(id) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("PauseContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/pause")) - if gotPath := req.URL.Path; gotPath != expectedURL.Path { - t.Errorf("PauseContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath) - } -} - -func TestPauseContainerNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound}) - err := client.PauseContainer("a2334") - expected := &NoSuchContainer{ID: "a2334"} - if !reflect.DeepEqual(err, expected) { - t.Errorf("PauseContainer: Wrong error returned. Want %#v. Got %#v.", expected, err) - } -} - -func TestUnpauseContainer(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent} - client := newTestClient(fakeRT) - id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - err := client.UnpauseContainer(id) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("PauseContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/unpause")) - if gotPath := req.URL.Path; gotPath != expectedURL.Path { - t.Errorf("PauseContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath) - } -} - -func TestUnpauseContainerNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound}) - err := client.UnpauseContainer("a2334") - expected := &NoSuchContainer{ID: "a2334"} - if !reflect.DeepEqual(err, expected) { - t.Errorf("PauseContainer: Wrong error returned. Want %#v. Got %#v.", expected, err) - } -} - -func TestKillContainer(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent} - client := newTestClient(fakeRT) - id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - err := client.KillContainer(KillContainerOptions{ID: id}) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("KillContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/kill")) - if gotPath := req.URL.Path; gotPath != expectedURL.Path { - t.Errorf("KillContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath) - } -} - -func TestKillContainerSignal(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent} - client := newTestClient(fakeRT) - id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - err := client.KillContainer(KillContainerOptions{ID: id, Signal: SIGTERM}) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("KillContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method) - } - if signal := req.URL.Query().Get("signal"); signal != "15" { - t.Errorf("KillContainer(%q): Wrong query string in request. Want %q. Got %q.", id, "15", signal) - } -} - -func TestKillContainerNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound}) - err := client.KillContainer(KillContainerOptions{ID: "a2334"}) - expected := &NoSuchContainer{ID: "a2334"} - if !reflect.DeepEqual(err, expected) { - t.Errorf("KillContainer: Wrong error returned. Want %#v. Got %#v.", expected, err) - } -} - -func TestRemoveContainer(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - opts := RemoveContainerOptions{ID: id} - err := client.RemoveContainer(opts) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - if req.Method != "DELETE" { - t.Errorf("RemoveContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "DELETE", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/containers/" + id)) - if gotPath := req.URL.Path; gotPath != expectedURL.Path { - t.Errorf("RemoveContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath) - } -} - -func TestRemoveContainerRemoveVolumes(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - opts := RemoveContainerOptions{ID: id, RemoveVolumes: true} - err := client.RemoveContainer(opts) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - params := map[string][]string(req.URL.Query()) - expected := map[string][]string{"v": {"1"}} - if !reflect.DeepEqual(params, expected) { - t.Errorf("RemoveContainer(%q): wrong parameters. Want %#v. Got %#v.", id, expected, params) - } -} - -func TestRemoveContainerNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound}) - err := client.RemoveContainer(RemoveContainerOptions{ID: "a2334"}) - expected := &NoSuchContainer{ID: "a2334"} - if !reflect.DeepEqual(err, expected) { - t.Errorf("RemoveContainer: Wrong error returned. Want %#v. Got %#v.", expected, err) - } -} - -func TestResizeContainerTTY(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - err := client.ResizeContainerTTY(id, 40, 80) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("ResizeContainerTTY(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/resize")) - if gotPath := req.URL.Path; gotPath != expectedURL.Path { - t.Errorf("ResizeContainerTTY(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath) - } - got := map[string][]string(req.URL.Query()) - expectedParams := map[string][]string{ - "w": {"80"}, - "h": {"40"}, - } - if !reflect.DeepEqual(got, expectedParams) { - t.Errorf("Expected %#v, got %#v.", expectedParams, got) - } -} - -func TestWaitContainer(t *testing.T) { - fakeRT := &FakeRoundTripper{message: `{"StatusCode": 56}`, status: http.StatusOK} - client := newTestClient(fakeRT) - id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - status, err := client.WaitContainer(id) - if err != nil { - t.Fatal(err) - } - if status != 56 { - t.Errorf("WaitContainer(%q): wrong return. Want 56. Got %d.", id, status) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("WaitContainer(%q): wrong HTTP method. Want %q. Got %q.", id, "POST", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/wait")) - if gotPath := req.URL.Path; gotPath != expectedURL.Path { - t.Errorf("WaitContainer(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath) - } -} - -func TestWaitContainerNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound}) - _, err := client.WaitContainer("a2334") - expected := &NoSuchContainer{ID: "a2334"} - if !reflect.DeepEqual(err, expected) { - t.Errorf("WaitContainer: Wrong error returned. Want %#v. Got %#v.", expected, err) - } -} - -func TestCommitContainer(t *testing.T) { - response := `{"Id":"596069db4bf5"}` - client := newTestClient(&FakeRoundTripper{message: response, status: http.StatusOK}) - id := "596069db4bf5" - image, err := client.CommitContainer(CommitContainerOptions{}) - if err != nil { - t.Fatal(err) - } - if image.ID != id { - t.Errorf("CommitContainer: Wrong image id. Want %q. Got %q.", id, image.ID) - } -} - -func TestCommitContainerParams(t *testing.T) { - cfg := Config{Memory: 67108864} - json, _ := json.Marshal(&cfg) - var tests = []struct { - input CommitContainerOptions - params map[string][]string - body []byte - }{ - {CommitContainerOptions{}, map[string][]string{}, nil}, - {CommitContainerOptions{Container: "44c004db4b17"}, map[string][]string{"container": {"44c004db4b17"}}, nil}, - { - CommitContainerOptions{Container: "44c004db4b17", Repository: "tsuru/python", Message: "something"}, - map[string][]string{"container": {"44c004db4b17"}, "repo": {"tsuru/python"}, "comment": {"something"}}, - nil, - }, - { - CommitContainerOptions{Container: "44c004db4b17", Run: &cfg}, - map[string][]string{"container": {"44c004db4b17"}}, - json, - }, - } - fakeRT := &FakeRoundTripper{message: "{}", status: http.StatusOK} - client := newTestClient(fakeRT) - u, _ := url.Parse(client.getURL("/commit")) - for _, tt := range tests { - if _, err := client.CommitContainer(tt.input); err != nil { - t.Error(err) - } - got := map[string][]string(fakeRT.requests[0].URL.Query()) - if !reflect.DeepEqual(got, tt.params) { - t.Errorf("Expected %#v, got %#v.", tt.params, got) - } - if path := fakeRT.requests[0].URL.Path; path != u.Path { - t.Errorf("Wrong path on request. Want %q. Got %q.", u.Path, path) - } - if meth := fakeRT.requests[0].Method; meth != "POST" { - t.Errorf("Wrong HTTP method. Want POST. Got %s.", meth) - } - if tt.body != nil { - if requestBody, err := ioutil.ReadAll(fakeRT.requests[0].Body); err == nil { - if bytes.Compare(requestBody, tt.body) != 0 { - t.Errorf("Expected body %#v, got %#v", tt.body, requestBody) - } - } else { - t.Errorf("Error reading request body: %#v", err) - } - } - fakeRT.Reset() - } -} - -func TestCommitContainerFailure(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusInternalServerError}) - _, err := client.CommitContainer(CommitContainerOptions{}) - if err == nil { - t.Error("Expected non-nil error, got .") - } -} - -func TestCommitContainerNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound}) - _, err := client.CommitContainer(CommitContainerOptions{}) - expected := &NoSuchContainer{ID: ""} - if !reflect.DeepEqual(err, expected) { - t.Errorf("CommitContainer: Wrong error returned. Want %#v. Got %#v.", expected, err) - } -} - -func TestAttachToContainerLogs(t *testing.T) { - var req http.Request - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte{1, 0, 0, 0, 0, 0, 0, 19}) - w.Write([]byte("something happened!")) - req = *r - })) - defer server.Close() - client, _ := NewClient(server.URL) - client.SkipServerVersionCheck = true - var buf bytes.Buffer - opts := AttachToContainerOptions{ - Container: "a123456", - OutputStream: &buf, - Stdout: true, - Stderr: true, - Logs: true, - } - err := client.AttachToContainer(opts) - if err != nil { - t.Fatal(err) - } - expected := "something happened!" - if buf.String() != expected { - t.Errorf("AttachToContainer for logs: wrong output. Want %q. Got %q.", expected, buf.String()) - } - if req.Method != "POST" { - t.Errorf("AttachToContainer: wrong HTTP method. Want POST. Got %s.", req.Method) - } - u, _ := url.Parse(client.getURL("/containers/a123456/attach")) - if req.URL.Path != u.Path { - t.Errorf("AttachToContainer for logs: wrong HTTP path. Want %q. Got %q.", u.Path, req.URL.Path) - } - expectedQs := map[string][]string{ - "logs": {"1"}, - "stdout": {"1"}, - "stderr": {"1"}, - } - got := map[string][]string(req.URL.Query()) - if !reflect.DeepEqual(got, expectedQs) { - t.Errorf("AttachToContainer: wrong query string. Want %#v. Got %#v.", expectedQs, got) - } -} - -func TestAttachToContainer(t *testing.T) { - var reader = strings.NewReader("send value") - var req http.Request - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte{1, 0, 0, 0, 0, 0, 0, 5}) - w.Write([]byte("hello")) - req = *r - })) - defer server.Close() - client, _ := NewClient(server.URL) - client.SkipServerVersionCheck = true - var stdout, stderr bytes.Buffer - opts := AttachToContainerOptions{ - Container: "a123456", - OutputStream: &stdout, - ErrorStream: &stderr, - InputStream: reader, - Stdin: true, - Stdout: true, - Stderr: true, - Stream: true, - RawTerminal: true, - } - err := client.AttachToContainer(opts) - if err != nil { - t.Fatal(err) - } - expected := map[string][]string{ - "stdin": {"1"}, - "stdout": {"1"}, - "stderr": {"1"}, - "stream": {"1"}, - } - got := map[string][]string(req.URL.Query()) - if !reflect.DeepEqual(got, expected) { - t.Errorf("AttachToContainer: wrong query string. Want %#v. Got %#v.", expected, got) - } -} - -func TestAttachToContainerSentinel(t *testing.T) { - var reader = strings.NewReader("send value") - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte{1, 0, 0, 0, 0, 0, 0, 5}) - w.Write([]byte("hello")) - })) - defer server.Close() - client, _ := NewClient(server.URL) - client.SkipServerVersionCheck = true - var stdout, stderr bytes.Buffer - success := make(chan struct{}) - opts := AttachToContainerOptions{ - Container: "a123456", - OutputStream: &stdout, - ErrorStream: &stderr, - InputStream: reader, - Stdin: true, - Stdout: true, - Stderr: true, - Stream: true, - RawTerminal: true, - Success: success, - } - go func() { - if err := client.AttachToContainer(opts); err != nil { - t.Error(err) - } - }() - success <- <-success -} - -func TestAttachToContainerNilStdout(t *testing.T) { - var reader = strings.NewReader("send value") - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte{1, 0, 0, 0, 0, 0, 0, 5}) - w.Write([]byte("hello")) - })) - defer server.Close() - client, _ := NewClient(server.URL) - client.SkipServerVersionCheck = true - var stderr bytes.Buffer - opts := AttachToContainerOptions{ - Container: "a123456", - OutputStream: nil, - ErrorStream: &stderr, - InputStream: reader, - Stdin: true, - Stdout: true, - Stderr: true, - Stream: true, - RawTerminal: true, - } - err := client.AttachToContainer(opts) - if err != nil { - t.Fatal(err) - } -} - -func TestAttachToContainerNilStderr(t *testing.T) { - var reader = strings.NewReader("send value") - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte{1, 0, 0, 0, 0, 0, 0, 5}) - w.Write([]byte("hello")) - })) - defer server.Close() - client, _ := NewClient(server.URL) - client.SkipServerVersionCheck = true - var stdout bytes.Buffer - opts := AttachToContainerOptions{ - Container: "a123456", - OutputStream: &stdout, - InputStream: reader, - Stdin: true, - Stdout: true, - Stderr: true, - Stream: true, - RawTerminal: true, - } - err := client.AttachToContainer(opts) - if err != nil { - t.Fatal(err) - } -} - -func TestAttachToContainerStdinOnly(t *testing.T) { - var reader = strings.NewReader("send value") - serverFinished := make(chan struct{}) - clientFinished := make(chan struct{}) - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - hj, ok := w.(http.Hijacker) - if !ok { - t.Fatal("cannot hijack server connection") - } - conn, _, err := hj.Hijack() - if err != nil { - t.Fatal(err) - } - // wait for client to indicate it's finished - <-clientFinished - // inform test that the server has finished - close(serverFinished) - conn.Close() - })) - defer server.Close() - client, _ := NewClient(server.URL) - client.SkipServerVersionCheck = true - success := make(chan struct{}) - opts := AttachToContainerOptions{ - Container: "a123456", - InputStream: reader, - Stdin: true, - Stdout: false, - Stderr: false, - Stream: true, - RawTerminal: false, - Success: success, - } - go func() { - if err := client.AttachToContainer(opts); err != nil { - t.Error(err) - } - // client's attach session is over - close(clientFinished) - }() - success <- <-success - // wait for server to finish handling attach - <-serverFinished -} - -func TestAttachToContainerRawTerminalFalse(t *testing.T) { - input := strings.NewReader("send value") - var req http.Request - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - req = *r - w.WriteHeader(http.StatusOK) - hj, ok := w.(http.Hijacker) - if !ok { - t.Fatal("cannot hijack server connection") - } - conn, _, err := hj.Hijack() - if err != nil { - t.Fatal(err) - } - conn.Write([]byte{1, 0, 0, 0, 0, 0, 0, 5}) - conn.Write([]byte("hello")) - conn.Write([]byte{2, 0, 0, 0, 0, 0, 0, 6}) - conn.Write([]byte("hello!")) - conn.Close() - })) - defer server.Close() - client, _ := NewClient(server.URL) - client.SkipServerVersionCheck = true - var stdout, stderr bytes.Buffer - opts := AttachToContainerOptions{ - Container: "a123456", - OutputStream: &stdout, - ErrorStream: &stderr, - InputStream: input, - Stdin: true, - Stdout: true, - Stderr: true, - Stream: true, - RawTerminal: false, - } - client.AttachToContainer(opts) - expected := map[string][]string{ - "stdin": {"1"}, - "stdout": {"1"}, - "stderr": {"1"}, - "stream": {"1"}, - } - got := map[string][]string(req.URL.Query()) - if !reflect.DeepEqual(got, expected) { - t.Errorf("AttachToContainer: wrong query string. Want %#v. Got %#v.", expected, got) - } - if stdout.String() != "hello" { - t.Errorf("AttachToContainer: wrong content written to stdout. Want %q. Got %q.", "hello", stdout.String()) - } - if stderr.String() != "hello!" { - t.Errorf("AttachToContainer: wrong content written to stderr. Want %q. Got %q.", "hello!", stderr.String()) - } -} - -func TestAttachToContainerWithoutContainer(t *testing.T) { - var client Client - err := client.AttachToContainer(AttachToContainerOptions{}) - expected := &NoSuchContainer{ID: ""} - if !reflect.DeepEqual(err, expected) { - t.Errorf("AttachToContainer: wrong error. Want %#v. Got %#v.", expected, err) - } -} - -func TestLogs(t *testing.T) { - var req http.Request - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - prefix := []byte{1, 0, 0, 0, 0, 0, 0, 19} - w.Write(prefix) - w.Write([]byte("something happened!")) - req = *r - })) - defer server.Close() - client, _ := NewClient(server.URL) - client.SkipServerVersionCheck = true - var buf bytes.Buffer - opts := LogsOptions{ - Container: "a123456", - OutputStream: &buf, - Follow: true, - Stdout: true, - Stderr: true, - Timestamps: true, - } - err := client.Logs(opts) - if err != nil { - t.Fatal(err) - } - expected := "something happened!" - if buf.String() != expected { - t.Errorf("Logs: wrong output. Want %q. Got %q.", expected, buf.String()) - } - if req.Method != "GET" { - t.Errorf("Logs: wrong HTTP method. Want GET. Got %s.", req.Method) - } - u, _ := url.Parse(client.getURL("/containers/a123456/logs")) - if req.URL.Path != u.Path { - t.Errorf("AttachToContainer for logs: wrong HTTP path. Want %q. Got %q.", u.Path, req.URL.Path) - } - expectedQs := map[string][]string{ - "follow": {"1"}, - "stdout": {"1"}, - "stderr": {"1"}, - "timestamps": {"1"}, - "tail": {"all"}, - } - got := map[string][]string(req.URL.Query()) - if !reflect.DeepEqual(got, expectedQs) { - t.Errorf("Logs: wrong query string. Want %#v. Got %#v.", expectedQs, got) - } -} - -func TestLogsNilStdoutDoesntFail(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - prefix := []byte{1, 0, 0, 0, 0, 0, 0, 19} - w.Write(prefix) - w.Write([]byte("something happened!")) - })) - defer server.Close() - client, _ := NewClient(server.URL) - client.SkipServerVersionCheck = true - opts := LogsOptions{ - Container: "a123456", - Follow: true, - Stdout: true, - Stderr: true, - Timestamps: true, - } - err := client.Logs(opts) - if err != nil { - t.Fatal(err) - } -} - -func TestLogsNilStderrDoesntFail(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - prefix := []byte{2, 0, 0, 0, 0, 0, 0, 19} - w.Write(prefix) - w.Write([]byte("something happened!")) - })) - defer server.Close() - client, _ := NewClient(server.URL) - client.SkipServerVersionCheck = true - opts := LogsOptions{ - Container: "a123456", - Follow: true, - Stdout: true, - Stderr: true, - Timestamps: true, - } - err := client.Logs(opts) - if err != nil { - t.Fatal(err) - } -} - -func TestLogsSpecifyingTail(t *testing.T) { - var req http.Request - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - prefix := []byte{1, 0, 0, 0, 0, 0, 0, 19} - w.Write(prefix) - w.Write([]byte("something happened!")) - req = *r - })) - defer server.Close() - client, _ := NewClient(server.URL) - client.SkipServerVersionCheck = true - var buf bytes.Buffer - opts := LogsOptions{ - Container: "a123456", - OutputStream: &buf, - Follow: true, - Stdout: true, - Stderr: true, - Timestamps: true, - Tail: "100", - } - err := client.Logs(opts) - if err != nil { - t.Fatal(err) - } - expected := "something happened!" - if buf.String() != expected { - t.Errorf("Logs: wrong output. Want %q. Got %q.", expected, buf.String()) - } - if req.Method != "GET" { - t.Errorf("Logs: wrong HTTP method. Want GET. Got %s.", req.Method) - } - u, _ := url.Parse(client.getURL("/containers/a123456/logs")) - if req.URL.Path != u.Path { - t.Errorf("AttachToContainer for logs: wrong HTTP path. Want %q. Got %q.", u.Path, req.URL.Path) - } - expectedQs := map[string][]string{ - "follow": {"1"}, - "stdout": {"1"}, - "stderr": {"1"}, - "timestamps": {"1"}, - "tail": {"100"}, - } - got := map[string][]string(req.URL.Query()) - if !reflect.DeepEqual(got, expectedQs) { - t.Errorf("Logs: wrong query string. Want %#v. Got %#v.", expectedQs, got) - } -} - -func TestLogsRawTerminal(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("something happened!")) - })) - defer server.Close() - client, _ := NewClient(server.URL) - client.SkipServerVersionCheck = true - var buf bytes.Buffer - opts := LogsOptions{ - Container: "a123456", - OutputStream: &buf, - Follow: true, - RawTerminal: true, - Stdout: true, - Stderr: true, - Timestamps: true, - Tail: "100", - } - err := client.Logs(opts) - if err != nil { - t.Fatal(err) - } - expected := "something happened!" - if buf.String() != expected { - t.Errorf("Logs: wrong output. Want %q. Got %q.", expected, buf.String()) - } -} - -func TestLogsNoContainer(t *testing.T) { - var client Client - err := client.Logs(LogsOptions{}) - expected := &NoSuchContainer{ID: ""} - if !reflect.DeepEqual(err, expected) { - t.Errorf("AttachToContainer: wrong error. Want %#v. Got %#v.", expected, err) - } -} - -func TestNoSuchContainerError(t *testing.T) { - var err = &NoSuchContainer{ID: "i345"} - expected := "No such container: i345" - if got := err.Error(); got != expected { - t.Errorf("NoSuchContainer: wrong message. Want %q. Got %q.", expected, got) - } -} - -func TestNoSuchContainerErrorMessage(t *testing.T) { - var err = &NoSuchContainer{ID: "i345", Err: errors.New("some advanced error info")} - expected := "some advanced error info" - if got := err.Error(); got != expected { - t.Errorf("NoSuchContainer: wrong message. Want %q. Got %q.", expected, got) - } -} - -func TestExportContainer(t *testing.T) { - content := "exported container tar content" - out := stdoutMock{bytes.NewBufferString(content)} - client := newTestClient(&FakeRoundTripper{status: http.StatusOK}) - opts := ExportContainerOptions{ID: "4fa6e0f0c678", OutputStream: out} - err := client.ExportContainer(opts) - if err != nil { - t.Errorf("ExportContainer: caugh error %#v while exporting container, expected nil", err.Error()) - } - if out.String() != content { - t.Errorf("ExportContainer: wrong stdout. Want %#v. Got %#v.", content, out.String()) - } -} - -func TestExportContainerViaUnixSocket(t *testing.T) { - if runtime.GOOS != "darwin" { - t.Skip(fmt.Sprintf("skipping test on %s", runtime.GOOS)) - } - content := "exported container tar content" - var buf []byte - out := bytes.NewBuffer(buf) - tempSocket := tempfile("export_socket") - defer os.Remove(tempSocket) - endpoint := "unix://" + tempSocket - u, _ := parseEndpoint(endpoint, false) - client := Client{ - HTTPClient: cleanhttp.DefaultClient(), - Dialer: &net.Dialer{}, - endpoint: endpoint, - endpointURL: u, - SkipServerVersionCheck: true, - } - listening := make(chan string) - done := make(chan int) - go runStreamConnServer(t, "unix", tempSocket, listening, done) - <-listening // wait for server to start - opts := ExportContainerOptions{ID: "4fa6e0f0c678", OutputStream: out} - err := client.ExportContainer(opts) - <-done // make sure server stopped - if err != nil { - t.Errorf("ExportContainer: caugh error %#v while exporting container, expected nil", err.Error()) - } - if out.String() != content { - t.Errorf("ExportContainer: wrong stdout. Want %#v. Got %#v.", content, out.String()) - } -} - -func runStreamConnServer(t *testing.T, network, laddr string, listening chan<- string, done chan<- int) { - defer close(done) - l, err := net.Listen(network, laddr) - if err != nil { - t.Errorf("Listen(%q, %q) failed: %v", network, laddr, err) - listening <- "" - return - } - defer l.Close() - listening <- l.Addr().String() - c, err := l.Accept() - if err != nil { - t.Logf("Accept failed: %v", err) - return - } - c.Write([]byte("HTTP/1.1 200 OK\n\nexported container tar content")) - c.Close() -} - -func tempfile(filename string) string { - return os.TempDir() + "/" + filename + "." + strconv.Itoa(os.Getpid()) -} - -func TestExportContainerNoId(t *testing.T) { - client := Client{} - out := stdoutMock{bytes.NewBufferString("")} - err := client.ExportContainer(ExportContainerOptions{OutputStream: out}) - e, ok := err.(*NoSuchContainer) - if !ok { - t.Errorf("ExportContainer: wrong error. Want NoSuchContainer. Got %#v.", e) - } - if e.ID != "" { - t.Errorf("ExportContainer: wrong ID. Want %q. Got %q", "", e.ID) - } -} - -func TestUploadToContainer(t *testing.T) { - content := "File content" - in := stdinMock{bytes.NewBufferString(content)} - fakeRT := &FakeRoundTripper{status: http.StatusOK} - client := newTestClient(fakeRT) - opts := UploadToContainerOptions{ - Path: "abc", - InputStream: in, - } - err := client.UploadToContainer("a123456", opts) - if err != nil { - t.Errorf("UploadToContainer: caught error %#v while uploading archive to container, expected nil", err) - } - - req := fakeRT.requests[0] - - if req.Method != "PUT" { - t.Errorf("UploadToContainer{Path:abc}: Wrong HTTP method. Want PUT. Got %s", req.Method) - } - - if pathParam := req.URL.Query().Get("path"); pathParam != "abc" { - t.Errorf("ListImages({Path:abc}): Wrong parameter. Want path=abc. Got path=%s", pathParam) - } - -} - -func TestDownloadFromContainer(t *testing.T) { - filecontent := "File content" - client := newTestClient(&FakeRoundTripper{message: filecontent, status: http.StatusOK}) - - var out bytes.Buffer - opts := DownloadFromContainerOptions{ - OutputStream: &out, - } - err := client.DownloadFromContainer("a123456", opts) - if err != nil { - t.Errorf("DownloadFromContainer: caught error %#v while downloading from container, expected nil", err.Error()) - } - if out.String() != filecontent { - t.Errorf("DownloadFromContainer: wrong stdout. Want %#v. Got %#v.", filecontent, out.String()) - } -} - -func TestCopyFromContainer(t *testing.T) { - content := "File content" - out := stdoutMock{bytes.NewBufferString(content)} - client := newTestClient(&FakeRoundTripper{status: http.StatusOK}) - opts := CopyFromContainerOptions{ - Container: "a123456", - OutputStream: &out, - } - err := client.CopyFromContainer(opts) - if err != nil { - t.Errorf("CopyFromContainer: caught error %#v while copying from container, expected nil", err.Error()) - } - if out.String() != content { - t.Errorf("CopyFromContainer: wrong stdout. Want %#v. Got %#v.", content, out.String()) - } -} - -func TestCopyFromContainerEmptyContainer(t *testing.T) { - client := newTestClient(&FakeRoundTripper{status: http.StatusOK}) - err := client.CopyFromContainer(CopyFromContainerOptions{}) - _, ok := err.(*NoSuchContainer) - if !ok { - t.Errorf("CopyFromContainer: invalid error returned. Want NoSuchContainer, got %#v.", err) - } -} - -func TestPassingNameOptToCreateContainerReturnsItInContainer(t *testing.T) { - jsonContainer := `{ - "Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2", - "Warnings": [] -}` - fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK} - client := newTestClient(fakeRT) - config := Config{AttachStdout: true, AttachStdin: true} - opts := CreateContainerOptions{Name: "TestCreateContainer", Config: &config} - container, err := client.CreateContainer(opts) - if err != nil { - t.Fatal(err) - } - if container.Name != "TestCreateContainer" { - t.Errorf("Container name expected to be TestCreateContainer, was %s", container.Name) - } -} - -func TestAlwaysRestart(t *testing.T) { - policy := AlwaysRestart() - if policy.Name != "always" { - t.Errorf("AlwaysRestart(): wrong policy name. Want %q. Got %q", "always", policy.Name) - } - if policy.MaximumRetryCount != 0 { - t.Errorf("AlwaysRestart(): wrong MaximumRetryCount. Want 0. Got %d", policy.MaximumRetryCount) - } -} - -func TestRestartOnFailure(t *testing.T) { - const retry = 5 - policy := RestartOnFailure(retry) - if policy.Name != "on-failure" { - t.Errorf("RestartOnFailure(%d): wrong policy name. Want %q. Got %q", retry, "on-failure", policy.Name) - } - if policy.MaximumRetryCount != retry { - t.Errorf("RestartOnFailure(%d): wrong MaximumRetryCount. Want %d. Got %d", retry, retry, policy.MaximumRetryCount) - } -} - -func TestNeverRestart(t *testing.T) { - policy := NeverRestart() - if policy.Name != "no" { - t.Errorf("NeverRestart(): wrong policy name. Want %q. Got %q", "always", policy.Name) - } - if policy.MaximumRetryCount != 0 { - t.Errorf("NeverRestart(): wrong MaximumRetryCount. Want 0. Got %d", policy.MaximumRetryCount) - } -} - -func TestTopContainer(t *testing.T) { - jsonTop := `{ - "Processes": [ - [ - "ubuntu", - "3087", - "815", - "0", - "01:44", - "?", - "00:00:00", - "cmd1" - ], - [ - "root", - "3158", - "3087", - "0", - "01:44", - "?", - "00:00:01", - "cmd2" - ] - ], - "Titles": [ - "UID", - "PID", - "PPID", - "C", - "STIME", - "TTY", - "TIME", - "CMD" - ] -}` - var expected TopResult - err := json.Unmarshal([]byte(jsonTop), &expected) - if err != nil { - t.Fatal(err) - } - id := "4fa6e0f0" - fakeRT := &FakeRoundTripper{message: jsonTop, status: http.StatusOK} - client := newTestClient(fakeRT) - processes, err := client.TopContainer(id, "") - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(processes, expected) { - t.Errorf("TopContainer: Expected %#v. Got %#v.", expected, processes) - } - if len(processes.Processes) != 2 || len(processes.Processes[0]) != 8 || - processes.Processes[0][7] != "cmd1" { - t.Errorf("TopContainer: Process list to include cmd1. Got %#v.", processes) - } - expectedURI := "/containers/" + id + "/top" - if !strings.HasSuffix(fakeRT.requests[0].URL.String(), expectedURI) { - t.Errorf("TopContainer: Expected URI to have %q. Got %q.", expectedURI, fakeRT.requests[0].URL.String()) - } -} - -func TestTopContainerNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound}) - _, err := client.TopContainer("abef348", "") - expected := &NoSuchContainer{ID: "abef348"} - if !reflect.DeepEqual(err, expected) { - t.Errorf("StopContainer: Wrong error returned. Want %#v. Got %#v.", expected, err) - } -} - -func TestTopContainerWithPsArgs(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "no such container", status: http.StatusNotFound} - client := newTestClient(fakeRT) - expectedErr := &NoSuchContainer{ID: "abef348"} - if _, err := client.TopContainer("abef348", "aux"); !reflect.DeepEqual(expectedErr, err) { - t.Errorf("TopContainer: Expected %v. Got %v.", expectedErr, err) - } - expectedURI := "/containers/abef348/top?ps_args=aux" - if !strings.HasSuffix(fakeRT.requests[0].URL.String(), expectedURI) { - t.Errorf("TopContainer: Expected URI to have %q. Got %q.", expectedURI, fakeRT.requests[0].URL.String()) - } -} - -func TestStatsTimeout(t *testing.T) { - l, err := net.Listen("unix", "/tmp/docker_test.sock") - if err != nil { - t.Fatal(err) - } - received := false - defer l.Close() - go func() { - l.Accept() - received = true - time.Sleep(time.Second) - }() - client, _ := NewClient("unix:///tmp/docker_test.sock") - client.SkipServerVersionCheck = true - errC := make(chan error, 1) - statsC := make(chan *Stats) - done := make(chan bool) - go func() { - errC <- client.Stats(StatsOptions{"c", statsC, true, done, time.Millisecond * 100}) - close(errC) - }() - err = <-errC - e, ok := err.(net.Error) - if !ok || !e.Timeout() { - t.Error("Failed to receive timeout exception") - } - if !received { - t.Fatal("Failed to receive message") - } -} - -func TestStats(t *testing.T) { - jsonStats1 := `{ - "read" : "2015-01-08T22:57:31.547920715Z", - "networks" : { - "eth0":{ - "rx_dropped" : 0, - "rx_bytes" : 648, - "rx_errors" : 0, - "tx_packets" : 8, - "tx_dropped" : 0, - "rx_packets" : 8, - "tx_errors" : 0, - "tx_bytes" : 648 - } - }, - "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": 189204833, - "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": { - "io_service_bytes_recursive": [ - { - "major": 8, - "minor": 0, - "op": "Read", - "value": 428795731968 - }, - { - "major": 8, - "minor": 0, - "op": "Write", - "value": 388177920 - } - ], - "io_serviced_recursive": [ - { - "major": 8, - "minor": 0, - "op": "Read", - "value": 25994442 - }, - { - "major": 8, - "minor": 0, - "op": "Write", - "value": 1734 - } - ], - "io_queue_recursive": [], - "io_service_time_recursive": [], - "io_wait_time_recursive": [], - "io_merged_recursive": [], - "io_time_recursive": [], - "sectors_recursive": [] - }, - "cpu_stats" : { - "cpu_usage" : { - "percpu_usage" : [ - 16970827, - 1839451, - 7107380, - 10571290 - ], - "usage_in_usermode" : 10000000, - "total_usage" : 36488948, - "usage_in_kernelmode" : 20000000 - }, - "system_cpu_usage" : 20091722000000000 - }, - "precpu_stats" : { - "cpu_usage" : { - "percpu_usage" : [ - 16970827, - 1839451, - 7107380, - 10571290 - ], - "usage_in_usermode" : 10000000, - "total_usage" : 36488948, - "usage_in_kernelmode" : 20000000 - }, - "system_cpu_usage" : 20091722000000000 - } - }` - // 1 second later, cache is 100 - jsonStats2 := `{ - "read" : "2015-01-08T22:57:32.547920715Z", - "networks" : { - "eth0":{ - "rx_dropped" : 0, - "rx_bytes" : 648, - "rx_errors" : 0, - "tx_packets" : 8, - "tx_dropped" : 0, - "rx_packets" : 8, - "tx_errors" : 0, - "tx_bytes" : 648 - } - }, - "memory_stats" : { - "stats" : { - "total_pgmajfault" : 0, - "cache" : 100, - "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, - "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": { - "io_service_bytes_recursive": [ - { - "major": 8, - "minor": 0, - "op": "Read", - "value": 428795731968 - }, - { - "major": 8, - "minor": 0, - "op": "Write", - "value": 388177920 - } - ], - "io_serviced_recursive": [ - { - "major": 8, - "minor": 0, - "op": "Read", - "value": 25994442 - }, - { - "major": 8, - "minor": 0, - "op": "Write", - "value": 1734 - } - ], - "io_queue_recursive": [], - "io_service_time_recursive": [], - "io_wait_time_recursive": [], - "io_merged_recursive": [], - "io_time_recursive": [], - "sectors_recursive": [] - }, - "cpu_stats" : { - "cpu_usage" : { - "percpu_usage" : [ - 16970827, - 1839451, - 7107380, - 10571290 - ], - "usage_in_usermode" : 10000000, - "total_usage" : 36488948, - "usage_in_kernelmode" : 20000000 - }, - "system_cpu_usage" : 20091722000000000 - }, - "precpu_stats" : { - "cpu_usage" : { - "percpu_usage" : [ - 16970827, - 1839451, - 7107380, - 10571290 - ], - "usage_in_usermode" : 10000000, - "total_usage" : 36488948, - "usage_in_kernelmode" : 20000000 - }, - "system_cpu_usage" : 20091722000000000 - } - }` - var expected1 Stats - var expected2 Stats - err := json.Unmarshal([]byte(jsonStats1), &expected1) - if err != nil { - t.Fatal(err) - } - err = json.Unmarshal([]byte(jsonStats2), &expected2) - if err != nil { - t.Fatal(err) - } - id := "4fa6e0f0" - - var req http.Request - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte(jsonStats1)) - w.Write([]byte(jsonStats2)) - req = *r - })) - defer server.Close() - client, _ := NewClient(server.URL) - client.SkipServerVersionCheck = true - errC := make(chan error, 1) - statsC := make(chan *Stats) - done := make(chan bool) - go func() { - errC <- client.Stats(StatsOptions{id, statsC, true, done, 0}) - close(errC) - }() - var resultStats []*Stats - for { - stats, ok := <-statsC - if !ok { - break - } - resultStats = append(resultStats, stats) - } - err = <-errC - if err != nil { - t.Fatal(err) - } - if len(resultStats) != 2 { - t.Fatalf("Stats: Expected 2 results. Got %d.", len(resultStats)) - } - if !reflect.DeepEqual(resultStats[0], &expected1) { - t.Errorf("Stats: Expected:\n%+v\nGot:\n%+v", expected1, resultStats[0]) - } - if !reflect.DeepEqual(resultStats[1], &expected2) { - t.Errorf("Stats: Expected:\n%+v\nGot:\n%+v", expected2, resultStats[1]) - } - if req.Method != "GET" { - t.Errorf("Stats: wrong HTTP method. Want GET. Got %s.", req.Method) - } - u, _ := url.Parse(client.getURL("/containers/" + id + "/stats")) - if req.URL.Path != u.Path { - t.Errorf("Stats: wrong HTTP path. Want %q. Got %q.", u.Path, req.URL.Path) - } -} - -func TestStatsContainerNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound}) - statsC := make(chan *Stats) - done := make(chan bool) - err := client.Stats(StatsOptions{"abef348", statsC, true, done, 0}) - expected := &NoSuchContainer{ID: "abef348"} - if !reflect.DeepEqual(err, expected) { - t.Errorf("Stats: Wrong error returned. Want %#v. Got %#v.", expected, err) - } -} - -func TestRenameContainer(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - opts := RenameContainerOptions{ID: "something_old", Name: "something_new"} - err := client.RenameContainer(opts) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("RenameContainer: wrong HTTP method. Want %q. Got %q.", "POST", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/containers/something_old/rename?name=something_new")) - if gotPath := req.URL.Path; gotPath != expectedURL.Path { - t.Errorf("RenameContainer: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath) - } - expectedValues := expectedURL.Query()["name"] - actualValues := req.URL.Query()["name"] - if len(actualValues) != 1 || expectedValues[0] != actualValues[0] { - t.Errorf("RenameContainer: Wrong params in request. Want %q. Got %q.", expectedValues, actualValues) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/env_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/env_test.go deleted file mode 100644 index df5169d06..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/env_test.go +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright 2014 go-dockerclient authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the DOCKER-LICENSE file. - -package docker - -import ( - "bytes" - "errors" - "reflect" - "sort" - "testing" -) - -func TestGet(t *testing.T) { - var tests = []struct { - input []string - query string - expected string - }{ - {[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, "PATH", "/usr/bin:/bin"}, - {[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, "PYTHONPATH", "/usr/local"}, - {[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, "PYTHONPATHI", ""}, - {[]string{"WAT="}, "WAT", ""}, - } - for _, tt := range tests { - env := Env(tt.input) - got := env.Get(tt.query) - if got != tt.expected { - t.Errorf("Env.Get(%q): wrong result. Want %q. Got %q", tt.query, tt.expected, got) - } - } -} - -func TestExists(t *testing.T) { - var tests = []struct { - input []string - query string - expected bool - }{ - {[]string{"WAT=", "PYTHONPATH=/usr/local"}, "WAT", true}, - {[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, "PYTHONPATH", true}, - {[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, "PYTHONPATHI", false}, - } - for _, tt := range tests { - env := Env(tt.input) - got := env.Exists(tt.query) - if got != tt.expected { - t.Errorf("Env.Exists(%q): wrong result. Want %v. Got %v", tt.query, tt.expected, got) - } - } -} - -func TestGetBool(t *testing.T) { - var tests = []struct { - input string - expected bool - }{ - {"EMTPY_VAR", false}, {"ZERO_VAR", false}, {"NO_VAR", false}, - {"FALSE_VAR", false}, {"NONE_VAR", false}, {"TRUE_VAR", true}, - {"WAT", true}, {"PATH", true}, {"ONE_VAR", true}, {"NO_VAR_TAB", false}, - } - env := Env([]string{ - "EMPTY_VAR=", "ZERO_VAR=0", "NO_VAR=no", "FALSE_VAR=false", - "NONE_VAR=none", "TRUE_VAR=true", "WAT=wat", "PATH=/usr/bin:/bin", - "ONE_VAR=1", "NO_VAR_TAB=0 \t\t\t", - }) - for _, tt := range tests { - got := env.GetBool(tt.input) - if got != tt.expected { - t.Errorf("Env.GetBool(%q): wrong result. Want %v. Got %v.", tt.input, tt.expected, got) - } - } -} - -func TestSetBool(t *testing.T) { - var tests = []struct { - input bool - expected string - }{ - {true, "1"}, {false, "0"}, - } - for _, tt := range tests { - var env Env - env.SetBool("SOME", tt.input) - if got := env.Get("SOME"); got != tt.expected { - t.Errorf("Env.SetBool(%v): wrong result. Want %q. Got %q", tt.input, tt.expected, got) - } - } -} - -func TestGetInt(t *testing.T) { - var tests = []struct { - input string - expected int - }{ - {"NEGATIVE_INTEGER", -10}, {"NON_INTEGER", -1}, {"ONE", 1}, {"TWO", 2}, - } - env := Env([]string{"NEGATIVE_INTEGER=-10", "NON_INTEGER=wat", "ONE=1", "TWO=2"}) - for _, tt := range tests { - got := env.GetInt(tt.input) - if got != tt.expected { - t.Errorf("Env.GetInt(%q): wrong result. Want %d. Got %d", tt.input, tt.expected, got) - } - } -} - -func TestSetInt(t *testing.T) { - var tests = []struct { - input int - expected string - }{ - {10, "10"}, {13, "13"}, {7, "7"}, {33, "33"}, - {0, "0"}, {-34, "-34"}, - } - for _, tt := range tests { - var env Env - env.SetInt("SOME", tt.input) - if got := env.Get("SOME"); got != tt.expected { - t.Errorf("Env.SetBool(%d): wrong result. Want %q. Got %q", tt.input, tt.expected, got) - } - } -} - -func TestGetInt64(t *testing.T) { - var tests = []struct { - input string - expected int64 - }{ - {"NEGATIVE_INTEGER", -10}, {"NON_INTEGER", -1}, {"ONE", 1}, {"TWO", 2}, - } - env := Env([]string{"NEGATIVE_INTEGER=-10", "NON_INTEGER=wat", "ONE=1", "TWO=2"}) - for _, tt := range tests { - got := env.GetInt64(tt.input) - if got != tt.expected { - t.Errorf("Env.GetInt64(%q): wrong result. Want %d. Got %d", tt.input, tt.expected, got) - } - } -} - -func TestSetInt64(t *testing.T) { - var tests = []struct { - input int64 - expected string - }{ - {10, "10"}, {13, "13"}, {7, "7"}, {33, "33"}, - {0, "0"}, {-34, "-34"}, - } - for _, tt := range tests { - var env Env - env.SetInt64("SOME", tt.input) - if got := env.Get("SOME"); got != tt.expected { - t.Errorf("Env.SetBool(%d): wrong result. Want %q. Got %q", tt.input, tt.expected, got) - } - } -} - -func TestGetJSON(t *testing.T) { - var p struct { - Name string `json:"name"` - Age int `json:"age"` - } - var env Env - env.Set("person", `{"name":"Gopher","age":5}`) - err := env.GetJSON("person", &p) - if err != nil { - t.Error(err) - } - if p.Name != "Gopher" { - t.Errorf("Env.GetJSON(%q): wrong name. Want %q. Got %q", "person", "Gopher", p.Name) - } - if p.Age != 5 { - t.Errorf("Env.GetJSON(%q): wrong age. Want %d. Got %d", "person", 5, p.Age) - } -} - -func TestGetJSONAbsent(t *testing.T) { - var l []string - var env Env - err := env.GetJSON("person", &l) - if err != nil { - t.Error(err) - } - if l != nil { - t.Errorf("Env.GetJSON(): get unexpected list %v", l) - } -} - -func TestGetJSONFailure(t *testing.T) { - var p []string - var env Env - env.Set("list-person", `{"name":"Gopher","age":5}`) - err := env.GetJSON("list-person", &p) - if err == nil { - t.Errorf("Env.GetJSON(%q): got unexpected error.", "list-person") - } -} - -func TestSetJSON(t *testing.T) { - var p1 = struct { - Name string `json:"name"` - Age int `json:"age"` - }{Name: "Gopher", Age: 5} - var env Env - err := env.SetJSON("person", p1) - if err != nil { - t.Error(err) - } - var p2 struct { - Name string `json:"name"` - Age int `json:"age"` - } - err = env.GetJSON("person", &p2) - if err != nil { - t.Error(err) - } - if !reflect.DeepEqual(p1, p2) { - t.Errorf("Env.SetJSON(%q): wrong result. Want %v. Got %v", "person", p1, p2) - } -} - -func TestSetJSONFailure(t *testing.T) { - var env Env - err := env.SetJSON("person", unmarshable{}) - if err == nil { - t.Error("Env.SetJSON(): got unexpected error") - } - if env.Exists("person") { - t.Errorf("Env.SetJSON(): should not define the key %q, but did", "person") - } -} - -func TestGetList(t *testing.T) { - var tests = []struct { - input string - expected []string - }{ - {"WAT=wat", []string{"wat"}}, - {`WAT=["wat","wet","wit","wot","wut"]`, []string{"wat", "wet", "wit", "wot", "wut"}}, - {"WAT=", nil}, - } - for _, tt := range tests { - env := Env([]string{tt.input}) - got := env.GetList("WAT") - if !reflect.DeepEqual(got, tt.expected) { - t.Errorf("Env.GetList(%q): wrong result. Want %v. Got %v", "WAT", tt.expected, got) - } - } -} - -func TestSetList(t *testing.T) { - list := []string{"a", "b", "c"} - var env Env - if err := env.SetList("SOME", list); err != nil { - t.Error(err) - } - if got := env.GetList("SOME"); !reflect.DeepEqual(got, list) { - t.Errorf("Env.SetList(%v): wrong result. Got %v", list, got) - } -} - -func TestSet(t *testing.T) { - var env Env - env.Set("PATH", "/home/bin:/bin") - env.Set("SOMETHING", "/usr/bin") - env.Set("PATH", "/bin") - if expected, got := "/usr/bin", env.Get("SOMETHING"); got != expected { - t.Errorf("Env.Set(%q): wrong result. Want %q. Got %q", expected, expected, got) - } - if expected, got := "/bin", env.Get("PATH"); got != expected { - t.Errorf("Env.Set(%q): wrong result. Want %q. Got %q", expected, expected, got) - } -} - -func TestDecode(t *testing.T) { - var tests = []struct { - input string - expectedOut []string - expectedErr string - }{ - { - `{"PATH":"/usr/bin:/bin","containers":54,"wat":["123","345"]}`, - []string{"PATH=/usr/bin:/bin", "containers=54", `wat=["123","345"]`}, - "", - }, - {"}}", nil, "invalid character '}' looking for beginning of value"}, - {`{}`, nil, ""}, - } - for _, tt := range tests { - var env Env - err := env.Decode(bytes.NewBufferString(tt.input)) - if tt.expectedErr == "" { - if err != nil { - t.Error(err) - } - } else if tt.expectedErr != err.Error() { - t.Errorf("Env.Decode(): invalid error. Want %q. Got %q.", tt.expectedErr, err) - } - got := []string(env) - sort.Strings(got) - sort.Strings(tt.expectedOut) - if !reflect.DeepEqual(got, tt.expectedOut) { - t.Errorf("Env.Decode(): wrong result. Want %v. Got %v.", tt.expectedOut, got) - } - } -} - -func TestSetAuto(t *testing.T) { - buf := bytes.NewBufferString("oi") - var tests = []struct { - input interface{} - expected string - }{ - {10, "10"}, - {10.3, "10"}, - {"oi", "oi"}, - {buf, "{}"}, - {unmarshable{}, "{}"}, - } - for _, tt := range tests { - var env Env - env.SetAuto("SOME", tt.input) - if got := env.Get("SOME"); got != tt.expected { - t.Errorf("Env.SetAuto(%v): wrong result. Want %q. Got %q", tt.input, tt.expected, got) - } - } -} - -func TestMap(t *testing.T) { - var tests = []struct { - input []string - expected map[string]string - }{ - {[]string{"PATH=/usr/bin:/bin", "PYTHONPATH=/usr/local"}, map[string]string{"PATH": "/usr/bin:/bin", "PYTHONPATH": "/usr/local"}}, - {nil, nil}, - } - for _, tt := range tests { - env := Env(tt.input) - got := env.Map() - if !reflect.DeepEqual(got, tt.expected) { - t.Errorf("Env.Map(): wrong result. Want %v. Got %v", tt.expected, got) - } - } -} - -type unmarshable struct { -} - -func (unmarshable) MarshalJSON() ([]byte, error) { - return nil, errors.New("cannot marshal") -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/event_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/event_test.go deleted file mode 100644 index a308538cc..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/event_test.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2014 go-dockerclient 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 docker - -import ( - "bufio" - "crypto/tls" - "crypto/x509" - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - "strings" - "testing" - "time" -) - -func TestEventListeners(t *testing.T) { - testEventListeners("TestEventListeners", t, httptest.NewServer, NewClient) -} - -func TestTLSEventListeners(t *testing.T) { - testEventListeners("TestTLSEventListeners", t, func(handler http.Handler) *httptest.Server { - server := httptest.NewUnstartedServer(handler) - - cert, err := tls.LoadX509KeyPair("testing/data/server.pem", "testing/data/serverkey.pem") - if err != nil { - t.Fatalf("Error loading server key pair: %s", err) - } - - caCert, err := ioutil.ReadFile("testing/data/ca.pem") - if err != nil { - t.Fatalf("Error loading ca certificate: %s", err) - } - caPool := x509.NewCertPool() - if !caPool.AppendCertsFromPEM(caCert) { - t.Fatalf("Could not add ca certificate") - } - - server.TLS = &tls.Config{ - Certificates: []tls.Certificate{cert}, - RootCAs: caPool, - } - server.StartTLS() - return server - }, func(url string) (*Client, error) { - return NewTLSClient(url, "testing/data/cert.pem", "testing/data/key.pem", "testing/data/ca.pem") - }) -} - -func testEventListeners(testName string, t *testing.T, buildServer func(http.Handler) *httptest.Server, buildClient func(string) (*Client, error)) { - response := `{"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924} -{"status":"start","id":"dfdf82bd3881","from":"base:latest","time":1374067924} -{"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} -{"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} -` - - server := buildServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - rsc := bufio.NewScanner(strings.NewReader(response)) - for rsc.Scan() { - w.Write([]byte(rsc.Text())) - w.(http.Flusher).Flush() - time.Sleep(10 * time.Millisecond) - } - })) - defer server.Close() - - client, err := buildClient(server.URL) - if err != nil { - t.Errorf("Failed to create client: %s", err) - } - client.SkipServerVersionCheck = true - - listener := make(chan *APIEvents, 10) - defer func() { - time.Sleep(10 * time.Millisecond) - if err := client.RemoveEventListener(listener); err != nil { - t.Error(err) - } - }() - - err = client.AddEventListener(listener) - if err != nil { - t.Errorf("Failed to add event listener: %s", err) - } - - timeout := time.After(1 * time.Second) - var count int - - for { - select { - case msg := <-listener: - t.Logf("Received: %v", *msg) - count++ - err = checkEvent(count, msg) - if err != nil { - t.Fatalf("Check event failed: %s", err) - } - if count == 4 { - return - } - case <-timeout: - t.Fatalf("%s timed out waiting on events", testName) - } - } -} - -func checkEvent(index int, event *APIEvents) error { - if event.ID != "dfdf82bd3881" { - return fmt.Errorf("event ID did not match. Expected dfdf82bd3881 got %s", event.ID) - } - if event.From != "base:latest" { - return fmt.Errorf("event from did not match. Expected base:latest got %s", event.From) - } - var status string - switch index { - case 1: - status = "create" - case 2: - status = "start" - case 3: - status = "stop" - case 4: - status = "destroy" - } - if event.Status != status { - return fmt.Errorf("event status did not match. Expected %s got %s", status, event.Status) - } - return nil -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/example_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/example_test.go deleted file mode 100644 index 8c2c719e6..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/example_test.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2014 go-dockerclient 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 docker_test - -import ( - "archive/tar" - "bytes" - "fmt" - "io" - "log" - "time" - - "github.com/fsouza/go-dockerclient" -) - -func ExampleClient_AttachToContainer() { - client, err := docker.NewClient("http://localhost:4243") - if err != nil { - log.Fatal(err) - } - client.SkipServerVersionCheck = true - // Reading logs from container a84849 and sending them to buf. - var buf bytes.Buffer - err = client.AttachToContainer(docker.AttachToContainerOptions{ - Container: "a84849", - OutputStream: &buf, - Logs: true, - Stdout: true, - Stderr: true, - }) - if err != nil { - log.Fatal(err) - } - log.Println(buf.String()) - buf.Reset() - err = client.AttachToContainer(docker.AttachToContainerOptions{ - Container: "a84849", - OutputStream: &buf, - Stdout: true, - Stream: true, - }) - if err != nil { - log.Fatal(err) - } - log.Println(buf.String()) -} - -func ExampleClient_CopyFromContainer() { - client, err := docker.NewClient("http://localhost:4243") - if err != nil { - log.Fatal(err) - } - cid := "a84849" - var buf bytes.Buffer - filename := "/tmp/output.txt" - err = client.CopyFromContainer(docker.CopyFromContainerOptions{ - Container: cid, - Resource: filename, - OutputStream: &buf, - }) - if err != nil { - log.Fatalf("Error while copying from %s: %s\n", cid, err) - } - content := new(bytes.Buffer) - r := bytes.NewReader(buf.Bytes()) - tr := tar.NewReader(r) - tr.Next() - if err != nil && err != io.EOF { - log.Fatal(err) - } - if _, err := io.Copy(content, tr); err != nil { - log.Fatal(err) - } - log.Println(buf.String()) -} - -func ExampleClient_BuildImage() { - client, err := docker.NewClient("http://localhost:4243") - if err != nil { - log.Fatal(err) - } - - t := time.Now() - inputbuf, outputbuf := bytes.NewBuffer(nil), bytes.NewBuffer(nil) - tr := tar.NewWriter(inputbuf) - tr.WriteHeader(&tar.Header{Name: "Dockerfile", Size: 10, ModTime: t, AccessTime: t, ChangeTime: t}) - tr.Write([]byte("FROM base\n")) - tr.Close() - opts := docker.BuildImageOptions{ - Name: "test", - InputStream: inputbuf, - OutputStream: outputbuf, - } - if err := client.BuildImage(opts); err != nil { - log.Fatal(err) - } -} - -func ExampleClient_ListenEvents() { - client, err := docker.NewClient("http://localhost:4243") - if err != nil { - log.Fatal(err) - } - - listener := make(chan *docker.APIEvents) - err = client.AddEventListener(listener) - if err != nil { - log.Fatal(err) - } - - defer func() { - - err = client.RemoveEventListener(listener) - if err != nil { - log.Fatal(err) - } - - }() - - timeout := time.After(1 * time.Second) - - for { - select { - case msg := <-listener: - log.Println(msg) - case <-timeout: - break - } - } - -} - -func ExampleEnv_Map() { - e := docker.Env([]string{"A=1", "B=2", "C=3"}) - envs := e.Map() - for k, v := range envs { - fmt.Printf("%s=%q\n", k, v) - } -} - -func ExampleEnv_SetJSON() { - type Person struct { - Name string - Age int - } - p := Person{Name: "Gopher", Age: 4} - var e docker.Env - err := e.SetJSON("person", p) - if err != nil { - log.Fatal(err) - } -} - -func ExampleEnv_GetJSON() { - type Person struct { - Name string - Age int - } - p := Person{Name: "Gopher", Age: 4} - var e docker.Env - e.Set("person", `{"name":"Gopher","age":4}`) - err := e.GetJSON("person", &p) - if err != nil { - log.Fatal(err) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec_test.go deleted file mode 100644 index 2dc8d2100..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec_test.go +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2015 go-dockerclient 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 docker - -import ( - "bytes" - "encoding/json" - "net/http" - "net/http/httptest" - "net/url" - "reflect" - "strings" - "testing" -) - -func TestExecCreate(t *testing.T) { - jsonContainer := `{"Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"}` - var expected struct{ ID string } - err := json.Unmarshal([]byte(jsonContainer), &expected) - if err != nil { - t.Fatal(err) - } - fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK} - client := newTestClient(fakeRT) - config := CreateExecOptions{ - Container: "test", - AttachStdin: true, - AttachStdout: true, - AttachStderr: false, - Tty: false, - Cmd: []string{"touch", "/tmp/file"}, - User: "a-user", - } - execObj, err := client.CreateExec(config) - if err != nil { - t.Fatal(err) - } - expectedID := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - if execObj.ID != expectedID { - t.Errorf("ExecCreate: wrong ID. Want %q. Got %q.", expectedID, execObj.ID) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("ExecCreate: wrong HTTP method. Want %q. Got %q.", "POST", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/containers/test/exec")) - if gotPath := req.URL.Path; gotPath != expectedURL.Path { - t.Errorf("ExecCreate: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath) - } - var gotBody struct{ ID string } - err = json.NewDecoder(req.Body).Decode(&gotBody) - if err != nil { - t.Fatal(err) - } -} - -func TestExecStartDetached(t *testing.T) { - execID := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - fakeRT := &FakeRoundTripper{status: http.StatusOK} - client := newTestClient(fakeRT) - config := StartExecOptions{ - Detach: true, - } - err := client.StartExec(execID, config) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("ExecStart: wrong HTTP method. Want %q. Got %q.", "POST", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/exec/" + execID + "/start")) - if gotPath := req.URL.Path; gotPath != expectedURL.Path { - t.Errorf("ExecCreate: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath) - } - t.Log(req.Body) - var gotBody struct{ Detach bool } - err = json.NewDecoder(req.Body).Decode(&gotBody) - if err != nil { - t.Fatal(err) - } - if !gotBody.Detach { - t.Fatal("Expected Detach in StartExecOptions to be true") - } -} - -func TestExecStartAndAttach(t *testing.T) { - var reader = strings.NewReader("send value") - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte{1, 0, 0, 0, 0, 0, 0, 5}) - w.Write([]byte("hello")) - })) - defer server.Close() - client, _ := NewClient(server.URL) - client.SkipServerVersionCheck = true - var stdout, stderr bytes.Buffer - success := make(chan struct{}) - execID := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - opts := StartExecOptions{ - OutputStream: &stdout, - ErrorStream: &stderr, - InputStream: reader, - RawTerminal: true, - Success: success, - } - go func() { - if err := client.StartExec(execID, opts); err != nil { - t.Error(err) - } - }() - <-success -} - -func TestExecResize(t *testing.T) { - execID := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2" - fakeRT := &FakeRoundTripper{status: http.StatusOK} - client := newTestClient(fakeRT) - err := client.ResizeExecTTY(execID, 10, 20) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("ExecStart: wrong HTTP method. Want %q. Got %q.", "POST", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/exec/" + execID + "/resize?h=10&w=20")) - if gotPath := req.URL.RequestURI(); gotPath != expectedURL.RequestURI() { - t.Errorf("ExecCreate: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath) - } -} - -func TestExecInspect(t *testing.T) { - jsonExec := `{ - "ID": "32adfeeec34250f9530ce1dafd40c6233832315e065ea6b362d745e2f63cde0e", - "Running": true, - "ExitCode": 0, - "ProcessConfig": { - "privileged": false, - "user": "", - "tty": true, - "entrypoint": "bash", - "arguments": [] - }, - "OpenStdin": true, - "OpenStderr": true, - "OpenStdout": true, - "Container": { - "State": { - "Running": true, - "Paused": false, - "Restarting": false, - "OOMKilled": false, - "Pid": 29392, - "ExitCode": 0, - "Error": "", - "StartedAt": "2015-01-21T17:08:59.634662178Z", - "FinishedAt": "0001-01-01T00:00:00Z" - }, - "ID": "922cd0568714763dc725b24b7c9801016b2a3de68e2a1dc989bf5abf07740521", - "Created": "2015-01-21T17:08:59.46407212Z", - "Path": "/bin/bash", - "Args": [ - "-lc", - "tsuru_unit_agent http://192.168.50.4:8080 689b30e0ab3adce374346de2e72512138e0e8b75 gtest /var/lib/tsuru/start && tail -f /dev/null" - ], - "Config": { - "Hostname": "922cd0568714", - "Domainname": "", - "User": "ubuntu", - "Memory": 0, - "MemorySwap": 0, - "CpuShares": 100, - "Cpuset": "", - "AttachStdin": false, - "AttachStdout": false, - "AttachStderr": false, - "PortSpecs": null, - "ExposedPorts": { - "8888/tcp": {} - }, - "Tty": false, - "OpenStdin": false, - "StdinOnce": false, - "Env": [ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" - ], - "Cmd": [ - "/bin/bash", - "-lc", - "tsuru_unit_agent http://192.168.50.4:8080 689b30e0ab3adce374346de2e72512138e0e8b75 gtest /var/lib/tsuru/start && tail -f /dev/null" - ], - "Image": "tsuru/app-gtest", - "Volumes": null, - "WorkingDir": "", - "Entrypoint": null, - "NetworkDisabled": false, - "MacAddress": "", - "OnBuild": null - }, - "Image": "a88060b8b54fde0f7168c86742d0ce83b80f3f10925d85c98fdad9ed00bef544", - "NetworkSettings": { - "IPAddress": "172.17.0.8", - "IPPrefixLen": 16, - "MacAddress": "02:42:ac:11:00:08", - "LinkLocalIPv6Address": "fe80::42:acff:fe11:8", - "LinkLocalIPv6PrefixLen": 64, - "GlobalIPv6Address": "", - "GlobalIPv6PrefixLen": 0, - "Gateway": "172.17.42.1", - "IPv6Gateway": "", - "Bridge": "docker0", - "PortMapping": null, - "Ports": { - "8888/tcp": [ - { - "HostIp": "0.0.0.0", - "HostPort": "49156" - } - ] - } - }, - "ResolvConfPath": "/var/lib/docker/containers/922cd0568714763dc725b24b7c9801016b2a3de68e2a1dc989bf5abf07740521/resolv.conf", - "HostnamePath": "/var/lib/docker/containers/922cd0568714763dc725b24b7c9801016b2a3de68e2a1dc989bf5abf07740521/hostname", - "HostsPath": "/var/lib/docker/containers/922cd0568714763dc725b24b7c9801016b2a3de68e2a1dc989bf5abf07740521/hosts", - "Name": "/c7e43b72288ee9d0270a", - "Driver": "aufs", - "ExecDriver": "native-0.2", - "MountLabel": "", - "ProcessLabel": "", - "AppArmorProfile": "", - "RestartCount": 0, - "UpdateDns": false, - "Volumes": {}, - "VolumesRW": {} - } - }` - var expected ExecInspect - err := json.Unmarshal([]byte(jsonExec), &expected) - if err != nil { - t.Fatal(err) - } - fakeRT := &FakeRoundTripper{message: jsonExec, status: http.StatusOK} - client := newTestClient(fakeRT) - expectedID := "32adfeeec34250f9530ce1dafd40c6233832315e065ea6b362d745e2f63cde0e" - execObj, err := client.InspectExec(expectedID) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(*execObj, expected) { - t.Errorf("ExecInspect: Expected %#v. Got %#v.", expected, *execObj) - } - req := fakeRT.requests[0] - if req.Method != "GET" { - t.Errorf("ExecInspect: wrong HTTP method. Want %q. Got %q.", "GET", req.Method) - } - expectedURL, _ := url.Parse(client.getURL("/exec/" + expectedID + "/json")) - if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path { - t.Errorf("ExecInspect: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/entry_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/entry_test.go deleted file mode 100644 index 2b1cc4e1d..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/entry_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package logrus - -import ( - "bytes" - "fmt" - "testing" - - "github.com/fsouza/go-dockerclient/external/github.com/stretchr/testify/assert" -) - -func TestEntryWithError(t *testing.T) { - - assert := assert.New(t) - - defer func() { - ErrorKey = "error" - }() - - err := fmt.Errorf("kaboom at layer %d", 4711) - - assert.Equal(err, WithError(err).Data["error"]) - - logger := New() - logger.Out = &bytes.Buffer{} - entry := NewEntry(logger) - - assert.Equal(err, entry.WithError(err).Data["error"]) - - ErrorKey = "err" - - assert.Equal(err, entry.WithError(err).Data["err"]) - -} - -func TestEntryPanicln(t *testing.T) { - errBoom := fmt.Errorf("boom time") - - defer func() { - p := recover() - assert.NotNil(t, p) - - switch pVal := p.(type) { - case *Entry: - assert.Equal(t, "kaboom", pVal.Message) - assert.Equal(t, errBoom, pVal.Data["err"]) - default: - t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal) - } - }() - - logger := New() - logger.Out = &bytes.Buffer{} - entry := NewEntry(logger) - entry.WithField("err", errBoom).Panicln("kaboom") -} - -func TestEntryPanicf(t *testing.T) { - errBoom := fmt.Errorf("boom again") - - defer func() { - p := recover() - assert.NotNil(t, p) - - switch pVal := p.(type) { - case *Entry: - assert.Equal(t, "kaboom true", pVal.Message) - assert.Equal(t, errBoom, pVal.Data["err"]) - default: - t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal) - } - }() - - logger := New() - logger.Out = &bytes.Buffer{} - entry := NewEntry(logger) - entry.WithField("err", errBoom).Panicf("kaboom %v", true) -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/formatter_bench_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/formatter_bench_test.go deleted file mode 100644 index c6d290c77..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/formatter_bench_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package logrus - -import ( - "fmt" - "testing" - "time" -) - -// smallFields is a small size data set for benchmarking -var smallFields = Fields{ - "foo": "bar", - "baz": "qux", - "one": "two", - "three": "four", -} - -// largeFields is a large size data set for benchmarking -var largeFields = Fields{ - "foo": "bar", - "baz": "qux", - "one": "two", - "three": "four", - "five": "six", - "seven": "eight", - "nine": "ten", - "eleven": "twelve", - "thirteen": "fourteen", - "fifteen": "sixteen", - "seventeen": "eighteen", - "nineteen": "twenty", - "a": "b", - "c": "d", - "e": "f", - "g": "h", - "i": "j", - "k": "l", - "m": "n", - "o": "p", - "q": "r", - "s": "t", - "u": "v", - "w": "x", - "y": "z", - "this": "will", - "make": "thirty", - "entries": "yeah", -} - -var errorFields = Fields{ - "foo": fmt.Errorf("bar"), - "baz": fmt.Errorf("qux"), -} - -func BenchmarkErrorTextFormatter(b *testing.B) { - doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields) -} - -func BenchmarkSmallTextFormatter(b *testing.B) { - doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields) -} - -func BenchmarkLargeTextFormatter(b *testing.B) { - doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields) -} - -func BenchmarkSmallColoredTextFormatter(b *testing.B) { - doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields) -} - -func BenchmarkLargeColoredTextFormatter(b *testing.B) { - doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields) -} - -func BenchmarkSmallJSONFormatter(b *testing.B) { - doBenchmark(b, &JSONFormatter{}, smallFields) -} - -func BenchmarkLargeJSONFormatter(b *testing.B) { - doBenchmark(b, &JSONFormatter{}, largeFields) -} - -func doBenchmark(b *testing.B, formatter Formatter, fields Fields) { - entry := &Entry{ - Time: time.Time{}, - Level: InfoLevel, - Message: "message", - Data: fields, - } - var d []byte - var err error - for i := 0; i < b.N; i++ { - d, err = formatter.Format(entry) - if err != nil { - b.Fatal(err) - } - b.SetBytes(int64(len(d))) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/hook_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/hook_test.go deleted file mode 100644 index 938b97495..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/hook_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package logrus - -import ( - "testing" - - "github.com/fsouza/go-dockerclient/external/github.com/stretchr/testify/assert" -) - -type TestHook struct { - Fired bool -} - -func (hook *TestHook) Fire(entry *Entry) error { - hook.Fired = true - return nil -} - -func (hook *TestHook) Levels() []Level { - return []Level{ - DebugLevel, - InfoLevel, - WarnLevel, - ErrorLevel, - FatalLevel, - PanicLevel, - } -} - -func TestHookFires(t *testing.T) { - hook := new(TestHook) - - LogAndAssertJSON(t, func(log *Logger) { - log.Hooks.Add(hook) - assert.Equal(t, hook.Fired, false) - - log.Print("test") - }, func(fields Fields) { - assert.Equal(t, hook.Fired, true) - }) -} - -type ModifyHook struct { -} - -func (hook *ModifyHook) Fire(entry *Entry) error { - entry.Data["wow"] = "whale" - return nil -} - -func (hook *ModifyHook) Levels() []Level { - return []Level{ - DebugLevel, - InfoLevel, - WarnLevel, - ErrorLevel, - FatalLevel, - PanicLevel, - } -} - -func TestHookCanModifyEntry(t *testing.T) { - hook := new(ModifyHook) - - LogAndAssertJSON(t, func(log *Logger) { - log.Hooks.Add(hook) - log.WithField("wow", "elephant").Print("test") - }, func(fields Fields) { - assert.Equal(t, fields["wow"], "whale") - }) -} - -func TestCanFireMultipleHooks(t *testing.T) { - hook1 := new(ModifyHook) - hook2 := new(TestHook) - - LogAndAssertJSON(t, func(log *Logger) { - log.Hooks.Add(hook1) - log.Hooks.Add(hook2) - - log.WithField("wow", "elephant").Print("test") - }, func(fields Fields) { - assert.Equal(t, fields["wow"], "whale") - assert.Equal(t, hook2.Fired, true) - }) -} - -type ErrorHook struct { - Fired bool -} - -func (hook *ErrorHook) Fire(entry *Entry) error { - hook.Fired = true - return nil -} - -func (hook *ErrorHook) Levels() []Level { - return []Level{ - ErrorLevel, - } -} - -func TestErrorHookShouldntFireOnInfo(t *testing.T) { - hook := new(ErrorHook) - - LogAndAssertJSON(t, func(log *Logger) { - log.Hooks.Add(hook) - log.Info("test") - }, func(fields Fields) { - assert.Equal(t, hook.Fired, false) - }) -} - -func TestErrorHookShouldFireOnError(t *testing.T) { - hook := new(ErrorHook) - - LogAndAssertJSON(t, func(log *Logger) { - log.Hooks.Add(hook) - log.Error("test") - }, func(fields Fields) { - assert.Equal(t, hook.Fired, true) - }) -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/json_formatter_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/json_formatter_test.go deleted file mode 100644 index 1d7087325..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/json_formatter_test.go +++ /dev/null @@ -1,120 +0,0 @@ -package logrus - -import ( - "encoding/json" - "errors" - - "testing" -) - -func TestErrorNotLost(t *testing.T) { - formatter := &JSONFormatter{} - - b, err := formatter.Format(WithField("error", errors.New("wild walrus"))) - if err != nil { - t.Fatal("Unable to format entry: ", err) - } - - entry := make(map[string]interface{}) - err = json.Unmarshal(b, &entry) - if err != nil { - t.Fatal("Unable to unmarshal formatted entry: ", err) - } - - if entry["error"] != "wild walrus" { - t.Fatal("Error field not set") - } -} - -func TestErrorNotLostOnFieldNotNamedError(t *testing.T) { - formatter := &JSONFormatter{} - - b, err := formatter.Format(WithField("omg", errors.New("wild walrus"))) - if err != nil { - t.Fatal("Unable to format entry: ", err) - } - - entry := make(map[string]interface{}) - err = json.Unmarshal(b, &entry) - if err != nil { - t.Fatal("Unable to unmarshal formatted entry: ", err) - } - - if entry["omg"] != "wild walrus" { - t.Fatal("Error field not set") - } -} - -func TestFieldClashWithTime(t *testing.T) { - formatter := &JSONFormatter{} - - b, err := formatter.Format(WithField("time", "right now!")) - if err != nil { - t.Fatal("Unable to format entry: ", err) - } - - entry := make(map[string]interface{}) - err = json.Unmarshal(b, &entry) - if err != nil { - t.Fatal("Unable to unmarshal formatted entry: ", err) - } - - if entry["fields.time"] != "right now!" { - t.Fatal("fields.time not set to original time field") - } - - if entry["time"] != "0001-01-01T00:00:00Z" { - t.Fatal("time field not set to current time, was: ", entry["time"]) - } -} - -func TestFieldClashWithMsg(t *testing.T) { - formatter := &JSONFormatter{} - - b, err := formatter.Format(WithField("msg", "something")) - if err != nil { - t.Fatal("Unable to format entry: ", err) - } - - entry := make(map[string]interface{}) - err = json.Unmarshal(b, &entry) - if err != nil { - t.Fatal("Unable to unmarshal formatted entry: ", err) - } - - if entry["fields.msg"] != "something" { - t.Fatal("fields.msg not set to original msg field") - } -} - -func TestFieldClashWithLevel(t *testing.T) { - formatter := &JSONFormatter{} - - b, err := formatter.Format(WithField("level", "something")) - if err != nil { - t.Fatal("Unable to format entry: ", err) - } - - entry := make(map[string]interface{}) - err = json.Unmarshal(b, &entry) - if err != nil { - t.Fatal("Unable to unmarshal formatted entry: ", err) - } - - if entry["fields.level"] != "something" { - t.Fatal("fields.level not set to original level field") - } -} - -func TestJSONEntryEndsWithNewline(t *testing.T) { - formatter := &JSONFormatter{} - - b, err := formatter.Format(WithField("level", "something")) - if err != nil { - t.Fatal("Unable to format entry: ", err) - } - - if b[len(b)-1] != '\n' { - t.Fatal("Expected JSON log entry to end with a newline") - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/logrus_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/logrus_test.go deleted file mode 100644 index e8719b090..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/logrus_test.go +++ /dev/null @@ -1,301 +0,0 @@ -package logrus - -import ( - "bytes" - "encoding/json" - "strconv" - "strings" - "sync" - "testing" - - "github.com/fsouza/go-dockerclient/external/github.com/stretchr/testify/assert" -) - -func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) { - var buffer bytes.Buffer - var fields Fields - - logger := New() - logger.Out = &buffer - logger.Formatter = new(JSONFormatter) - - log(logger) - - err := json.Unmarshal(buffer.Bytes(), &fields) - assert.Nil(t, err) - - assertions(fields) -} - -func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) { - var buffer bytes.Buffer - - logger := New() - logger.Out = &buffer - logger.Formatter = &TextFormatter{ - DisableColors: true, - } - - log(logger) - - fields := make(map[string]string) - for _, kv := range strings.Split(buffer.String(), " ") { - if !strings.Contains(kv, "=") { - continue - } - kvArr := strings.Split(kv, "=") - key := strings.TrimSpace(kvArr[0]) - val := kvArr[1] - if kvArr[1][0] == '"' { - var err error - val, err = strconv.Unquote(val) - assert.NoError(t, err) - } - fields[key] = val - } - assertions(fields) -} - -func TestPrint(t *testing.T) { - LogAndAssertJSON(t, func(log *Logger) { - log.Print("test") - }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test") - assert.Equal(t, fields["level"], "info") - }) -} - -func TestInfo(t *testing.T) { - LogAndAssertJSON(t, func(log *Logger) { - log.Info("test") - }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test") - assert.Equal(t, fields["level"], "info") - }) -} - -func TestWarn(t *testing.T) { - LogAndAssertJSON(t, func(log *Logger) { - log.Warn("test") - }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test") - assert.Equal(t, fields["level"], "warning") - }) -} - -func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) { - LogAndAssertJSON(t, func(log *Logger) { - log.Infoln("test", "test") - }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test test") - }) -} - -func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) { - LogAndAssertJSON(t, func(log *Logger) { - log.Infoln("test", 10) - }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test 10") - }) -} - -func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) { - LogAndAssertJSON(t, func(log *Logger) { - log.Infoln(10, 10) - }, func(fields Fields) { - assert.Equal(t, fields["msg"], "10 10") - }) -} - -func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) { - LogAndAssertJSON(t, func(log *Logger) { - log.Infoln(10, 10) - }, func(fields Fields) { - assert.Equal(t, fields["msg"], "10 10") - }) -} - -func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) { - LogAndAssertJSON(t, func(log *Logger) { - log.Info("test", 10) - }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test10") - }) -} - -func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) { - LogAndAssertJSON(t, func(log *Logger) { - log.Info("test", "test") - }, func(fields Fields) { - assert.Equal(t, fields["msg"], "testtest") - }) -} - -func TestWithFieldsShouldAllowAssignments(t *testing.T) { - var buffer bytes.Buffer - var fields Fields - - logger := New() - logger.Out = &buffer - logger.Formatter = new(JSONFormatter) - - localLog := logger.WithFields(Fields{ - "key1": "value1", - }) - - localLog.WithField("key2", "value2").Info("test") - err := json.Unmarshal(buffer.Bytes(), &fields) - assert.Nil(t, err) - - assert.Equal(t, "value2", fields["key2"]) - assert.Equal(t, "value1", fields["key1"]) - - buffer = bytes.Buffer{} - fields = Fields{} - localLog.Info("test") - err = json.Unmarshal(buffer.Bytes(), &fields) - assert.Nil(t, err) - - _, ok := fields["key2"] - assert.Equal(t, false, ok) - assert.Equal(t, "value1", fields["key1"]) -} - -func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) { - LogAndAssertJSON(t, func(log *Logger) { - log.WithField("msg", "hello").Info("test") - }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test") - }) -} - -func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) { - LogAndAssertJSON(t, func(log *Logger) { - log.WithField("msg", "hello").Info("test") - }, func(fields Fields) { - assert.Equal(t, fields["msg"], "test") - assert.Equal(t, fields["fields.msg"], "hello") - }) -} - -func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) { - LogAndAssertJSON(t, func(log *Logger) { - log.WithField("time", "hello").Info("test") - }, func(fields Fields) { - assert.Equal(t, fields["fields.time"], "hello") - }) -} - -func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) { - LogAndAssertJSON(t, func(log *Logger) { - log.WithField("level", 1).Info("test") - }, func(fields Fields) { - assert.Equal(t, fields["level"], "info") - assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only - }) -} - -func TestDefaultFieldsAreNotPrefixed(t *testing.T) { - LogAndAssertText(t, func(log *Logger) { - ll := log.WithField("herp", "derp") - ll.Info("hello") - ll.Info("bye") - }, func(fields map[string]string) { - for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} { - if _, ok := fields[fieldName]; ok { - t.Fatalf("should not have prefixed %q: %v", fieldName, fields) - } - } - }) -} - -func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) { - - var buffer bytes.Buffer - var fields Fields - - logger := New() - logger.Out = &buffer - logger.Formatter = new(JSONFormatter) - - llog := logger.WithField("context", "eating raw fish") - - llog.Info("looks delicious") - - err := json.Unmarshal(buffer.Bytes(), &fields) - assert.NoError(t, err, "should have decoded first message") - assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields") - assert.Equal(t, fields["msg"], "looks delicious") - assert.Equal(t, fields["context"], "eating raw fish") - - buffer.Reset() - - llog.Warn("omg it is!") - - err = json.Unmarshal(buffer.Bytes(), &fields) - assert.NoError(t, err, "should have decoded second message") - assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields") - assert.Equal(t, fields["msg"], "omg it is!") - assert.Equal(t, fields["context"], "eating raw fish") - assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry") - -} - -func TestConvertLevelToString(t *testing.T) { - assert.Equal(t, "debug", DebugLevel.String()) - assert.Equal(t, "info", InfoLevel.String()) - assert.Equal(t, "warning", WarnLevel.String()) - assert.Equal(t, "error", ErrorLevel.String()) - assert.Equal(t, "fatal", FatalLevel.String()) - assert.Equal(t, "panic", PanicLevel.String()) -} - -func TestParseLevel(t *testing.T) { - l, err := ParseLevel("panic") - assert.Nil(t, err) - assert.Equal(t, PanicLevel, l) - - l, err = ParseLevel("fatal") - assert.Nil(t, err) - assert.Equal(t, FatalLevel, l) - - l, err = ParseLevel("error") - assert.Nil(t, err) - assert.Equal(t, ErrorLevel, l) - - l, err = ParseLevel("warn") - assert.Nil(t, err) - assert.Equal(t, WarnLevel, l) - - l, err = ParseLevel("warning") - assert.Nil(t, err) - assert.Equal(t, WarnLevel, l) - - l, err = ParseLevel("info") - assert.Nil(t, err) - assert.Equal(t, InfoLevel, l) - - l, err = ParseLevel("debug") - assert.Nil(t, err) - assert.Equal(t, DebugLevel, l) - - l, err = ParseLevel("invalid") - assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error()) -} - -func TestGetSetLevelRace(t *testing.T) { - wg := sync.WaitGroup{} - for i := 0; i < 100; i++ { - wg.Add(1) - go func(i int) { - defer wg.Done() - if i%2 == 0 { - SetLevel(InfoLevel) - } else { - GetLevel() - } - }(i) - - } - wg.Wait() -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/text_formatter_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/text_formatter_test.go deleted file mode 100644 index e25a44f67..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus/text_formatter_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package logrus - -import ( - "bytes" - "errors" - "testing" - "time" -) - -func TestQuoting(t *testing.T) { - tf := &TextFormatter{DisableColors: true} - - checkQuoting := func(q bool, value interface{}) { - b, _ := tf.Format(WithField("test", value)) - idx := bytes.Index(b, ([]byte)("test=")) - cont := bytes.Contains(b[idx+5:], []byte{'"'}) - if cont != q { - if q { - t.Errorf("quoting expected for: %#v", value) - } else { - t.Errorf("quoting not expected for: %#v", value) - } - } - } - - checkQuoting(false, "abcd") - checkQuoting(false, "v1.0") - checkQuoting(false, "1234567890") - checkQuoting(true, "/foobar") - checkQuoting(true, "x y") - checkQuoting(true, "x,y") - checkQuoting(false, errors.New("invalid")) - checkQuoting(true, errors.New("invalid argument")) -} - -func TestTimestampFormat(t *testing.T) { - checkTimeStr := func(format string) { - customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format} - customStr, _ := customFormatter.Format(WithField("test", "test")) - timeStart := bytes.Index(customStr, ([]byte)("time=")) - timeEnd := bytes.Index(customStr, ([]byte)("level=")) - timeStr := customStr[timeStart+5 : timeEnd-1] - if timeStr[0] == '"' && timeStr[len(timeStr)-1] == '"' { - timeStr = timeStr[1 : len(timeStr)-1] - } - if format == "" { - format = time.RFC3339 - } - _, e := time.Parse(format, (string)(timeStr)) - if e != nil { - t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e) - } - } - - checkTimeStr("2006-01-02T15:04:05.000000000Z07:00") - checkTimeStr("Mon Jan _2 15:04:05 2006") - checkTimeStr("") -} - -// TODO add tests for sorting etc., this requires a parser for the text -// formatter output. diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/envfile_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/envfile_test.go deleted file mode 100644 index a2e2200fa..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/envfile_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package opts - -import ( - "bufio" - "fmt" - "io/ioutil" - "os" - "reflect" - "strings" - "testing" -) - -func tmpFileWithContent(content string, t *testing.T) string { - tmpFile, err := ioutil.TempFile("", "envfile-test") - if err != nil { - t.Fatal(err) - } - defer tmpFile.Close() - - tmpFile.WriteString(content) - return tmpFile.Name() -} - -// Test ParseEnvFile for a file with a few well formatted lines -func TestParseEnvFileGoodFile(t *testing.T) { - content := `foo=bar - baz=quux -# comment - -_foobar=foobaz -with.dots=working -and_underscore=working too -` - // Adding a newline + a line with pure whitespace. - // This is being done like this instead of the block above - // because it's common for editors to trim trailing whitespace - // from lines, which becomes annoying since that's the - // exact thing we need to test. - content += "\n \t " - tmpFile := tmpFileWithContent(content, t) - defer os.Remove(tmpFile) - - lines, err := ParseEnvFile(tmpFile) - if err != nil { - t.Fatal(err) - } - - expectedLines := []string{ - "foo=bar", - "baz=quux", - "_foobar=foobaz", - "with.dots=working", - "and_underscore=working too", - } - - if !reflect.DeepEqual(lines, expectedLines) { - t.Fatal("lines not equal to expected_lines") - } -} - -// Test ParseEnvFile for an empty file -func TestParseEnvFileEmptyFile(t *testing.T) { - tmpFile := tmpFileWithContent("", t) - defer os.Remove(tmpFile) - - lines, err := ParseEnvFile(tmpFile) - if err != nil { - t.Fatal(err) - } - - if len(lines) != 0 { - t.Fatal("lines not empty; expected empty") - } -} - -// Test ParseEnvFile for a non existent file -func TestParseEnvFileNonExistentFile(t *testing.T) { - _, err := ParseEnvFile("foo_bar_baz") - if err == nil { - t.Fatal("ParseEnvFile succeeded; expected failure") - } - if _, ok := err.(*os.PathError); !ok { - t.Fatalf("Expected a PathError, got [%v]", err) - } -} - -// Test ParseEnvFile for a badly formatted file -func TestParseEnvFileBadlyFormattedFile(t *testing.T) { - content := `foo=bar - f =quux -` - - tmpFile := tmpFileWithContent(content, t) - defer os.Remove(tmpFile) - - _, err := ParseEnvFile(tmpFile) - if err == nil { - t.Fatalf("Expected a ErrBadEnvVariable, got nothing") - } - if _, ok := err.(ErrBadEnvVariable); !ok { - t.Fatalf("Expected a ErrBadEnvVariable, got [%v]", err) - } - expectedMessage := "poorly formatted environment: variable 'f ' has white spaces" - if err.Error() != expectedMessage { - t.Fatalf("Expected [%v], got [%v]", expectedMessage, err.Error()) - } -} - -// Test ParseEnvFile for a file with a line exceeding bufio.MaxScanTokenSize -func TestParseEnvFileLineTooLongFile(t *testing.T) { - content := strings.Repeat("a", bufio.MaxScanTokenSize+42) - content = fmt.Sprint("foo=", content) - - tmpFile := tmpFileWithContent(content, t) - defer os.Remove(tmpFile) - - _, err := ParseEnvFile(tmpFile) - if err == nil { - t.Fatal("ParseEnvFile succeeded; expected failure") - } -} - -// ParseEnvFile with a random file, pass through -func TestParseEnvFileRandomFile(t *testing.T) { - content := `first line -another invalid line` - tmpFile := tmpFileWithContent(content, t) - defer os.Remove(tmpFile) - - _, err := ParseEnvFile(tmpFile) - - if err == nil { - t.Fatalf("Expected a ErrBadEnvVariable, got nothing") - } - if _, ok := err.(ErrBadEnvVariable); !ok { - t.Fatalf("Expected a ErrBadEnvvariable, got [%v]", err) - } - expectedMessage := "poorly formatted environment: variable 'first line' has white spaces" - if err.Error() != expectedMessage { - t.Fatalf("Expected [%v], got [%v]", expectedMessage, err.Error()) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/hosts_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/hosts_test.go deleted file mode 100644 index e497e2865..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/hosts_test.go +++ /dev/null @@ -1,164 +0,0 @@ -package opts - -import ( - "runtime" - "testing" -) - -func TestParseHost(t *testing.T) { - invalid := map[string]string{ - "anything": "Invalid bind address format: anything", - "something with spaces": "Invalid bind address format: something with spaces", - "://": "Invalid bind address format: ://", - "unknown://": "Invalid bind address format: unknown://", - "tcp://:port": "Invalid bind address format: :port", - "tcp://invalid": "Invalid bind address format: invalid", - "tcp://invalid:port": "Invalid bind address format: invalid:port", - } - const defaultHTTPHost = "tcp://127.0.0.1:2375" - var defaultHOST = "unix:///var/run/docker.sock" - - if runtime.GOOS == "windows" { - defaultHOST = defaultHTTPHost - } - valid := map[string]string{ - "": defaultHOST, - "fd://": "fd://", - "fd://something": "fd://something", - "tcp://host:": "tcp://host:2375", - "tcp://": "tcp://localhost:2375", - "tcp://:2375": "tcp://localhost:2375", // default ip address - "tcp://:2376": "tcp://localhost:2376", // default ip address - "tcp://0.0.0.0:8080": "tcp://0.0.0.0:8080", - "tcp://192.168.0.0:12000": "tcp://192.168.0.0:12000", - "tcp://192.168:8080": "tcp://192.168:8080", - "tcp://0.0.0.0:1234567890": "tcp://0.0.0.0:1234567890", // yeah it's valid :P - "tcp://docker.com:2375": "tcp://docker.com:2375", - "unix://": "unix:///var/run/docker.sock", // default unix:// value - "unix://path/to/socket": "unix://path/to/socket", - } - - for value, errorMessage := range invalid { - if _, err := ParseHost(defaultHTTPHost, value); err == nil || err.Error() != errorMessage { - t.Fatalf("Expected an error for %v with [%v], got [%v]", value, errorMessage, err) - } - } - for value, expected := range valid { - if actual, err := ParseHost(defaultHTTPHost, value); err != nil || actual != expected { - t.Fatalf("Expected for %v [%v], got [%v, %v]", value, expected, actual, err) - } - } -} - -func TestParseDockerDaemonHost(t *testing.T) { - var ( - defaultHTTPHost = "tcp://localhost:2375" - defaultHTTPSHost = "tcp://localhost:2376" - defaultUnix = "/var/run/docker.sock" - defaultHOST = "unix:///var/run/docker.sock" - ) - if runtime.GOOS == "windows" { - defaultHOST = defaultHTTPHost - } - invalids := map[string]string{ - "0.0.0.0": "Invalid bind address format: 0.0.0.0", - "tcp:a.b.c.d": "Invalid bind address format: tcp:a.b.c.d", - "tcp:a.b.c.d/path": "Invalid bind address format: tcp:a.b.c.d/path", - "udp://127.0.0.1": "Invalid bind address format: udp://127.0.0.1", - "udp://127.0.0.1:2375": "Invalid bind address format: udp://127.0.0.1:2375", - "tcp://unix:///run/docker.sock": "Invalid bind address format: unix", - "tcp": "Invalid bind address format: tcp", - "unix": "Invalid bind address format: unix", - "fd": "Invalid bind address format: fd", - } - valids := map[string]string{ - "0.0.0.1:": "tcp://0.0.0.1:2375", - "0.0.0.1:5555": "tcp://0.0.0.1:5555", - "0.0.0.1:5555/path": "tcp://0.0.0.1:5555/path", - "[::1]:": "tcp://[::1]:2375", - "[::1]:5555/path": "tcp://[::1]:5555/path", - "[0:0:0:0:0:0:0:1]:": "tcp://[0:0:0:0:0:0:0:1]:2375", - "[0:0:0:0:0:0:0:1]:5555/path": "tcp://[0:0:0:0:0:0:0:1]:5555/path", - ":6666": "tcp://localhost:6666", - ":6666/path": "tcp://localhost:6666/path", - "": defaultHOST, - " ": defaultHOST, - " ": defaultHOST, - "tcp://": defaultHTTPHost, - "tcp://:7777": "tcp://localhost:7777", - "tcp://:7777/path": "tcp://localhost:7777/path", - " tcp://:7777/path ": "tcp://localhost:7777/path", - "unix:///run/docker.sock": "unix:///run/docker.sock", - "unix://": "unix:///var/run/docker.sock", - "fd://": "fd://", - "fd://something": "fd://something", - "localhost:": "tcp://localhost:2375", - "localhost:5555": "tcp://localhost:5555", - "localhost:5555/path": "tcp://localhost:5555/path", - } - for invalidAddr, expectedError := range invalids { - if addr, err := parseDockerDaemonHost(defaultHTTPHost, defaultHTTPSHost, defaultUnix, "", invalidAddr); err == nil || err.Error() != expectedError { - t.Errorf("tcp %v address expected error %v return, got %s and addr %v", invalidAddr, expectedError, err, addr) - } - } - for validAddr, expectedAddr := range valids { - if addr, err := parseDockerDaemonHost(defaultHTTPHost, defaultHTTPSHost, defaultUnix, "", validAddr); err != nil || addr != expectedAddr { - t.Errorf("%v -> expected %v, got (%v) addr (%v)", validAddr, expectedAddr, err, addr) - } - } -} - -func TestParseTCP(t *testing.T) { - var ( - defaultHTTPHost = "tcp://127.0.0.1:2376" - ) - invalids := map[string]string{ - "0.0.0.0": "Invalid bind address format: 0.0.0.0", - "tcp:a.b.c.d": "Invalid bind address format: tcp:a.b.c.d", - "tcp:a.b.c.d/path": "Invalid bind address format: tcp:a.b.c.d/path", - "udp://127.0.0.1": "Invalid proto, expected tcp: udp://127.0.0.1", - "udp://127.0.0.1:2375": "Invalid proto, expected tcp: udp://127.0.0.1:2375", - } - valids := map[string]string{ - "": defaultHTTPHost, - "tcp://": defaultHTTPHost, - "0.0.0.1:": "tcp://0.0.0.1:2376", - "0.0.0.1:5555": "tcp://0.0.0.1:5555", - "0.0.0.1:5555/path": "tcp://0.0.0.1:5555/path", - ":6666": "tcp://127.0.0.1:6666", - ":6666/path": "tcp://127.0.0.1:6666/path", - "tcp://:7777": "tcp://127.0.0.1:7777", - "tcp://:7777/path": "tcp://127.0.0.1:7777/path", - "[::1]:": "tcp://[::1]:2376", - "[::1]:5555": "tcp://[::1]:5555", - "[::1]:5555/path": "tcp://[::1]:5555/path", - "[0:0:0:0:0:0:0:1]:": "tcp://[0:0:0:0:0:0:0:1]:2376", - "[0:0:0:0:0:0:0:1]:5555": "tcp://[0:0:0:0:0:0:0:1]:5555", - "[0:0:0:0:0:0:0:1]:5555/path": "tcp://[0:0:0:0:0:0:0:1]:5555/path", - "localhost:": "tcp://localhost:2376", - "localhost:5555": "tcp://localhost:5555", - "localhost:5555/path": "tcp://localhost:5555/path", - } - for invalidAddr, expectedError := range invalids { - if addr, err := parseTCPAddr(invalidAddr, defaultHTTPHost); err == nil || err.Error() != expectedError { - t.Errorf("tcp %v address expected error %v return, got %s and addr %v", invalidAddr, expectedError, err, addr) - } - } - for validAddr, expectedAddr := range valids { - if addr, err := parseTCPAddr(validAddr, defaultHTTPHost); err != nil || addr != expectedAddr { - t.Errorf("%v -> expected %v, got %v and addr %v", validAddr, expectedAddr, err, addr) - } - } -} - -func TestParseInvalidUnixAddrInvalid(t *testing.T) { - if _, err := parseUnixAddr("tcp://127.0.0.1", "unix:///var/run/docker.sock"); err == nil || err.Error() != "Invalid proto, expected unix: tcp://127.0.0.1" { - t.Fatalf("Expected an error, got %v", err) - } - if _, err := parseUnixAddr("unix://tcp://127.0.0.1", "/var/run/docker.sock"); err == nil || err.Error() != "Invalid proto, expected unix: tcp://127.0.0.1" { - t.Fatalf("Expected an error, got %v", err) - } - if v, err := parseUnixAddr("", "/var/run/docker.sock"); err != nil || v != "unix:///var/run/docker.sock" { - t.Fatalf("Expected an %v, got %v", v, "unix:///var/run/docker.sock") - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/ip_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/ip_test.go deleted file mode 100644 index 1027d84a0..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/ip_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package opts - -import ( - "net" - "testing" -) - -func TestIpOptString(t *testing.T) { - addresses := []string{"", "0.0.0.0"} - var ip net.IP - - for _, address := range addresses { - stringAddress := NewIPOpt(&ip, address).String() - if stringAddress != address { - t.Fatalf("IpOpt string should be `%s`, not `%s`", address, stringAddress) - } - } -} - -func TestNewIpOptInvalidDefaultVal(t *testing.T) { - ip := net.IPv4(127, 0, 0, 1) - defaultVal := "Not an ip" - - ipOpt := NewIPOpt(&ip, defaultVal) - - expected := "127.0.0.1" - if ipOpt.String() != expected { - t.Fatalf("Expected [%v], got [%v]", expected, ipOpt.String()) - } -} - -func TestNewIpOptValidDefaultVal(t *testing.T) { - ip := net.IPv4(127, 0, 0, 1) - defaultVal := "192.168.1.1" - - ipOpt := NewIPOpt(&ip, defaultVal) - - expected := "192.168.1.1" - if ipOpt.String() != expected { - t.Fatalf("Expected [%v], got [%v]", expected, ipOpt.String()) - } -} - -func TestIpOptSetInvalidVal(t *testing.T) { - ip := net.IPv4(127, 0, 0, 1) - ipOpt := &IPOpt{IP: &ip} - - invalidIP := "invalid ip" - expectedError := "invalid ip is not an ip address" - err := ipOpt.Set(invalidIP) - if err == nil || err.Error() != expectedError { - t.Fatalf("Expected an Error with [%v], got [%v]", expectedError, err.Error()) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/opts_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/opts_test.go deleted file mode 100644 index e2af1c11d..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/opts/opts_test.go +++ /dev/null @@ -1,301 +0,0 @@ -package opts - -import ( - "fmt" - "os" - "strings" - "testing" -) - -func TestValidateIPAddress(t *testing.T) { - if ret, err := ValidateIPAddress(`1.2.3.4`); err != nil || ret == "" { - t.Fatalf("ValidateIPAddress(`1.2.3.4`) got %s %s", ret, err) - } - - if ret, err := ValidateIPAddress(`127.0.0.1`); err != nil || ret == "" { - t.Fatalf("ValidateIPAddress(`127.0.0.1`) got %s %s", ret, err) - } - - if ret, err := ValidateIPAddress(`::1`); err != nil || ret == "" { - t.Fatalf("ValidateIPAddress(`::1`) got %s %s", ret, err) - } - - if ret, err := ValidateIPAddress(`127`); err == nil || ret != "" { - t.Fatalf("ValidateIPAddress(`127`) got %s %s", ret, err) - } - - if ret, err := ValidateIPAddress(`random invalid string`); err == nil || ret != "" { - t.Fatalf("ValidateIPAddress(`random invalid string`) got %s %s", ret, err) - } - -} - -func TestMapOpts(t *testing.T) { - tmpMap := make(map[string]string) - o := NewMapOpts(tmpMap, logOptsValidator) - o.Set("max-size=1") - if o.String() != "map[max-size:1]" { - t.Errorf("%s != [map[max-size:1]", o.String()) - } - - o.Set("max-file=2") - if len(tmpMap) != 2 { - t.Errorf("map length %d != 2", len(tmpMap)) - } - - if tmpMap["max-file"] != "2" { - t.Errorf("max-file = %s != 2", tmpMap["max-file"]) - } - - if tmpMap["max-size"] != "1" { - t.Errorf("max-size = %s != 1", tmpMap["max-size"]) - } - if o.Set("dummy-val=3") == nil { - t.Errorf("validator is not being called") - } -} - -func TestValidateMACAddress(t *testing.T) { - if _, err := ValidateMACAddress(`92:d0:c6:0a:29:33`); err != nil { - t.Fatalf("ValidateMACAddress(`92:d0:c6:0a:29:33`) got %s", err) - } - - if _, err := ValidateMACAddress(`92:d0:c6:0a:33`); err == nil { - t.Fatalf("ValidateMACAddress(`92:d0:c6:0a:33`) succeeded; expected failure on invalid MAC") - } - - if _, err := ValidateMACAddress(`random invalid string`); err == nil { - t.Fatalf("ValidateMACAddress(`random invalid string`) succeeded; expected failure on invalid MAC") - } -} - -func TestListOptsWithoutValidator(t *testing.T) { - o := NewListOpts(nil) - o.Set("foo") - if o.String() != "[foo]" { - t.Errorf("%s != [foo]", o.String()) - } - o.Set("bar") - if o.Len() != 2 { - t.Errorf("%d != 2", o.Len()) - } - o.Set("bar") - if o.Len() != 3 { - t.Errorf("%d != 3", o.Len()) - } - if !o.Get("bar") { - t.Error("o.Get(\"bar\") == false") - } - if o.Get("baz") { - t.Error("o.Get(\"baz\") == true") - } - o.Delete("foo") - if o.String() != "[bar bar]" { - t.Errorf("%s != [bar bar]", o.String()) - } - listOpts := o.GetAll() - if len(listOpts) != 2 || listOpts[0] != "bar" || listOpts[1] != "bar" { - t.Errorf("Expected [[bar bar]], got [%v]", listOpts) - } - mapListOpts := o.GetMap() - if len(mapListOpts) != 1 { - t.Errorf("Expected [map[bar:{}]], got [%v]", mapListOpts) - } - -} - -func TestListOptsWithValidator(t *testing.T) { - // Re-using logOptsvalidator (used by MapOpts) - o := NewListOpts(logOptsValidator) - o.Set("foo") - if o.String() != "[]" { - t.Errorf("%s != []", o.String()) - } - o.Set("foo=bar") - if o.String() != "[]" { - t.Errorf("%s != []", o.String()) - } - o.Set("max-file=2") - if o.Len() != 1 { - t.Errorf("%d != 1", o.Len()) - } - if !o.Get("max-file=2") { - t.Error("o.Get(\"max-file=2\") == false") - } - if o.Get("baz") { - t.Error("o.Get(\"baz\") == true") - } - o.Delete("max-file=2") - if o.String() != "[]" { - t.Errorf("%s != []", o.String()) - } -} - -func TestValidateDNSSearch(t *testing.T) { - valid := []string{ - `.`, - `a`, - `a.`, - `1.foo`, - `17.foo`, - `foo.bar`, - `foo.bar.baz`, - `foo.bar.`, - `foo.bar.baz`, - `foo1.bar2`, - `foo1.bar2.baz`, - `1foo.2bar.`, - `1foo.2bar.baz`, - `foo-1.bar-2`, - `foo-1.bar-2.baz`, - `foo-1.bar-2.`, - `foo-1.bar-2.baz`, - `1-foo.2-bar`, - `1-foo.2-bar.baz`, - `1-foo.2-bar.`, - `1-foo.2-bar.baz`, - } - - invalid := []string{ - ``, - ` `, - ` `, - `17`, - `17.`, - `.17`, - `17-.`, - `17-.foo`, - `.foo`, - `foo-.bar`, - `-foo.bar`, - `foo.bar-`, - `foo.bar-.baz`, - `foo.-bar`, - `foo.-bar.baz`, - `foo.bar.baz.this.should.fail.on.long.name.beause.it.is.longer.thanisshouldbethis.should.fail.on.long.name.beause.it.is.longer.thanisshouldbethis.should.fail.on.long.name.beause.it.is.longer.thanisshouldbethis.should.fail.on.long.name.beause.it.is.longer.thanisshouldbe`, - } - - for _, domain := range valid { - if ret, err := ValidateDNSSearch(domain); err != nil || ret == "" { - t.Fatalf("ValidateDNSSearch(`"+domain+"`) got %s %s", ret, err) - } - } - - for _, domain := range invalid { - if ret, err := ValidateDNSSearch(domain); err == nil || ret != "" { - t.Fatalf("ValidateDNSSearch(`"+domain+"`) got %s %s", ret, err) - } - } -} - -func TestValidateExtraHosts(t *testing.T) { - valid := []string{ - `myhost:192.168.0.1`, - `thathost:10.0.2.1`, - `anipv6host:2003:ab34:e::1`, - `ipv6local:::1`, - } - - invalid := map[string]string{ - `myhost:192.notanipaddress.1`: `invalid IP`, - `thathost-nosemicolon10.0.0.1`: `bad format`, - `anipv6host:::::1`: `invalid IP`, - `ipv6local:::0::`: `invalid IP`, - } - - for _, extrahost := range valid { - if _, err := ValidateExtraHost(extrahost); err != nil { - t.Fatalf("ValidateExtraHost(`"+extrahost+"`) should succeed: error %v", err) - } - } - - for extraHost, expectedError := range invalid { - if _, err := ValidateExtraHost(extraHost); err == nil { - t.Fatalf("ValidateExtraHost(`%q`) should have failed validation", extraHost) - } else { - if !strings.Contains(err.Error(), expectedError) { - t.Fatalf("ValidateExtraHost(`%q`) error should contain %q", extraHost, expectedError) - } - } - } -} - -func TestValidateAttach(t *testing.T) { - valid := []string{ - "stdin", - "stdout", - "stderr", - "STDIN", - "STDOUT", - "STDERR", - } - if _, err := ValidateAttach("invalid"); err == nil { - t.Fatalf("Expected error with [valid streams are STDIN, STDOUT and STDERR], got nothing") - } - - for _, attach := range valid { - value, err := ValidateAttach(attach) - if err != nil { - t.Fatal(err) - } - if value != strings.ToLower(attach) { - t.Fatalf("Expected [%v], got [%v]", attach, value) - } - } -} - -func TestValidateEnv(t *testing.T) { - valids := map[string]string{ - "a": "a", - "something": "something", - "_=a": "_=a", - "env1=value1": "env1=value1", - "_env1=value1": "_env1=value1", - "env2=value2=value3": "env2=value2=value3", - "env3=abc!qwe": "env3=abc!qwe", - "env_4=value 4": "env_4=value 4", - "PATH": fmt.Sprintf("PATH=%v", os.Getenv("PATH")), - "PATH=something": "PATH=something", - "asd!qwe": "asd!qwe", - "1asd": "1asd", - "123": "123", - "some space": "some space", - " some space before": " some space before", - "some space after ": "some space after ", - } - for value, expected := range valids { - actual, err := ValidateEnv(value) - if err != nil { - t.Fatal(err) - } - if actual != expected { - t.Fatalf("Expected [%v], got [%v]", expected, actual) - } - } -} - -func TestValidateLabel(t *testing.T) { - if _, err := ValidateLabel("label"); err == nil || err.Error() != "bad attribute format: label" { - t.Fatalf("Expected an error [bad attribute format: label], go %v", err) - } - if actual, err := ValidateLabel("key1=value1"); err != nil || actual != "key1=value1" { - t.Fatalf("Expected [key1=value1], got [%v,%v]", actual, err) - } - // Validate it's working with more than one = - if actual, err := ValidateLabel("key1=value1=value2"); err != nil { - t.Fatalf("Expected [key1=value1=value2], got [%v,%v]", actual, err) - } - // Validate it's working with one more - if actual, err := ValidateLabel("key1=value1=value2=value3"); err != nil { - t.Fatalf("Expected [key1=value1=value2=value2], got [%v,%v]", actual, err) - } -} - -func logOptsValidator(val string) (string, error) { - allowedKeys := map[string]string{"max-size": "1", "max-file": "2"} - vals := strings.Split(val, "=") - if allowedKeys[vals[0]] != "" { - return val, nil - } - return "", fmt.Errorf("invalid key %s", vals[0]) -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/archive_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/archive_test.go deleted file mode 100644 index 94deff3f4..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/archive_test.go +++ /dev/null @@ -1,1248 +0,0 @@ -package archive - -import ( - "archive/tar" - "bytes" - "fmt" - "io" - "io/ioutil" - "os" - "os/exec" - "path" - "path/filepath" - "strings" - "syscall" - "testing" - "time" - - "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system" -) - -func TestIsArchiveNilHeader(t *testing.T) { - out := IsArchive(nil) - if out { - t.Fatalf("isArchive should return false as nil is not a valid archive header") - } -} - -func TestIsArchiveInvalidHeader(t *testing.T) { - header := []byte{0x00, 0x01, 0x02} - out := IsArchive(header) - if out { - t.Fatalf("isArchive should return false as %s is not a valid archive header", header) - } -} - -func TestIsArchiveBzip2(t *testing.T) { - header := []byte{0x42, 0x5A, 0x68} - out := IsArchive(header) - if !out { - t.Fatalf("isArchive should return true as %s is a bz2 header", header) - } -} - -func TestIsArchive7zip(t *testing.T) { - header := []byte{0x50, 0x4b, 0x03, 0x04} - out := IsArchive(header) - if out { - t.Fatalf("isArchive should return false as %s is a 7z header and it is not supported", header) - } -} - -func TestIsArchivePathDir(t *testing.T) { - cmd := exec.Command("/bin/sh", "-c", "mkdir -p /tmp/archivedir") - output, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("Fail to create an archive file for test : %s.", output) - } - if IsArchivePath("/tmp/archivedir") { - t.Fatalf("Incorrectly recognised directory as an archive") - } -} - -func TestIsArchivePathInvalidFile(t *testing.T) { - cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1K count=1 of=/tmp/archive && gzip --stdout /tmp/archive > /tmp/archive.gz") - output, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("Fail to create an archive file for test : %s.", output) - } - if IsArchivePath("/tmp/archive") { - t.Fatalf("Incorrectly recognised invalid tar path as archive") - } - if IsArchivePath("/tmp/archive.gz") { - t.Fatalf("Incorrectly recognised invalid compressed tar path as archive") - } -} - -func TestIsArchivePathTar(t *testing.T) { - cmd := exec.Command("/bin/sh", "-c", "touch /tmp/archivedata && tar -cf /tmp/archive /tmp/archivedata && gzip --stdout /tmp/archive > /tmp/archive.gz") - output, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("Fail to create an archive file for test : %s.", output) - } - if !IsArchivePath("/tmp/archive") { - t.Fatalf("Did not recognise valid tar path as archive") - } - if !IsArchivePath("/tmp/archive.gz") { - t.Fatalf("Did not recognise valid compressed tar path as archive") - } -} - -func TestDecompressStreamGzip(t *testing.T) { - cmd := exec.Command("/bin/sh", "-c", "touch /tmp/archive && gzip -f /tmp/archive") - output, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("Fail to create an archive file for test : %s.", output) - } - archive, err := os.Open("/tmp/archive.gz") - _, err = DecompressStream(archive) - if err != nil { - t.Fatalf("Failed to decompress a gzip file.") - } -} - -func TestDecompressStreamBzip2(t *testing.T) { - cmd := exec.Command("/bin/sh", "-c", "touch /tmp/archive && bzip2 -f /tmp/archive") - output, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("Fail to create an archive file for test : %s.", output) - } - archive, err := os.Open("/tmp/archive.bz2") - _, err = DecompressStream(archive) - if err != nil { - t.Fatalf("Failed to decompress a bzip2 file.") - } -} - -func TestDecompressStreamXz(t *testing.T) { - cmd := exec.Command("/bin/sh", "-c", "touch /tmp/archive && xz -f /tmp/archive") - output, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("Fail to create an archive file for test : %s.", output) - } - archive, err := os.Open("/tmp/archive.xz") - _, err = DecompressStream(archive) - if err != nil { - t.Fatalf("Failed to decompress a xz file.") - } -} - -func TestCompressStreamXzUnsuported(t *testing.T) { - dest, err := os.Create("/tmp/dest") - if err != nil { - t.Fatalf("Fail to create the destination file") - } - _, err = CompressStream(dest, Xz) - if err == nil { - t.Fatalf("Should fail as xz is unsupported for compression format.") - } -} - -func TestCompressStreamBzip2Unsupported(t *testing.T) { - dest, err := os.Create("/tmp/dest") - if err != nil { - t.Fatalf("Fail to create the destination file") - } - _, err = CompressStream(dest, Xz) - if err == nil { - t.Fatalf("Should fail as xz is unsupported for compression format.") - } -} - -func TestCompressStreamInvalid(t *testing.T) { - dest, err := os.Create("/tmp/dest") - if err != nil { - t.Fatalf("Fail to create the destination file") - } - _, err = CompressStream(dest, -1) - if err == nil { - t.Fatalf("Should fail as xz is unsupported for compression format.") - } -} - -func TestExtensionInvalid(t *testing.T) { - compression := Compression(-1) - output := compression.Extension() - if output != "" { - t.Fatalf("The extension of an invalid compression should be an empty string.") - } -} - -func TestExtensionUncompressed(t *testing.T) { - compression := Uncompressed - output := compression.Extension() - if output != "tar" { - t.Fatalf("The extension of a uncompressed archive should be 'tar'.") - } -} -func TestExtensionBzip2(t *testing.T) { - compression := Bzip2 - output := compression.Extension() - if output != "tar.bz2" { - t.Fatalf("The extension of a bzip2 archive should be 'tar.bz2'") - } -} -func TestExtensionGzip(t *testing.T) { - compression := Gzip - output := compression.Extension() - if output != "tar.gz" { - t.Fatalf("The extension of a bzip2 archive should be 'tar.gz'") - } -} -func TestExtensionXz(t *testing.T) { - compression := Xz - output := compression.Extension() - if output != "tar.xz" { - t.Fatalf("The extension of a bzip2 archive should be 'tar.xz'") - } -} - -func TestCmdStreamLargeStderr(t *testing.T) { - cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello") - out, _, err := cmdStream(cmd, nil) - if err != nil { - t.Fatalf("Failed to start command: %s", err) - } - errCh := make(chan error) - go func() { - _, err := io.Copy(ioutil.Discard, out) - errCh <- err - }() - select { - case err := <-errCh: - if err != nil { - t.Fatalf("Command should not have failed (err=%.100s...)", err) - } - case <-time.After(5 * time.Second): - t.Fatalf("Command did not complete in 5 seconds; probable deadlock") - } -} - -func TestCmdStreamBad(t *testing.T) { - badCmd := exec.Command("/bin/sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1") - out, _, err := cmdStream(badCmd, nil) - if err != nil { - t.Fatalf("Failed to start command: %s", err) - } - if output, err := ioutil.ReadAll(out); err == nil { - t.Fatalf("Command should have failed") - } else if err.Error() != "exit status 1: error couldn't reverse the phase pulser\n" { - t.Fatalf("Wrong error value (%s)", err) - } else if s := string(output); s != "hello\n" { - t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output) - } -} - -func TestCmdStreamGood(t *testing.T) { - cmd := exec.Command("/bin/sh", "-c", "echo hello; exit 0") - out, _, err := cmdStream(cmd, nil) - if err != nil { - t.Fatal(err) - } - if output, err := ioutil.ReadAll(out); err != nil { - t.Fatalf("Command should not have failed (err=%s)", err) - } else if s := string(output); s != "hello\n" { - t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output) - } -} - -func TestUntarPathWithInvalidDest(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-archive-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tempFolder) - invalidDestFolder := path.Join(tempFolder, "invalidDest") - // Create a src file - srcFile := path.Join(tempFolder, "src") - tarFile := path.Join(tempFolder, "src.tar") - os.Create(srcFile) - os.Create(invalidDestFolder) // being a file (not dir) should cause an error - cmd := exec.Command("/bin/sh", "-c", "tar cf "+tarFile+" "+srcFile) - _, err = cmd.CombinedOutput() - if err != nil { - t.Fatal(err) - } - - err = UntarPath(tarFile, invalidDestFolder) - if err == nil { - t.Fatalf("UntarPath with invalid destination path should throw an error.") - } -} - -func TestUntarPathWithInvalidSrc(t *testing.T) { - dest, err := ioutil.TempDir("", "docker-archive-test") - if err != nil { - t.Fatalf("Fail to create the destination file") - } - defer os.RemoveAll(dest) - err = UntarPath("/invalid/path", dest) - if err == nil { - t.Fatalf("UntarPath with invalid src path should throw an error.") - } -} - -func TestUntarPath(t *testing.T) { - tmpFolder, err := ioutil.TempDir("", "docker-archive-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpFolder) - srcFile := path.Join(tmpFolder, "src") - tarFile := path.Join(tmpFolder, "src.tar") - os.Create(path.Join(tmpFolder, "src")) - cmd := exec.Command("/bin/sh", "-c", "tar cf "+tarFile+" "+srcFile) - _, err = cmd.CombinedOutput() - if err != nil { - t.Fatal(err) - } - destFolder := path.Join(tmpFolder, "dest") - err = os.MkdirAll(destFolder, 0740) - if err != nil { - t.Fatalf("Fail to create the destination file") - } - err = UntarPath(tarFile, destFolder) - if err != nil { - t.Fatalf("UntarPath shouldn't throw an error, %s.", err) - } - expectedFile := path.Join(destFolder, srcFile) - _, err = os.Stat(expectedFile) - if err != nil { - t.Fatalf("Destination folder should contain the source file but did not.") - } -} - -// Do the same test as above but with the destination as file, it should fail -func TestUntarPathWithDestinationFile(t *testing.T) { - tmpFolder, err := ioutil.TempDir("", "docker-archive-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpFolder) - srcFile := path.Join(tmpFolder, "src") - tarFile := path.Join(tmpFolder, "src.tar") - os.Create(path.Join(tmpFolder, "src")) - cmd := exec.Command("/bin/sh", "-c", "tar cf "+tarFile+" "+srcFile) - _, err = cmd.CombinedOutput() - if err != nil { - t.Fatal(err) - } - destFile := path.Join(tmpFolder, "dest") - _, err = os.Create(destFile) - if err != nil { - t.Fatalf("Fail to create the destination file") - } - err = UntarPath(tarFile, destFile) - if err == nil { - t.Fatalf("UntarPath should throw an error if the destination if a file") - } -} - -// Do the same test as above but with the destination folder already exists -// and the destination file is a directory -// It's working, see https://github.com/docker/docker/issues/10040 -func TestUntarPathWithDestinationSrcFileAsFolder(t *testing.T) { - tmpFolder, err := ioutil.TempDir("", "docker-archive-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpFolder) - srcFile := path.Join(tmpFolder, "src") - tarFile := path.Join(tmpFolder, "src.tar") - os.Create(srcFile) - cmd := exec.Command("/bin/sh", "-c", "tar cf "+tarFile+" "+srcFile) - _, err = cmd.CombinedOutput() - if err != nil { - t.Fatal(err) - } - destFolder := path.Join(tmpFolder, "dest") - err = os.MkdirAll(destFolder, 0740) - if err != nil { - t.Fatalf("Fail to create the destination folder") - } - // Let's create a folder that will has the same path as the extracted file (from tar) - destSrcFileAsFolder := path.Join(destFolder, srcFile) - err = os.MkdirAll(destSrcFileAsFolder, 0740) - if err != nil { - t.Fatal(err) - } - err = UntarPath(tarFile, destFolder) - if err != nil { - t.Fatalf("UntarPath should throw not throw an error if the extracted file already exists and is a folder") - } -} - -func TestCopyWithTarInvalidSrc(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-archive-test") - if err != nil { - t.Fatal(nil) - } - destFolder := path.Join(tempFolder, "dest") - invalidSrc := path.Join(tempFolder, "doesnotexists") - err = os.MkdirAll(destFolder, 0740) - if err != nil { - t.Fatal(err) - } - err = CopyWithTar(invalidSrc, destFolder) - if err == nil { - t.Fatalf("archiver.CopyWithTar with invalid src path should throw an error.") - } -} - -func TestCopyWithTarInexistentDestWillCreateIt(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-archive-test") - if err != nil { - t.Fatal(nil) - } - srcFolder := path.Join(tempFolder, "src") - inexistentDestFolder := path.Join(tempFolder, "doesnotexists") - err = os.MkdirAll(srcFolder, 0740) - if err != nil { - t.Fatal(err) - } - err = CopyWithTar(srcFolder, inexistentDestFolder) - if err != nil { - t.Fatalf("CopyWithTar with an inexistent folder shouldn't fail.") - } - _, err = os.Stat(inexistentDestFolder) - if err != nil { - t.Fatalf("CopyWithTar with an inexistent folder should create it.") - } -} - -// Test CopyWithTar with a file as src -func TestCopyWithTarSrcFile(t *testing.T) { - folder, err := ioutil.TempDir("", "docker-archive-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(folder) - dest := path.Join(folder, "dest") - srcFolder := path.Join(folder, "src") - src := path.Join(folder, path.Join("src", "src")) - err = os.MkdirAll(srcFolder, 0740) - if err != nil { - t.Fatal(err) - } - err = os.MkdirAll(dest, 0740) - if err != nil { - t.Fatal(err) - } - ioutil.WriteFile(src, []byte("content"), 0777) - err = CopyWithTar(src, dest) - if err != nil { - t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err) - } - _, err = os.Stat(dest) - // FIXME Check the content - if err != nil { - t.Fatalf("Destination file should be the same as the source.") - } -} - -// Test CopyWithTar with a folder as src -func TestCopyWithTarSrcFolder(t *testing.T) { - folder, err := ioutil.TempDir("", "docker-archive-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(folder) - dest := path.Join(folder, "dest") - src := path.Join(folder, path.Join("src", "folder")) - err = os.MkdirAll(src, 0740) - if err != nil { - t.Fatal(err) - } - err = os.MkdirAll(dest, 0740) - if err != nil { - t.Fatal(err) - } - ioutil.WriteFile(path.Join(src, "file"), []byte("content"), 0777) - err = CopyWithTar(src, dest) - if err != nil { - t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err) - } - _, err = os.Stat(dest) - // FIXME Check the content (the file inside) - if err != nil { - t.Fatalf("Destination folder should contain the source file but did not.") - } -} - -func TestCopyFileWithTarInvalidSrc(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-archive-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tempFolder) - destFolder := path.Join(tempFolder, "dest") - err = os.MkdirAll(destFolder, 0740) - if err != nil { - t.Fatal(err) - } - invalidFile := path.Join(tempFolder, "doesnotexists") - err = CopyFileWithTar(invalidFile, destFolder) - if err == nil { - t.Fatalf("archiver.CopyWithTar with invalid src path should throw an error.") - } -} - -func TestCopyFileWithTarInexistentDestWillCreateIt(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-archive-test") - if err != nil { - t.Fatal(nil) - } - defer os.RemoveAll(tempFolder) - srcFile := path.Join(tempFolder, "src") - inexistentDestFolder := path.Join(tempFolder, "doesnotexists") - _, err = os.Create(srcFile) - if err != nil { - t.Fatal(err) - } - err = CopyFileWithTar(srcFile, inexistentDestFolder) - if err != nil { - t.Fatalf("CopyWithTar with an inexistent folder shouldn't fail.") - } - _, err = os.Stat(inexistentDestFolder) - if err != nil { - t.Fatalf("CopyWithTar with an inexistent folder should create it.") - } - // FIXME Test the src file and content -} - -func TestCopyFileWithTarSrcFolder(t *testing.T) { - folder, err := ioutil.TempDir("", "docker-archive-copyfilewithtar-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(folder) - dest := path.Join(folder, "dest") - src := path.Join(folder, "srcfolder") - err = os.MkdirAll(src, 0740) - if err != nil { - t.Fatal(err) - } - err = os.MkdirAll(dest, 0740) - if err != nil { - t.Fatal(err) - } - err = CopyFileWithTar(src, dest) - if err == nil { - t.Fatalf("CopyFileWithTar should throw an error with a folder.") - } -} - -func TestCopyFileWithTarSrcFile(t *testing.T) { - folder, err := ioutil.TempDir("", "docker-archive-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(folder) - dest := path.Join(folder, "dest") - srcFolder := path.Join(folder, "src") - src := path.Join(folder, path.Join("src", "src")) - err = os.MkdirAll(srcFolder, 0740) - if err != nil { - t.Fatal(err) - } - err = os.MkdirAll(dest, 0740) - if err != nil { - t.Fatal(err) - } - ioutil.WriteFile(src, []byte("content"), 0777) - err = CopyWithTar(src, dest+"/") - if err != nil { - t.Fatalf("archiver.CopyFileWithTar shouldn't throw an error, %s.", err) - } - _, err = os.Stat(dest) - if err != nil { - t.Fatalf("Destination folder should contain the source file but did not.") - } -} - -func TestTarFiles(t *testing.T) { - // try without hardlinks - if err := checkNoChanges(1000, false); err != nil { - t.Fatal(err) - } - // try with hardlinks - if err := checkNoChanges(1000, true); err != nil { - t.Fatal(err) - } -} - -func checkNoChanges(fileNum int, hardlinks bool) error { - srcDir, err := ioutil.TempDir("", "docker-test-srcDir") - if err != nil { - return err - } - defer os.RemoveAll(srcDir) - - destDir, err := ioutil.TempDir("", "docker-test-destDir") - if err != nil { - return err - } - defer os.RemoveAll(destDir) - - _, err = prepareUntarSourceDirectory(fileNum, srcDir, hardlinks) - if err != nil { - return err - } - - err = TarUntar(srcDir, destDir) - if err != nil { - return err - } - - changes, err := ChangesDirs(destDir, srcDir) - if err != nil { - return err - } - if len(changes) > 0 { - return fmt.Errorf("with %d files and %v hardlinks: expected 0 changes, got %d", fileNum, hardlinks, len(changes)) - } - return nil -} - -func tarUntar(t *testing.T, origin string, options *TarOptions) ([]Change, error) { - archive, err := TarWithOptions(origin, options) - if err != nil { - t.Fatal(err) - } - defer archive.Close() - - buf := make([]byte, 10) - if _, err := archive.Read(buf); err != nil { - return nil, err - } - wrap := io.MultiReader(bytes.NewReader(buf), archive) - - detectedCompression := DetectCompression(buf) - compression := options.Compression - if detectedCompression.Extension() != compression.Extension() { - return nil, fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension()) - } - - tmp, err := ioutil.TempDir("", "docker-test-untar") - if err != nil { - return nil, err - } - defer os.RemoveAll(tmp) - if err := Untar(wrap, tmp, nil); err != nil { - return nil, err - } - if _, err := os.Stat(tmp); err != nil { - return nil, err - } - - return ChangesDirs(origin, tmp) -} - -func TestTarUntar(t *testing.T) { - origin, err := ioutil.TempDir("", "docker-test-untar-origin") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(origin) - if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil { - t.Fatal(err) - } - if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil { - t.Fatal(err) - } - if err := ioutil.WriteFile(path.Join(origin, "3"), []byte("will be ignored"), 0700); err != nil { - t.Fatal(err) - } - - for _, c := range []Compression{ - Uncompressed, - Gzip, - } { - changes, err := tarUntar(t, origin, &TarOptions{ - Compression: c, - ExcludePatterns: []string{"3"}, - }) - - if err != nil { - t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err) - } - - if len(changes) != 1 || changes[0].Path != "/3" { - t.Fatalf("Unexpected differences after tarUntar: %v", changes) - } - } -} - -func TestTarUntarWithXattr(t *testing.T) { - origin, err := ioutil.TempDir("", "docker-test-untar-origin") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(origin) - if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil { - t.Fatal(err) - } - if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil { - t.Fatal(err) - } - if err := ioutil.WriteFile(path.Join(origin, "3"), []byte("will be ignored"), 0700); err != nil { - t.Fatal(err) - } - if err := system.Lsetxattr(path.Join(origin, "2"), "security.capability", []byte{0x00}, 0); err != nil { - t.Fatal(err) - } - - for _, c := range []Compression{ - Uncompressed, - Gzip, - } { - changes, err := tarUntar(t, origin, &TarOptions{ - Compression: c, - ExcludePatterns: []string{"3"}, - }) - - if err != nil { - t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err) - } - - if len(changes) != 1 || changes[0].Path != "/3" { - t.Fatalf("Unexpected differences after tarUntar: %v", changes) - } - capability, _ := system.Lgetxattr(path.Join(origin, "2"), "security.capability") - if capability == nil && capability[0] != 0x00 { - t.Fatalf("Untar should have kept the 'security.capability' xattr.") - } - } -} - -func TestTarWithOptions(t *testing.T) { - origin, err := ioutil.TempDir("", "docker-test-untar-origin") - if err != nil { - t.Fatal(err) - } - if _, err := ioutil.TempDir(origin, "folder"); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(origin) - if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil { - t.Fatal(err) - } - if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil { - t.Fatal(err) - } - - cases := []struct { - opts *TarOptions - numChanges int - }{ - {&TarOptions{IncludeFiles: []string{"1"}}, 2}, - {&TarOptions{ExcludePatterns: []string{"2"}}, 1}, - {&TarOptions{ExcludePatterns: []string{"1", "folder*"}}, 2}, - {&TarOptions{IncludeFiles: []string{"1", "1"}}, 2}, - {&TarOptions{IncludeFiles: []string{"1"}, RebaseNames: map[string]string{"1": "test"}}, 4}, - } - for _, testCase := range cases { - changes, err := tarUntar(t, origin, testCase.opts) - if err != nil { - t.Fatalf("Error tar/untar when testing inclusion/exclusion: %s", err) - } - if len(changes) != testCase.numChanges { - t.Errorf("Expected %d changes, got %d for %+v:", - testCase.numChanges, len(changes), testCase.opts) - } - } -} - -// Some tar archives such as http://haproxy.1wt.eu/download/1.5/src/devel/haproxy-1.5-dev21.tar.gz -// use PAX Global Extended Headers. -// Failing prevents the archives from being uncompressed during ADD -func TestTypeXGlobalHeaderDoesNotFail(t *testing.T) { - hdr := tar.Header{Typeflag: tar.TypeXGlobalHeader} - tmpDir, err := ioutil.TempDir("", "docker-test-archive-pax-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) - err = createTarFile(filepath.Join(tmpDir, "pax_global_header"), tmpDir, &hdr, nil, true, nil) - if err != nil { - t.Fatal(err) - } -} - -// Some tar have both GNU specific (huge uid) and Ustar specific (long name) things. -// Not supposed to happen (should use PAX instead of Ustar for long name) but it does and it should still work. -func TestUntarUstarGnuConflict(t *testing.T) { - f, err := os.Open("testdata/broken.tar") - if err != nil { - t.Fatal(err) - } - found := false - tr := tar.NewReader(f) - // Iterate through the files in the archive. - for { - hdr, err := tr.Next() - if err == io.EOF { - // end of tar archive - break - } - if err != nil { - t.Fatal(err) - } - if hdr.Name == "root/.cpanm/work/1395823785.24209/Plack-1.0030/blib/man3/Plack::Middleware::LighttpdScriptNameFix.3pm" { - found = true - break - } - } - if !found { - t.Fatalf("%s not found in the archive", "root/.cpanm/work/1395823785.24209/Plack-1.0030/blib/man3/Plack::Middleware::LighttpdScriptNameFix.3pm") - } -} - -func TestTarWithBlockCharFifo(t *testing.T) { - origin, err := ioutil.TempDir("", "docker-test-tar-hardlink") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(origin) - if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil { - t.Fatal(err) - } - if err := system.Mknod(path.Join(origin, "2"), syscall.S_IFBLK, int(system.Mkdev(int64(12), int64(5)))); err != nil { - t.Fatal(err) - } - if err := system.Mknod(path.Join(origin, "3"), syscall.S_IFCHR, int(system.Mkdev(int64(12), int64(5)))); err != nil { - t.Fatal(err) - } - if err := system.Mknod(path.Join(origin, "4"), syscall.S_IFIFO, int(system.Mkdev(int64(12), int64(5)))); err != nil { - t.Fatal(err) - } - - dest, err := ioutil.TempDir("", "docker-test-tar-hardlink-dest") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dest) - - // we'll do this in two steps to separate failure - fh, err := Tar(origin, Uncompressed) - if err != nil { - t.Fatal(err) - } - - // ensure we can read the whole thing with no error, before writing back out - buf, err := ioutil.ReadAll(fh) - if err != nil { - t.Fatal(err) - } - - bRdr := bytes.NewReader(buf) - err = Untar(bRdr, dest, &TarOptions{Compression: Uncompressed}) - if err != nil { - t.Fatal(err) - } - - changes, err := ChangesDirs(origin, dest) - if err != nil { - t.Fatal(err) - } - if len(changes) > 0 { - t.Fatalf("Tar with special device (block, char, fifo) should keep them (recreate them when untar) : %v", changes) - } -} - -func TestTarWithHardLink(t *testing.T) { - origin, err := ioutil.TempDir("", "docker-test-tar-hardlink") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(origin) - if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil { - t.Fatal(err) - } - if err := os.Link(path.Join(origin, "1"), path.Join(origin, "2")); err != nil { - t.Fatal(err) - } - - var i1, i2 uint64 - if i1, err = getNlink(path.Join(origin, "1")); err != nil { - t.Fatal(err) - } - // sanity check that we can hardlink - if i1 != 2 { - t.Skipf("skipping since hardlinks don't work here; expected 2 links, got %d", i1) - } - - dest, err := ioutil.TempDir("", "docker-test-tar-hardlink-dest") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dest) - - // we'll do this in two steps to separate failure - fh, err := Tar(origin, Uncompressed) - if err != nil { - t.Fatal(err) - } - - // ensure we can read the whole thing with no error, before writing back out - buf, err := ioutil.ReadAll(fh) - if err != nil { - t.Fatal(err) - } - - bRdr := bytes.NewReader(buf) - err = Untar(bRdr, dest, &TarOptions{Compression: Uncompressed}) - if err != nil { - t.Fatal(err) - } - - if i1, err = getInode(path.Join(dest, "1")); err != nil { - t.Fatal(err) - } - if i2, err = getInode(path.Join(dest, "2")); err != nil { - t.Fatal(err) - } - - if i1 != i2 { - t.Errorf("expected matching inodes, but got %d and %d", i1, i2) - } -} - -func getNlink(path string) (uint64, error) { - stat, err := os.Stat(path) - if err != nil { - return 0, err - } - statT, ok := stat.Sys().(*syscall.Stat_t) - if !ok { - return 0, fmt.Errorf("expected type *syscall.Stat_t, got %t", stat.Sys()) - } - // We need this conversion on ARM64 - return uint64(statT.Nlink), nil -} - -func getInode(path string) (uint64, error) { - stat, err := os.Stat(path) - if err != nil { - return 0, err - } - statT, ok := stat.Sys().(*syscall.Stat_t) - if !ok { - return 0, fmt.Errorf("expected type *syscall.Stat_t, got %t", stat.Sys()) - } - return statT.Ino, nil -} - -func prepareUntarSourceDirectory(numberOfFiles int, targetPath string, makeLinks bool) (int, error) { - fileData := []byte("fooo") - for n := 0; n < numberOfFiles; n++ { - fileName := fmt.Sprintf("file-%d", n) - if err := ioutil.WriteFile(path.Join(targetPath, fileName), fileData, 0700); err != nil { - return 0, err - } - if makeLinks { - if err := os.Link(path.Join(targetPath, fileName), path.Join(targetPath, fileName+"-link")); err != nil { - return 0, err - } - } - } - totalSize := numberOfFiles * len(fileData) - return totalSize, nil -} - -func BenchmarkTarUntar(b *testing.B) { - origin, err := ioutil.TempDir("", "docker-test-untar-origin") - if err != nil { - b.Fatal(err) - } - tempDir, err := ioutil.TempDir("", "docker-test-untar-destination") - if err != nil { - b.Fatal(err) - } - target := path.Join(tempDir, "dest") - n, err := prepareUntarSourceDirectory(100, origin, false) - if err != nil { - b.Fatal(err) - } - defer os.RemoveAll(origin) - defer os.RemoveAll(tempDir) - - b.ResetTimer() - b.SetBytes(int64(n)) - for n := 0; n < b.N; n++ { - err := TarUntar(origin, target) - if err != nil { - b.Fatal(err) - } - os.RemoveAll(target) - } -} - -func BenchmarkTarUntarWithLinks(b *testing.B) { - origin, err := ioutil.TempDir("", "docker-test-untar-origin") - if err != nil { - b.Fatal(err) - } - tempDir, err := ioutil.TempDir("", "docker-test-untar-destination") - if err != nil { - b.Fatal(err) - } - target := path.Join(tempDir, "dest") - n, err := prepareUntarSourceDirectory(100, origin, true) - if err != nil { - b.Fatal(err) - } - defer os.RemoveAll(origin) - defer os.RemoveAll(tempDir) - - b.ResetTimer() - b.SetBytes(int64(n)) - for n := 0; n < b.N; n++ { - err := TarUntar(origin, target) - if err != nil { - b.Fatal(err) - } - os.RemoveAll(target) - } -} - -func TestUntarInvalidFilenames(t *testing.T) { - for i, headers := range [][]*tar.Header{ - { - { - Name: "../victim/dotdot", - Typeflag: tar.TypeReg, - Mode: 0644, - }, - }, - { - { - // Note the leading slash - Name: "/../victim/slash-dotdot", - Typeflag: tar.TypeReg, - Mode: 0644, - }, - }, - } { - if err := testBreakout("untar", "docker-TestUntarInvalidFilenames", headers); err != nil { - t.Fatalf("i=%d. %v", i, err) - } - } -} - -func TestUntarHardlinkToSymlink(t *testing.T) { - for i, headers := range [][]*tar.Header{ - { - { - Name: "symlink1", - Typeflag: tar.TypeSymlink, - Linkname: "regfile", - Mode: 0644, - }, - { - Name: "symlink2", - Typeflag: tar.TypeLink, - Linkname: "symlink1", - Mode: 0644, - }, - { - Name: "regfile", - Typeflag: tar.TypeReg, - Mode: 0644, - }, - }, - } { - if err := testBreakout("untar", "docker-TestUntarHardlinkToSymlink", headers); err != nil { - t.Fatalf("i=%d. %v", i, err) - } - } -} - -func TestUntarInvalidHardlink(t *testing.T) { - for i, headers := range [][]*tar.Header{ - { // try reading victim/hello (../) - { - Name: "dotdot", - Typeflag: tar.TypeLink, - Linkname: "../victim/hello", - Mode: 0644, - }, - }, - { // try reading victim/hello (/../) - { - Name: "slash-dotdot", - Typeflag: tar.TypeLink, - // Note the leading slash - Linkname: "/../victim/hello", - Mode: 0644, - }, - }, - { // try writing victim/file - { - Name: "loophole-victim", - Typeflag: tar.TypeLink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "loophole-victim/file", - Typeflag: tar.TypeReg, - Mode: 0644, - }, - }, - { // try reading victim/hello (hardlink, symlink) - { - Name: "loophole-victim", - Typeflag: tar.TypeLink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "symlink", - Typeflag: tar.TypeSymlink, - Linkname: "loophole-victim/hello", - Mode: 0644, - }, - }, - { // Try reading victim/hello (hardlink, hardlink) - { - Name: "loophole-victim", - Typeflag: tar.TypeLink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "hardlink", - Typeflag: tar.TypeLink, - Linkname: "loophole-victim/hello", - Mode: 0644, - }, - }, - { // Try removing victim directory (hardlink) - { - Name: "loophole-victim", - Typeflag: tar.TypeLink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "loophole-victim", - Typeflag: tar.TypeReg, - Mode: 0644, - }, - }, - } { - if err := testBreakout("untar", "docker-TestUntarInvalidHardlink", headers); err != nil { - t.Fatalf("i=%d. %v", i, err) - } - } -} - -func TestUntarInvalidSymlink(t *testing.T) { - for i, headers := range [][]*tar.Header{ - { // try reading victim/hello (../) - { - Name: "dotdot", - Typeflag: tar.TypeSymlink, - Linkname: "../victim/hello", - Mode: 0644, - }, - }, - { // try reading victim/hello (/../) - { - Name: "slash-dotdot", - Typeflag: tar.TypeSymlink, - // Note the leading slash - Linkname: "/../victim/hello", - Mode: 0644, - }, - }, - { // try writing victim/file - { - Name: "loophole-victim", - Typeflag: tar.TypeSymlink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "loophole-victim/file", - Typeflag: tar.TypeReg, - Mode: 0644, - }, - }, - { // try reading victim/hello (symlink, symlink) - { - Name: "loophole-victim", - Typeflag: tar.TypeSymlink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "symlink", - Typeflag: tar.TypeSymlink, - Linkname: "loophole-victim/hello", - Mode: 0644, - }, - }, - { // try reading victim/hello (symlink, hardlink) - { - Name: "loophole-victim", - Typeflag: tar.TypeSymlink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "hardlink", - Typeflag: tar.TypeLink, - Linkname: "loophole-victim/hello", - Mode: 0644, - }, - }, - { // try removing victim directory (symlink) - { - Name: "loophole-victim", - Typeflag: tar.TypeSymlink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "loophole-victim", - Typeflag: tar.TypeReg, - Mode: 0644, - }, - }, - { // try writing to victim/newdir/newfile with a symlink in the path - { - // this header needs to be before the next one, or else there is an error - Name: "dir/loophole", - Typeflag: tar.TypeSymlink, - Linkname: "../../victim", - Mode: 0755, - }, - { - Name: "dir/loophole/newdir/newfile", - Typeflag: tar.TypeReg, - Mode: 0644, - }, - }, - } { - if err := testBreakout("untar", "docker-TestUntarInvalidSymlink", headers); err != nil { - t.Fatalf("i=%d. %v", i, err) - } - } -} - -func TestTempArchiveCloseMultipleTimes(t *testing.T) { - reader := ioutil.NopCloser(strings.NewReader("hello")) - tempArchive, err := NewTempArchive(reader, "") - buf := make([]byte, 10) - n, err := tempArchive.Read(buf) - if n != 5 { - t.Fatalf("Expected to read 5 bytes. Read %d instead", n) - } - for i := 0; i < 3; i++ { - if err = tempArchive.Close(); err != nil { - t.Fatalf("i=%d. Unexpected error closing temp archive: %v", i, err) - } - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/archive_unix_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/archive_unix_test.go deleted file mode 100644 index 18f45c480..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/archive_unix_test.go +++ /dev/null @@ -1,60 +0,0 @@ -// +build !windows - -package archive - -import ( - "os" - "testing" -) - -func TestCanonicalTarNameForPath(t *testing.T) { - cases := []struct{ in, expected string }{ - {"foo", "foo"}, - {"foo/bar", "foo/bar"}, - {"foo/dir/", "foo/dir/"}, - } - for _, v := range cases { - if out, err := CanonicalTarNameForPath(v.in); err != nil { - t.Fatalf("cannot get canonical name for path: %s: %v", v.in, err) - } else if out != v.expected { - t.Fatalf("wrong canonical tar name. expected:%s got:%s", v.expected, out) - } - } -} - -func TestCanonicalTarName(t *testing.T) { - cases := []struct { - in string - isDir bool - expected string - }{ - {"foo", false, "foo"}, - {"foo", true, "foo/"}, - {"foo/bar", false, "foo/bar"}, - {"foo/bar", true, "foo/bar/"}, - } - for _, v := range cases { - if out, err := canonicalTarName(v.in, v.isDir); err != nil { - t.Fatalf("cannot get canonical name for path: %s: %v", v.in, err) - } else if out != v.expected { - t.Fatalf("wrong canonical tar name. expected:%s got:%s", v.expected, out) - } - } -} - -func TestChmodTarEntry(t *testing.T) { - cases := []struct { - in, expected os.FileMode - }{ - {0000, 0000}, - {0777, 0777}, - {0644, 0644}, - {0755, 0755}, - {0444, 0444}, - } - for _, v := range cases { - if out := chmodTarEntry(v.in); out != v.expected { - t.Fatalf("wrong chmod. expected:%v got:%v", v.expected, out) - } - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/archive_windows_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/archive_windows_test.go deleted file mode 100644 index b7abc4022..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/archive_windows_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// +build windows - -package archive - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" -) - -func TestCopyFileWithInvalidDest(t *testing.T) { - folder, err := ioutil.TempDir("", "docker-archive-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(folder) - dest := "c:dest" - srcFolder := filepath.Join(folder, "src") - src := filepath.Join(folder, "src", "src") - err = os.MkdirAll(srcFolder, 0740) - if err != nil { - t.Fatal(err) - } - ioutil.WriteFile(src, []byte("content"), 0777) - err = CopyWithTar(src, dest) - if err == nil { - t.Fatalf("archiver.CopyWithTar should throw an error on invalid dest.") - } -} - -func TestCanonicalTarNameForPath(t *testing.T) { - cases := []struct { - in, expected string - shouldFail bool - }{ - {"foo", "foo", false}, - {"foo/bar", "___", true}, // unix-styled windows path must fail - {`foo\bar`, "foo/bar", false}, - } - for _, v := range cases { - if out, err := CanonicalTarNameForPath(v.in); err != nil && !v.shouldFail { - t.Fatalf("cannot get canonical name for path: %s: %v", v.in, err) - } else if v.shouldFail && err == nil { - t.Fatalf("canonical path call should have failed with error. in=%s out=%s", v.in, out) - } else if !v.shouldFail && out != v.expected { - t.Fatalf("wrong canonical tar name. expected:%s got:%s", v.expected, out) - } - } -} - -func TestCanonicalTarName(t *testing.T) { - cases := []struct { - in string - isDir bool - expected string - }{ - {"foo", false, "foo"}, - {"foo", true, "foo/"}, - {`foo\bar`, false, "foo/bar"}, - {`foo\bar`, true, "foo/bar/"}, - } - for _, v := range cases { - if out, err := canonicalTarName(v.in, v.isDir); err != nil { - t.Fatalf("cannot get canonical name for path: %s: %v", v.in, err) - } else if out != v.expected { - t.Fatalf("wrong canonical tar name. expected:%s got:%s", v.expected, out) - } - } -} - -func TestChmodTarEntry(t *testing.T) { - cases := []struct { - in, expected os.FileMode - }{ - {0000, 0111}, - {0777, 0755}, - {0644, 0755}, - {0755, 0755}, - {0444, 0555}, - } - for _, v := range cases { - if out := chmodTarEntry(v.in); out != v.expected { - t.Fatalf("wrong chmod. expected:%v got:%v", v.expected, out) - } - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/changes_posix_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/changes_posix_test.go deleted file mode 100644 index 5a3282b5a..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/changes_posix_test.go +++ /dev/null @@ -1,127 +0,0 @@ -package archive - -import ( - "archive/tar" - "fmt" - "io" - "io/ioutil" - "os" - "path" - "sort" - "testing" -) - -func TestHardLinkOrder(t *testing.T) { - names := []string{"file1.txt", "file2.txt", "file3.txt"} - msg := []byte("Hey y'all") - - // Create dir - src, err := ioutil.TempDir("", "docker-hardlink-test-src-") - if err != nil { - t.Fatal(err) - } - //defer os.RemoveAll(src) - for _, name := range names { - func() { - fh, err := os.Create(path.Join(src, name)) - if err != nil { - t.Fatal(err) - } - defer fh.Close() - if _, err = fh.Write(msg); err != nil { - t.Fatal(err) - } - }() - } - // Create dest, with changes that includes hardlinks - dest, err := ioutil.TempDir("", "docker-hardlink-test-dest-") - if err != nil { - t.Fatal(err) - } - os.RemoveAll(dest) // we just want the name, at first - if err := copyDir(src, dest); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dest) - for _, name := range names { - for i := 0; i < 5; i++ { - if err := os.Link(path.Join(dest, name), path.Join(dest, fmt.Sprintf("%s.link%d", name, i))); err != nil { - t.Fatal(err) - } - } - } - - // get changes - changes, err := ChangesDirs(dest, src) - if err != nil { - t.Fatal(err) - } - - // sort - sort.Sort(changesByPath(changes)) - - // ExportChanges - ar, err := ExportChanges(dest, changes, nil, nil) - if err != nil { - t.Fatal(err) - } - hdrs, err := walkHeaders(ar) - if err != nil { - t.Fatal(err) - } - - // reverse sort - sort.Sort(sort.Reverse(changesByPath(changes))) - // ExportChanges - arRev, err := ExportChanges(dest, changes, nil, nil) - if err != nil { - t.Fatal(err) - } - hdrsRev, err := walkHeaders(arRev) - if err != nil { - t.Fatal(err) - } - - // line up the two sets - sort.Sort(tarHeaders(hdrs)) - sort.Sort(tarHeaders(hdrsRev)) - - // compare Size and LinkName - for i := range hdrs { - if hdrs[i].Name != hdrsRev[i].Name { - t.Errorf("headers - expected name %q; but got %q", hdrs[i].Name, hdrsRev[i].Name) - } - if hdrs[i].Size != hdrsRev[i].Size { - t.Errorf("headers - %q expected size %d; but got %d", hdrs[i].Name, hdrs[i].Size, hdrsRev[i].Size) - } - if hdrs[i].Typeflag != hdrsRev[i].Typeflag { - t.Errorf("headers - %q expected type %d; but got %d", hdrs[i].Name, hdrs[i].Typeflag, hdrsRev[i].Typeflag) - } - if hdrs[i].Linkname != hdrsRev[i].Linkname { - t.Errorf("headers - %q expected linkname %q; but got %q", hdrs[i].Name, hdrs[i].Linkname, hdrsRev[i].Linkname) - } - } - -} - -type tarHeaders []tar.Header - -func (th tarHeaders) Len() int { return len(th) } -func (th tarHeaders) Swap(i, j int) { th[j], th[i] = th[i], th[j] } -func (th tarHeaders) Less(i, j int) bool { return th[i].Name < th[j].Name } - -func walkHeaders(r io.Reader) ([]tar.Header, error) { - t := tar.NewReader(r) - headers := []tar.Header{} - for { - hdr, err := t.Next() - if err != nil { - if err == io.EOF { - break - } - return headers, err - } - headers = append(headers, *hdr) - } - return headers, nil -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/changes_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/changes_test.go deleted file mode 100644 index f4316ce21..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/changes_test.go +++ /dev/null @@ -1,527 +0,0 @@ -package archive - -import ( - "io/ioutil" - "os" - "os/exec" - "path" - "sort" - "testing" - "time" -) - -func max(x, y int) int { - if x >= y { - return x - } - return y -} - -func copyDir(src, dst string) error { - cmd := exec.Command("cp", "-a", src, dst) - if err := cmd.Run(); err != nil { - return err - } - return nil -} - -type FileType uint32 - -const ( - Regular FileType = iota - Dir - Symlink -) - -type FileData struct { - filetype FileType - path string - contents string - permissions os.FileMode -} - -func createSampleDir(t *testing.T, root string) { - files := []FileData{ - {Regular, "file1", "file1\n", 0600}, - {Regular, "file2", "file2\n", 0666}, - {Regular, "file3", "file3\n", 0404}, - {Regular, "file4", "file4\n", 0600}, - {Regular, "file5", "file5\n", 0600}, - {Regular, "file6", "file6\n", 0600}, - {Regular, "file7", "file7\n", 0600}, - {Dir, "dir1", "", 0740}, - {Regular, "dir1/file1-1", "file1-1\n", 01444}, - {Regular, "dir1/file1-2", "file1-2\n", 0666}, - {Dir, "dir2", "", 0700}, - {Regular, "dir2/file2-1", "file2-1\n", 0666}, - {Regular, "dir2/file2-2", "file2-2\n", 0666}, - {Dir, "dir3", "", 0700}, - {Regular, "dir3/file3-1", "file3-1\n", 0666}, - {Regular, "dir3/file3-2", "file3-2\n", 0666}, - {Dir, "dir4", "", 0700}, - {Regular, "dir4/file3-1", "file4-1\n", 0666}, - {Regular, "dir4/file3-2", "file4-2\n", 0666}, - {Symlink, "symlink1", "target1", 0666}, - {Symlink, "symlink2", "target2", 0666}, - {Symlink, "symlink3", root + "/file1", 0666}, - {Symlink, "symlink4", root + "/symlink3", 0666}, - {Symlink, "dirSymlink", root + "/dir1", 0740}, - } - - now := time.Now() - for _, info := range files { - p := path.Join(root, info.path) - if info.filetype == Dir { - if err := os.MkdirAll(p, info.permissions); err != nil { - t.Fatal(err) - } - } else if info.filetype == Regular { - if err := ioutil.WriteFile(p, []byte(info.contents), info.permissions); err != nil { - t.Fatal(err) - } - } else if info.filetype == Symlink { - if err := os.Symlink(info.contents, p); err != nil { - t.Fatal(err) - } - } - - if info.filetype != Symlink { - // Set a consistent ctime, atime for all files and dirs - if err := os.Chtimes(p, now, now); err != nil { - t.Fatal(err) - } - } - } -} - -func TestChangeString(t *testing.T) { - modifiyChange := Change{"change", ChangeModify} - toString := modifiyChange.String() - if toString != "C change" { - t.Fatalf("String() of a change with ChangeModifiy Kind should have been %s but was %s", "C change", toString) - } - addChange := Change{"change", ChangeAdd} - toString = addChange.String() - if toString != "A change" { - t.Fatalf("String() of a change with ChangeAdd Kind should have been %s but was %s", "A change", toString) - } - deleteChange := Change{"change", ChangeDelete} - toString = deleteChange.String() - if toString != "D change" { - t.Fatalf("String() of a change with ChangeDelete Kind should have been %s but was %s", "D change", toString) - } -} - -func TestChangesWithNoChanges(t *testing.T) { - rwLayer, err := ioutil.TempDir("", "docker-changes-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(rwLayer) - layer, err := ioutil.TempDir("", "docker-changes-test-layer") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(layer) - createSampleDir(t, layer) - changes, err := Changes([]string{layer}, rwLayer) - if err != nil { - t.Fatal(err) - } - if len(changes) != 0 { - t.Fatalf("Changes with no difference should have detect no changes, but detected %d", len(changes)) - } -} - -func TestChangesWithChanges(t *testing.T) { - // Mock the readonly layer - layer, err := ioutil.TempDir("", "docker-changes-test-layer") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(layer) - createSampleDir(t, layer) - os.MkdirAll(path.Join(layer, "dir1/subfolder"), 0740) - - // Mock the RW layer - rwLayer, err := ioutil.TempDir("", "docker-changes-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(rwLayer) - - // Create a folder in RW layer - dir1 := path.Join(rwLayer, "dir1") - os.MkdirAll(dir1, 0740) - deletedFile := path.Join(dir1, ".wh.file1-2") - ioutil.WriteFile(deletedFile, []byte{}, 0600) - modifiedFile := path.Join(dir1, "file1-1") - ioutil.WriteFile(modifiedFile, []byte{0x00}, 01444) - // Let's add a subfolder for a newFile - subfolder := path.Join(dir1, "subfolder") - os.MkdirAll(subfolder, 0740) - newFile := path.Join(subfolder, "newFile") - ioutil.WriteFile(newFile, []byte{}, 0740) - - changes, err := Changes([]string{layer}, rwLayer) - if err != nil { - t.Fatal(err) - } - - expectedChanges := []Change{ - {"/dir1", ChangeModify}, - {"/dir1/file1-1", ChangeModify}, - {"/dir1/file1-2", ChangeDelete}, - {"/dir1/subfolder", ChangeModify}, - {"/dir1/subfolder/newFile", ChangeAdd}, - } - checkChanges(expectedChanges, changes, t) -} - -// See https://github.com/docker/docker/pull/13590 -func TestChangesWithChangesGH13590(t *testing.T) { - baseLayer, err := ioutil.TempDir("", "docker-changes-test.") - defer os.RemoveAll(baseLayer) - - dir3 := path.Join(baseLayer, "dir1/dir2/dir3") - os.MkdirAll(dir3, 07400) - - file := path.Join(dir3, "file.txt") - ioutil.WriteFile(file, []byte("hello"), 0666) - - layer, err := ioutil.TempDir("", "docker-changes-test2.") - defer os.RemoveAll(layer) - - // Test creating a new file - if err := copyDir(baseLayer+"/dir1", layer+"/"); err != nil { - t.Fatalf("Cmd failed: %q", err) - } - - os.Remove(path.Join(layer, "dir1/dir2/dir3/file.txt")) - file = path.Join(layer, "dir1/dir2/dir3/file1.txt") - ioutil.WriteFile(file, []byte("bye"), 0666) - - changes, err := Changes([]string{baseLayer}, layer) - if err != nil { - t.Fatal(err) - } - - expectedChanges := []Change{ - {"/dir1/dir2/dir3", ChangeModify}, - {"/dir1/dir2/dir3/file1.txt", ChangeAdd}, - } - checkChanges(expectedChanges, changes, t) - - // Now test changing a file - layer, err = ioutil.TempDir("", "docker-changes-test3.") - defer os.RemoveAll(layer) - - if err := copyDir(baseLayer+"/dir1", layer+"/"); err != nil { - t.Fatalf("Cmd failed: %q", err) - } - - file = path.Join(layer, "dir1/dir2/dir3/file.txt") - ioutil.WriteFile(file, []byte("bye"), 0666) - - changes, err = Changes([]string{baseLayer}, layer) - if err != nil { - t.Fatal(err) - } - - expectedChanges = []Change{ - {"/dir1/dir2/dir3/file.txt", ChangeModify}, - } - checkChanges(expectedChanges, changes, t) -} - -// Create an directory, copy it, make sure we report no changes between the two -func TestChangesDirsEmpty(t *testing.T) { - src, err := ioutil.TempDir("", "docker-changes-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(src) - createSampleDir(t, src) - dst := src + "-copy" - if err := copyDir(src, dst); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dst) - changes, err := ChangesDirs(dst, src) - if err != nil { - t.Fatal(err) - } - - if len(changes) != 0 { - t.Fatalf("Reported changes for identical dirs: %v", changes) - } - os.RemoveAll(src) - os.RemoveAll(dst) -} - -func mutateSampleDir(t *testing.T, root string) { - // Remove a regular file - if err := os.RemoveAll(path.Join(root, "file1")); err != nil { - t.Fatal(err) - } - - // Remove a directory - if err := os.RemoveAll(path.Join(root, "dir1")); err != nil { - t.Fatal(err) - } - - // Remove a symlink - if err := os.RemoveAll(path.Join(root, "symlink1")); err != nil { - t.Fatal(err) - } - - // Rewrite a file - if err := ioutil.WriteFile(path.Join(root, "file2"), []byte("fileNN\n"), 0777); err != nil { - t.Fatal(err) - } - - // Replace a file - if err := os.RemoveAll(path.Join(root, "file3")); err != nil { - t.Fatal(err) - } - if err := ioutil.WriteFile(path.Join(root, "file3"), []byte("fileMM\n"), 0404); err != nil { - t.Fatal(err) - } - - // Touch file - if err := os.Chtimes(path.Join(root, "file4"), time.Now().Add(time.Second), time.Now().Add(time.Second)); err != nil { - t.Fatal(err) - } - - // Replace file with dir - if err := os.RemoveAll(path.Join(root, "file5")); err != nil { - t.Fatal(err) - } - if err := os.MkdirAll(path.Join(root, "file5"), 0666); err != nil { - t.Fatal(err) - } - - // Create new file - if err := ioutil.WriteFile(path.Join(root, "filenew"), []byte("filenew\n"), 0777); err != nil { - t.Fatal(err) - } - - // Create new dir - if err := os.MkdirAll(path.Join(root, "dirnew"), 0766); err != nil { - t.Fatal(err) - } - - // Create a new symlink - if err := os.Symlink("targetnew", path.Join(root, "symlinknew")); err != nil { - t.Fatal(err) - } - - // Change a symlink - if err := os.RemoveAll(path.Join(root, "symlink2")); err != nil { - t.Fatal(err) - } - if err := os.Symlink("target2change", path.Join(root, "symlink2")); err != nil { - t.Fatal(err) - } - - // Replace dir with file - if err := os.RemoveAll(path.Join(root, "dir2")); err != nil { - t.Fatal(err) - } - if err := ioutil.WriteFile(path.Join(root, "dir2"), []byte("dir2\n"), 0777); err != nil { - t.Fatal(err) - } - - // Touch dir - if err := os.Chtimes(path.Join(root, "dir3"), time.Now().Add(time.Second), time.Now().Add(time.Second)); err != nil { - t.Fatal(err) - } -} - -func TestChangesDirsMutated(t *testing.T) { - src, err := ioutil.TempDir("", "docker-changes-test") - if err != nil { - t.Fatal(err) - } - createSampleDir(t, src) - dst := src + "-copy" - if err := copyDir(src, dst); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(src) - defer os.RemoveAll(dst) - - mutateSampleDir(t, dst) - - changes, err := ChangesDirs(dst, src) - if err != nil { - t.Fatal(err) - } - - sort.Sort(changesByPath(changes)) - - expectedChanges := []Change{ - {"/dir1", ChangeDelete}, - {"/dir2", ChangeModify}, - {"/dirnew", ChangeAdd}, - {"/file1", ChangeDelete}, - {"/file2", ChangeModify}, - {"/file3", ChangeModify}, - {"/file4", ChangeModify}, - {"/file5", ChangeModify}, - {"/filenew", ChangeAdd}, - {"/symlink1", ChangeDelete}, - {"/symlink2", ChangeModify}, - {"/symlinknew", ChangeAdd}, - } - - for i := 0; i < max(len(changes), len(expectedChanges)); i++ { - if i >= len(expectedChanges) { - t.Fatalf("unexpected change %s\n", changes[i].String()) - } - if i >= len(changes) { - t.Fatalf("no change for expected change %s\n", expectedChanges[i].String()) - } - if changes[i].Path == expectedChanges[i].Path { - if changes[i] != expectedChanges[i] { - t.Fatalf("Wrong change for %s, expected %s, got %s\n", changes[i].Path, changes[i].String(), expectedChanges[i].String()) - } - } else if changes[i].Path < expectedChanges[i].Path { - t.Fatalf("unexpected change %s\n", changes[i].String()) - } else { - t.Fatalf("no change for expected change %s != %s\n", expectedChanges[i].String(), changes[i].String()) - } - } -} - -func TestApplyLayer(t *testing.T) { - src, err := ioutil.TempDir("", "docker-changes-test") - if err != nil { - t.Fatal(err) - } - createSampleDir(t, src) - defer os.RemoveAll(src) - dst := src + "-copy" - if err := copyDir(src, dst); err != nil { - t.Fatal(err) - } - mutateSampleDir(t, dst) - defer os.RemoveAll(dst) - - changes, err := ChangesDirs(dst, src) - if err != nil { - t.Fatal(err) - } - - layer, err := ExportChanges(dst, changes, nil, nil) - if err != nil { - t.Fatal(err) - } - - layerCopy, err := NewTempArchive(layer, "") - if err != nil { - t.Fatal(err) - } - - if _, err := ApplyLayer(src, layerCopy); err != nil { - t.Fatal(err) - } - - changes2, err := ChangesDirs(src, dst) - if err != nil { - t.Fatal(err) - } - - if len(changes2) != 0 { - t.Fatalf("Unexpected differences after reapplying mutation: %v", changes2) - } -} - -func TestChangesSizeWithHardlinks(t *testing.T) { - srcDir, err := ioutil.TempDir("", "docker-test-srcDir") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(srcDir) - - destDir, err := ioutil.TempDir("", "docker-test-destDir") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(destDir) - - creationSize, err := prepareUntarSourceDirectory(100, destDir, true) - if err != nil { - t.Fatal(err) - } - - changes, err := ChangesDirs(destDir, srcDir) - if err != nil { - t.Fatal(err) - } - - got := ChangesSize(destDir, changes) - if got != int64(creationSize) { - t.Errorf("Expected %d bytes of changes, got %d", creationSize, got) - } -} - -func TestChangesSizeWithNoChanges(t *testing.T) { - size := ChangesSize("/tmp", nil) - if size != 0 { - t.Fatalf("ChangesSizes with no changes should be 0, was %d", size) - } -} - -func TestChangesSizeWithOnlyDeleteChanges(t *testing.T) { - changes := []Change{ - {Path: "deletedPath", Kind: ChangeDelete}, - } - size := ChangesSize("/tmp", changes) - if size != 0 { - t.Fatalf("ChangesSizes with only delete changes should be 0, was %d", size) - } -} - -func TestChangesSize(t *testing.T) { - parentPath, err := ioutil.TempDir("", "docker-changes-test") - defer os.RemoveAll(parentPath) - addition := path.Join(parentPath, "addition") - if err := ioutil.WriteFile(addition, []byte{0x01, 0x01, 0x01}, 0744); err != nil { - t.Fatal(err) - } - modification := path.Join(parentPath, "modification") - if err = ioutil.WriteFile(modification, []byte{0x01, 0x01, 0x01}, 0744); err != nil { - t.Fatal(err) - } - changes := []Change{ - {Path: "addition", Kind: ChangeAdd}, - {Path: "modification", Kind: ChangeModify}, - } - size := ChangesSize(parentPath, changes) - if size != 6 { - t.Fatalf("Expected 6 bytes of changes, got %d", size) - } -} - -func checkChanges(expectedChanges, changes []Change, t *testing.T) { - sort.Sort(changesByPath(expectedChanges)) - sort.Sort(changesByPath(changes)) - for i := 0; i < max(len(changes), len(expectedChanges)); i++ { - if i >= len(expectedChanges) { - t.Fatalf("unexpected change %s\n", changes[i].String()) - } - if i >= len(changes) { - t.Fatalf("no change for expected change %s\n", expectedChanges[i].String()) - } - if changes[i].Path == expectedChanges[i].Path { - if changes[i] != expectedChanges[i] { - t.Fatalf("Wrong change for %s, expected %s, got %s\n", changes[i].Path, changes[i].String(), expectedChanges[i].String()) - } - } else if changes[i].Path < expectedChanges[i].Path { - t.Fatalf("unexpected change %s\n", changes[i].String()) - } else { - t.Fatalf("no change for expected change %s != %s\n", expectedChanges[i].String(), changes[i].String()) - } - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/copy_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/copy_test.go deleted file mode 100644 index f1dc23824..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/copy_test.go +++ /dev/null @@ -1,974 +0,0 @@ -package archive - -import ( - "bytes" - "crypto/sha256" - "encoding/hex" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "testing" -) - -func removeAllPaths(paths ...string) { - for _, path := range paths { - os.RemoveAll(path) - } -} - -func getTestTempDirs(t *testing.T) (tmpDirA, tmpDirB string) { - var err error - - if tmpDirA, err = ioutil.TempDir("", "archive-copy-test"); err != nil { - t.Fatal(err) - } - - if tmpDirB, err = ioutil.TempDir("", "archive-copy-test"); err != nil { - t.Fatal(err) - } - - return -} - -func isNotDir(err error) bool { - return strings.Contains(err.Error(), "not a directory") -} - -func joinTrailingSep(pathElements ...string) string { - joined := filepath.Join(pathElements...) - - return fmt.Sprintf("%s%c", joined, filepath.Separator) -} - -func fileContentsEqual(t *testing.T, filenameA, filenameB string) (err error) { - t.Logf("checking for equal file contents: %q and %q\n", filenameA, filenameB) - - fileA, err := os.Open(filenameA) - if err != nil { - return - } - defer fileA.Close() - - fileB, err := os.Open(filenameB) - if err != nil { - return - } - defer fileB.Close() - - hasher := sha256.New() - - if _, err = io.Copy(hasher, fileA); err != nil { - return - } - - hashA := hasher.Sum(nil) - hasher.Reset() - - if _, err = io.Copy(hasher, fileB); err != nil { - return - } - - hashB := hasher.Sum(nil) - - if !bytes.Equal(hashA, hashB) { - err = fmt.Errorf("file content hashes not equal - expected %s, got %s", hex.EncodeToString(hashA), hex.EncodeToString(hashB)) - } - - return -} - -func dirContentsEqual(t *testing.T, newDir, oldDir string) (err error) { - t.Logf("checking for equal directory contents: %q and %q\n", newDir, oldDir) - - var changes []Change - - if changes, err = ChangesDirs(newDir, oldDir); err != nil { - return - } - - if len(changes) != 0 { - err = fmt.Errorf("expected no changes between directories, but got: %v", changes) - } - - return -} - -func logDirContents(t *testing.T, dirPath string) { - logWalkedPaths := filepath.WalkFunc(func(path string, info os.FileInfo, err error) error { - if err != nil { - t.Errorf("stat error for path %q: %s", path, err) - return nil - } - - if info.IsDir() { - path = joinTrailingSep(path) - } - - t.Logf("\t%s", path) - - return nil - }) - - t.Logf("logging directory contents: %q", dirPath) - - if err := filepath.Walk(dirPath, logWalkedPaths); err != nil { - t.Fatal(err) - } -} - -func testCopyHelper(t *testing.T, srcPath, dstPath string) (err error) { - t.Logf("copying from %q to %q (not follow symbol link)", srcPath, dstPath) - - return CopyResource(srcPath, dstPath, false) -} - -func testCopyHelperFSym(t *testing.T, srcPath, dstPath string) (err error) { - t.Logf("copying from %q to %q (follow symbol link)", srcPath, dstPath) - - return CopyResource(srcPath, dstPath, true) -} - -// Basic assumptions about SRC and DST: -// 1. SRC must exist. -// 2. If SRC ends with a trailing separator, it must be a directory. -// 3. DST parent directory must exist. -// 4. If DST exists as a file, it must not end with a trailing separator. - -// First get these easy error cases out of the way. - -// Test for error when SRC does not exist. -func TestCopyErrSrcNotExists(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - if _, err := CopyInfoSourcePath(filepath.Join(tmpDirA, "file1"), false); !os.IsNotExist(err) { - t.Fatalf("expected IsNotExist error, but got %T: %s", err, err) - } -} - -// Test for error when SRC ends in a trailing -// path separator but it exists as a file. -func TestCopyErrSrcNotDir(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A with some sample files and directories. - createSampleDir(t, tmpDirA) - - if _, err := CopyInfoSourcePath(joinTrailingSep(tmpDirA, "file1"), false); !isNotDir(err) { - t.Fatalf("expected IsNotDir error, but got %T: %s", err, err) - } -} - -// Test for error when SRC is a valid file or directory, -// but the DST parent directory does not exist. -func TestCopyErrDstParentNotExists(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A with some sample files and directories. - createSampleDir(t, tmpDirA) - - srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false} - - // Try with a file source. - content, err := TarResource(srcInfo) - if err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - defer content.Close() - - // Copy to a file whose parent does not exist. - if err = CopyTo(content, srcInfo, filepath.Join(tmpDirB, "fakeParentDir", "file1")); err == nil { - t.Fatal("expected IsNotExist error, but got nil instead") - } - - if !os.IsNotExist(err) { - t.Fatalf("expected IsNotExist error, but got %T: %s", err, err) - } - - // Try with a directory source. - srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true} - - content, err = TarResource(srcInfo) - if err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - defer content.Close() - - // Copy to a directory whose parent does not exist. - if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "fakeParentDir", "fakeDstDir")); err == nil { - t.Fatal("expected IsNotExist error, but got nil instead") - } - - if !os.IsNotExist(err) { - t.Fatalf("expected IsNotExist error, but got %T: %s", err, err) - } -} - -// Test for error when DST ends in a trailing -// path separator but exists as a file. -func TestCopyErrDstNotDir(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A and B with some sample files and directories. - createSampleDir(t, tmpDirA) - createSampleDir(t, tmpDirB) - - // Try with a file source. - srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false} - - content, err := TarResource(srcInfo) - if err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - defer content.Close() - - if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil { - t.Fatal("expected IsNotDir error, but got nil instead") - } - - if !isNotDir(err) { - t.Fatalf("expected IsNotDir error, but got %T: %s", err, err) - } - - // Try with a directory source. - srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true} - - content, err = TarResource(srcInfo) - if err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - defer content.Close() - - if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil { - t.Fatal("expected IsNotDir error, but got nil instead") - } - - if !isNotDir(err) { - t.Fatalf("expected IsNotDir error, but got %T: %s", err, err) - } -} - -// Possibilities are reduced to the remaining 10 cases: -// -// case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action -// =================================================================================================== -// A | no | - | no | - | no | create file -// B | no | - | no | - | yes | error -// C | no | - | yes | no | - | overwrite file -// D | no | - | yes | yes | - | create file in dst dir -// E | yes | no | no | - | - | create dir, copy contents -// F | yes | no | yes | no | - | error -// G | yes | no | yes | yes | - | copy dir and contents -// H | yes | yes | no | - | - | create dir, copy contents -// I | yes | yes | yes | no | - | error -// J | yes | yes | yes | yes | - | copy dir contents -// - -// A. SRC specifies a file and DST (no trailing path separator) doesn't -// exist. This should create a file with the name DST and copy the -// contents of the source file into it. -func TestCopyCaseA(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A with some sample files and directories. - createSampleDir(t, tmpDirA) - - srcPath := filepath.Join(tmpDirA, "file1") - dstPath := filepath.Join(tmpDirB, "itWorks.txt") - - var err error - - if err = testCopyHelper(t, srcPath, dstPath); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = fileContentsEqual(t, srcPath, dstPath); err != nil { - t.Fatal(err) - } - os.Remove(dstPath) - - symlinkPath := filepath.Join(tmpDirA, "symlink3") - symlinkPath1 := filepath.Join(tmpDirA, "symlink4") - linkTarget := filepath.Join(tmpDirA, "file1") - - if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = fileContentsEqual(t, linkTarget, dstPath); err != nil { - t.Fatal(err) - } - os.Remove(dstPath) - if err = testCopyHelperFSym(t, symlinkPath1, dstPath); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = fileContentsEqual(t, linkTarget, dstPath); err != nil { - t.Fatal(err) - } -} - -// B. SRC specifies a file and DST (with trailing path separator) doesn't -// exist. This should cause an error because the copy operation cannot -// create a directory when copying a single file. -func TestCopyCaseB(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A with some sample files and directories. - createSampleDir(t, tmpDirA) - - srcPath := filepath.Join(tmpDirA, "file1") - dstDir := joinTrailingSep(tmpDirB, "testDir") - - var err error - - if err = testCopyHelper(t, srcPath, dstDir); err == nil { - t.Fatal("expected ErrDirNotExists error, but got nil instead") - } - - if err != ErrDirNotExists { - t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err) - } - - symlinkPath := filepath.Join(tmpDirA, "symlink3") - - if err = testCopyHelperFSym(t, symlinkPath, dstDir); err == nil { - t.Fatal("expected ErrDirNotExists error, but got nil instead") - } - if err != ErrDirNotExists { - t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err) - } - -} - -// C. SRC specifies a file and DST exists as a file. This should overwrite -// the file at DST with the contents of the source file. -func TestCopyCaseC(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A and B with some sample files and directories. - createSampleDir(t, tmpDirA) - createSampleDir(t, tmpDirB) - - srcPath := filepath.Join(tmpDirA, "file1") - dstPath := filepath.Join(tmpDirB, "file2") - - var err error - - // Ensure they start out different. - if err = fileContentsEqual(t, srcPath, dstPath); err == nil { - t.Fatal("expected different file contents") - } - - if err = testCopyHelper(t, srcPath, dstPath); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = fileContentsEqual(t, srcPath, dstPath); err != nil { - t.Fatal(err) - } -} - -// C. Symbol link following version: -// SRC specifies a file and DST exists as a file. This should overwrite -// the file at DST with the contents of the source file. -func TestCopyCaseCFSym(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A and B with some sample files and directories. - createSampleDir(t, tmpDirA) - createSampleDir(t, tmpDirB) - - symlinkPathBad := filepath.Join(tmpDirA, "symlink1") - symlinkPath := filepath.Join(tmpDirA, "symlink3") - linkTarget := filepath.Join(tmpDirA, "file1") - dstPath := filepath.Join(tmpDirB, "file2") - - var err error - - // first to test broken link - if err = testCopyHelperFSym(t, symlinkPathBad, dstPath); err == nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - // test symbol link -> symbol link -> target - // Ensure they start out different. - if err = fileContentsEqual(t, linkTarget, dstPath); err == nil { - t.Fatal("expected different file contents") - } - - if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = fileContentsEqual(t, linkTarget, dstPath); err != nil { - t.Fatal(err) - } -} - -// D. SRC specifies a file and DST exists as a directory. This should place -// a copy of the source file inside it using the basename from SRC. Ensure -// this works whether DST has a trailing path separator or not. -func TestCopyCaseD(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A and B with some sample files and directories. - createSampleDir(t, tmpDirA) - createSampleDir(t, tmpDirB) - - srcPath := filepath.Join(tmpDirA, "file1") - dstDir := filepath.Join(tmpDirB, "dir1") - dstPath := filepath.Join(dstDir, "file1") - - var err error - - // Ensure that dstPath doesn't exist. - if _, err = os.Stat(dstPath); !os.IsNotExist(err) { - t.Fatalf("did not expect dstPath %q to exist", dstPath) - } - - if err = testCopyHelper(t, srcPath, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = fileContentsEqual(t, srcPath, dstPath); err != nil { - t.Fatal(err) - } - - // Now try again but using a trailing path separator for dstDir. - - if err = os.RemoveAll(dstDir); err != nil { - t.Fatalf("unable to remove dstDir: %s", err) - } - - if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { - t.Fatalf("unable to make dstDir: %s", err) - } - - dstDir = joinTrailingSep(tmpDirB, "dir1") - - if err = testCopyHelper(t, srcPath, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = fileContentsEqual(t, srcPath, dstPath); err != nil { - t.Fatal(err) - } -} - -// D. Symbol link following version: -// SRC specifies a file and DST exists as a directory. This should place -// a copy of the source file inside it using the basename from SRC. Ensure -// this works whether DST has a trailing path separator or not. -func TestCopyCaseDFSym(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A and B with some sample files and directories. - createSampleDir(t, tmpDirA) - createSampleDir(t, tmpDirB) - - srcPath := filepath.Join(tmpDirA, "symlink4") - linkTarget := filepath.Join(tmpDirA, "file1") - dstDir := filepath.Join(tmpDirB, "dir1") - dstPath := filepath.Join(dstDir, "symlink4") - - var err error - - // Ensure that dstPath doesn't exist. - if _, err = os.Stat(dstPath); !os.IsNotExist(err) { - t.Fatalf("did not expect dstPath %q to exist", dstPath) - } - - if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = fileContentsEqual(t, linkTarget, dstPath); err != nil { - t.Fatal(err) - } - - // Now try again but using a trailing path separator for dstDir. - - if err = os.RemoveAll(dstDir); err != nil { - t.Fatalf("unable to remove dstDir: %s", err) - } - - if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { - t.Fatalf("unable to make dstDir: %s", err) - } - - dstDir = joinTrailingSep(tmpDirB, "dir1") - - if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = fileContentsEqual(t, linkTarget, dstPath); err != nil { - t.Fatal(err) - } -} - -// E. SRC specifies a directory and DST does not exist. This should create a -// directory at DST and copy the contents of the SRC directory into the DST -// directory. Ensure this works whether DST has a trailing path separator or -// not. -func TestCopyCaseE(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A with some sample files and directories. - createSampleDir(t, tmpDirA) - - srcDir := filepath.Join(tmpDirA, "dir1") - dstDir := filepath.Join(tmpDirB, "testDir") - - var err error - - if err = testCopyHelper(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, dstDir, srcDir); err != nil { - t.Log("dir contents not equal") - logDirContents(t, tmpDirA) - logDirContents(t, tmpDirB) - t.Fatal(err) - } - - // Now try again but using a trailing path separator for dstDir. - - if err = os.RemoveAll(dstDir); err != nil { - t.Fatalf("unable to remove dstDir: %s", err) - } - - dstDir = joinTrailingSep(tmpDirB, "testDir") - - if err = testCopyHelper(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, dstDir, srcDir); err != nil { - t.Fatal(err) - } -} - -// E. Symbol link following version: -// SRC specifies a directory and DST does not exist. This should create a -// directory at DST and copy the contents of the SRC directory into the DST -// directory. Ensure this works whether DST has a trailing path separator or -// not. -func TestCopyCaseEFSym(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A with some sample files and directories. - createSampleDir(t, tmpDirA) - - srcDir := filepath.Join(tmpDirA, "dirSymlink") - linkTarget := filepath.Join(tmpDirA, "dir1") - dstDir := filepath.Join(tmpDirB, "testDir") - - var err error - - if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { - t.Log("dir contents not equal") - logDirContents(t, tmpDirA) - logDirContents(t, tmpDirB) - t.Fatal(err) - } - - // Now try again but using a trailing path separator for dstDir. - - if err = os.RemoveAll(dstDir); err != nil { - t.Fatalf("unable to remove dstDir: %s", err) - } - - dstDir = joinTrailingSep(tmpDirB, "testDir") - - if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { - t.Fatal(err) - } -} - -// F. SRC specifies a directory and DST exists as a file. This should cause an -// error as it is not possible to overwrite a file with a directory. -func TestCopyCaseF(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A and B with some sample files and directories. - createSampleDir(t, tmpDirA) - createSampleDir(t, tmpDirB) - - srcDir := filepath.Join(tmpDirA, "dir1") - symSrcDir := filepath.Join(tmpDirA, "dirSymlink") - dstFile := filepath.Join(tmpDirB, "file1") - - var err error - - if err = testCopyHelper(t, srcDir, dstFile); err == nil { - t.Fatal("expected ErrCannotCopyDir error, but got nil instead") - } - - if err != ErrCannotCopyDir { - t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) - } - - // now test with symbol link - if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil { - t.Fatal("expected ErrCannotCopyDir error, but got nil instead") - } - - if err != ErrCannotCopyDir { - t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) - } -} - -// G. SRC specifies a directory and DST exists as a directory. This should copy -// the SRC directory and all its contents to the DST directory. Ensure this -// works whether DST has a trailing path separator or not. -func TestCopyCaseG(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A and B with some sample files and directories. - createSampleDir(t, tmpDirA) - createSampleDir(t, tmpDirB) - - srcDir := filepath.Join(tmpDirA, "dir1") - dstDir := filepath.Join(tmpDirB, "dir2") - resultDir := filepath.Join(dstDir, "dir1") - - var err error - - if err = testCopyHelper(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, resultDir, srcDir); err != nil { - t.Fatal(err) - } - - // Now try again but using a trailing path separator for dstDir. - - if err = os.RemoveAll(dstDir); err != nil { - t.Fatalf("unable to remove dstDir: %s", err) - } - - if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { - t.Fatalf("unable to make dstDir: %s", err) - } - - dstDir = joinTrailingSep(tmpDirB, "dir2") - - if err = testCopyHelper(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, resultDir, srcDir); err != nil { - t.Fatal(err) - } -} - -// G. Symbol link version: -// SRC specifies a directory and DST exists as a directory. This should copy -// the SRC directory and all its contents to the DST directory. Ensure this -// works whether DST has a trailing path separator or not. -func TestCopyCaseGFSym(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A and B with some sample files and directories. - createSampleDir(t, tmpDirA) - createSampleDir(t, tmpDirB) - - srcDir := filepath.Join(tmpDirA, "dirSymlink") - linkTarget := filepath.Join(tmpDirA, "dir1") - dstDir := filepath.Join(tmpDirB, "dir2") - resultDir := filepath.Join(dstDir, "dirSymlink") - - var err error - - if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, resultDir, linkTarget); err != nil { - t.Fatal(err) - } - - // Now try again but using a trailing path separator for dstDir. - - if err = os.RemoveAll(dstDir); err != nil { - t.Fatalf("unable to remove dstDir: %s", err) - } - - if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { - t.Fatalf("unable to make dstDir: %s", err) - } - - dstDir = joinTrailingSep(tmpDirB, "dir2") - - if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, resultDir, linkTarget); err != nil { - t.Fatal(err) - } -} - -// H. SRC specifies a directory's contents only and DST does not exist. This -// should create a directory at DST and copy the contents of the SRC -// directory (but not the directory itself) into the DST directory. Ensure -// this works whether DST has a trailing path separator or not. -func TestCopyCaseH(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A with some sample files and directories. - createSampleDir(t, tmpDirA) - - srcDir := joinTrailingSep(tmpDirA, "dir1") + "." - dstDir := filepath.Join(tmpDirB, "testDir") - - var err error - - if err = testCopyHelper(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, dstDir, srcDir); err != nil { - t.Log("dir contents not equal") - logDirContents(t, tmpDirA) - logDirContents(t, tmpDirB) - t.Fatal(err) - } - - // Now try again but using a trailing path separator for dstDir. - - if err = os.RemoveAll(dstDir); err != nil { - t.Fatalf("unable to remove dstDir: %s", err) - } - - dstDir = joinTrailingSep(tmpDirB, "testDir") - - if err = testCopyHelper(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, dstDir, srcDir); err != nil { - t.Log("dir contents not equal") - logDirContents(t, tmpDirA) - logDirContents(t, tmpDirB) - t.Fatal(err) - } -} - -// H. Symbol link following version: -// SRC specifies a directory's contents only and DST does not exist. This -// should create a directory at DST and copy the contents of the SRC -// directory (but not the directory itself) into the DST directory. Ensure -// this works whether DST has a trailing path separator or not. -func TestCopyCaseHFSym(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A with some sample files and directories. - createSampleDir(t, tmpDirA) - - srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "." - linkTarget := filepath.Join(tmpDirA, "dir1") - dstDir := filepath.Join(tmpDirB, "testDir") - - var err error - - if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { - t.Log("dir contents not equal") - logDirContents(t, tmpDirA) - logDirContents(t, tmpDirB) - t.Fatal(err) - } - - // Now try again but using a trailing path separator for dstDir. - - if err = os.RemoveAll(dstDir); err != nil { - t.Fatalf("unable to remove dstDir: %s", err) - } - - dstDir = joinTrailingSep(tmpDirB, "testDir") - - if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { - t.Log("dir contents not equal") - logDirContents(t, tmpDirA) - logDirContents(t, tmpDirB) - t.Fatal(err) - } -} - -// I. SRC specifies a directory's contents only and DST exists as a file. This -// should cause an error as it is not possible to overwrite a file with a -// directory. -func TestCopyCaseI(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A and B with some sample files and directories. - createSampleDir(t, tmpDirA) - createSampleDir(t, tmpDirB) - - srcDir := joinTrailingSep(tmpDirA, "dir1") + "." - symSrcDir := filepath.Join(tmpDirB, "dirSymlink") - dstFile := filepath.Join(tmpDirB, "file1") - - var err error - - if err = testCopyHelper(t, srcDir, dstFile); err == nil { - t.Fatal("expected ErrCannotCopyDir error, but got nil instead") - } - - if err != ErrCannotCopyDir { - t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) - } - - // now try with symbol link of dir - if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil { - t.Fatal("expected ErrCannotCopyDir error, but got nil instead") - } - - if err != ErrCannotCopyDir { - t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) - } -} - -// J. SRC specifies a directory's contents only and DST exists as a directory. -// This should copy the contents of the SRC directory (but not the directory -// itself) into the DST directory. Ensure this works whether DST has a -// trailing path separator or not. -func TestCopyCaseJ(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A and B with some sample files and directories. - createSampleDir(t, tmpDirA) - createSampleDir(t, tmpDirB) - - srcDir := joinTrailingSep(tmpDirA, "dir1") + "." - dstDir := filepath.Join(tmpDirB, "dir5") - - var err error - - // first to create an empty dir - if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { - t.Fatalf("unable to make dstDir: %s", err) - } - - if err = testCopyHelper(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, dstDir, srcDir); err != nil { - t.Fatal(err) - } - - // Now try again but using a trailing path separator for dstDir. - - if err = os.RemoveAll(dstDir); err != nil { - t.Fatalf("unable to remove dstDir: %s", err) - } - - if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { - t.Fatalf("unable to make dstDir: %s", err) - } - - dstDir = joinTrailingSep(tmpDirB, "dir5") - - if err = testCopyHelper(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, dstDir, srcDir); err != nil { - t.Fatal(err) - } -} - -// J. Symbol link following version: -// SRC specifies a directory's contents only and DST exists as a directory. -// This should copy the contents of the SRC directory (but not the directory -// itself) into the DST directory. Ensure this works whether DST has a -// trailing path separator or not. -func TestCopyCaseJFSym(t *testing.T) { - tmpDirA, tmpDirB := getTestTempDirs(t) - defer removeAllPaths(tmpDirA, tmpDirB) - - // Load A and B with some sample files and directories. - createSampleDir(t, tmpDirA) - createSampleDir(t, tmpDirB) - - srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "." - linkTarget := filepath.Join(tmpDirA, "dir1") - dstDir := filepath.Join(tmpDirB, "dir5") - - var err error - - // first to create an empty dir - if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { - t.Fatalf("unable to make dstDir: %s", err) - } - - if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { - t.Fatal(err) - } - - // Now try again but using a trailing path separator for dstDir. - - if err = os.RemoveAll(dstDir); err != nil { - t.Fatalf("unable to remove dstDir: %s", err) - } - - if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { - t.Fatalf("unable to make dstDir: %s", err) - } - - dstDir = joinTrailingSep(tmpDirB, "dir5") - - if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { - t.Fatalf("unexpected error %T: %s", err, err) - } - - if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { - t.Fatal(err) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/diff_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/diff_test.go deleted file mode 100644 index 4388d69c7..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/diff_test.go +++ /dev/null @@ -1,370 +0,0 @@ -package archive - -import ( - "archive/tar" - "io" - "io/ioutil" - "os" - "path/filepath" - "reflect" - "testing" - - "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils" -) - -func TestApplyLayerInvalidFilenames(t *testing.T) { - for i, headers := range [][]*tar.Header{ - { - { - Name: "../victim/dotdot", - Typeflag: tar.TypeReg, - Mode: 0644, - }, - }, - { - { - // Note the leading slash - Name: "/../victim/slash-dotdot", - Typeflag: tar.TypeReg, - Mode: 0644, - }, - }, - } { - if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidFilenames", headers); err != nil { - t.Fatalf("i=%d. %v", i, err) - } - } -} - -func TestApplyLayerInvalidHardlink(t *testing.T) { - for i, headers := range [][]*tar.Header{ - { // try reading victim/hello (../) - { - Name: "dotdot", - Typeflag: tar.TypeLink, - Linkname: "../victim/hello", - Mode: 0644, - }, - }, - { // try reading victim/hello (/../) - { - Name: "slash-dotdot", - Typeflag: tar.TypeLink, - // Note the leading slash - Linkname: "/../victim/hello", - Mode: 0644, - }, - }, - { // try writing victim/file - { - Name: "loophole-victim", - Typeflag: tar.TypeLink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "loophole-victim/file", - Typeflag: tar.TypeReg, - Mode: 0644, - }, - }, - { // try reading victim/hello (hardlink, symlink) - { - Name: "loophole-victim", - Typeflag: tar.TypeLink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "symlink", - Typeflag: tar.TypeSymlink, - Linkname: "loophole-victim/hello", - Mode: 0644, - }, - }, - { // Try reading victim/hello (hardlink, hardlink) - { - Name: "loophole-victim", - Typeflag: tar.TypeLink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "hardlink", - Typeflag: tar.TypeLink, - Linkname: "loophole-victim/hello", - Mode: 0644, - }, - }, - { // Try removing victim directory (hardlink) - { - Name: "loophole-victim", - Typeflag: tar.TypeLink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "loophole-victim", - Typeflag: tar.TypeReg, - Mode: 0644, - }, - }, - } { - if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidHardlink", headers); err != nil { - t.Fatalf("i=%d. %v", i, err) - } - } -} - -func TestApplyLayerInvalidSymlink(t *testing.T) { - for i, headers := range [][]*tar.Header{ - { // try reading victim/hello (../) - { - Name: "dotdot", - Typeflag: tar.TypeSymlink, - Linkname: "../victim/hello", - Mode: 0644, - }, - }, - { // try reading victim/hello (/../) - { - Name: "slash-dotdot", - Typeflag: tar.TypeSymlink, - // Note the leading slash - Linkname: "/../victim/hello", - Mode: 0644, - }, - }, - { // try writing victim/file - { - Name: "loophole-victim", - Typeflag: tar.TypeSymlink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "loophole-victim/file", - Typeflag: tar.TypeReg, - Mode: 0644, - }, - }, - { // try reading victim/hello (symlink, symlink) - { - Name: "loophole-victim", - Typeflag: tar.TypeSymlink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "symlink", - Typeflag: tar.TypeSymlink, - Linkname: "loophole-victim/hello", - Mode: 0644, - }, - }, - { // try reading victim/hello (symlink, hardlink) - { - Name: "loophole-victim", - Typeflag: tar.TypeSymlink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "hardlink", - Typeflag: tar.TypeLink, - Linkname: "loophole-victim/hello", - Mode: 0644, - }, - }, - { // try removing victim directory (symlink) - { - Name: "loophole-victim", - Typeflag: tar.TypeSymlink, - Linkname: "../victim", - Mode: 0755, - }, - { - Name: "loophole-victim", - Typeflag: tar.TypeReg, - Mode: 0644, - }, - }, - } { - if err := testBreakout("applylayer", "docker-TestApplyLayerInvalidSymlink", headers); err != nil { - t.Fatalf("i=%d. %v", i, err) - } - } -} - -func TestApplyLayerWhiteouts(t *testing.T) { - wd, err := ioutil.TempDir("", "graphdriver-test-whiteouts") - if err != nil { - return - } - defer os.RemoveAll(wd) - - base := []string{ - ".baz", - "bar/", - "bar/bax", - "bar/bay/", - "baz", - "foo/", - "foo/.abc", - "foo/.bcd/", - "foo/.bcd/a", - "foo/cde/", - "foo/cde/def", - "foo/cde/efg", - "foo/fgh", - "foobar", - } - - type tcase struct { - change, expected []string - } - - tcases := []tcase{ - { - base, - base, - }, - { - []string{ - ".bay", - ".wh.baz", - "foo/", - "foo/.bce", - "foo/.wh..wh..opq", - "foo/cde/", - "foo/cde/efg", - }, - []string{ - ".bay", - ".baz", - "bar/", - "bar/bax", - "bar/bay/", - "foo/", - "foo/.bce", - "foo/cde/", - "foo/cde/efg", - "foobar", - }, - }, - { - []string{ - ".bay", - ".wh..baz", - ".wh.foobar", - "foo/", - "foo/.abc", - "foo/.wh.cde", - "bar/", - }, - []string{ - ".bay", - "bar/", - "bar/bax", - "bar/bay/", - "foo/", - "foo/.abc", - "foo/.bce", - }, - }, - { - []string{ - ".abc", - ".wh..wh..opq", - "foobar", - }, - []string{ - ".abc", - "foobar", - }, - }, - } - - for i, tc := range tcases { - l, err := makeTestLayer(tc.change) - if err != nil { - t.Fatal(err) - } - - _, err = UnpackLayer(wd, l, nil) - if err != nil { - t.Fatal(err) - } - err = l.Close() - if err != nil { - t.Fatal(err) - } - - paths, err := readDirContents(wd) - if err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(tc.expected, paths) { - t.Fatalf("invalid files for layer %d: expected %q, got %q", i, tc.expected, paths) - } - } - -} - -func makeTestLayer(paths []string) (rc io.ReadCloser, err error) { - tmpDir, err := ioutil.TempDir("", "graphdriver-test-mklayer") - if err != nil { - return - } - defer func() { - if err != nil { - os.RemoveAll(tmpDir) - } - }() - for _, p := range paths { - if p[len(p)-1] == filepath.Separator { - if err = os.MkdirAll(filepath.Join(tmpDir, p), 0700); err != nil { - return - } - } else { - if err = ioutil.WriteFile(filepath.Join(tmpDir, p), nil, 0600); err != nil { - return - } - } - } - archive, err := Tar(tmpDir, Uncompressed) - if err != nil { - return - } - return ioutils.NewReadCloserWrapper(archive, func() error { - err := archive.Close() - os.RemoveAll(tmpDir) - return err - }), nil -} - -func readDirContents(root string) ([]string, error) { - var files []string - err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if path == root { - return nil - } - rel, err := filepath.Rel(root, path) - if err != nil { - return err - } - if info.IsDir() { - rel = rel + "/" - } - files = append(files, rel) - return nil - }) - if err != nil { - return nil, err - } - return files, nil -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/utils_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/utils_test.go deleted file mode 100644 index 98719032f..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/utils_test.go +++ /dev/null @@ -1,166 +0,0 @@ -package archive - -import ( - "archive/tar" - "bytes" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "time" -) - -var testUntarFns = map[string]func(string, io.Reader) error{ - "untar": func(dest string, r io.Reader) error { - return Untar(r, dest, nil) - }, - "applylayer": func(dest string, r io.Reader) error { - _, err := ApplyLayer(dest, Reader(r)) - return err - }, -} - -// testBreakout is a helper function that, within the provided `tmpdir` directory, -// creates a `victim` folder with a generated `hello` file in it. -// `untar` extracts to a directory named `dest`, the tar file created from `headers`. -// -// Here are the tested scenarios: -// - removed `victim` folder (write) -// - removed files from `victim` folder (write) -// - new files in `victim` folder (write) -// - modified files in `victim` folder (write) -// - file in `dest` with same content as `victim/hello` (read) -// -// When using testBreakout make sure you cover one of the scenarios listed above. -func testBreakout(untarFn string, tmpdir string, headers []*tar.Header) error { - tmpdir, err := ioutil.TempDir("", tmpdir) - if err != nil { - return err - } - defer os.RemoveAll(tmpdir) - - dest := filepath.Join(tmpdir, "dest") - if err := os.Mkdir(dest, 0755); err != nil { - return err - } - - victim := filepath.Join(tmpdir, "victim") - if err := os.Mkdir(victim, 0755); err != nil { - return err - } - hello := filepath.Join(victim, "hello") - helloData, err := time.Now().MarshalText() - if err != nil { - return err - } - if err := ioutil.WriteFile(hello, helloData, 0644); err != nil { - return err - } - helloStat, err := os.Stat(hello) - if err != nil { - return err - } - - reader, writer := io.Pipe() - go func() { - t := tar.NewWriter(writer) - for _, hdr := range headers { - t.WriteHeader(hdr) - } - t.Close() - }() - - untar := testUntarFns[untarFn] - if untar == nil { - return fmt.Errorf("could not find untar function %q in testUntarFns", untarFn) - } - if err := untar(dest, reader); err != nil { - if _, ok := err.(breakoutError); !ok { - // If untar returns an error unrelated to an archive breakout, - // then consider this an unexpected error and abort. - return err - } - // Here, untar detected the breakout. - // Let's move on verifying that indeed there was no breakout. - fmt.Printf("breakoutError: %v\n", err) - } - - // Check victim folder - f, err := os.Open(victim) - if err != nil { - // codepath taken if victim folder was removed - return fmt.Errorf("archive breakout: error reading %q: %v", victim, err) - } - defer f.Close() - - // Check contents of victim folder - // - // We are only interested in getting 2 files from the victim folder, because if all is well - // we expect only one result, the `hello` file. If there is a second result, it cannot - // hold the same name `hello` and we assume that a new file got created in the victim folder. - // That is enough to detect an archive breakout. - names, err := f.Readdirnames(2) - if err != nil { - // codepath taken if victim is not a folder - return fmt.Errorf("archive breakout: error reading directory content of %q: %v", victim, err) - } - for _, name := range names { - if name != "hello" { - // codepath taken if new file was created in victim folder - return fmt.Errorf("archive breakout: new file %q", name) - } - } - - // Check victim/hello - f, err = os.Open(hello) - if err != nil { - // codepath taken if read permissions were removed - return fmt.Errorf("archive breakout: could not lstat %q: %v", hello, err) - } - defer f.Close() - b, err := ioutil.ReadAll(f) - if err != nil { - return err - } - fi, err := f.Stat() - if err != nil { - return err - } - if helloStat.IsDir() != fi.IsDir() || - // TODO: cannot check for fi.ModTime() change - helloStat.Mode() != fi.Mode() || - helloStat.Size() != fi.Size() || - !bytes.Equal(helloData, b) { - // codepath taken if hello has been modified - return fmt.Errorf("archive breakout: file %q has been modified. Contents: expected=%q, got=%q. FileInfo: expected=%#v, got=%#v", hello, helloData, b, helloStat, fi) - } - - // Check that nothing in dest/ has the same content as victim/hello. - // Since victim/hello was generated with time.Now(), it is safe to assume - // that any file whose content matches exactly victim/hello, managed somehow - // to access victim/hello. - return filepath.Walk(dest, func(path string, info os.FileInfo, err error) error { - if info.IsDir() { - if err != nil { - // skip directory if error - return filepath.SkipDir - } - // enter directory - return nil - } - if err != nil { - // skip file if error - return nil - } - b, err := ioutil.ReadFile(path) - if err != nil { - // Houston, we have a problem. Aborting (space)walk. - return err - } - if bytes.Equal(helloData, b) { - return fmt.Errorf("archive breakout: file %q has been accessed via %q", hello, path) - } - return nil - }) -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/wrap_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/wrap_test.go deleted file mode 100644 index 46ab36697..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/wrap_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package archive - -import ( - "archive/tar" - "bytes" - "io" - "testing" -) - -func TestGenerateEmptyFile(t *testing.T) { - archive, err := Generate("emptyFile") - if err != nil { - t.Fatal(err) - } - if archive == nil { - t.Fatal("The generated archive should not be nil.") - } - - expectedFiles := [][]string{ - {"emptyFile", ""}, - } - - tr := tar.NewReader(archive) - actualFiles := make([][]string, 0, 10) - i := 0 - for { - hdr, err := tr.Next() - if err == io.EOF { - break - } - if err != nil { - t.Fatal(err) - } - buf := new(bytes.Buffer) - buf.ReadFrom(tr) - content := buf.String() - actualFiles = append(actualFiles, []string{hdr.Name, content}) - i++ - } - if len(actualFiles) != len(expectedFiles) { - t.Fatalf("Number of expected file %d, got %d.", len(expectedFiles), len(actualFiles)) - } - for i := 0; i < len(expectedFiles); i++ { - actual := actualFiles[i] - expected := expectedFiles[i] - if actual[0] != expected[0] { - t.Fatalf("Expected name '%s', Actual name '%s'", expected[0], actual[0]) - } - if actual[1] != expected[1] { - t.Fatalf("Expected content '%s', Actual content '%s'", expected[1], actual[1]) - } - } -} - -func TestGenerateWithContent(t *testing.T) { - archive, err := Generate("file", "content") - if err != nil { - t.Fatal(err) - } - if archive == nil { - t.Fatal("The generated archive should not be nil.") - } - - expectedFiles := [][]string{ - {"file", "content"}, - } - - tr := tar.NewReader(archive) - actualFiles := make([][]string, 0, 10) - i := 0 - for { - hdr, err := tr.Next() - if err == io.EOF { - break - } - if err != nil { - t.Fatal(err) - } - buf := new(bytes.Buffer) - buf.ReadFrom(tr) - content := buf.String() - actualFiles = append(actualFiles, []string{hdr.Name, content}) - i++ - } - if len(actualFiles) != len(expectedFiles) { - t.Fatalf("Number of expected file %d, got %d.", len(expectedFiles), len(actualFiles)) - } - for i := 0; i < len(expectedFiles); i++ { - actual := actualFiles[i] - expected := expectedFiles[i] - if actual[0] != expected[0] { - t.Fatalf("Expected name '%s', Actual name '%s'", expected[0], actual[0]) - } - if actual[1] != expected[1] { - t.Fatalf("Expected content '%s', Actual content '%s'", expected[1], actual[1]) - } - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/fileutils/fileutils_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/fileutils/fileutils_test.go deleted file mode 100644 index 2d584c667..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/fileutils/fileutils_test.go +++ /dev/null @@ -1,573 +0,0 @@ -package fileutils - -import ( - "io/ioutil" - "os" - "path" - "path/filepath" - "runtime" - "strings" - "testing" -) - -// CopyFile with invalid src -func TestCopyFileWithInvalidSrc(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") - defer os.RemoveAll(tempFolder) - if err != nil { - t.Fatal(err) - } - bytes, err := CopyFile("/invalid/file/path", path.Join(tempFolder, "dest")) - if err == nil { - t.Fatal("Should have fail to copy an invalid src file") - } - if bytes != 0 { - t.Fatal("Should have written 0 bytes") - } - -} - -// CopyFile with invalid dest -func TestCopyFileWithInvalidDest(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") - defer os.RemoveAll(tempFolder) - if err != nil { - t.Fatal(err) - } - src := path.Join(tempFolder, "file") - err = ioutil.WriteFile(src, []byte("content"), 0740) - if err != nil { - t.Fatal(err) - } - bytes, err := CopyFile(src, path.Join(tempFolder, "/invalid/dest/path")) - if err == nil { - t.Fatal("Should have fail to copy an invalid src file") - } - if bytes != 0 { - t.Fatal("Should have written 0 bytes") - } - -} - -// CopyFile with same src and dest -func TestCopyFileWithSameSrcAndDest(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") - defer os.RemoveAll(tempFolder) - if err != nil { - t.Fatal(err) - } - file := path.Join(tempFolder, "file") - err = ioutil.WriteFile(file, []byte("content"), 0740) - if err != nil { - t.Fatal(err) - } - bytes, err := CopyFile(file, file) - if err != nil { - t.Fatal(err) - } - if bytes != 0 { - t.Fatal("Should have written 0 bytes as it is the same file.") - } -} - -// CopyFile with same src and dest but path is different and not clean -func TestCopyFileWithSameSrcAndDestWithPathNameDifferent(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") - defer os.RemoveAll(tempFolder) - if err != nil { - t.Fatal(err) - } - testFolder := path.Join(tempFolder, "test") - err = os.MkdirAll(testFolder, 0740) - if err != nil { - t.Fatal(err) - } - file := path.Join(testFolder, "file") - sameFile := testFolder + "/../test/file" - err = ioutil.WriteFile(file, []byte("content"), 0740) - if err != nil { - t.Fatal(err) - } - bytes, err := CopyFile(file, sameFile) - if err != nil { - t.Fatal(err) - } - if bytes != 0 { - t.Fatal("Should have written 0 bytes as it is the same file.") - } -} - -func TestCopyFile(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") - defer os.RemoveAll(tempFolder) - if err != nil { - t.Fatal(err) - } - src := path.Join(tempFolder, "src") - dest := path.Join(tempFolder, "dest") - ioutil.WriteFile(src, []byte("content"), 0777) - ioutil.WriteFile(dest, []byte("destContent"), 0777) - bytes, err := CopyFile(src, dest) - if err != nil { - t.Fatal(err) - } - if bytes != 7 { - t.Fatalf("Should have written %d bytes but wrote %d", 7, bytes) - } - actual, err := ioutil.ReadFile(dest) - if err != nil { - t.Fatal(err) - } - if string(actual) != "content" { - t.Fatalf("Dest content was '%s', expected '%s'", string(actual), "content") - } -} - -// Reading a symlink to a directory must return the directory -func TestReadSymlinkedDirectoryExistingDirectory(t *testing.T) { - var err error - if err = os.Mkdir("/tmp/testReadSymlinkToExistingDirectory", 0777); err != nil { - t.Errorf("failed to create directory: %s", err) - } - - if err = os.Symlink("/tmp/testReadSymlinkToExistingDirectory", "/tmp/dirLinkTest"); err != nil { - t.Errorf("failed to create symlink: %s", err) - } - - var path string - if path, err = ReadSymlinkedDirectory("/tmp/dirLinkTest"); err != nil { - t.Fatalf("failed to read symlink to directory: %s", err) - } - - if path != "/tmp/testReadSymlinkToExistingDirectory" { - t.Fatalf("symlink returned unexpected directory: %s", path) - } - - if err = os.Remove("/tmp/testReadSymlinkToExistingDirectory"); err != nil { - t.Errorf("failed to remove temporary directory: %s", err) - } - - if err = os.Remove("/tmp/dirLinkTest"); err != nil { - t.Errorf("failed to remove symlink: %s", err) - } -} - -// Reading a non-existing symlink must fail -func TestReadSymlinkedDirectoryNonExistingSymlink(t *testing.T) { - var path string - var err error - if path, err = ReadSymlinkedDirectory("/tmp/test/foo/Non/ExistingPath"); err == nil { - t.Fatalf("error expected for non-existing symlink") - } - - if path != "" { - t.Fatalf("expected empty path, but '%s' was returned", path) - } -} - -// Reading a symlink to a file must fail -func TestReadSymlinkedDirectoryToFile(t *testing.T) { - var err error - var file *os.File - - if file, err = os.Create("/tmp/testReadSymlinkToFile"); err != nil { - t.Fatalf("failed to create file: %s", err) - } - - file.Close() - - if err = os.Symlink("/tmp/testReadSymlinkToFile", "/tmp/fileLinkTest"); err != nil { - t.Errorf("failed to create symlink: %s", err) - } - - var path string - if path, err = ReadSymlinkedDirectory("/tmp/fileLinkTest"); err == nil { - t.Fatalf("ReadSymlinkedDirectory on a symlink to a file should've failed") - } - - if path != "" { - t.Fatalf("path should've been empty: %s", path) - } - - if err = os.Remove("/tmp/testReadSymlinkToFile"); err != nil { - t.Errorf("failed to remove file: %s", err) - } - - if err = os.Remove("/tmp/fileLinkTest"); err != nil { - t.Errorf("failed to remove symlink: %s", err) - } -} - -func TestWildcardMatches(t *testing.T) { - match, _ := Matches("fileutils.go", []string{"*"}) - if match != true { - t.Errorf("failed to get a wildcard match, got %v", match) - } -} - -// A simple pattern match should return true. -func TestPatternMatches(t *testing.T) { - match, _ := Matches("fileutils.go", []string{"*.go"}) - if match != true { - t.Errorf("failed to get a match, got %v", match) - } -} - -// An exclusion followed by an inclusion should return true. -func TestExclusionPatternMatchesPatternBefore(t *testing.T) { - match, _ := Matches("fileutils.go", []string{"!fileutils.go", "*.go"}) - if match != true { - t.Errorf("failed to get true match on exclusion pattern, got %v", match) - } -} - -// A folder pattern followed by an exception should return false. -func TestPatternMatchesFolderExclusions(t *testing.T) { - match, _ := Matches("docs/README.md", []string{"docs", "!docs/README.md"}) - if match != false { - t.Errorf("failed to get a false match on exclusion pattern, got %v", match) - } -} - -// A folder pattern followed by an exception should return false. -func TestPatternMatchesFolderWithSlashExclusions(t *testing.T) { - match, _ := Matches("docs/README.md", []string{"docs/", "!docs/README.md"}) - if match != false { - t.Errorf("failed to get a false match on exclusion pattern, got %v", match) - } -} - -// A folder pattern followed by an exception should return false. -func TestPatternMatchesFolderWildcardExclusions(t *testing.T) { - match, _ := Matches("docs/README.md", []string{"docs/*", "!docs/README.md"}) - if match != false { - t.Errorf("failed to get a false match on exclusion pattern, got %v", match) - } -} - -// A pattern followed by an exclusion should return false. -func TestExclusionPatternMatchesPatternAfter(t *testing.T) { - match, _ := Matches("fileutils.go", []string{"*.go", "!fileutils.go"}) - if match != false { - t.Errorf("failed to get false match on exclusion pattern, got %v", match) - } -} - -// A filename evaluating to . should return false. -func TestExclusionPatternMatchesWholeDirectory(t *testing.T) { - match, _ := Matches(".", []string{"*.go"}) - if match != false { - t.Errorf("failed to get false match on ., got %v", match) - } -} - -// A single ! pattern should return an error. -func TestSingleExclamationError(t *testing.T) { - _, err := Matches("fileutils.go", []string{"!"}) - if err == nil { - t.Errorf("failed to get an error for a single exclamation point, got %v", err) - } -} - -// A string preceded with a ! should return true from Exclusion. -func TestExclusion(t *testing.T) { - exclusion := exclusion("!") - if !exclusion { - t.Errorf("failed to get true for a single !, got %v", exclusion) - } -} - -// Matches with no patterns -func TestMatchesWithNoPatterns(t *testing.T) { - matches, err := Matches("/any/path/there", []string{}) - if err != nil { - t.Fatal(err) - } - if matches { - t.Fatalf("Should not have match anything") - } -} - -// Matches with malformed patterns -func TestMatchesWithMalformedPatterns(t *testing.T) { - matches, err := Matches("/any/path/there", []string{"["}) - if err == nil { - t.Fatal("Should have failed because of a malformed syntax in the pattern") - } - if matches { - t.Fatalf("Should not have match anything") - } -} - -// Test lots of variants of patterns & strings -func TestMatches(t *testing.T) { - tests := []struct { - pattern string - text string - pass bool - }{ - {"**", "file", true}, - {"**", "file/", true}, - {"**/", "file", true}, // weird one - {"**/", "file/", true}, - {"**", "/", true}, - {"**/", "/", true}, - {"**", "dir/file", true}, - {"**/", "dir/file", false}, - {"**", "dir/file/", true}, - {"**/", "dir/file/", true}, - {"**/**", "dir/file", true}, - {"**/**", "dir/file/", true}, - {"dir/**", "dir/file", true}, - {"dir/**", "dir/file/", true}, - {"dir/**", "dir/dir2/file", true}, - {"dir/**", "dir/dir2/file/", true}, - {"**/dir2/*", "dir/dir2/file", true}, - {"**/dir2/*", "dir/dir2/file/", false}, - {"**/dir2/**", "dir/dir2/dir3/file", true}, - {"**/dir2/**", "dir/dir2/dir3/file/", true}, - {"**file", "file", true}, - {"**file", "dir/file", true}, - {"**/file", "dir/file", true}, - {"**file", "dir/dir/file", true}, - {"**/file", "dir/dir/file", true}, - {"**/file*", "dir/dir/file", true}, - {"**/file*", "dir/dir/file.txt", true}, - {"**/file*txt", "dir/dir/file.txt", true}, - {"**/file*.txt", "dir/dir/file.txt", true}, - {"**/file*.txt*", "dir/dir/file.txt", true}, - {"**/**/*.txt", "dir/dir/file.txt", true}, - {"**/**/*.txt2", "dir/dir/file.txt", false}, - {"**/*.txt", "file.txt", true}, - {"**/**/*.txt", "file.txt", true}, - {"a**/*.txt", "a/file.txt", true}, - {"a**/*.txt", "a/dir/file.txt", true}, - {"a**/*.txt", "a/dir/dir/file.txt", true}, - {"a/*.txt", "a/dir/file.txt", false}, - {"a/*.txt", "a/file.txt", true}, - {"a/*.txt**", "a/file.txt", true}, - {"a[b-d]e", "ae", false}, - {"a[b-d]e", "ace", true}, - {"a[b-d]e", "aae", false}, - {"a[^b-d]e", "aze", true}, - {".*", ".foo", true}, - {".*", "foo", false}, - {"abc.def", "abcdef", false}, - {"abc.def", "abc.def", true}, - {"abc.def", "abcZdef", false}, - {"abc?def", "abcZdef", true}, - {"abc?def", "abcdef", false}, - {"a\\*b", "a*b", true}, - {"a\\", "a", false}, - {"a\\", "a\\", false}, - {"a\\\\", "a\\", true}, - {"**/foo/bar", "foo/bar", true}, - {"**/foo/bar", "dir/foo/bar", true}, - {"**/foo/bar", "dir/dir2/foo/bar", true}, - {"abc/**", "abc", false}, - {"abc/**", "abc/def", true}, - {"abc/**", "abc/def/ghi", true}, - } - - for _, test := range tests { - res, _ := regexpMatch(test.pattern, test.text) - if res != test.pass { - t.Fatalf("Failed: %v - res:%v", test, res) - } - } -} - -// An empty string should return true from Empty. -func TestEmpty(t *testing.T) { - empty := empty("") - if !empty { - t.Errorf("failed to get true for an empty string, got %v", empty) - } -} - -func TestCleanPatterns(t *testing.T) { - cleaned, _, _, _ := CleanPatterns([]string{"docs", "config"}) - if len(cleaned) != 2 { - t.Errorf("expected 2 element slice, got %v", len(cleaned)) - } -} - -func TestCleanPatternsStripEmptyPatterns(t *testing.T) { - cleaned, _, _, _ := CleanPatterns([]string{"docs", "config", ""}) - if len(cleaned) != 2 { - t.Errorf("expected 2 element slice, got %v", len(cleaned)) - } -} - -func TestCleanPatternsExceptionFlag(t *testing.T) { - _, _, exceptions, _ := CleanPatterns([]string{"docs", "!docs/README.md"}) - if !exceptions { - t.Errorf("expected exceptions to be true, got %v", exceptions) - } -} - -func TestCleanPatternsLeadingSpaceTrimmed(t *testing.T) { - _, _, exceptions, _ := CleanPatterns([]string{"docs", " !docs/README.md"}) - if !exceptions { - t.Errorf("expected exceptions to be true, got %v", exceptions) - } -} - -func TestCleanPatternsTrailingSpaceTrimmed(t *testing.T) { - _, _, exceptions, _ := CleanPatterns([]string{"docs", "!docs/README.md "}) - if !exceptions { - t.Errorf("expected exceptions to be true, got %v", exceptions) - } -} - -func TestCleanPatternsErrorSingleException(t *testing.T) { - _, _, _, err := CleanPatterns([]string{"!"}) - if err == nil { - t.Errorf("expected error on single exclamation point, got %v", err) - } -} - -func TestCleanPatternsFolderSplit(t *testing.T) { - _, dirs, _, _ := CleanPatterns([]string{"docs/config/CONFIG.md"}) - if dirs[0][0] != "docs" { - t.Errorf("expected first element in dirs slice to be docs, got %v", dirs[0][1]) - } - if dirs[0][1] != "config" { - t.Errorf("expected first element in dirs slice to be config, got %v", dirs[0][1]) - } -} - -func TestCreateIfNotExistsDir(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tempFolder) - - folderToCreate := filepath.Join(tempFolder, "tocreate") - - if err := CreateIfNotExists(folderToCreate, true); err != nil { - t.Fatal(err) - } - fileinfo, err := os.Stat(folderToCreate) - if err != nil { - t.Fatalf("Should have create a folder, got %v", err) - } - - if !fileinfo.IsDir() { - t.Fatalf("Should have been a dir, seems it's not") - } -} - -func TestCreateIfNotExistsFile(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tempFolder) - - fileToCreate := filepath.Join(tempFolder, "file/to/create") - - if err := CreateIfNotExists(fileToCreate, false); err != nil { - t.Fatal(err) - } - fileinfo, err := os.Stat(fileToCreate) - if err != nil { - t.Fatalf("Should have create a file, got %v", err) - } - - if fileinfo.IsDir() { - t.Fatalf("Should have been a file, seems it's not") - } -} - -// These matchTests are stolen from go's filepath Match tests. -type matchTest struct { - pattern, s string - match bool - err error -} - -var matchTests = []matchTest{ - {"abc", "abc", true, nil}, - {"*", "abc", true, nil}, - {"*c", "abc", true, nil}, - {"a*", "a", true, nil}, - {"a*", "abc", true, nil}, - {"a*", "ab/c", false, nil}, - {"a*/b", "abc/b", true, nil}, - {"a*/b", "a/c/b", false, nil}, - {"a*b*c*d*e*/f", "axbxcxdxe/f", true, nil}, - {"a*b*c*d*e*/f", "axbxcxdxexxx/f", true, nil}, - {"a*b*c*d*e*/f", "axbxcxdxe/xxx/f", false, nil}, - {"a*b*c*d*e*/f", "axbxcxdxexxx/fff", false, nil}, - {"a*b?c*x", "abxbbxdbxebxczzx", true, nil}, - {"a*b?c*x", "abxbbxdbxebxczzy", false, nil}, - {"ab[c]", "abc", true, nil}, - {"ab[b-d]", "abc", true, nil}, - {"ab[e-g]", "abc", false, nil}, - {"ab[^c]", "abc", false, nil}, - {"ab[^b-d]", "abc", false, nil}, - {"ab[^e-g]", "abc", true, nil}, - {"a\\*b", "a*b", true, nil}, - {"a\\*b", "ab", false, nil}, - {"a?b", "a☺b", true, nil}, - {"a[^a]b", "a☺b", true, nil}, - {"a???b", "a☺b", false, nil}, - {"a[^a][^a][^a]b", "a☺b", false, nil}, - {"[a-ζ]*", "α", true, nil}, - {"*[a-ζ]", "A", false, nil}, - {"a?b", "a/b", false, nil}, - {"a*b", "a/b", false, nil}, - {"[\\]a]", "]", true, nil}, - {"[\\-]", "-", true, nil}, - {"[x\\-]", "x", true, nil}, - {"[x\\-]", "-", true, nil}, - {"[x\\-]", "z", false, nil}, - {"[\\-x]", "x", true, nil}, - {"[\\-x]", "-", true, nil}, - {"[\\-x]", "a", false, nil}, - {"[]a]", "]", false, filepath.ErrBadPattern}, - {"[-]", "-", false, filepath.ErrBadPattern}, - {"[x-]", "x", false, filepath.ErrBadPattern}, - {"[x-]", "-", false, filepath.ErrBadPattern}, - {"[x-]", "z", false, filepath.ErrBadPattern}, - {"[-x]", "x", false, filepath.ErrBadPattern}, - {"[-x]", "-", false, filepath.ErrBadPattern}, - {"[-x]", "a", false, filepath.ErrBadPattern}, - {"\\", "a", false, filepath.ErrBadPattern}, - {"[a-b-c]", "a", false, filepath.ErrBadPattern}, - {"[", "a", false, filepath.ErrBadPattern}, - {"[^", "a", false, filepath.ErrBadPattern}, - {"[^bc", "a", false, filepath.ErrBadPattern}, - {"a[", "a", false, filepath.ErrBadPattern}, // was nil but IMO its wrong - {"a[", "ab", false, filepath.ErrBadPattern}, - {"*x", "xxx", true, nil}, -} - -func errp(e error) string { - if e == nil { - return "" - } - return e.Error() -} - -// TestMatch test's our version of filepath.Match, called regexpMatch. -func TestMatch(t *testing.T) { - for _, tt := range matchTests { - pattern := tt.pattern - s := tt.s - if runtime.GOOS == "windows" { - if strings.Index(pattern, "\\") >= 0 { - // no escape allowed on windows. - continue - } - pattern = filepath.Clean(pattern) - s = filepath.Clean(s) - } - ok, err := regexpMatch(pattern, s) - if ok != tt.match || err != tt.err { - t.Fatalf("Match(%#q, %#q) = %v, %q want %v, %q", pattern, s, ok, errp(err), tt.match, errp(tt.err)) - } - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/homedir/homedir_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/homedir/homedir_test.go deleted file mode 100644 index 7a95cb2bd..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/homedir/homedir_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package homedir - -import ( - "path/filepath" - "testing" -) - -func TestGet(t *testing.T) { - home := Get() - if home == "" { - t.Fatal("returned home directory is empty") - } - - if !filepath.IsAbs(home) { - t.Fatalf("returned path is not absolute: %s", home) - } -} - -func TestGetShortcutString(t *testing.T) { - shortcut := GetShortcutString() - if shortcut == "" { - t.Fatal("returned shortcut string is empty") - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/idtools/idtools_unix_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/idtools/idtools_unix_test.go deleted file mode 100644 index 55b338c96..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/idtools/idtools_unix_test.go +++ /dev/null @@ -1,243 +0,0 @@ -// +build !windows - -package idtools - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "syscall" - "testing" -) - -type node struct { - uid int - gid int -} - -func TestMkdirAllAs(t *testing.T) { - dirName, err := ioutil.TempDir("", "mkdirall") - if err != nil { - t.Fatalf("Couldn't create temp dir: %v", err) - } - defer os.RemoveAll(dirName) - - testTree := map[string]node{ - "usr": {0, 0}, - "usr/bin": {0, 0}, - "lib": {33, 33}, - "lib/x86_64": {45, 45}, - "lib/x86_64/share": {1, 1}, - } - - if err := buildTree(dirName, testTree); err != nil { - t.Fatal(err) - } - - // test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid - if err := MkdirAllAs(filepath.Join(dirName, "usr", "share"), 0755, 99, 99); err != nil { - t.Fatal(err) - } - testTree["usr/share"] = node{99, 99} - verifyTree, err := readTree(dirName, "") - if err != nil { - t.Fatal(err) - } - if err := compareTrees(testTree, verifyTree); err != nil { - t.Fatal(err) - } - - // test 2-deep new directories--both should be owned by the uid/gid pair - if err := MkdirAllAs(filepath.Join(dirName, "lib", "some", "other"), 0755, 101, 101); err != nil { - t.Fatal(err) - } - testTree["lib/some"] = node{101, 101} - testTree["lib/some/other"] = node{101, 101} - verifyTree, err = readTree(dirName, "") - if err != nil { - t.Fatal(err) - } - if err := compareTrees(testTree, verifyTree); err != nil { - t.Fatal(err) - } - - // test a directory that already exists; should be chowned, but nothing else - if err := MkdirAllAs(filepath.Join(dirName, "usr"), 0755, 102, 102); err != nil { - t.Fatal(err) - } - testTree["usr"] = node{102, 102} - verifyTree, err = readTree(dirName, "") - if err != nil { - t.Fatal(err) - } - if err := compareTrees(testTree, verifyTree); err != nil { - t.Fatal(err) - } -} - -func TestMkdirAllNewAs(t *testing.T) { - - dirName, err := ioutil.TempDir("", "mkdirnew") - if err != nil { - t.Fatalf("Couldn't create temp dir: %v", err) - } - defer os.RemoveAll(dirName) - - testTree := map[string]node{ - "usr": {0, 0}, - "usr/bin": {0, 0}, - "lib": {33, 33}, - "lib/x86_64": {45, 45}, - "lib/x86_64/share": {1, 1}, - } - - if err := buildTree(dirName, testTree); err != nil { - t.Fatal(err) - } - - // test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid - if err := MkdirAllNewAs(filepath.Join(dirName, "usr", "share"), 0755, 99, 99); err != nil { - t.Fatal(err) - } - testTree["usr/share"] = node{99, 99} - verifyTree, err := readTree(dirName, "") - if err != nil { - t.Fatal(err) - } - if err := compareTrees(testTree, verifyTree); err != nil { - t.Fatal(err) - } - - // test 2-deep new directories--both should be owned by the uid/gid pair - if err := MkdirAllNewAs(filepath.Join(dirName, "lib", "some", "other"), 0755, 101, 101); err != nil { - t.Fatal(err) - } - testTree["lib/some"] = node{101, 101} - testTree["lib/some/other"] = node{101, 101} - verifyTree, err = readTree(dirName, "") - if err != nil { - t.Fatal(err) - } - if err := compareTrees(testTree, verifyTree); err != nil { - t.Fatal(err) - } - - // test a directory that already exists; should NOT be chowned - if err := MkdirAllNewAs(filepath.Join(dirName, "usr"), 0755, 102, 102); err != nil { - t.Fatal(err) - } - verifyTree, err = readTree(dirName, "") - if err != nil { - t.Fatal(err) - } - if err := compareTrees(testTree, verifyTree); err != nil { - t.Fatal(err) - } -} - -func TestMkdirAs(t *testing.T) { - - dirName, err := ioutil.TempDir("", "mkdir") - if err != nil { - t.Fatalf("Couldn't create temp dir: %v", err) - } - defer os.RemoveAll(dirName) - - testTree := map[string]node{ - "usr": {0, 0}, - } - if err := buildTree(dirName, testTree); err != nil { - t.Fatal(err) - } - - // test a directory that already exists; should just chown to the requested uid/gid - if err := MkdirAs(filepath.Join(dirName, "usr"), 0755, 99, 99); err != nil { - t.Fatal(err) - } - testTree["usr"] = node{99, 99} - verifyTree, err := readTree(dirName, "") - if err != nil { - t.Fatal(err) - } - if err := compareTrees(testTree, verifyTree); err != nil { - t.Fatal(err) - } - - // create a subdir under a dir which doesn't exist--should fail - if err := MkdirAs(filepath.Join(dirName, "usr", "bin", "subdir"), 0755, 102, 102); err == nil { - t.Fatalf("Trying to create a directory with Mkdir where the parent doesn't exist should have failed") - } - - // create a subdir under an existing dir; should only change the ownership of the new subdir - if err := MkdirAs(filepath.Join(dirName, "usr", "bin"), 0755, 102, 102); err != nil { - t.Fatal(err) - } - testTree["usr/bin"] = node{102, 102} - verifyTree, err = readTree(dirName, "") - if err != nil { - t.Fatal(err) - } - if err := compareTrees(testTree, verifyTree); err != nil { - t.Fatal(err) - } -} - -func buildTree(base string, tree map[string]node) error { - for path, node := range tree { - fullPath := filepath.Join(base, path) - if err := os.MkdirAll(fullPath, 0755); err != nil { - return fmt.Errorf("Couldn't create path: %s; error: %v", fullPath, err) - } - if err := os.Chown(fullPath, node.uid, node.gid); err != nil { - return fmt.Errorf("Couldn't chown path: %s; error: %v", fullPath, err) - } - } - return nil -} - -func readTree(base, root string) (map[string]node, error) { - tree := make(map[string]node) - - dirInfos, err := ioutil.ReadDir(base) - if err != nil { - return nil, fmt.Errorf("Couldn't read directory entries for %q: %v", base, err) - } - - for _, info := range dirInfos { - s := &syscall.Stat_t{} - if err := syscall.Stat(filepath.Join(base, info.Name()), s); err != nil { - return nil, fmt.Errorf("Can't stat file %q: %v", filepath.Join(base, info.Name()), err) - } - tree[filepath.Join(root, info.Name())] = node{int(s.Uid), int(s.Gid)} - if info.IsDir() { - // read the subdirectory - subtree, err := readTree(filepath.Join(base, info.Name()), filepath.Join(root, info.Name())) - if err != nil { - return nil, err - } - for path, nodeinfo := range subtree { - tree[path] = nodeinfo - } - } - } - return tree, nil -} - -func compareTrees(left, right map[string]node) error { - if len(left) != len(right) { - return fmt.Errorf("Trees aren't the same size") - } - for path, nodeLeft := range left { - if nodeRight, ok := right[path]; ok { - if nodeRight.uid != nodeLeft.uid || nodeRight.gid != nodeLeft.gid { - // mismatch - return fmt.Errorf("mismatched ownership for %q: expected: %d:%d, got: %d:%d", path, - nodeLeft.uid, nodeLeft.gid, nodeRight.uid, nodeRight.gid) - } - continue - } - return fmt.Errorf("right tree didn't contain path %q", path) - } - return nil -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/bytespipe_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/bytespipe_test.go deleted file mode 100644 index b051139ad..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/bytespipe_test.go +++ /dev/null @@ -1,158 +0,0 @@ -package ioutils - -import ( - "crypto/sha1" - "encoding/hex" - "math/rand" - "testing" - "time" -) - -func TestBytesPipeRead(t *testing.T) { - buf := NewBytesPipe(nil) - buf.Write([]byte("12")) - buf.Write([]byte("34")) - buf.Write([]byte("56")) - buf.Write([]byte("78")) - buf.Write([]byte("90")) - rd := make([]byte, 4) - n, err := buf.Read(rd) - if err != nil { - t.Fatal(err) - } - if n != 4 { - t.Fatalf("Wrong number of bytes read: %d, should be %d", n, 4) - } - if string(rd) != "1234" { - t.Fatalf("Read %s, but must be %s", rd, "1234") - } - n, err = buf.Read(rd) - if err != nil { - t.Fatal(err) - } - if n != 4 { - t.Fatalf("Wrong number of bytes read: %d, should be %d", n, 4) - } - if string(rd) != "5678" { - t.Fatalf("Read %s, but must be %s", rd, "5679") - } - n, err = buf.Read(rd) - if err != nil { - t.Fatal(err) - } - if n != 2 { - t.Fatalf("Wrong number of bytes read: %d, should be %d", n, 2) - } - if string(rd[:n]) != "90" { - t.Fatalf("Read %s, but must be %s", rd, "90") - } -} - -func TestBytesPipeWrite(t *testing.T) { - buf := NewBytesPipe(nil) - buf.Write([]byte("12")) - buf.Write([]byte("34")) - buf.Write([]byte("56")) - buf.Write([]byte("78")) - buf.Write([]byte("90")) - if string(buf.buf[0]) != "1234567890" { - t.Fatalf("Buffer %s, must be %s", buf.buf, "1234567890") - } -} - -// Write and read in different speeds/chunk sizes and check valid data is read. -func TestBytesPipeWriteRandomChunks(t *testing.T) { - cases := []struct{ iterations, writesPerLoop, readsPerLoop int }{ - {100, 10, 1}, - {1000, 10, 5}, - {1000, 100, 0}, - {1000, 5, 6}, - {10000, 50, 25}, - } - - testMessage := []byte("this is a random string for testing") - // random slice sizes to read and write - writeChunks := []int{25, 35, 15, 20} - readChunks := []int{5, 45, 20, 25} - - for _, c := range cases { - // first pass: write directly to hash - hash := sha1.New() - for i := 0; i < c.iterations*c.writesPerLoop; i++ { - if _, err := hash.Write(testMessage[:writeChunks[i%len(writeChunks)]]); err != nil { - t.Fatal(err) - } - } - expected := hex.EncodeToString(hash.Sum(nil)) - - // write/read through buffer - buf := NewBytesPipe(nil) - hash.Reset() - - done := make(chan struct{}) - - go func() { - // random delay before read starts - <-time.After(time.Duration(rand.Intn(10)) * time.Millisecond) - for i := 0; ; i++ { - p := make([]byte, readChunks[(c.iterations*c.readsPerLoop+i)%len(readChunks)]) - n, _ := buf.Read(p) - if n == 0 { - break - } - hash.Write(p[:n]) - } - - close(done) - }() - - for i := 0; i < c.iterations; i++ { - for w := 0; w < c.writesPerLoop; w++ { - buf.Write(testMessage[:writeChunks[(i*c.writesPerLoop+w)%len(writeChunks)]]) - } - } - buf.Close() - <-done - - actual := hex.EncodeToString(hash.Sum(nil)) - - if expected != actual { - t.Fatalf("BytesPipe returned invalid data. Expected checksum %v, got %v", expected, actual) - } - - } -} - -func BenchmarkBytesPipeWrite(b *testing.B) { - for i := 0; i < b.N; i++ { - readBuf := make([]byte, 1024) - buf := NewBytesPipe(nil) - go func() { - var err error - for err == nil { - _, err = buf.Read(readBuf) - } - }() - for j := 0; j < 1000; j++ { - buf.Write([]byte("pretty short line, because why not?")) - } - buf.Close() - } -} - -func BenchmarkBytesPipeRead(b *testing.B) { - rd := make([]byte, 512) - for i := 0; i < b.N; i++ { - b.StopTimer() - buf := NewBytesPipe(nil) - for j := 0; j < 500; j++ { - buf.Write(make([]byte, 1024)) - } - b.StartTimer() - for j := 0; j < 1000; j++ { - if n, _ := buf.Read(rd); n != 512 { - b.Fatalf("Wrong number of bytes: %d", n) - } - } - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/fmt_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/fmt_test.go deleted file mode 100644 index 896886329..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/fmt_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package ioutils - -import "testing" - -func TestFprintfIfNotEmpty(t *testing.T) { - wc := NewWriteCounter(&NopWriter{}) - n, _ := FprintfIfNotEmpty(wc, "foo%s", "") - - if wc.Count != 0 || n != 0 { - t.Errorf("Wrong count: %v vs. %v vs. 0", wc.Count, n) - } - - n, _ = FprintfIfNotEmpty(wc, "foo%s", "bar") - if wc.Count != 6 || n != 6 { - t.Errorf("Wrong count: %v vs. %v vs. 6", wc.Count, n) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/multireader_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/multireader_test.go deleted file mode 100644 index de495b56d..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/multireader_test.go +++ /dev/null @@ -1,149 +0,0 @@ -package ioutils - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "os" - "strings" - "testing" -) - -func TestMultiReadSeekerReadAll(t *testing.T) { - str := "hello world" - s1 := strings.NewReader(str + " 1") - s2 := strings.NewReader(str + " 2") - s3 := strings.NewReader(str + " 3") - mr := MultiReadSeeker(s1, s2, s3) - - expectedSize := int64(s1.Len() + s2.Len() + s3.Len()) - - b, err := ioutil.ReadAll(mr) - if err != nil { - t.Fatal(err) - } - - expected := "hello world 1hello world 2hello world 3" - if string(b) != expected { - t.Fatalf("ReadAll failed, got: %q, expected %q", string(b), expected) - } - - size, err := mr.Seek(0, os.SEEK_END) - if err != nil { - t.Fatal(err) - } - if size != expectedSize { - t.Fatalf("reader size does not match, got %d, expected %d", size, expectedSize) - } - - // Reset the position and read again - pos, err := mr.Seek(0, os.SEEK_SET) - if err != nil { - t.Fatal(err) - } - if pos != 0 { - t.Fatalf("expected position to be set to 0, got %d", pos) - } - - b, err = ioutil.ReadAll(mr) - if err != nil { - t.Fatal(err) - } - - if string(b) != expected { - t.Fatalf("ReadAll failed, got: %q, expected %q", string(b), expected) - } -} - -func TestMultiReadSeekerReadEach(t *testing.T) { - str := "hello world" - s1 := strings.NewReader(str + " 1") - s2 := strings.NewReader(str + " 2") - s3 := strings.NewReader(str + " 3") - mr := MultiReadSeeker(s1, s2, s3) - - var totalBytes int64 - for i, s := range []*strings.Reader{s1, s2, s3} { - sLen := int64(s.Len()) - buf := make([]byte, s.Len()) - expected := []byte(fmt.Sprintf("%s %d", str, i+1)) - - if _, err := mr.Read(buf); err != nil && err != io.EOF { - t.Fatal(err) - } - - if !bytes.Equal(buf, expected) { - t.Fatalf("expected %q to be %q", string(buf), string(expected)) - } - - pos, err := mr.Seek(0, os.SEEK_CUR) - if err != nil { - t.Fatalf("iteration: %d, error: %v", i+1, err) - } - - // check that the total bytes read is the current position of the seeker - totalBytes += sLen - if pos != totalBytes { - t.Fatalf("expected current position to be: %d, got: %d, iteration: %d", totalBytes, pos, i+1) - } - - // This tests not only that SEEK_SET and SEEK_CUR give the same values, but that the next iteration is in the expected position as well - newPos, err := mr.Seek(pos, os.SEEK_SET) - if err != nil { - t.Fatal(err) - } - if newPos != pos { - t.Fatalf("expected to get same position when calling SEEK_SET with value from SEEK_CUR, cur: %d, set: %d", pos, newPos) - } - } -} - -func TestMultiReadSeekerReadSpanningChunks(t *testing.T) { - str := "hello world" - s1 := strings.NewReader(str + " 1") - s2 := strings.NewReader(str + " 2") - s3 := strings.NewReader(str + " 3") - mr := MultiReadSeeker(s1, s2, s3) - - buf := make([]byte, s1.Len()+3) - _, err := mr.Read(buf) - if err != nil { - t.Fatal(err) - } - - // expected is the contents of s1 + 3 bytes from s2, ie, the `hel` at the end of this string - expected := "hello world 1hel" - if string(buf) != expected { - t.Fatalf("expected %s to be %s", string(buf), expected) - } -} - -func TestMultiReadSeekerNegativeSeek(t *testing.T) { - str := "hello world" - s1 := strings.NewReader(str + " 1") - s2 := strings.NewReader(str + " 2") - s3 := strings.NewReader(str + " 3") - mr := MultiReadSeeker(s1, s2, s3) - - s1Len := s1.Len() - s2Len := s2.Len() - s3Len := s3.Len() - - s, err := mr.Seek(int64(-1*s3.Len()), os.SEEK_END) - if err != nil { - t.Fatal(err) - } - if s != int64(s1Len+s2Len) { - t.Fatalf("expected %d to be %d", s, s1.Len()+s2.Len()) - } - - buf := make([]byte, s3Len) - if _, err := mr.Read(buf); err != nil && err != io.EOF { - t.Fatal(err) - } - expected := fmt.Sprintf("%s %d", str, 3) - if string(buf) != fmt.Sprintf("%s %d", str, 3) { - t.Fatalf("expected %q to be %q", string(buf), expected) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/readers_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/readers_test.go deleted file mode 100644 index 8f9e03c31..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/readers_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package ioutils - -import ( - "fmt" - "io/ioutil" - "strings" - "testing" - "time" - - "github.com/fsouza/go-dockerclient/external/golang.org/x/net/context" -) - -// Implement io.Reader -type errorReader struct{} - -func (r *errorReader) Read(p []byte) (int, error) { - return 0, fmt.Errorf("Error reader always fail.") -} - -func TestReadCloserWrapperClose(t *testing.T) { - reader := strings.NewReader("A string reader") - wrapper := NewReadCloserWrapper(reader, func() error { - return fmt.Errorf("This will be called when closing") - }) - err := wrapper.Close() - if err == nil || !strings.Contains(err.Error(), "This will be called when closing") { - t.Fatalf("readCloserWrapper should have call the anonymous func and thus, fail.") - } -} - -func TestReaderErrWrapperReadOnError(t *testing.T) { - called := false - reader := &errorReader{} - wrapper := NewReaderErrWrapper(reader, func() { - called = true - }) - _, err := wrapper.Read([]byte{}) - if err == nil || !strings.Contains(err.Error(), "Error reader always fail.") { - t.Fatalf("readErrWrapper should returned an error") - } - if !called { - t.Fatalf("readErrWrapper should have call the anonymous function on failure") - } -} - -func TestReaderErrWrapperRead(t *testing.T) { - reader := strings.NewReader("a string reader.") - wrapper := NewReaderErrWrapper(reader, func() { - t.Fatalf("readErrWrapper should not have called the anonymous function") - }) - // Read 20 byte (should be ok with the string above) - num, err := wrapper.Read(make([]byte, 20)) - if err != nil { - t.Fatal(err) - } - if num != 16 { - t.Fatalf("readerErrWrapper should have read 16 byte, but read %d", num) - } -} - -func TestHashData(t *testing.T) { - reader := strings.NewReader("hash-me") - actual, err := HashData(reader) - if err != nil { - t.Fatal(err) - } - expected := "sha256:4d11186aed035cc624d553e10db358492c84a7cd6b9670d92123c144930450aa" - if actual != expected { - t.Fatalf("Expecting %s, got %s", expected, actual) - } -} - -type perpetualReader struct{} - -func (p *perpetualReader) Read(buf []byte) (n int, err error) { - for i := 0; i != len(buf); i++ { - buf[i] = 'a' - } - return len(buf), nil -} - -func TestCancelReadCloser(t *testing.T) { - ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond) - cancelReadCloser := NewCancelReadCloser(ctx, ioutil.NopCloser(&perpetualReader{})) - for { - var buf [128]byte - _, err := cancelReadCloser.Read(buf[:]) - if err == context.DeadlineExceeded { - break - } else if err != nil { - t.Fatalf("got unexpected error: %v", err) - } - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/writers_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/writers_test.go deleted file mode 100644 index 564b1cd4f..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/ioutils/writers_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package ioutils - -import ( - "bytes" - "strings" - "testing" -) - -func TestWriteCloserWrapperClose(t *testing.T) { - called := false - writer := bytes.NewBuffer([]byte{}) - wrapper := NewWriteCloserWrapper(writer, func() error { - called = true - return nil - }) - if err := wrapper.Close(); err != nil { - t.Fatal(err) - } - if !called { - t.Fatalf("writeCloserWrapper should have call the anonymous function.") - } -} - -func TestNopWriteCloser(t *testing.T) { - writer := bytes.NewBuffer([]byte{}) - wrapper := NopWriteCloser(writer) - if err := wrapper.Close(); err != nil { - t.Fatal("NopWriteCloser always return nil on Close.") - } - -} - -func TestNopWriter(t *testing.T) { - nw := &NopWriter{} - l, err := nw.Write([]byte{'c'}) - if err != nil { - t.Fatal(err) - } - if l != 1 { - t.Fatalf("Expected 1 got %d", l) - } -} - -func TestWriteCounter(t *testing.T) { - dummy1 := "This is a dummy string." - dummy2 := "This is another dummy string." - totalLength := int64(len(dummy1) + len(dummy2)) - - reader1 := strings.NewReader(dummy1) - reader2 := strings.NewReader(dummy2) - - var buffer bytes.Buffer - wc := NewWriteCounter(&buffer) - - reader1.WriteTo(wc) - reader2.WriteTo(wc) - - if wc.Count != totalLength { - t.Errorf("Wrong count: %d vs. %d", wc.Count, totalLength) - } - - if buffer.String() != dummy1+dummy2 { - t.Error("Wrong message written") - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/longpath/longpath_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/longpath/longpath_test.go deleted file mode 100644 index 01865eff0..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/longpath/longpath_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package longpath - -import ( - "strings" - "testing" -) - -func TestStandardLongPath(t *testing.T) { - c := `C:\simple\path` - longC := AddPrefix(c) - if !strings.EqualFold(longC, `\\?\C:\simple\path`) { - t.Errorf("Wrong long path returned. Original = %s ; Long = %s", c, longC) - } -} - -func TestUNCLongPath(t *testing.T) { - c := `\\server\share\path` - longC := AddPrefix(c) - if !strings.EqualFold(longC, `\\?\UNC\server\share\path`) { - t.Errorf("Wrong UNC long path returned. Original = %s ; Long = %s", c, longC) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/pools/pools_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/pools/pools_test.go deleted file mode 100644 index 78689800b..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/pools/pools_test.go +++ /dev/null @@ -1,162 +0,0 @@ -package pools - -import ( - "bufio" - "bytes" - "io" - "strings" - "testing" -) - -func TestBufioReaderPoolGetWithNoReaderShouldCreateOne(t *testing.T) { - reader := BufioReader32KPool.Get(nil) - if reader == nil { - t.Fatalf("BufioReaderPool should have create a bufio.Reader but did not.") - } -} - -func TestBufioReaderPoolPutAndGet(t *testing.T) { - sr := bufio.NewReader(strings.NewReader("foobar")) - reader := BufioReader32KPool.Get(sr) - if reader == nil { - t.Fatalf("BufioReaderPool should not return a nil reader.") - } - // verify the first 3 byte - buf1 := make([]byte, 3) - _, err := reader.Read(buf1) - if err != nil { - t.Fatal(err) - } - if actual := string(buf1); actual != "foo" { - t.Fatalf("The first letter should have been 'foo' but was %v", actual) - } - BufioReader32KPool.Put(reader) - // Try to read the next 3 bytes - _, err = sr.Read(make([]byte, 3)) - if err == nil || err != io.EOF { - t.Fatalf("The buffer should have been empty, issue an EOF error.") - } -} - -type simpleReaderCloser struct { - io.Reader - closed bool -} - -func (r *simpleReaderCloser) Close() error { - r.closed = true - return nil -} - -func TestNewReadCloserWrapperWithAReadCloser(t *testing.T) { - br := bufio.NewReader(strings.NewReader("")) - sr := &simpleReaderCloser{ - Reader: strings.NewReader("foobar"), - closed: false, - } - reader := BufioReader32KPool.NewReadCloserWrapper(br, sr) - if reader == nil { - t.Fatalf("NewReadCloserWrapper should not return a nil reader.") - } - // Verify the content of reader - buf := make([]byte, 3) - _, err := reader.Read(buf) - if err != nil { - t.Fatal(err) - } - if actual := string(buf); actual != "foo" { - t.Fatalf("The first 3 letter should have been 'foo' but were %v", actual) - } - reader.Close() - // Read 3 more bytes "bar" - _, err = reader.Read(buf) - if err != nil { - t.Fatal(err) - } - if actual := string(buf); actual != "bar" { - t.Fatalf("The first 3 letter should have been 'bar' but were %v", actual) - } - if !sr.closed { - t.Fatalf("The ReaderCloser should have been closed, it is not.") - } -} - -func TestBufioWriterPoolGetWithNoReaderShouldCreateOne(t *testing.T) { - writer := BufioWriter32KPool.Get(nil) - if writer == nil { - t.Fatalf("BufioWriterPool should have create a bufio.Writer but did not.") - } -} - -func TestBufioWriterPoolPutAndGet(t *testing.T) { - buf := new(bytes.Buffer) - bw := bufio.NewWriter(buf) - writer := BufioWriter32KPool.Get(bw) - if writer == nil { - t.Fatalf("BufioReaderPool should not return a nil writer.") - } - written, err := writer.Write([]byte("foobar")) - if err != nil { - t.Fatal(err) - } - if written != 6 { - t.Fatalf("Should have written 6 bytes, but wrote %v bytes", written) - } - // Make sure we Flush all the way ? - writer.Flush() - bw.Flush() - if len(buf.Bytes()) != 6 { - t.Fatalf("The buffer should contain 6 bytes ('foobar') but contains %v ('%v')", buf.Bytes(), string(buf.Bytes())) - } - // Reset the buffer - buf.Reset() - BufioWriter32KPool.Put(writer) - // Try to write something - written, err = writer.Write([]byte("barfoo")) - if err != nil { - t.Fatal(err) - } - // If we now try to flush it, it should panic (the writer is nil) - // recover it - defer func() { - if r := recover(); r == nil { - t.Fatal("Trying to flush the writter should have 'paniced', did not.") - } - }() - writer.Flush() -} - -type simpleWriterCloser struct { - io.Writer - closed bool -} - -func (r *simpleWriterCloser) Close() error { - r.closed = true - return nil -} - -func TestNewWriteCloserWrapperWithAWriteCloser(t *testing.T) { - buf := new(bytes.Buffer) - bw := bufio.NewWriter(buf) - sw := &simpleWriterCloser{ - Writer: new(bytes.Buffer), - closed: false, - } - bw.Flush() - writer := BufioWriter32KPool.NewWriteCloserWrapper(bw, sw) - if writer == nil { - t.Fatalf("BufioReaderPool should not return a nil writer.") - } - written, err := writer.Write([]byte("foobar")) - if err != nil { - t.Fatal(err) - } - if written != 6 { - t.Fatalf("Should have written 6 bytes, but wrote %v bytes", written) - } - writer.Close() - if !sw.closed { - t.Fatalf("The ReaderCloser should have been closed, it is not.") - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/stdcopy/stdcopy_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/stdcopy/stdcopy_test.go deleted file mode 100644 index 88d88d41e..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/stdcopy/stdcopy_test.go +++ /dev/null @@ -1,261 +0,0 @@ -package stdcopy - -import ( - "bytes" - "errors" - "io" - "io/ioutil" - "strings" - "testing" -) - -func TestNewStdWriter(t *testing.T) { - writer := NewStdWriter(ioutil.Discard, Stdout) - if writer == nil { - t.Fatalf("NewStdWriter with an invalid StdType should not return nil.") - } -} - -func TestWriteWithUnitializedStdWriter(t *testing.T) { - writer := StdWriter{ - Writer: nil, - prefix: Stdout, - sizeBuf: make([]byte, 4), - } - n, err := writer.Write([]byte("Something here")) - if n != 0 || err == nil { - t.Fatalf("Should fail when given an uncomplete or uninitialized StdWriter") - } -} - -func TestWriteWithNilBytes(t *testing.T) { - writer := NewStdWriter(ioutil.Discard, Stdout) - n, err := writer.Write(nil) - if err != nil { - t.Fatalf("Shouldn't have fail when given no data") - } - if n > 0 { - t.Fatalf("Write should have written 0 byte, but has written %d", n) - } -} - -func TestWrite(t *testing.T) { - writer := NewStdWriter(ioutil.Discard, Stdout) - data := []byte("Test StdWrite.Write") - n, err := writer.Write(data) - if err != nil { - t.Fatalf("Error while writing with StdWrite") - } - if n != len(data) { - t.Fatalf("Write should have written %d byte but wrote %d.", len(data), n) - } -} - -type errWriter struct { - n int - err error -} - -func (f *errWriter) Write(buf []byte) (int, error) { - return f.n, f.err -} - -func TestWriteWithWriterError(t *testing.T) { - expectedError := errors.New("expected") - expectedReturnedBytes := 10 - writer := NewStdWriter(&errWriter{ - n: stdWriterPrefixLen + expectedReturnedBytes, - err: expectedError}, Stdout) - data := []byte("This won't get written, sigh") - n, err := writer.Write(data) - if err != expectedError { - t.Fatalf("Didn't get expected error.") - } - if n != expectedReturnedBytes { - t.Fatalf("Didn't get expected writen bytes %d, got %d.", - expectedReturnedBytes, n) - } -} - -func TestWriteDoesNotReturnNegativeWrittenBytes(t *testing.T) { - writer := NewStdWriter(&errWriter{n: -1}, Stdout) - data := []byte("This won't get written, sigh") - actual, _ := writer.Write(data) - if actual != 0 { - t.Fatalf("Expected returned written bytes equal to 0, got %d", actual) - } -} - -func getSrcBuffer(stdOutBytes, stdErrBytes []byte) (buffer *bytes.Buffer, err error) { - buffer = new(bytes.Buffer) - dstOut := NewStdWriter(buffer, Stdout) - _, err = dstOut.Write(stdOutBytes) - if err != nil { - return - } - dstErr := NewStdWriter(buffer, Stderr) - _, err = dstErr.Write(stdErrBytes) - return -} - -func TestStdCopyWriteAndRead(t *testing.T) { - stdOutBytes := []byte(strings.Repeat("o", startingBufLen)) - stdErrBytes := []byte(strings.Repeat("e", startingBufLen)) - buffer, err := getSrcBuffer(stdOutBytes, stdErrBytes) - if err != nil { - t.Fatal(err) - } - written, err := StdCopy(ioutil.Discard, ioutil.Discard, buffer) - if err != nil { - t.Fatal(err) - } - expectedTotalWritten := len(stdOutBytes) + len(stdErrBytes) - if written != int64(expectedTotalWritten) { - t.Fatalf("Expected to have total of %d bytes written, got %d", expectedTotalWritten, written) - } -} - -type customReader struct { - n int - err error - totalCalls int - correctCalls int - src *bytes.Buffer -} - -func (f *customReader) Read(buf []byte) (int, error) { - f.totalCalls++ - if f.totalCalls <= f.correctCalls { - return f.src.Read(buf) - } - return f.n, f.err -} - -func TestStdCopyReturnsErrorReadingHeader(t *testing.T) { - expectedError := errors.New("error") - reader := &customReader{ - err: expectedError} - written, err := StdCopy(ioutil.Discard, ioutil.Discard, reader) - if written != 0 { - t.Fatalf("Expected 0 bytes read, got %d", written) - } - if err != expectedError { - t.Fatalf("Didn't get expected error") - } -} - -func TestStdCopyReturnsErrorReadingFrame(t *testing.T) { - expectedError := errors.New("error") - stdOutBytes := []byte(strings.Repeat("o", startingBufLen)) - stdErrBytes := []byte(strings.Repeat("e", startingBufLen)) - buffer, err := getSrcBuffer(stdOutBytes, stdErrBytes) - if err != nil { - t.Fatal(err) - } - reader := &customReader{ - correctCalls: 1, - n: stdWriterPrefixLen + 1, - err: expectedError, - src: buffer} - written, err := StdCopy(ioutil.Discard, ioutil.Discard, reader) - if written != 0 { - t.Fatalf("Expected 0 bytes read, got %d", written) - } - if err != expectedError { - t.Fatalf("Didn't get expected error") - } -} - -func TestStdCopyDetectsCorruptedFrame(t *testing.T) { - stdOutBytes := []byte(strings.Repeat("o", startingBufLen)) - stdErrBytes := []byte(strings.Repeat("e", startingBufLen)) - buffer, err := getSrcBuffer(stdOutBytes, stdErrBytes) - if err != nil { - t.Fatal(err) - } - reader := &customReader{ - correctCalls: 1, - n: stdWriterPrefixLen + 1, - err: io.EOF, - src: buffer} - written, err := StdCopy(ioutil.Discard, ioutil.Discard, reader) - if written != startingBufLen { - t.Fatalf("Expected 0 bytes read, got %d", written) - } - if err != nil { - t.Fatal("Didn't get nil error") - } -} - -func TestStdCopyWithInvalidInputHeader(t *testing.T) { - dstOut := NewStdWriter(ioutil.Discard, Stdout) - dstErr := NewStdWriter(ioutil.Discard, Stderr) - src := strings.NewReader("Invalid input") - _, err := StdCopy(dstOut, dstErr, src) - if err == nil { - t.Fatal("StdCopy with invalid input header should fail.") - } -} - -func TestStdCopyWithCorruptedPrefix(t *testing.T) { - data := []byte{0x01, 0x02, 0x03} - src := bytes.NewReader(data) - written, err := StdCopy(nil, nil, src) - if err != nil { - t.Fatalf("StdCopy should not return an error with corrupted prefix.") - } - if written != 0 { - t.Fatalf("StdCopy should have written 0, but has written %d", written) - } -} - -func TestStdCopyReturnsWriteErrors(t *testing.T) { - stdOutBytes := []byte(strings.Repeat("o", startingBufLen)) - stdErrBytes := []byte(strings.Repeat("e", startingBufLen)) - buffer, err := getSrcBuffer(stdOutBytes, stdErrBytes) - if err != nil { - t.Fatal(err) - } - expectedError := errors.New("expected") - - dstOut := &errWriter{err: expectedError} - - written, err := StdCopy(dstOut, ioutil.Discard, buffer) - if written != 0 { - t.Fatalf("StdCopy should have written 0, but has written %d", written) - } - if err != expectedError { - t.Fatalf("Didn't get expected error, got %v", err) - } -} - -func TestStdCopyDetectsNotFullyWrittenFrames(t *testing.T) { - stdOutBytes := []byte(strings.Repeat("o", startingBufLen)) - stdErrBytes := []byte(strings.Repeat("e", startingBufLen)) - buffer, err := getSrcBuffer(stdOutBytes, stdErrBytes) - if err != nil { - t.Fatal(err) - } - dstOut := &errWriter{n: startingBufLen - 10} - - written, err := StdCopy(dstOut, ioutil.Discard, buffer) - if written != 0 { - t.Fatalf("StdCopy should have return 0 written bytes, but returned %d", written) - } - if err != io.ErrShortWrite { - t.Fatalf("Didn't get expected io.ErrShortWrite error") - } -} - -func BenchmarkWrite(b *testing.B) { - w := NewStdWriter(ioutil.Discard, Stdout) - data := []byte("Test line for testing stdwriter performance\n") - data = bytes.Repeat(data, 100) - b.SetBytes(int64(len(data))) - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err := w.Write(data); err != nil { - b.Fatal(err) - } - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_test.go deleted file mode 100644 index 5c87df32a..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package system - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - "time" -) - -// prepareTempFile creates a temporary file in a temporary directory. -func prepareTempFile(t *testing.T) (string, string) { - dir, err := ioutil.TempDir("", "docker-system-test") - if err != nil { - t.Fatal(err) - } - - file := filepath.Join(dir, "exist") - if err := ioutil.WriteFile(file, []byte("hello"), 0644); err != nil { - t.Fatal(err) - } - return file, dir -} - -// TestChtimes tests Chtimes on a tempfile. Test only mTime, because aTime is OS dependent -func TestChtimes(t *testing.T) { - file, dir := prepareTempFile(t) - defer os.RemoveAll(dir) - - beforeUnixEpochTime := time.Unix(0, 0).Add(-100 * time.Second) - unixEpochTime := time.Unix(0, 0) - afterUnixEpochTime := time.Unix(100, 0) - unixMaxTime := maxTime - - // Test both aTime and mTime set to Unix Epoch - Chtimes(file, unixEpochTime, unixEpochTime) - - f, err := os.Stat(file) - if err != nil { - t.Fatal(err) - } - - if f.ModTime() != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, f.ModTime()) - } - - // Test aTime before Unix Epoch and mTime set to Unix Epoch - Chtimes(file, beforeUnixEpochTime, unixEpochTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - if f.ModTime() != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, f.ModTime()) - } - - // Test aTime set to Unix Epoch and mTime before Unix Epoch - Chtimes(file, unixEpochTime, beforeUnixEpochTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - if f.ModTime() != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, f.ModTime()) - } - - // Test both aTime and mTime set to after Unix Epoch (valid time) - Chtimes(file, afterUnixEpochTime, afterUnixEpochTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - if f.ModTime() != afterUnixEpochTime { - t.Fatalf("Expected: %s, got: %s", afterUnixEpochTime, f.ModTime()) - } - - // Test both aTime and mTime set to Unix max time - Chtimes(file, unixMaxTime, unixMaxTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - if f.ModTime().Truncate(time.Second) != unixMaxTime.Truncate(time.Second) { - t.Fatalf("Expected: %s, got: %s", unixMaxTime.Truncate(time.Second), f.ModTime().Truncate(time.Second)) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_unix_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_unix_test.go deleted file mode 100644 index fcd594023..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_unix_test.go +++ /dev/null @@ -1,91 +0,0 @@ -// +build linux freebsd - -package system - -import ( - "os" - "syscall" - "testing" - "time" -) - -// TestChtimes tests Chtimes access time on a tempfile on Linux -func TestChtimesLinux(t *testing.T) { - file, dir := prepareTempFile(t) - defer os.RemoveAll(dir) - - beforeUnixEpochTime := time.Unix(0, 0).Add(-100 * time.Second) - unixEpochTime := time.Unix(0, 0) - afterUnixEpochTime := time.Unix(100, 0) - unixMaxTime := maxTime - - // Test both aTime and mTime set to Unix Epoch - Chtimes(file, unixEpochTime, unixEpochTime) - - f, err := os.Stat(file) - if err != nil { - t.Fatal(err) - } - - stat := f.Sys().(*syscall.Stat_t) - aTime := time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) - if aTime != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) - } - - // Test aTime before Unix Epoch and mTime set to Unix Epoch - Chtimes(file, beforeUnixEpochTime, unixEpochTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - stat = f.Sys().(*syscall.Stat_t) - aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) - if aTime != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) - } - - // Test aTime set to Unix Epoch and mTime before Unix Epoch - Chtimes(file, unixEpochTime, beforeUnixEpochTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - stat = f.Sys().(*syscall.Stat_t) - aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) - if aTime != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) - } - - // Test both aTime and mTime set to after Unix Epoch (valid time) - Chtimes(file, afterUnixEpochTime, afterUnixEpochTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - stat = f.Sys().(*syscall.Stat_t) - aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) - if aTime != afterUnixEpochTime { - t.Fatalf("Expected: %s, got: %s", afterUnixEpochTime, aTime) - } - - // Test both aTime and mTime set to Unix max time - Chtimes(file, unixMaxTime, unixMaxTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - stat = f.Sys().(*syscall.Stat_t) - aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) - if aTime.Truncate(time.Second) != unixMaxTime.Truncate(time.Second) { - t.Fatalf("Expected: %s, got: %s", unixMaxTime.Truncate(time.Second), aTime.Truncate(time.Second)) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_windows_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_windows_test.go deleted file mode 100644 index be57558e1..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_windows_test.go +++ /dev/null @@ -1,86 +0,0 @@ -// +build windows - -package system - -import ( - "os" - "syscall" - "testing" - "time" -) - -// TestChtimes tests Chtimes access time on a tempfile on Windows -func TestChtimesWindows(t *testing.T) { - file, dir := prepareTempFile(t) - defer os.RemoveAll(dir) - - beforeUnixEpochTime := time.Unix(0, 0).Add(-100 * time.Second) - unixEpochTime := time.Unix(0, 0) - afterUnixEpochTime := time.Unix(100, 0) - unixMaxTime := maxTime - - // Test both aTime and mTime set to Unix Epoch - Chtimes(file, unixEpochTime, unixEpochTime) - - f, err := os.Stat(file) - if err != nil { - t.Fatal(err) - } - - aTime := time.Unix(0, f.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) - if aTime != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) - } - - // Test aTime before Unix Epoch and mTime set to Unix Epoch - Chtimes(file, beforeUnixEpochTime, unixEpochTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - aTime = time.Unix(0, f.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) - if aTime != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) - } - - // Test aTime set to Unix Epoch and mTime before Unix Epoch - Chtimes(file, unixEpochTime, beforeUnixEpochTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - aTime = time.Unix(0, f.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) - if aTime != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) - } - - // Test both aTime and mTime set to after Unix Epoch (valid time) - Chtimes(file, afterUnixEpochTime, afterUnixEpochTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - aTime = time.Unix(0, f.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) - if aTime != afterUnixEpochTime { - t.Fatalf("Expected: %s, got: %s", afterUnixEpochTime, aTime) - } - - // Test both aTime and mTime set to Unix max time - Chtimes(file, unixMaxTime, unixMaxTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - aTime = time.Unix(0, f.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) - if aTime.Truncate(time.Second) != unixMaxTime.Truncate(time.Second) { - t.Fatalf("Expected: %s, got: %s", unixMaxTime.Truncate(time.Second), aTime.Truncate(time.Second)) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/lstat_unix_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/lstat_unix_test.go deleted file mode 100644 index 062cf53bf..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/lstat_unix_test.go +++ /dev/null @@ -1,30 +0,0 @@ -// +build linux freebsd - -package system - -import ( - "os" - "testing" -) - -// TestLstat tests Lstat for existing and non existing files -func TestLstat(t *testing.T) { - file, invalid, _, dir := prepareFiles(t) - defer os.RemoveAll(dir) - - statFile, err := Lstat(file) - if err != nil { - t.Fatal(err) - } - if statFile == nil { - t.Fatal("returned empty stat for existing file") - } - - statInvalid, err := Lstat(invalid) - if err == nil { - t.Fatal("did not return error for non-existing file") - } - if statInvalid != nil { - t.Fatal("returned non-nil stat for non-existing file") - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/meminfo_unix_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/meminfo_unix_test.go deleted file mode 100644 index dda048379..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/meminfo_unix_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// +build linux freebsd - -package system - -import ( - "strings" - "testing" - - "github.com/fsouza/go-dockerclient/external/github.com/docker/go-units" -) - -// TestMemInfo tests parseMemInfo with a static meminfo string -func TestMemInfo(t *testing.T) { - const input = ` - MemTotal: 1 kB - MemFree: 2 kB - SwapTotal: 3 kB - SwapFree: 4 kB - Malformed1: - Malformed2: 1 - Malformed3: 2 MB - Malformed4: X kB - ` - meminfo, err := parseMemInfo(strings.NewReader(input)) - if err != nil { - t.Fatal(err) - } - if meminfo.MemTotal != 1*units.KiB { - t.Fatalf("Unexpected MemTotal: %d", meminfo.MemTotal) - } - if meminfo.MemFree != 2*units.KiB { - t.Fatalf("Unexpected MemFree: %d", meminfo.MemFree) - } - if meminfo.SwapTotal != 3*units.KiB { - t.Fatalf("Unexpected SwapTotal: %d", meminfo.SwapTotal) - } - if meminfo.SwapFree != 4*units.KiB { - t.Fatalf("Unexpected SwapFree: %d", meminfo.SwapFree) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/stat_unix_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/stat_unix_test.go deleted file mode 100644 index dee8d30a1..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/stat_unix_test.go +++ /dev/null @@ -1,39 +0,0 @@ -// +build linux freebsd - -package system - -import ( - "os" - "syscall" - "testing" -) - -// TestFromStatT tests fromStatT for a tempfile -func TestFromStatT(t *testing.T) { - file, _, _, dir := prepareFiles(t) - defer os.RemoveAll(dir) - - stat := &syscall.Stat_t{} - err := syscall.Lstat(file, stat) - - s, err := fromStatT(stat) - if err != nil { - t.Fatal(err) - } - - if stat.Mode != s.Mode() { - t.Fatal("got invalid mode") - } - if stat.Uid != s.UID() { - t.Fatal("got invalid uid") - } - if stat.Gid != s.GID() { - t.Fatal("got invalid gid") - } - if stat.Rdev != s.Rdev() { - t.Fatal("got invalid rdev") - } - if stat.Mtim != s.Mtim() { - t.Fatal("got invalid mtim") - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/utimes_unix_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/utimes_unix_test.go deleted file mode 100644 index 1ee0d099f..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/utimes_unix_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// +build linux freebsd - -package system - -import ( - "io/ioutil" - "os" - "path/filepath" - "syscall" - "testing" -) - -// prepareFiles creates files for testing in the temp directory -func prepareFiles(t *testing.T) (string, string, string, string) { - dir, err := ioutil.TempDir("", "docker-system-test") - if err != nil { - t.Fatal(err) - } - - file := filepath.Join(dir, "exist") - if err := ioutil.WriteFile(file, []byte("hello"), 0644); err != nil { - t.Fatal(err) - } - - invalid := filepath.Join(dir, "doesnt-exist") - - symlink := filepath.Join(dir, "symlink") - if err := os.Symlink(file, symlink); err != nil { - t.Fatal(err) - } - - return file, invalid, symlink, dir -} - -func TestLUtimesNano(t *testing.T) { - file, invalid, symlink, dir := prepareFiles(t) - defer os.RemoveAll(dir) - - before, err := os.Stat(file) - if err != nil { - t.Fatal(err) - } - - ts := []syscall.Timespec{{0, 0}, {0, 0}} - if err := LUtimesNano(symlink, ts); err != nil { - t.Fatal(err) - } - - symlinkInfo, err := os.Lstat(symlink) - if err != nil { - t.Fatal(err) - } - if before.ModTime().Unix() == symlinkInfo.ModTime().Unix() { - t.Fatal("The modification time of the symlink should be different") - } - - fileInfo, err := os.Stat(file) - if err != nil { - t.Fatal(err) - } - if before.ModTime().Unix() != fileInfo.ModTime().Unix() { - t.Fatal("The modification time of the file should be same") - } - - if err := LUtimesNano(invalid, ts); err == nil { - t.Fatal("Doesn't return an error on a non-existing file") - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/go-units/duration_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/go-units/duration_test.go deleted file mode 100644 index 63baa515b..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/go-units/duration_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package units - -import ( - "fmt" - "testing" - "time" -) - -func ExampleHumanDuration() { - fmt.Println(HumanDuration(450 * time.Millisecond)) - fmt.Println(HumanDuration(47 * time.Second)) - fmt.Println(HumanDuration(1 * time.Minute)) - fmt.Println(HumanDuration(3 * time.Minute)) - fmt.Println(HumanDuration(35 * time.Minute)) - fmt.Println(HumanDuration(35*time.Minute + 40*time.Second)) - fmt.Println(HumanDuration(1 * time.Hour)) - fmt.Println(HumanDuration(1*time.Hour + 45*time.Minute)) - fmt.Println(HumanDuration(3 * time.Hour)) - fmt.Println(HumanDuration(3*time.Hour + 59*time.Minute)) - fmt.Println(HumanDuration(3*time.Hour + 60*time.Minute)) - fmt.Println(HumanDuration(24 * time.Hour)) - fmt.Println(HumanDuration(24*time.Hour + 12*time.Hour)) - fmt.Println(HumanDuration(2 * 24 * time.Hour)) - fmt.Println(HumanDuration(7 * 24 * time.Hour)) - fmt.Println(HumanDuration(13*24*time.Hour + 5*time.Hour)) - fmt.Println(HumanDuration(2 * 7 * 24 * time.Hour)) - fmt.Println(HumanDuration(2*7*24*time.Hour + 4*24*time.Hour)) - fmt.Println(HumanDuration(3 * 7 * 24 * time.Hour)) - fmt.Println(HumanDuration(4 * 7 * 24 * time.Hour)) - fmt.Println(HumanDuration(4*7*24*time.Hour + 3*24*time.Hour)) - fmt.Println(HumanDuration(1 * 30 * 24 * time.Hour)) - fmt.Println(HumanDuration(1*30*24*time.Hour + 2*7*24*time.Hour)) - fmt.Println(HumanDuration(2 * 30 * 24 * time.Hour)) - fmt.Println(HumanDuration(3*30*24*time.Hour + 1*7*24*time.Hour)) - fmt.Println(HumanDuration(5*30*24*time.Hour + 2*7*24*time.Hour)) - fmt.Println(HumanDuration(13 * 30 * 24 * time.Hour)) - fmt.Println(HumanDuration(23 * 30 * 24 * time.Hour)) - fmt.Println(HumanDuration(24 * 30 * 24 * time.Hour)) - fmt.Println(HumanDuration(24*30*24*time.Hour + 2*7*24*time.Hour)) - fmt.Println(HumanDuration(3*365*24*time.Hour + 2*30*24*time.Hour)) -} - -func TestHumanDuration(t *testing.T) { - // Useful duration abstractions - day := 24 * time.Hour - week := 7 * day - month := 30 * day - year := 365 * day - - assertEquals(t, "Less than a second", HumanDuration(450*time.Millisecond)) - assertEquals(t, "47 seconds", HumanDuration(47*time.Second)) - assertEquals(t, "About a minute", HumanDuration(1*time.Minute)) - assertEquals(t, "3 minutes", HumanDuration(3*time.Minute)) - assertEquals(t, "35 minutes", HumanDuration(35*time.Minute)) - assertEquals(t, "35 minutes", HumanDuration(35*time.Minute+40*time.Second)) - assertEquals(t, "About an hour", HumanDuration(1*time.Hour)) - assertEquals(t, "About an hour", HumanDuration(1*time.Hour+45*time.Minute)) - assertEquals(t, "3 hours", HumanDuration(3*time.Hour)) - assertEquals(t, "3 hours", HumanDuration(3*time.Hour+59*time.Minute)) - assertEquals(t, "4 hours", HumanDuration(3*time.Hour+60*time.Minute)) - assertEquals(t, "24 hours", HumanDuration(24*time.Hour)) - assertEquals(t, "36 hours", HumanDuration(1*day+12*time.Hour)) - assertEquals(t, "2 days", HumanDuration(2*day)) - assertEquals(t, "7 days", HumanDuration(7*day)) - assertEquals(t, "13 days", HumanDuration(13*day+5*time.Hour)) - assertEquals(t, "2 weeks", HumanDuration(2*week)) - assertEquals(t, "2 weeks", HumanDuration(2*week+4*day)) - assertEquals(t, "3 weeks", HumanDuration(3*week)) - assertEquals(t, "4 weeks", HumanDuration(4*week)) - assertEquals(t, "4 weeks", HumanDuration(4*week+3*day)) - assertEquals(t, "4 weeks", HumanDuration(1*month)) - assertEquals(t, "6 weeks", HumanDuration(1*month+2*week)) - assertEquals(t, "8 weeks", HumanDuration(2*month)) - assertEquals(t, "3 months", HumanDuration(3*month+1*week)) - assertEquals(t, "5 months", HumanDuration(5*month+2*week)) - assertEquals(t, "13 months", HumanDuration(13*month)) - assertEquals(t, "23 months", HumanDuration(23*month)) - assertEquals(t, "24 months", HumanDuration(24*month)) - assertEquals(t, "2 years", HumanDuration(24*month+2*week)) - assertEquals(t, "3 years", HumanDuration(3*year+2*month)) -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/go-units/size_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/go-units/size_test.go deleted file mode 100644 index a968f5c0d..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/go-units/size_test.go +++ /dev/null @@ -1,160 +0,0 @@ -package units - -import ( - "fmt" - "reflect" - "runtime" - "strings" - "testing" -) - -func ExampleBytesSize() { - fmt.Println(BytesSize(1024)) - fmt.Println(BytesSize(1024 * 1024)) - fmt.Println(BytesSize(1048576)) - fmt.Println(BytesSize(2 * MiB)) - fmt.Println(BytesSize(3.42 * GiB)) - fmt.Println(BytesSize(5.372 * TiB)) - fmt.Println(BytesSize(2.22 * PiB)) -} - -func ExampleHumanSize() { - fmt.Println(HumanSize(1000)) - fmt.Println(HumanSize(1024)) - fmt.Println(HumanSize(1000000)) - fmt.Println(HumanSize(1048576)) - fmt.Println(HumanSize(2 * MB)) - fmt.Println(HumanSize(float64(3.42 * GB))) - fmt.Println(HumanSize(float64(5.372 * TB))) - fmt.Println(HumanSize(float64(2.22 * PB))) -} - -func ExampleFromHumanSize() { - fmt.Println(FromHumanSize("32")) - fmt.Println(FromHumanSize("32b")) - fmt.Println(FromHumanSize("32B")) - fmt.Println(FromHumanSize("32k")) - fmt.Println(FromHumanSize("32K")) - fmt.Println(FromHumanSize("32kb")) - fmt.Println(FromHumanSize("32Kb")) - fmt.Println(FromHumanSize("32Mb")) - fmt.Println(FromHumanSize("32Gb")) - fmt.Println(FromHumanSize("32Tb")) - fmt.Println(FromHumanSize("32Pb")) -} - -func ExampleRAMInBytes() { - fmt.Println(RAMInBytes("32")) - fmt.Println(RAMInBytes("32b")) - fmt.Println(RAMInBytes("32B")) - fmt.Println(RAMInBytes("32k")) - fmt.Println(RAMInBytes("32K")) - fmt.Println(RAMInBytes("32kb")) - fmt.Println(RAMInBytes("32Kb")) - fmt.Println(RAMInBytes("32Mb")) - fmt.Println(RAMInBytes("32Gb")) - fmt.Println(RAMInBytes("32Tb")) - fmt.Println(RAMInBytes("32Pb")) - fmt.Println(RAMInBytes("32PB")) - fmt.Println(RAMInBytes("32P")) -} - -func TestBytesSize(t *testing.T) { - assertEquals(t, "1 KiB", BytesSize(1024)) - assertEquals(t, "1 MiB", BytesSize(1024*1024)) - assertEquals(t, "1 MiB", BytesSize(1048576)) - assertEquals(t, "2 MiB", BytesSize(2*MiB)) - assertEquals(t, "3.42 GiB", BytesSize(3.42*GiB)) - assertEquals(t, "5.372 TiB", BytesSize(5.372*TiB)) - assertEquals(t, "2.22 PiB", BytesSize(2.22*PiB)) -} - -func TestHumanSize(t *testing.T) { - assertEquals(t, "1 kB", HumanSize(1000)) - assertEquals(t, "1.024 kB", HumanSize(1024)) - assertEquals(t, "1 MB", HumanSize(1000000)) - assertEquals(t, "1.049 MB", HumanSize(1048576)) - assertEquals(t, "2 MB", HumanSize(2*MB)) - assertEquals(t, "3.42 GB", HumanSize(float64(3.42*GB))) - assertEquals(t, "5.372 TB", HumanSize(float64(5.372*TB))) - assertEquals(t, "2.22 PB", HumanSize(float64(2.22*PB))) -} - -func TestFromHumanSize(t *testing.T) { - assertSuccessEquals(t, 32, FromHumanSize, "32") - assertSuccessEquals(t, 32, FromHumanSize, "32b") - assertSuccessEquals(t, 32, FromHumanSize, "32B") - assertSuccessEquals(t, 32*KB, FromHumanSize, "32k") - assertSuccessEquals(t, 32*KB, FromHumanSize, "32K") - assertSuccessEquals(t, 32*KB, FromHumanSize, "32kb") - assertSuccessEquals(t, 32*KB, FromHumanSize, "32Kb") - assertSuccessEquals(t, 32*MB, FromHumanSize, "32Mb") - assertSuccessEquals(t, 32*GB, FromHumanSize, "32Gb") - assertSuccessEquals(t, 32*TB, FromHumanSize, "32Tb") - assertSuccessEquals(t, 32*PB, FromHumanSize, "32Pb") - - assertError(t, FromHumanSize, "") - assertError(t, FromHumanSize, "hello") - assertError(t, FromHumanSize, "-32") - assertError(t, FromHumanSize, "32.3") - assertError(t, FromHumanSize, " 32 ") - assertError(t, FromHumanSize, "32.3Kb") - assertError(t, FromHumanSize, "32 mb") - assertError(t, FromHumanSize, "32m b") - assertError(t, FromHumanSize, "32bm") -} - -func TestRAMInBytes(t *testing.T) { - assertSuccessEquals(t, 32, RAMInBytes, "32") - assertSuccessEquals(t, 32, RAMInBytes, "32b") - assertSuccessEquals(t, 32, RAMInBytes, "32B") - assertSuccessEquals(t, 32*KiB, RAMInBytes, "32k") - assertSuccessEquals(t, 32*KiB, RAMInBytes, "32K") - assertSuccessEquals(t, 32*KiB, RAMInBytes, "32kb") - assertSuccessEquals(t, 32*KiB, RAMInBytes, "32Kb") - assertSuccessEquals(t, 32*MiB, RAMInBytes, "32Mb") - assertSuccessEquals(t, 32*GiB, RAMInBytes, "32Gb") - assertSuccessEquals(t, 32*TiB, RAMInBytes, "32Tb") - assertSuccessEquals(t, 32*PiB, RAMInBytes, "32Pb") - assertSuccessEquals(t, 32*PiB, RAMInBytes, "32PB") - assertSuccessEquals(t, 32*PiB, RAMInBytes, "32P") - - assertError(t, RAMInBytes, "") - assertError(t, RAMInBytes, "hello") - assertError(t, RAMInBytes, "-32") - assertError(t, RAMInBytes, "32.3") - assertError(t, RAMInBytes, " 32 ") - assertError(t, RAMInBytes, "32.3Kb") - assertError(t, RAMInBytes, "32 mb") - assertError(t, RAMInBytes, "32m b") - assertError(t, RAMInBytes, "32bm") -} - -func assertEquals(t *testing.T, expected, actual interface{}) { - if expected != actual { - t.Errorf("Expected '%v' but got '%v'", expected, actual) - } -} - -// func that maps to the parse function signatures as testing abstraction -type parseFn func(string) (int64, error) - -// Define 'String()' for pretty-print -func (fn parseFn) String() string { - fnName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() - return fnName[strings.LastIndex(fnName, ".")+1:] -} - -func assertSuccessEquals(t *testing.T, expected int64, fn parseFn, arg string) { - res, err := fn(arg) - if err != nil || res != expected { - t.Errorf("%s(\"%s\") -> expected '%d' but got '%d' with error '%v'", fn, arg, expected, res, err) - } -} - -func assertError(t *testing.T, fn parseFn, arg string) { - res, err := fn(arg) - if err == nil && res != -1 { - t.Errorf("%s(\"%s\") -> expected error but got '%d'", fn, arg, res) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/go-units/ulimit_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/go-units/ulimit_test.go deleted file mode 100644 index 3e7f10fc2..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/docker/go-units/ulimit_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package units - -import ( - "fmt" - "strconv" - "testing" -) - -func ExampleParseUlimit() { - fmt.Println(ParseUlimit("nofile=512:1024")) - fmt.Println(ParseUlimit("nofile=1024")) - fmt.Println(ParseUlimit("cpu=2:4")) - fmt.Println(ParseUlimit("cpu=6")) -} - -func TestParseUlimitValid(t *testing.T) { - u1 := &Ulimit{"nofile", 1024, 512} - if u2, _ := ParseUlimit("nofile=512:1024"); *u1 != *u2 { - t.Fatalf("expected %q, but got %q", u1, u2) - } -} - -func TestParseUlimitInvalidLimitType(t *testing.T) { - if _, err := ParseUlimit("notarealtype=1024:1024"); err == nil { - t.Fatalf("expected error on invalid ulimit type") - } -} - -func TestParseUlimitBadFormat(t *testing.T) { - if _, err := ParseUlimit("nofile:1024:1024"); err == nil { - t.Fatal("expected error on bad syntax") - } - - if _, err := ParseUlimit("nofile"); err == nil { - t.Fatal("expected error on bad syntax") - } - - if _, err := ParseUlimit("nofile="); err == nil { - t.Fatal("expected error on bad syntax") - } - if _, err := ParseUlimit("nofile=:"); err == nil { - t.Fatal("expected error on bad syntax") - } - if _, err := ParseUlimit("nofile=:1024"); err == nil { - t.Fatal("expected error on bad syntax") - } -} - -func TestParseUlimitHardLessThanSoft(t *testing.T) { - if _, err := ParseUlimit("nofile=1024:1"); err == nil { - t.Fatal("expected error on hard limit less than soft limit") - } -} - -func TestParseUlimitInvalidValueType(t *testing.T) { - if _, err := ParseUlimit("nofile=asdf"); err == nil { - t.Fatal("expected error on bad value type, but got no error") - } else if _, ok := err.(*strconv.NumError); !ok { - t.Fatalf("expected error on bad value type, but got `%s`", err) - } - - if _, err := ParseUlimit("nofile=1024:asdf"); err == nil { - t.Fatal("expected error on bad value type, but got no error") - } else if _, ok := err.(*strconv.NumError); !ok { - t.Fatalf("expected error on bad value type, but got `%s`", err) - } -} - -func TestUlimitStringOutput(t *testing.T) { - u := &Ulimit{"nofile", 1024, 512} - if s := u.String(); s != "nofile=512:1024" { - t.Fatal("expected String to return nofile=512:1024, but got", s) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/README.md b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/README.md deleted file mode 100644 index c60a31b05..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/README.md +++ /dev/null @@ -1,7 +0,0 @@ -context -======= -[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context) - -gorilla/context is a general purpose registry for global request variables. - -Read the full documentation here: http://www.gorillatoolkit.org/pkg/context diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/context.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/context.go deleted file mode 100644 index 81cb128b1..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/context.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2012 The Gorilla 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 context - -import ( - "net/http" - "sync" - "time" -) - -var ( - mutex sync.RWMutex - data = make(map[*http.Request]map[interface{}]interface{}) - datat = make(map[*http.Request]int64) -) - -// Set stores a value for a given key in a given request. -func Set(r *http.Request, key, val interface{}) { - mutex.Lock() - if data[r] == nil { - data[r] = make(map[interface{}]interface{}) - datat[r] = time.Now().Unix() - } - data[r][key] = val - mutex.Unlock() -} - -// Get returns a value stored for a given key in a given request. -func Get(r *http.Request, key interface{}) interface{} { - mutex.RLock() - if ctx := data[r]; ctx != nil { - value := ctx[key] - mutex.RUnlock() - return value - } - mutex.RUnlock() - return nil -} - -// GetOk returns stored value and presence state like multi-value return of map access. -func GetOk(r *http.Request, key interface{}) (interface{}, bool) { - mutex.RLock() - if _, ok := data[r]; ok { - value, ok := data[r][key] - mutex.RUnlock() - return value, ok - } - mutex.RUnlock() - return nil, false -} - -// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests. -func GetAll(r *http.Request) map[interface{}]interface{} { - mutex.RLock() - if context, ok := data[r]; ok { - result := make(map[interface{}]interface{}, len(context)) - for k, v := range context { - result[k] = v - } - mutex.RUnlock() - return result - } - mutex.RUnlock() - return nil -} - -// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if -// the request was registered. -func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) { - mutex.RLock() - context, ok := data[r] - result := make(map[interface{}]interface{}, len(context)) - for k, v := range context { - result[k] = v - } - mutex.RUnlock() - return result, ok -} - -// Delete removes a value stored for a given key in a given request. -func Delete(r *http.Request, key interface{}) { - mutex.Lock() - if data[r] != nil { - delete(data[r], key) - } - mutex.Unlock() -} - -// Clear removes all values stored for a given request. -// -// This is usually called by a handler wrapper to clean up request -// variables at the end of a request lifetime. See ClearHandler(). -func Clear(r *http.Request) { - mutex.Lock() - clear(r) - mutex.Unlock() -} - -// clear is Clear without the lock. -func clear(r *http.Request) { - delete(data, r) - delete(datat, r) -} - -// Purge removes request data stored for longer than maxAge, in seconds. -// It returns the amount of requests removed. -// -// If maxAge <= 0, all request data is removed. -// -// This is only used for sanity check: in case context cleaning was not -// properly set some request data can be kept forever, consuming an increasing -// amount of memory. In case this is detected, Purge() must be called -// periodically until the problem is fixed. -func Purge(maxAge int) int { - mutex.Lock() - count := 0 - if maxAge <= 0 { - count = len(data) - data = make(map[*http.Request]map[interface{}]interface{}) - datat = make(map[*http.Request]int64) - } else { - min := time.Now().Unix() - int64(maxAge) - for r := range data { - if datat[r] < min { - clear(r) - count++ - } - } - } - mutex.Unlock() - return count -} - -// ClearHandler wraps an http.Handler and clears request values at the end -// of a request lifetime. -func ClearHandler(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - defer Clear(r) - h.ServeHTTP(w, r) - }) -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/context_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/context_test.go deleted file mode 100644 index 9814c501e..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/context_test.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2012 The Gorilla 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 context - -import ( - "net/http" - "testing" -) - -type keyType int - -const ( - key1 keyType = iota - key2 -) - -func TestContext(t *testing.T) { - assertEqual := func(val interface{}, exp interface{}) { - if val != exp { - t.Errorf("Expected %v, got %v.", exp, val) - } - } - - r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) - emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil) - - // Get() - assertEqual(Get(r, key1), nil) - - // Set() - Set(r, key1, "1") - assertEqual(Get(r, key1), "1") - assertEqual(len(data[r]), 1) - - Set(r, key2, "2") - assertEqual(Get(r, key2), "2") - assertEqual(len(data[r]), 2) - - //GetOk - value, ok := GetOk(r, key1) - assertEqual(value, "1") - assertEqual(ok, true) - - value, ok = GetOk(r, "not exists") - assertEqual(value, nil) - assertEqual(ok, false) - - Set(r, "nil value", nil) - value, ok = GetOk(r, "nil value") - assertEqual(value, nil) - assertEqual(ok, true) - - // GetAll() - values := GetAll(r) - assertEqual(len(values), 3) - - // GetAll() for empty request - values = GetAll(emptyR) - if values != nil { - t.Error("GetAll didn't return nil value for invalid request") - } - - // GetAllOk() - values, ok = GetAllOk(r) - assertEqual(len(values), 3) - assertEqual(ok, true) - - // GetAllOk() for empty request - values, ok = GetAllOk(emptyR) - assertEqual(value, nil) - assertEqual(ok, false) - - // Delete() - Delete(r, key1) - assertEqual(Get(r, key1), nil) - assertEqual(len(data[r]), 2) - - Delete(r, key2) - assertEqual(Get(r, key2), nil) - assertEqual(len(data[r]), 1) - - // Clear() - Clear(r) - assertEqual(len(data), 0) -} - -func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) { - <-wait - for i := 0; i < iterations; i++ { - Get(r, key) - } - done <- struct{}{} - -} - -func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) { - <-wait - for i := 0; i < iterations; i++ { - Set(r, key, value) - } - done <- struct{}{} - -} - -func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) { - - b.StopTimer() - r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) - done := make(chan struct{}) - b.StartTimer() - - for i := 0; i < b.N; i++ { - wait := make(chan struct{}) - - for i := 0; i < numReaders; i++ { - go parallelReader(r, "test", iterations, wait, done) - } - - for i := 0; i < numWriters; i++ { - go parallelWriter(r, "test", "123", iterations, wait, done) - } - - close(wait) - - for i := 0; i < numReaders+numWriters; i++ { - <-done - } - - } - -} - -func BenchmarkMutexSameReadWrite1(b *testing.B) { - benchmarkMutex(b, 1, 1, 32) -} -func BenchmarkMutexSameReadWrite2(b *testing.B) { - benchmarkMutex(b, 2, 2, 32) -} -func BenchmarkMutexSameReadWrite4(b *testing.B) { - benchmarkMutex(b, 4, 4, 32) -} -func BenchmarkMutex1(b *testing.B) { - benchmarkMutex(b, 2, 8, 32) -} -func BenchmarkMutex2(b *testing.B) { - benchmarkMutex(b, 16, 4, 64) -} -func BenchmarkMutex3(b *testing.B) { - benchmarkMutex(b, 1, 2, 128) -} -func BenchmarkMutex4(b *testing.B) { - benchmarkMutex(b, 128, 32, 256) -} -func BenchmarkMutex5(b *testing.B) { - benchmarkMutex(b, 1024, 2048, 64) -} -func BenchmarkMutex6(b *testing.B) { - benchmarkMutex(b, 2048, 1024, 512) -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/doc.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/doc.go deleted file mode 100644 index 73c740031..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/doc.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2012 The Gorilla 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 context stores values shared during a request lifetime. - -For example, a router can set variables extracted from the URL and later -application handlers can access those values, or it can be used to store -sessions values to be saved at the end of a request. There are several -others common uses. - -The idea was posted by Brad Fitzpatrick to the go-nuts mailing list: - - http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53 - -Here's the basic usage: first define the keys that you will need. The key -type is interface{} so a key can be of any type that supports equality. -Here we define a key using a custom int type to avoid name collisions: - - package foo - - import ( - "github.com/gorilla/context" - ) - - type key int - - const MyKey key = 0 - -Then set a variable. Variables are bound to an http.Request object, so you -need a request instance to set a value: - - context.Set(r, MyKey, "bar") - -The application can later access the variable using the same key you provided: - - func MyHandler(w http.ResponseWriter, r *http.Request) { - // val is "bar". - val := context.Get(r, foo.MyKey) - - // returns ("bar", true) - val, ok := context.GetOk(r, foo.MyKey) - // ... - } - -And that's all about the basic usage. We discuss some other ideas below. - -Any type can be stored in the context. To enforce a given type, make the key -private and wrap Get() and Set() to accept and return values of a specific -type: - - type key int - - const mykey key = 0 - - // GetMyKey returns a value for this package from the request values. - func GetMyKey(r *http.Request) SomeType { - if rv := context.Get(r, mykey); rv != nil { - return rv.(SomeType) - } - return nil - } - - // SetMyKey sets a value for this package in the request values. - func SetMyKey(r *http.Request, val SomeType) { - context.Set(r, mykey, val) - } - -Variables must be cleared at the end of a request, to remove all values -that were stored. This can be done in an http.Handler, after a request was -served. Just call Clear() passing the request: - - context.Clear(r) - -...or use ClearHandler(), which conveniently wraps an http.Handler to clear -variables at the end of a request lifetime. - -The Routers from the packages gorilla/mux and gorilla/pat call Clear() -so if you are using either of them you don't need to clear the context manually. -*/ -package context diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/README.md b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/README.md deleted file mode 100644 index b987c9e5d..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/README.md +++ /dev/null @@ -1,240 +0,0 @@ -mux -=== -[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux) -[![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux) - -Package `gorilla/mux` implements a request router and dispatcher. - -The name mux stands for "HTTP request multiplexer". Like the standard `http.ServeMux`, `mux.Router` matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions. The main features are: - -* Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers. -* URL hosts and paths can have variables with an optional regular expression. -* Registered URLs can be built, or "reversed", which helps maintaining references to resources. -* Routes can be used as subrouters: nested routes are only tested if the parent route matches. This is useful to define groups of routes that share common conditions like a host, a path prefix or other repeated attributes. As a bonus, this optimizes request matching. -* It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`. - -Let's start registering a couple of URL paths and handlers: - -```go -func main() { - r := mux.NewRouter() - r.HandleFunc("/", HomeHandler) - r.HandleFunc("/products", ProductsHandler) - r.HandleFunc("/articles", ArticlesHandler) - http.Handle("/", r) -} -``` - -Here we register three routes mapping URL paths to handlers. This is equivalent to how `http.HandleFunc()` works: if an incoming request URL matches one of the paths, the corresponding handler is called passing (`http.ResponseWriter`, `*http.Request`) as parameters. - -Paths can have variables. They are defined using the format `{name}` or `{name:pattern}`. If a regular expression pattern is not defined, the matched variable will be anything until the next slash. For example: - -```go -r := mux.NewRouter() -r.HandleFunc("/products/{key}", ProductHandler) -r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) -r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) -``` - -The names are used to create a map of route variables which can be retrieved calling `mux.Vars()`: - -```go -vars := mux.Vars(request) -category := vars["category"] -``` - -And this is all you need to know about the basic usage. More advanced options are explained below. - -Routes can also be restricted to a domain or subdomain. Just define a host pattern to be matched. They can also have variables: - -```go -r := mux.NewRouter() -// Only matches if domain is "www.example.com". -r.Host("www.example.com") -// Matches a dynamic subdomain. -r.Host("{subdomain:[a-z]+}.domain.com") -``` - -There are several other matchers that can be added. To match path prefixes: - -```go -r.PathPrefix("/products/") -``` - -...or HTTP methods: - -```go -r.Methods("GET", "POST") -``` - -...or URL schemes: - -```go -r.Schemes("https") -``` - -...or header values: - -```go -r.Headers("X-Requested-With", "XMLHttpRequest") -``` - -...or query values: - -```go -r.Queries("key", "value") -``` - -...or to use a custom matcher function: - -```go -r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { - return r.ProtoMajor == 0 -}) -``` - -...and finally, it is possible to combine several matchers in a single route: - -```go -r.HandleFunc("/products", ProductsHandler). - Host("www.example.com"). - Methods("GET"). - Schemes("http") -``` - -Setting the same matching conditions again and again can be boring, so we have a way to group several routes that share the same requirements. We call it "subrouting". - -For example, let's say we have several URLs that should only match when the host is `www.example.com`. Create a route for that host and get a "subrouter" from it: - -```go -r := mux.NewRouter() -s := r.Host("www.example.com").Subrouter() -``` - -Then register routes in the subrouter: - -```go -s.HandleFunc("/products/", ProductsHandler) -s.HandleFunc("/products/{key}", ProductHandler) -s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) -``` - -The three URL paths we registered above will only be tested if the domain is `www.example.com`, because the subrouter is tested first. This is not only convenient, but also optimizes request matching. You can create subrouters combining any attribute matchers accepted by a route. - -Subrouters can be used to create domain or path "namespaces": you define subrouters in a central place and then parts of the app can register its paths relatively to a given subrouter. - -There's one more thing about subroutes. When a subrouter has a path prefix, the inner routes use it as base for their paths: - -```go -r := mux.NewRouter() -s := r.PathPrefix("/products").Subrouter() -// "/products/" -s.HandleFunc("/", ProductsHandler) -// "/products/{key}/" -s.HandleFunc("/{key}/", ProductHandler) -// "/products/{key}/details" -s.HandleFunc("/{key}/details", ProductDetailsHandler) -``` - -Now let's see how to build registered URLs. - -Routes can be named. All routes that define a name can have their URLs built, or "reversed". We define a name calling `Name()` on a route. For example: - -```go -r := mux.NewRouter() -r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). - Name("article") -``` - -To build a URL, get the route and call the `URL()` method, passing a sequence of key/value pairs for the route variables. For the previous route, we would do: - -```go -url, err := r.Get("article").URL("category", "technology", "id", "42") -``` - -...and the result will be a `url.URL` with the following path: - -``` -"/articles/technology/42" -``` - -This also works for host variables: - -```go -r := mux.NewRouter() -r.Host("{subdomain}.domain.com"). - Path("/articles/{category}/{id:[0-9]+}"). - HandlerFunc(ArticleHandler). - Name("article") - -// url.String() will be "http://news.domain.com/articles/technology/42" -url, err := r.Get("article").URL("subdomain", "news", - "category", "technology", - "id", "42") -``` - -All variables defined in the route are required, and their values must conform to the corresponding patterns. These requirements guarantee that a generated URL will always match a registered route -- the only exception is for explicitly defined "build-only" routes which never match. - -Regex support also exists for matching Headers within a route. For example, we could do: - -```go -r.HeadersRegexp("Content-Type", "application/(text|json)") -``` - -...and the route will match both requests with a Content-Type of `application/json` as well as `application/text` - -There's also a way to build only the URL host or path for a route: use the methods `URLHost()` or `URLPath()` instead. For the previous route, we would do: - -```go -// "http://news.domain.com/" -host, err := r.Get("article").URLHost("subdomain", "news") - -// "/articles/technology/42" -path, err := r.Get("article").URLPath("category", "technology", "id", "42") -``` - -And if you use subrouters, host and path defined separately can be built as well: - -```go -r := mux.NewRouter() -s := r.Host("{subdomain}.domain.com").Subrouter() -s.Path("/articles/{category}/{id:[0-9]+}"). - HandlerFunc(ArticleHandler). - Name("article") - -// "http://news.domain.com/articles/technology/42" -url, err := r.Get("article").URL("subdomain", "news", - "category", "technology", - "id", "42") -``` - -## Full Example - -Here's a complete, runnable example of a small `mux` based server: - -```go -package main - -import ( - "net/http" - - "github.com/gorilla/mux" -) - -func YourHandler(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("Gorilla!\n")) -} - -func main() { - r := mux.NewRouter() - // Routes consist of a path and a handler function. - r.HandleFunc("/", YourHandler) - - // Bind to a port and pass our router in - http.ListenAndServe(":8000", r) -} -``` - -## License - -BSD licensed. See the LICENSE file for details. diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/bench_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/bench_test.go deleted file mode 100644 index c5f97b2b2..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/bench_test.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2012 The Gorilla 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 mux - -import ( - "net/http" - "testing" -) - -func BenchmarkMux(b *testing.B) { - router := new(Router) - handler := func(w http.ResponseWriter, r *http.Request) {} - router.HandleFunc("/v1/{v1}", handler) - - request, _ := http.NewRequest("GET", "/v1/anything", nil) - for i := 0; i < b.N; i++ { - router.ServeHTTP(nil, request) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/doc.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/doc.go deleted file mode 100644 index 49798cb5c..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/doc.go +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2012 The Gorilla 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 gorilla/mux implements a request router and dispatcher. - -The name mux stands for "HTTP request multiplexer". Like the standard -http.ServeMux, mux.Router matches incoming requests against a list of -registered routes and calls a handler for the route that matches the URL -or other conditions. The main features are: - - * Requests can be matched based on URL host, path, path prefix, schemes, - header and query values, HTTP methods or using custom matchers. - * URL hosts and paths can have variables with an optional regular - expression. - * Registered URLs can be built, or "reversed", which helps maintaining - references to resources. - * Routes can be used as subrouters: nested routes are only tested if the - parent route matches. This is useful to define groups of routes that - share common conditions like a host, a path prefix or other repeated - attributes. As a bonus, this optimizes request matching. - * It implements the http.Handler interface so it is compatible with the - standard http.ServeMux. - -Let's start registering a couple of URL paths and handlers: - - func main() { - r := mux.NewRouter() - r.HandleFunc("/", HomeHandler) - r.HandleFunc("/products", ProductsHandler) - r.HandleFunc("/articles", ArticlesHandler) - http.Handle("/", r) - } - -Here we register three routes mapping URL paths to handlers. This is -equivalent to how http.HandleFunc() works: if an incoming request URL matches -one of the paths, the corresponding handler is called passing -(http.ResponseWriter, *http.Request) as parameters. - -Paths can have variables. They are defined using the format {name} or -{name:pattern}. If a regular expression pattern is not defined, the matched -variable will be anything until the next slash. For example: - - r := mux.NewRouter() - r.HandleFunc("/products/{key}", ProductHandler) - r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) - r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) - -The names are used to create a map of route variables which can be retrieved -calling mux.Vars(): - - vars := mux.Vars(request) - category := vars["category"] - -And this is all you need to know about the basic usage. More advanced options -are explained below. - -Routes can also be restricted to a domain or subdomain. Just define a host -pattern to be matched. They can also have variables: - - r := mux.NewRouter() - // Only matches if domain is "www.example.com". - r.Host("www.example.com") - // Matches a dynamic subdomain. - r.Host("{subdomain:[a-z]+}.domain.com") - -There are several other matchers that can be added. To match path prefixes: - - r.PathPrefix("/products/") - -...or HTTP methods: - - r.Methods("GET", "POST") - -...or URL schemes: - - r.Schemes("https") - -...or header values: - - r.Headers("X-Requested-With", "XMLHttpRequest") - -...or query values: - - r.Queries("key", "value") - -...or to use a custom matcher function: - - r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { - return r.ProtoMajor == 0 - }) - -...and finally, it is possible to combine several matchers in a single route: - - r.HandleFunc("/products", ProductsHandler). - Host("www.example.com"). - Methods("GET"). - Schemes("http") - -Setting the same matching conditions again and again can be boring, so we have -a way to group several routes that share the same requirements. -We call it "subrouting". - -For example, let's say we have several URLs that should only match when the -host is "www.example.com". Create a route for that host and get a "subrouter" -from it: - - r := mux.NewRouter() - s := r.Host("www.example.com").Subrouter() - -Then register routes in the subrouter: - - s.HandleFunc("/products/", ProductsHandler) - s.HandleFunc("/products/{key}", ProductHandler) - s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) - -The three URL paths we registered above will only be tested if the domain is -"www.example.com", because the subrouter is tested first. This is not -only convenient, but also optimizes request matching. You can create -subrouters combining any attribute matchers accepted by a route. - -Subrouters can be used to create domain or path "namespaces": you define -subrouters in a central place and then parts of the app can register its -paths relatively to a given subrouter. - -There's one more thing about subroutes. When a subrouter has a path prefix, -the inner routes use it as base for their paths: - - r := mux.NewRouter() - s := r.PathPrefix("/products").Subrouter() - // "/products/" - s.HandleFunc("/", ProductsHandler) - // "/products/{key}/" - s.HandleFunc("/{key}/", ProductHandler) - // "/products/{key}/details" - s.HandleFunc("/{key}/details", ProductDetailsHandler) - -Now let's see how to build registered URLs. - -Routes can be named. All routes that define a name can have their URLs built, -or "reversed". We define a name calling Name() on a route. For example: - - r := mux.NewRouter() - r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). - Name("article") - -To build a URL, get the route and call the URL() method, passing a sequence of -key/value pairs for the route variables. For the previous route, we would do: - - url, err := r.Get("article").URL("category", "technology", "id", "42") - -...and the result will be a url.URL with the following path: - - "/articles/technology/42" - -This also works for host variables: - - r := mux.NewRouter() - r.Host("{subdomain}.domain.com"). - Path("/articles/{category}/{id:[0-9]+}"). - HandlerFunc(ArticleHandler). - Name("article") - - // url.String() will be "http://news.domain.com/articles/technology/42" - url, err := r.Get("article").URL("subdomain", "news", - "category", "technology", - "id", "42") - -All variables defined in the route are required, and their values must -conform to the corresponding patterns. These requirements guarantee that a -generated URL will always match a registered route -- the only exception is -for explicitly defined "build-only" routes which never match. - -Regex support also exists for matching Headers within a route. For example, we could do: - - r.HeadersRegexp("Content-Type", "application/(text|json)") - -...and the route will match both requests with a Content-Type of `application/json` as well as -`application/text` - -There's also a way to build only the URL host or path for a route: -use the methods URLHost() or URLPath() instead. For the previous route, -we would do: - - // "http://news.domain.com/" - host, err := r.Get("article").URLHost("subdomain", "news") - - // "/articles/technology/42" - path, err := r.Get("article").URLPath("category", "technology", "id", "42") - -And if you use subrouters, host and path defined separately can be built -as well: - - r := mux.NewRouter() - s := r.Host("{subdomain}.domain.com").Subrouter() - s.Path("/articles/{category}/{id:[0-9]+}"). - HandlerFunc(ArticleHandler). - Name("article") - - // "http://news.domain.com/articles/technology/42" - url, err := r.Get("article").URL("subdomain", "news", - "category", "technology", - "id", "42") -*/ -package mux diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/mux.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/mux.go deleted file mode 100644 index cb03ddfe5..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/mux.go +++ /dev/null @@ -1,481 +0,0 @@ -// Copyright 2012 The Gorilla 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 mux - -import ( - "errors" - "fmt" - "net/http" - "path" - "regexp" - - "github.com/fsouza/go-dockerclient/external/github.com/gorilla/context" -) - -// NewRouter returns a new router instance. -func NewRouter() *Router { - return &Router{namedRoutes: make(map[string]*Route), KeepContext: false} -} - -// Router registers routes to be matched and dispatches a handler. -// -// It implements the http.Handler interface, so it can be registered to serve -// requests: -// -// var router = mux.NewRouter() -// -// func main() { -// http.Handle("/", router) -// } -// -// Or, for Google App Engine, register it in a init() function: -// -// func init() { -// http.Handle("/", router) -// } -// -// This will send all incoming requests to the router. -type Router struct { - // Configurable Handler to be used when no route matches. - NotFoundHandler http.Handler - // Parent route, if this is a subrouter. - parent parentRoute - // Routes to be matched, in order. - routes []*Route - // Routes by name for URL building. - namedRoutes map[string]*Route - // See Router.StrictSlash(). This defines the flag for new routes. - strictSlash bool - // If true, do not clear the request context after handling the request - KeepContext bool -} - -// Match matches registered routes against the request. -func (r *Router) Match(req *http.Request, match *RouteMatch) bool { - for _, route := range r.routes { - if route.Match(req, match) { - return true - } - } - - // Closest match for a router (includes sub-routers) - if r.NotFoundHandler != nil { - match.Handler = r.NotFoundHandler - return true - } - return false -} - -// ServeHTTP dispatches the handler registered in the matched route. -// -// When there is a match, the route variables can be retrieved calling -// mux.Vars(request). -func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { - // Clean path to canonical form and redirect. - if p := cleanPath(req.URL.Path); p != req.URL.Path { - - // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query. - // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue: - // http://code.google.com/p/go/issues/detail?id=5252 - url := *req.URL - url.Path = p - p = url.String() - - w.Header().Set("Location", p) - w.WriteHeader(http.StatusMovedPermanently) - return - } - var match RouteMatch - var handler http.Handler - if r.Match(req, &match) { - handler = match.Handler - setVars(req, match.Vars) - setCurrentRoute(req, match.Route) - } - if handler == nil { - handler = http.NotFoundHandler() - } - if !r.KeepContext { - defer context.Clear(req) - } - handler.ServeHTTP(w, req) -} - -// Get returns a route registered with the given name. -func (r *Router) Get(name string) *Route { - return r.getNamedRoutes()[name] -} - -// GetRoute returns a route registered with the given name. This method -// was renamed to Get() and remains here for backwards compatibility. -func (r *Router) GetRoute(name string) *Route { - return r.getNamedRoutes()[name] -} - -// StrictSlash defines the trailing slash behavior for new routes. The initial -// value is false. -// -// When true, if the route path is "/path/", accessing "/path" will redirect -// to the former and vice versa. In other words, your application will always -// see the path as specified in the route. -// -// When false, if the route path is "/path", accessing "/path/" will not match -// this route and vice versa. -// -// Special case: when a route sets a path prefix using the PathPrefix() method, -// strict slash is ignored for that route because the redirect behavior can't -// be determined from a prefix alone. However, any subrouters created from that -// route inherit the original StrictSlash setting. -func (r *Router) StrictSlash(value bool) *Router { - r.strictSlash = value - return r -} - -// ---------------------------------------------------------------------------- -// parentRoute -// ---------------------------------------------------------------------------- - -// getNamedRoutes returns the map where named routes are registered. -func (r *Router) getNamedRoutes() map[string]*Route { - if r.namedRoutes == nil { - if r.parent != nil { - r.namedRoutes = r.parent.getNamedRoutes() - } else { - r.namedRoutes = make(map[string]*Route) - } - } - return r.namedRoutes -} - -// getRegexpGroup returns regexp definitions from the parent route, if any. -func (r *Router) getRegexpGroup() *routeRegexpGroup { - if r.parent != nil { - return r.parent.getRegexpGroup() - } - return nil -} - -func (r *Router) buildVars(m map[string]string) map[string]string { - if r.parent != nil { - m = r.parent.buildVars(m) - } - return m -} - -// ---------------------------------------------------------------------------- -// Route factories -// ---------------------------------------------------------------------------- - -// NewRoute registers an empty route. -func (r *Router) NewRoute() *Route { - route := &Route{parent: r, strictSlash: r.strictSlash} - r.routes = append(r.routes, route) - return route -} - -// Handle registers a new route with a matcher for the URL path. -// See Route.Path() and Route.Handler(). -func (r *Router) Handle(path string, handler http.Handler) *Route { - return r.NewRoute().Path(path).Handler(handler) -} - -// HandleFunc registers a new route with a matcher for the URL path. -// See Route.Path() and Route.HandlerFunc(). -func (r *Router) HandleFunc(path string, f func(http.ResponseWriter, - *http.Request)) *Route { - return r.NewRoute().Path(path).HandlerFunc(f) -} - -// Headers registers a new route with a matcher for request header values. -// See Route.Headers(). -func (r *Router) Headers(pairs ...string) *Route { - return r.NewRoute().Headers(pairs...) -} - -// Host registers a new route with a matcher for the URL host. -// See Route.Host(). -func (r *Router) Host(tpl string) *Route { - return r.NewRoute().Host(tpl) -} - -// MatcherFunc registers a new route with a custom matcher function. -// See Route.MatcherFunc(). -func (r *Router) MatcherFunc(f MatcherFunc) *Route { - return r.NewRoute().MatcherFunc(f) -} - -// Methods registers a new route with a matcher for HTTP methods. -// See Route.Methods(). -func (r *Router) Methods(methods ...string) *Route { - return r.NewRoute().Methods(methods...) -} - -// Path registers a new route with a matcher for the URL path. -// See Route.Path(). -func (r *Router) Path(tpl string) *Route { - return r.NewRoute().Path(tpl) -} - -// PathPrefix registers a new route with a matcher for the URL path prefix. -// See Route.PathPrefix(). -func (r *Router) PathPrefix(tpl string) *Route { - return r.NewRoute().PathPrefix(tpl) -} - -// Queries registers a new route with a matcher for URL query values. -// See Route.Queries(). -func (r *Router) Queries(pairs ...string) *Route { - return r.NewRoute().Queries(pairs...) -} - -// Schemes registers a new route with a matcher for URL schemes. -// See Route.Schemes(). -func (r *Router) Schemes(schemes ...string) *Route { - return r.NewRoute().Schemes(schemes...) -} - -// BuildVars registers a new route with a custom function for modifying -// route variables before building a URL. -func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route { - return r.NewRoute().BuildVarsFunc(f) -} - -// Walk walks the router and all its sub-routers, calling walkFn for each route -// in the tree. The routes are walked in the order they were added. Sub-routers -// are explored depth-first. -func (r *Router) Walk(walkFn WalkFunc) error { - return r.walk(walkFn, []*Route{}) -} - -// SkipRouter is used as a return value from WalkFuncs to indicate that the -// router that walk is about to descend down to should be skipped. -var SkipRouter = errors.New("skip this router") - -// WalkFunc is the type of the function called for each route visited by Walk. -// At every invocation, it is given the current route, and the current router, -// and a list of ancestor routes that lead to the current route. -type WalkFunc func(route *Route, router *Router, ancestors []*Route) error - -func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error { - for _, t := range r.routes { - if t.regexp == nil || t.regexp.path == nil || t.regexp.path.template == "" { - continue - } - - err := walkFn(t, r, ancestors) - if err == SkipRouter { - continue - } - for _, sr := range t.matchers { - if h, ok := sr.(*Router); ok { - err := h.walk(walkFn, ancestors) - if err != nil { - return err - } - } - } - if h, ok := t.handler.(*Router); ok { - ancestors = append(ancestors, t) - err := h.walk(walkFn, ancestors) - if err != nil { - return err - } - ancestors = ancestors[:len(ancestors)-1] - } - } - return nil -} - -// ---------------------------------------------------------------------------- -// Context -// ---------------------------------------------------------------------------- - -// RouteMatch stores information about a matched route. -type RouteMatch struct { - Route *Route - Handler http.Handler - Vars map[string]string -} - -type contextKey int - -const ( - varsKey contextKey = iota - routeKey -) - -// Vars returns the route variables for the current request, if any. -func Vars(r *http.Request) map[string]string { - if rv := context.Get(r, varsKey); rv != nil { - return rv.(map[string]string) - } - return nil -} - -// CurrentRoute returns the matched route for the current request, if any. -// This only works when called inside the handler of the matched route -// because the matched route is stored in the request context which is cleared -// after the handler returns, unless the KeepContext option is set on the -// Router. -func CurrentRoute(r *http.Request) *Route { - if rv := context.Get(r, routeKey); rv != nil { - return rv.(*Route) - } - return nil -} - -func setVars(r *http.Request, val interface{}) { - if val != nil { - context.Set(r, varsKey, val) - } -} - -func setCurrentRoute(r *http.Request, val interface{}) { - if val != nil { - context.Set(r, routeKey, val) - } -} - -// ---------------------------------------------------------------------------- -// Helpers -// ---------------------------------------------------------------------------- - -// cleanPath returns the canonical path for p, eliminating . and .. elements. -// Borrowed from the net/http package. -func cleanPath(p string) string { - if p == "" { - return "/" - } - if p[0] != '/' { - p = "/" + p - } - np := path.Clean(p) - // path.Clean removes trailing slash except for root; - // put the trailing slash back if necessary. - if p[len(p)-1] == '/' && np != "/" { - np += "/" - } - return np -} - -// uniqueVars returns an error if two slices contain duplicated strings. -func uniqueVars(s1, s2 []string) error { - for _, v1 := range s1 { - for _, v2 := range s2 { - if v1 == v2 { - return fmt.Errorf("mux: duplicated route variable %q", v2) - } - } - } - return nil -} - -// checkPairs returns the count of strings passed in, and an error if -// the count is not an even number. -func checkPairs(pairs ...string) (int, error) { - length := len(pairs) - if length%2 != 0 { - return length, fmt.Errorf( - "mux: number of parameters must be multiple of 2, got %v", pairs) - } - return length, nil -} - -// mapFromPairsToString converts variadic string parameters to a -// string to string map. -func mapFromPairsToString(pairs ...string) (map[string]string, error) { - length, err := checkPairs(pairs...) - if err != nil { - return nil, err - } - m := make(map[string]string, length/2) - for i := 0; i < length; i += 2 { - m[pairs[i]] = pairs[i+1] - } - return m, nil -} - -// mapFromPairsToRegex converts variadic string paramers to a -// string to regex map. -func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) { - length, err := checkPairs(pairs...) - if err != nil { - return nil, err - } - m := make(map[string]*regexp.Regexp, length/2) - for i := 0; i < length; i += 2 { - regex, err := regexp.Compile(pairs[i+1]) - if err != nil { - return nil, err - } - m[pairs[i]] = regex - } - return m, nil -} - -// matchInArray returns true if the given string value is in the array. -func matchInArray(arr []string, value string) bool { - for _, v := range arr { - if v == value { - return true - } - } - return false -} - -// matchMapWithString returns true if the given key/value pairs exist in a given map. -func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool { - for k, v := range toCheck { - // Check if key exists. - if canonicalKey { - k = http.CanonicalHeaderKey(k) - } - if values := toMatch[k]; values == nil { - return false - } else if v != "" { - // If value was defined as an empty string we only check that the - // key exists. Otherwise we also check for equality. - valueExists := false - for _, value := range values { - if v == value { - valueExists = true - break - } - } - if !valueExists { - return false - } - } - } - return true -} - -// matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against -// the given regex -func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool { - for k, v := range toCheck { - // Check if key exists. - if canonicalKey { - k = http.CanonicalHeaderKey(k) - } - if values := toMatch[k]; values == nil { - return false - } else if v != nil { - // If value was defined as an empty string we only check that the - // key exists. Otherwise we also check for equality. - valueExists := false - for _, value := range values { - if v.MatchString(value) { - valueExists = true - break - } - } - if !valueExists { - return false - } - } - } - return true -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/mux_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/mux_test.go deleted file mode 100644 index 98f73fe9c..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/mux_test.go +++ /dev/null @@ -1,1358 +0,0 @@ -// Copyright 2012 The Gorilla 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 mux - -import ( - "fmt" - "net/http" - "strings" - "testing" - - "github.com/fsouza/go-dockerclient/external/github.com/gorilla/context" -) - -func (r *Route) GoString() string { - matchers := make([]string, len(r.matchers)) - for i, m := range r.matchers { - matchers[i] = fmt.Sprintf("%#v", m) - } - return fmt.Sprintf("&Route{matchers:[]matcher{%s}}", strings.Join(matchers, ", ")) -} - -func (r *routeRegexp) GoString() string { - return fmt.Sprintf("&routeRegexp{template: %q, matchHost: %t, matchQuery: %t, strictSlash: %t, regexp: regexp.MustCompile(%q), reverse: %q, varsN: %v, varsR: %v", r.template, r.matchHost, r.matchQuery, r.strictSlash, r.regexp.String(), r.reverse, r.varsN, r.varsR) -} - -type routeTest struct { - title string // title of the test - route *Route // the route being tested - request *http.Request // a request to test the route - vars map[string]string // the expected vars of the match - host string // the expected host of the match - path string // the expected path of the match - shouldMatch bool // whether the request is expected to match the route at all - shouldRedirect bool // whether the request should result in a redirect -} - -func TestHost(t *testing.T) { - // newRequestHost a new request with a method, url, and host header - newRequestHost := func(method, url, host string) *http.Request { - req, err := http.NewRequest(method, url, nil) - if err != nil { - panic(err) - } - req.Host = host - return req - } - - tests := []routeTest{ - { - title: "Host route match", - route: new(Route).Host("aaa.bbb.ccc"), - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), - vars: map[string]string{}, - host: "aaa.bbb.ccc", - path: "", - shouldMatch: true, - }, - { - title: "Host route, wrong host in request URL", - route: new(Route).Host("aaa.bbb.ccc"), - request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), - vars: map[string]string{}, - host: "aaa.bbb.ccc", - path: "", - shouldMatch: false, - }, - { - title: "Host route with port, match", - route: new(Route).Host("aaa.bbb.ccc:1234"), - request: newRequest("GET", "http://aaa.bbb.ccc:1234/111/222/333"), - vars: map[string]string{}, - host: "aaa.bbb.ccc:1234", - path: "", - shouldMatch: true, - }, - { - title: "Host route with port, wrong port in request URL", - route: new(Route).Host("aaa.bbb.ccc:1234"), - request: newRequest("GET", "http://aaa.bbb.ccc:9999/111/222/333"), - vars: map[string]string{}, - host: "aaa.bbb.ccc:1234", - path: "", - shouldMatch: false, - }, - { - title: "Host route, match with host in request header", - route: new(Route).Host("aaa.bbb.ccc"), - request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc"), - vars: map[string]string{}, - host: "aaa.bbb.ccc", - path: "", - shouldMatch: true, - }, - { - title: "Host route, wrong host in request header", - route: new(Route).Host("aaa.bbb.ccc"), - request: newRequestHost("GET", "/111/222/333", "aaa.222.ccc"), - vars: map[string]string{}, - host: "aaa.bbb.ccc", - path: "", - shouldMatch: false, - }, - // BUG {new(Route).Host("aaa.bbb.ccc:1234"), newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:1234"), map[string]string{}, "aaa.bbb.ccc:1234", "", true}, - { - title: "Host route with port, wrong host in request header", - route: new(Route).Host("aaa.bbb.ccc:1234"), - request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:9999"), - vars: map[string]string{}, - host: "aaa.bbb.ccc:1234", - path: "", - shouldMatch: false, - }, - { - title: "Host route with pattern, match", - route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"), - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), - vars: map[string]string{"v1": "bbb"}, - host: "aaa.bbb.ccc", - path: "", - shouldMatch: true, - }, - { - title: "Host route with pattern, additional capturing group, match", - route: new(Route).Host("aaa.{v1:[a-z]{2}(b|c)}.ccc"), - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), - vars: map[string]string{"v1": "bbb"}, - host: "aaa.bbb.ccc", - path: "", - shouldMatch: true, - }, - { - title: "Host route with pattern, wrong host in request URL", - route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"), - request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), - vars: map[string]string{"v1": "bbb"}, - host: "aaa.bbb.ccc", - path: "", - shouldMatch: false, - }, - { - title: "Host route with multiple patterns, match", - route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"), - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), - vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"}, - host: "aaa.bbb.ccc", - path: "", - shouldMatch: true, - }, - { - title: "Host route with multiple patterns, wrong host in request URL", - route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"), - request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), - vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"}, - host: "aaa.bbb.ccc", - path: "", - shouldMatch: false, - }, - { - title: "Host route with hyphenated name and pattern, match", - route: new(Route).Host("aaa.{v-1:[a-z]{3}}.ccc"), - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), - vars: map[string]string{"v-1": "bbb"}, - host: "aaa.bbb.ccc", - path: "", - shouldMatch: true, - }, - { - title: "Host route with hyphenated name and pattern, additional capturing group, match", - route: new(Route).Host("aaa.{v-1:[a-z]{2}(b|c)}.ccc"), - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), - vars: map[string]string{"v-1": "bbb"}, - host: "aaa.bbb.ccc", - path: "", - shouldMatch: true, - }, - { - title: "Host route with multiple hyphenated names and patterns, match", - route: new(Route).Host("{v-1:[a-z]{3}}.{v-2:[a-z]{3}}.{v-3:[a-z]{3}}"), - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), - vars: map[string]string{"v-1": "aaa", "v-2": "bbb", "v-3": "ccc"}, - host: "aaa.bbb.ccc", - path: "", - shouldMatch: true, - }, - { - title: "Path route with single pattern with pipe, match", - route: new(Route).Path("/{category:a|b/c}"), - request: newRequest("GET", "http://localhost/a"), - vars: map[string]string{"category": "a"}, - host: "", - path: "/a", - shouldMatch: true, - }, - { - title: "Path route with single pattern with pipe, match", - route: new(Route).Path("/{category:a|b/c}"), - request: newRequest("GET", "http://localhost/b/c"), - vars: map[string]string{"category": "b/c"}, - host: "", - path: "/b/c", - shouldMatch: true, - }, - { - title: "Path route with multiple patterns with pipe, match", - route: new(Route).Path("/{category:a|b/c}/{product}/{id:[0-9]+}"), - request: newRequest("GET", "http://localhost/a/product_name/1"), - vars: map[string]string{"category": "a", "product": "product_name", "id": "1"}, - host: "", - path: "/a/product_name/1", - shouldMatch: true, - }, - { - title: "Path route with multiple patterns with pipe, match", - route: new(Route).Path("/{category:a|b/c}/{product}/{id:[0-9]+}"), - request: newRequest("GET", "http://localhost/b/c/product_name/1"), - vars: map[string]string{"category": "b/c", "product": "product_name", "id": "1"}, - host: "", - path: "/b/c/product_name/1", - shouldMatch: true, - }, - } - for _, test := range tests { - testRoute(t, test) - } -} - -func TestPath(t *testing.T) { - tests := []routeTest{ - { - title: "Path route, match", - route: new(Route).Path("/111/222/333"), - request: newRequest("GET", "http://localhost/111/222/333"), - vars: map[string]string{}, - host: "", - path: "/111/222/333", - shouldMatch: true, - }, - { - title: "Path route, match with trailing slash in request and path", - route: new(Route).Path("/111/"), - request: newRequest("GET", "http://localhost/111/"), - vars: map[string]string{}, - host: "", - path: "/111/", - shouldMatch: true, - }, - { - title: "Path route, do not match with trailing slash in path", - route: new(Route).Path("/111/"), - request: newRequest("GET", "http://localhost/111"), - vars: map[string]string{}, - host: "", - path: "/111", - shouldMatch: false, - }, - { - title: "Path route, do not match with trailing slash in request", - route: new(Route).Path("/111"), - request: newRequest("GET", "http://localhost/111/"), - vars: map[string]string{}, - host: "", - path: "/111/", - shouldMatch: false, - }, - { - title: "Path route, wrong path in request in request URL", - route: new(Route).Path("/111/222/333"), - request: newRequest("GET", "http://localhost/1/2/3"), - vars: map[string]string{}, - host: "", - path: "/111/222/333", - shouldMatch: false, - }, - { - title: "Path route with pattern, match", - route: new(Route).Path("/111/{v1:[0-9]{3}}/333"), - request: newRequest("GET", "http://localhost/111/222/333"), - vars: map[string]string{"v1": "222"}, - host: "", - path: "/111/222/333", - shouldMatch: true, - }, - { - title: "Path route with pattern, URL in request does not match", - route: new(Route).Path("/111/{v1:[0-9]{3}}/333"), - request: newRequest("GET", "http://localhost/111/aaa/333"), - vars: map[string]string{"v1": "222"}, - host: "", - path: "/111/222/333", - shouldMatch: false, - }, - { - title: "Path route with multiple patterns, match", - route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"), - request: newRequest("GET", "http://localhost/111/222/333"), - vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"}, - host: "", - path: "/111/222/333", - shouldMatch: true, - }, - { - title: "Path route with multiple patterns, URL in request does not match", - route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"), - request: newRequest("GET", "http://localhost/111/aaa/333"), - vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"}, - host: "", - path: "/111/222/333", - shouldMatch: false, - }, - { - title: "Path route with multiple patterns with pipe, match", - route: new(Route).Path("/{category:a|(b/c)}/{product}/{id:[0-9]+}"), - request: newRequest("GET", "http://localhost/a/product_name/1"), - vars: map[string]string{"category": "a", "product": "product_name", "id": "1"}, - host: "", - path: "/a/product_name/1", - shouldMatch: true, - }, - { - title: "Path route with hyphenated name and pattern, match", - route: new(Route).Path("/111/{v-1:[0-9]{3}}/333"), - request: newRequest("GET", "http://localhost/111/222/333"), - vars: map[string]string{"v-1": "222"}, - host: "", - path: "/111/222/333", - shouldMatch: true, - }, - { - title: "Path route with multiple hyphenated names and patterns, match", - route: new(Route).Path("/{v-1:[0-9]{3}}/{v-2:[0-9]{3}}/{v-3:[0-9]{3}}"), - request: newRequest("GET", "http://localhost/111/222/333"), - vars: map[string]string{"v-1": "111", "v-2": "222", "v-3": "333"}, - host: "", - path: "/111/222/333", - shouldMatch: true, - }, - { - title: "Path route with multiple hyphenated names and patterns with pipe, match", - route: new(Route).Path("/{product-category:a|(b/c)}/{product-name}/{product-id:[0-9]+}"), - request: newRequest("GET", "http://localhost/a/product_name/1"), - vars: map[string]string{"product-category": "a", "product-name": "product_name", "product-id": "1"}, - host: "", - path: "/a/product_name/1", - shouldMatch: true, - }, - } - - for _, test := range tests { - testRoute(t, test) - } -} - -func TestPathPrefix(t *testing.T) { - tests := []routeTest{ - { - title: "PathPrefix route, match", - route: new(Route).PathPrefix("/111"), - request: newRequest("GET", "http://localhost/111/222/333"), - vars: map[string]string{}, - host: "", - path: "/111", - shouldMatch: true, - }, - { - title: "PathPrefix route, match substring", - route: new(Route).PathPrefix("/1"), - request: newRequest("GET", "http://localhost/111/222/333"), - vars: map[string]string{}, - host: "", - path: "/1", - shouldMatch: true, - }, - { - title: "PathPrefix route, URL prefix in request does not match", - route: new(Route).PathPrefix("/111"), - request: newRequest("GET", "http://localhost/1/2/3"), - vars: map[string]string{}, - host: "", - path: "/111", - shouldMatch: false, - }, - { - title: "PathPrefix route with pattern, match", - route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"), - request: newRequest("GET", "http://localhost/111/222/333"), - vars: map[string]string{"v1": "222"}, - host: "", - path: "/111/222", - shouldMatch: true, - }, - { - title: "PathPrefix route with pattern, URL prefix in request does not match", - route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"), - request: newRequest("GET", "http://localhost/111/aaa/333"), - vars: map[string]string{"v1": "222"}, - host: "", - path: "/111/222", - shouldMatch: false, - }, - { - title: "PathPrefix route with multiple patterns, match", - route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"), - request: newRequest("GET", "http://localhost/111/222/333"), - vars: map[string]string{"v1": "111", "v2": "222"}, - host: "", - path: "/111/222", - shouldMatch: true, - }, - { - title: "PathPrefix route with multiple patterns, URL prefix in request does not match", - route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"), - request: newRequest("GET", "http://localhost/111/aaa/333"), - vars: map[string]string{"v1": "111", "v2": "222"}, - host: "", - path: "/111/222", - shouldMatch: false, - }, - } - - for _, test := range tests { - testRoute(t, test) - } -} - -func TestHostPath(t *testing.T) { - tests := []routeTest{ - { - title: "Host and Path route, match", - route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"), - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Host and Path route, wrong host in request URL", - route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"), - request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Host and Path route with pattern, match", - route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"), - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), - vars: map[string]string{"v1": "bbb", "v2": "222"}, - host: "aaa.bbb.ccc", - path: "/111/222/333", - shouldMatch: true, - }, - { - title: "Host and Path route with pattern, URL in request does not match", - route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"), - request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), - vars: map[string]string{"v1": "bbb", "v2": "222"}, - host: "aaa.bbb.ccc", - path: "/111/222/333", - shouldMatch: false, - }, - { - title: "Host and Path route with multiple patterns, match", - route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"), - request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), - vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"}, - host: "aaa.bbb.ccc", - path: "/111/222/333", - shouldMatch: true, - }, - { - title: "Host and Path route with multiple patterns, URL in request does not match", - route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"), - request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), - vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"}, - host: "aaa.bbb.ccc", - path: "/111/222/333", - shouldMatch: false, - }, - } - - for _, test := range tests { - testRoute(t, test) - } -} - -func TestHeaders(t *testing.T) { - // newRequestHeaders creates a new request with a method, url, and headers - newRequestHeaders := func(method, url string, headers map[string]string) *http.Request { - req, err := http.NewRequest(method, url, nil) - if err != nil { - panic(err) - } - for k, v := range headers { - req.Header.Add(k, v) - } - return req - } - - tests := []routeTest{ - { - title: "Headers route, match", - route: new(Route).Headers("foo", "bar", "baz", "ding"), - request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "ding"}), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Headers route, bad header values", - route: new(Route).Headers("foo", "bar", "baz", "ding"), - request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "dong"}), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Headers route, regex header values to match", - route: new(Route).Headers("foo", "ba[zr]"), - request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar"}), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Headers route, regex header values to match", - route: new(Route).HeadersRegexp("foo", "ba[zr]"), - request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "baz"}), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: true, - }, - } - - for _, test := range tests { - testRoute(t, test) - } - -} - -func TestMethods(t *testing.T) { - tests := []routeTest{ - { - title: "Methods route, match GET", - route: new(Route).Methods("GET", "POST"), - request: newRequest("GET", "http://localhost"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Methods route, match POST", - route: new(Route).Methods("GET", "POST"), - request: newRequest("POST", "http://localhost"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Methods route, bad method", - route: new(Route).Methods("GET", "POST"), - request: newRequest("PUT", "http://localhost"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - } - - for _, test := range tests { - testRoute(t, test) - } -} - -func TestQueries(t *testing.T) { - tests := []routeTest{ - { - title: "Queries route, match", - route: new(Route).Queries("foo", "bar", "baz", "ding"), - request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route, match with a query string", - route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"), - request: newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route, match with a query string out of order", - route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"), - request: newRequest("GET", "http://www.example.com/api?baz=ding&foo=bar"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route, bad query", - route: new(Route).Queries("foo", "bar", "baz", "ding"), - request: newRequest("GET", "http://localhost?foo=bar&baz=dong"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Queries route with pattern, match", - route: new(Route).Queries("foo", "{v1}"), - request: newRequest("GET", "http://localhost?foo=bar"), - vars: map[string]string{"v1": "bar"}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route with multiple patterns, match", - route: new(Route).Queries("foo", "{v1}", "baz", "{v2}"), - request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), - vars: map[string]string{"v1": "bar", "v2": "ding"}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route with regexp pattern, match", - route: new(Route).Queries("foo", "{v1:[0-9]+}"), - request: newRequest("GET", "http://localhost?foo=10"), - vars: map[string]string{"v1": "10"}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route with regexp pattern, regexp does not match", - route: new(Route).Queries("foo", "{v1:[0-9]+}"), - request: newRequest("GET", "http://localhost?foo=a"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Queries route with regexp pattern with quantifier, match", - route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), - request: newRequest("GET", "http://localhost?foo=1"), - vars: map[string]string{"v1": "1"}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route with regexp pattern with quantifier, additional variable in query string, match", - route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), - request: newRequest("GET", "http://localhost?bar=2&foo=1"), - vars: map[string]string{"v1": "1"}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route with regexp pattern with quantifier, regexp does not match", - route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), - request: newRequest("GET", "http://localhost?foo=12"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Queries route with regexp pattern with quantifier, additional capturing group", - route: new(Route).Queries("foo", "{v1:[0-9]{1}(a|b)}"), - request: newRequest("GET", "http://localhost?foo=1a"), - vars: map[string]string{"v1": "1a"}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route with regexp pattern with quantifier, additional variable in query string, regexp does not match", - route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), - request: newRequest("GET", "http://localhost?foo=12"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Queries route with hyphenated name, match", - route: new(Route).Queries("foo", "{v-1}"), - request: newRequest("GET", "http://localhost?foo=bar"), - vars: map[string]string{"v-1": "bar"}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route with multiple hyphenated names, match", - route: new(Route).Queries("foo", "{v-1}", "baz", "{v-2}"), - request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), - vars: map[string]string{"v-1": "bar", "v-2": "ding"}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route with hyphenate name and pattern, match", - route: new(Route).Queries("foo", "{v-1:[0-9]+}"), - request: newRequest("GET", "http://localhost?foo=10"), - vars: map[string]string{"v-1": "10"}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route with hyphenated name and pattern with quantifier, additional capturing group", - route: new(Route).Queries("foo", "{v-1:[0-9]{1}(a|b)}"), - request: newRequest("GET", "http://localhost?foo=1a"), - vars: map[string]string{"v-1": "1a"}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route with empty value, should match", - route: new(Route).Queries("foo", ""), - request: newRequest("GET", "http://localhost?foo=bar"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route with empty value and no parameter in request, should not match", - route: new(Route).Queries("foo", ""), - request: newRequest("GET", "http://localhost"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Queries route with empty value and empty parameter in request, should match", - route: new(Route).Queries("foo", ""), - request: newRequest("GET", "http://localhost?foo="), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route with overlapping value, should not match", - route: new(Route).Queries("foo", "bar"), - request: newRequest("GET", "http://localhost?foo=barfoo"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Queries route with no parameter in request, should not match", - route: new(Route).Queries("foo", "{bar}"), - request: newRequest("GET", "http://localhost"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - { - title: "Queries route with empty parameter in request, should match", - route: new(Route).Queries("foo", "{bar}"), - request: newRequest("GET", "http://localhost?foo="), - vars: map[string]string{"foo": ""}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Queries route, bad submatch", - route: new(Route).Queries("foo", "bar", "baz", "ding"), - request: newRequest("GET", "http://localhost?fffoo=bar&baz=dingggg"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - } - - for _, test := range tests { - testRoute(t, test) - } -} - -func TestSchemes(t *testing.T) { - tests := []routeTest{ - // Schemes - { - title: "Schemes route, match https", - route: new(Route).Schemes("https", "ftp"), - request: newRequest("GET", "https://localhost"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Schemes route, match ftp", - route: new(Route).Schemes("https", "ftp"), - request: newRequest("GET", "ftp://localhost"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "Schemes route, bad scheme", - route: new(Route).Schemes("https", "ftp"), - request: newRequest("GET", "http://localhost"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - } - for _, test := range tests { - testRoute(t, test) - } -} - -func TestMatcherFunc(t *testing.T) { - m := func(r *http.Request, m *RouteMatch) bool { - if r.URL.Host == "aaa.bbb.ccc" { - return true - } - return false - } - - tests := []routeTest{ - { - title: "MatchFunc route, match", - route: new(Route).MatcherFunc(m), - request: newRequest("GET", "http://aaa.bbb.ccc"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: true, - }, - { - title: "MatchFunc route, non-match", - route: new(Route).MatcherFunc(m), - request: newRequest("GET", "http://aaa.222.ccc"), - vars: map[string]string{}, - host: "", - path: "", - shouldMatch: false, - }, - } - - for _, test := range tests { - testRoute(t, test) - } -} - -func TestBuildVarsFunc(t *testing.T) { - tests := []routeTest{ - { - title: "BuildVarsFunc set on route", - route: new(Route).Path(`/111/{v1:\d}{v2:.*}`).BuildVarsFunc(func(vars map[string]string) map[string]string { - vars["v1"] = "3" - vars["v2"] = "a" - return vars - }), - request: newRequest("GET", "http://localhost/111/2"), - path: "/111/3a", - shouldMatch: true, - }, - { - title: "BuildVarsFunc set on route and parent route", - route: new(Route).PathPrefix(`/{v1:\d}`).BuildVarsFunc(func(vars map[string]string) map[string]string { - vars["v1"] = "2" - return vars - }).Subrouter().Path(`/{v2:\w}`).BuildVarsFunc(func(vars map[string]string) map[string]string { - vars["v2"] = "b" - return vars - }), - request: newRequest("GET", "http://localhost/1/a"), - path: "/2/b", - shouldMatch: true, - }, - } - - for _, test := range tests { - testRoute(t, test) - } -} - -func TestSubRouter(t *testing.T) { - subrouter1 := new(Route).Host("{v1:[a-z]+}.google.com").Subrouter() - subrouter2 := new(Route).PathPrefix("/foo/{v1}").Subrouter() - - tests := []routeTest{ - { - route: subrouter1.Path("/{v2:[a-z]+}"), - request: newRequest("GET", "http://aaa.google.com/bbb"), - vars: map[string]string{"v1": "aaa", "v2": "bbb"}, - host: "aaa.google.com", - path: "/bbb", - shouldMatch: true, - }, - { - route: subrouter1.Path("/{v2:[a-z]+}"), - request: newRequest("GET", "http://111.google.com/111"), - vars: map[string]string{"v1": "aaa", "v2": "bbb"}, - host: "aaa.google.com", - path: "/bbb", - shouldMatch: false, - }, - { - route: subrouter2.Path("/baz/{v2}"), - request: newRequest("GET", "http://localhost/foo/bar/baz/ding"), - vars: map[string]string{"v1": "bar", "v2": "ding"}, - host: "", - path: "/foo/bar/baz/ding", - shouldMatch: true, - }, - { - route: subrouter2.Path("/baz/{v2}"), - request: newRequest("GET", "http://localhost/foo/bar"), - vars: map[string]string{"v1": "bar", "v2": "ding"}, - host: "", - path: "/foo/bar/baz/ding", - shouldMatch: false, - }, - } - - for _, test := range tests { - testRoute(t, test) - } -} - -func TestNamedRoutes(t *testing.T) { - r1 := NewRouter() - r1.NewRoute().Name("a") - r1.NewRoute().Name("b") - r1.NewRoute().Name("c") - - r2 := r1.NewRoute().Subrouter() - r2.NewRoute().Name("d") - r2.NewRoute().Name("e") - r2.NewRoute().Name("f") - - r3 := r2.NewRoute().Subrouter() - r3.NewRoute().Name("g") - r3.NewRoute().Name("h") - r3.NewRoute().Name("i") - - if r1.namedRoutes == nil || len(r1.namedRoutes) != 9 { - t.Errorf("Expected 9 named routes, got %v", r1.namedRoutes) - } else if r1.Get("i") == nil { - t.Errorf("Subroute name not registered") - } -} - -func TestStrictSlash(t *testing.T) { - r := NewRouter() - r.StrictSlash(true) - - tests := []routeTest{ - { - title: "Redirect path without slash", - route: r.NewRoute().Path("/111/"), - request: newRequest("GET", "http://localhost/111"), - vars: map[string]string{}, - host: "", - path: "/111/", - shouldMatch: true, - shouldRedirect: true, - }, - { - title: "Do not redirect path with slash", - route: r.NewRoute().Path("/111/"), - request: newRequest("GET", "http://localhost/111/"), - vars: map[string]string{}, - host: "", - path: "/111/", - shouldMatch: true, - shouldRedirect: false, - }, - { - title: "Redirect path with slash", - route: r.NewRoute().Path("/111"), - request: newRequest("GET", "http://localhost/111/"), - vars: map[string]string{}, - host: "", - path: "/111", - shouldMatch: true, - shouldRedirect: true, - }, - { - title: "Do not redirect path without slash", - route: r.NewRoute().Path("/111"), - request: newRequest("GET", "http://localhost/111"), - vars: map[string]string{}, - host: "", - path: "/111", - shouldMatch: true, - shouldRedirect: false, - }, - { - title: "Propagate StrictSlash to subrouters", - route: r.NewRoute().PathPrefix("/static/").Subrouter().Path("/images/"), - request: newRequest("GET", "http://localhost/static/images"), - vars: map[string]string{}, - host: "", - path: "/static/images/", - shouldMatch: true, - shouldRedirect: true, - }, - { - title: "Ignore StrictSlash for path prefix", - route: r.NewRoute().PathPrefix("/static/"), - request: newRequest("GET", "http://localhost/static/logo.png"), - vars: map[string]string{}, - host: "", - path: "/static/", - shouldMatch: true, - shouldRedirect: false, - }, - } - - for _, test := range tests { - testRoute(t, test) - } -} - -func TestWalkSingleDepth(t *testing.T) { - r0 := NewRouter() - r1 := NewRouter() - r2 := NewRouter() - - r0.Path("/g") - r0.Path("/o") - r0.Path("/d").Handler(r1) - r0.Path("/r").Handler(r2) - r0.Path("/a") - - r1.Path("/z") - r1.Path("/i") - r1.Path("/l") - r1.Path("/l") - - r2.Path("/i") - r2.Path("/l") - r2.Path("/l") - - paths := []string{"g", "o", "r", "i", "l", "l", "a"} - depths := []int{0, 0, 0, 1, 1, 1, 0} - i := 0 - err := r0.Walk(func(route *Route, router *Router, ancestors []*Route) error { - matcher := route.matchers[0].(*routeRegexp) - if matcher.template == "/d" { - return SkipRouter - } - if len(ancestors) != depths[i] { - t.Errorf(`Expected depth of %d at i = %d; got "%d"`, depths[i], i, len(ancestors)) - } - if matcher.template != "/"+paths[i] { - t.Errorf(`Expected "/%s" at i = %d; got "%s"`, paths[i], i, matcher.template) - } - i++ - return nil - }) - if err != nil { - panic(err) - } - if i != len(paths) { - t.Errorf("Expected %d routes, found %d", len(paths), i) - } -} - -func TestWalkNested(t *testing.T) { - router := NewRouter() - - g := router.Path("/g").Subrouter() - o := g.PathPrefix("/o").Subrouter() - r := o.PathPrefix("/r").Subrouter() - i := r.PathPrefix("/i").Subrouter() - l1 := i.PathPrefix("/l").Subrouter() - l2 := l1.PathPrefix("/l").Subrouter() - l2.Path("/a") - - paths := []string{"/g", "/g/o", "/g/o/r", "/g/o/r/i", "/g/o/r/i/l", "/g/o/r/i/l/l", "/g/o/r/i/l/l/a"} - idx := 0 - err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error { - path := paths[idx] - tpl := route.regexp.path.template - if tpl != path { - t.Errorf(`Expected %s got %s`, path, tpl) - } - idx++ - return nil - }) - if err != nil { - panic(err) - } - if idx != len(paths) { - t.Errorf("Expected %d routes, found %d", len(paths), idx) - } -} - -func TestSubrouterErrorHandling(t *testing.T) { - superRouterCalled := false - subRouterCalled := false - - router := NewRouter() - router.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - superRouterCalled = true - }) - subRouter := router.PathPrefix("/bign8").Subrouter() - subRouter.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - subRouterCalled = true - }) - - req, _ := http.NewRequest("GET", "http://localhost/bign8/was/here", nil) - router.ServeHTTP(NewRecorder(), req) - - if superRouterCalled { - t.Error("Super router 404 handler called when sub-router 404 handler is available.") - } - if !subRouterCalled { - t.Error("Sub-router 404 handler was not called.") - } -} - -// ---------------------------------------------------------------------------- -// Helpers -// ---------------------------------------------------------------------------- - -func getRouteTemplate(route *Route) string { - host, path := "none", "none" - if route.regexp != nil { - if route.regexp.host != nil { - host = route.regexp.host.template - } - if route.regexp.path != nil { - path = route.regexp.path.template - } - } - return fmt.Sprintf("Host: %v, Path: %v", host, path) -} - -func testRoute(t *testing.T, test routeTest) { - request := test.request - route := test.route - vars := test.vars - shouldMatch := test.shouldMatch - host := test.host - path := test.path - url := test.host + test.path - shouldRedirect := test.shouldRedirect - - var match RouteMatch - ok := route.Match(request, &match) - if ok != shouldMatch { - msg := "Should match" - if !shouldMatch { - msg = "Should not match" - } - t.Errorf("(%v) %v:\nRoute: %#v\nRequest: %#v\nVars: %v\n", test.title, msg, route, request, vars) - return - } - if shouldMatch { - if test.vars != nil && !stringMapEqual(test.vars, match.Vars) { - t.Errorf("(%v) Vars not equal: expected %v, got %v", test.title, vars, match.Vars) - return - } - if host != "" { - u, _ := test.route.URLHost(mapToPairs(match.Vars)...) - if host != u.Host { - t.Errorf("(%v) URLHost not equal: expected %v, got %v -- %v", test.title, host, u.Host, getRouteTemplate(route)) - return - } - } - if path != "" { - u, _ := route.URLPath(mapToPairs(match.Vars)...) - if path != u.Path { - t.Errorf("(%v) URLPath not equal: expected %v, got %v -- %v", test.title, path, u.Path, getRouteTemplate(route)) - return - } - } - if url != "" { - u, _ := route.URL(mapToPairs(match.Vars)...) - if url != u.Host+u.Path { - t.Errorf("(%v) URL not equal: expected %v, got %v -- %v", test.title, url, u.Host+u.Path, getRouteTemplate(route)) - return - } - } - if shouldRedirect && match.Handler == nil { - t.Errorf("(%v) Did not redirect", test.title) - return - } - if !shouldRedirect && match.Handler != nil { - t.Errorf("(%v) Unexpected redirect", test.title) - return - } - } -} - -// Tests that the context is cleared or not cleared properly depending on -// the configuration of the router -func TestKeepContext(t *testing.T) { - func1 := func(w http.ResponseWriter, r *http.Request) {} - - r := NewRouter() - r.HandleFunc("/", func1).Name("func1") - - req, _ := http.NewRequest("GET", "http://localhost/", nil) - context.Set(req, "t", 1) - - res := new(http.ResponseWriter) - r.ServeHTTP(*res, req) - - if _, ok := context.GetOk(req, "t"); ok { - t.Error("Context should have been cleared at end of request") - } - - r.KeepContext = true - - req, _ = http.NewRequest("GET", "http://localhost/", nil) - context.Set(req, "t", 1) - - r.ServeHTTP(*res, req) - if _, ok := context.GetOk(req, "t"); !ok { - t.Error("Context should NOT have been cleared at end of request") - } - -} - -type TestA301ResponseWriter struct { - hh http.Header - status int -} - -func (ho TestA301ResponseWriter) Header() http.Header { - return http.Header(ho.hh) -} - -func (ho TestA301ResponseWriter) Write(b []byte) (int, error) { - return 0, nil -} - -func (ho TestA301ResponseWriter) WriteHeader(code int) { - ho.status = code -} - -func Test301Redirect(t *testing.T) { - m := make(http.Header) - - func1 := func(w http.ResponseWriter, r *http.Request) {} - func2 := func(w http.ResponseWriter, r *http.Request) {} - - r := NewRouter() - r.HandleFunc("/api/", func2).Name("func2") - r.HandleFunc("/", func1).Name("func1") - - req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil) - - res := TestA301ResponseWriter{ - hh: m, - status: 0, - } - r.ServeHTTP(&res, req) - - if "http://localhost/api/?abc=def" != res.hh["Location"][0] { - t.Errorf("Should have complete URL with query string") - } -} - -// https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW -func TestSubrouterHeader(t *testing.T) { - expected := "func1 response" - func1 := func(w http.ResponseWriter, r *http.Request) { - fmt.Fprint(w, expected) - } - func2 := func(http.ResponseWriter, *http.Request) {} - - r := NewRouter() - s := r.Headers("SomeSpecialHeader", "").Subrouter() - s.HandleFunc("/", func1).Name("func1") - r.HandleFunc("/", func2).Name("func2") - - req, _ := http.NewRequest("GET", "http://localhost/", nil) - req.Header.Add("SomeSpecialHeader", "foo") - match := new(RouteMatch) - matched := r.Match(req, match) - if !matched { - t.Errorf("Should match request") - } - if match.Route.GetName() != "func1" { - t.Errorf("Expecting func1 handler, got %s", match.Route.GetName()) - } - resp := NewRecorder() - match.Handler.ServeHTTP(resp, req) - if resp.Body.String() != expected { - t.Errorf("Expecting %q", expected) - } -} - -// mapToPairs converts a string map to a slice of string pairs -func mapToPairs(m map[string]string) []string { - var i int - p := make([]string, len(m)*2) - for k, v := range m { - p[i] = k - p[i+1] = v - i += 2 - } - return p -} - -// stringMapEqual checks the equality of two string maps -func stringMapEqual(m1, m2 map[string]string) bool { - nil1 := m1 == nil - nil2 := m2 == nil - if nil1 != nil2 || len(m1) != len(m2) { - return false - } - for k, v := range m1 { - if v != m2[k] { - return false - } - } - return true -} - -// newRequest is a helper function to create a new request with a method and url -func newRequest(method, url string) *http.Request { - req, err := http.NewRequest(method, url, nil) - if err != nil { - panic(err) - } - return req -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/old_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/old_test.go deleted file mode 100644 index 755db483e..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/old_test.go +++ /dev/null @@ -1,714 +0,0 @@ -// Old tests ported to Go1. This is a mess. Want to drop it one day. - -// Copyright 2011 Gorilla 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 mux - -import ( - "bytes" - "net/http" - "testing" -) - -// ---------------------------------------------------------------------------- -// ResponseRecorder -// ---------------------------------------------------------------------------- -// 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. - -// ResponseRecorder is an implementation of http.ResponseWriter that -// records its mutations for later inspection in tests. -type ResponseRecorder struct { - Code int // the HTTP response code from WriteHeader - HeaderMap http.Header // the HTTP response headers - Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to - Flushed bool -} - -// NewRecorder returns an initialized ResponseRecorder. -func NewRecorder() *ResponseRecorder { - return &ResponseRecorder{ - HeaderMap: make(http.Header), - Body: new(bytes.Buffer), - } -} - -// DefaultRemoteAddr is the default remote address to return in RemoteAddr if -// an explicit DefaultRemoteAddr isn't set on ResponseRecorder. -const DefaultRemoteAddr = "1.2.3.4" - -// Header returns the response headers. -func (rw *ResponseRecorder) Header() http.Header { - return rw.HeaderMap -} - -// Write always succeeds and writes to rw.Body, if not nil. -func (rw *ResponseRecorder) Write(buf []byte) (int, error) { - if rw.Body != nil { - rw.Body.Write(buf) - } - if rw.Code == 0 { - rw.Code = http.StatusOK - } - return len(buf), nil -} - -// WriteHeader sets rw.Code. -func (rw *ResponseRecorder) WriteHeader(code int) { - rw.Code = code -} - -// Flush sets rw.Flushed to true. -func (rw *ResponseRecorder) Flush() { - rw.Flushed = true -} - -// ---------------------------------------------------------------------------- - -func TestRouteMatchers(t *testing.T) { - var scheme, host, path, query, method string - var headers map[string]string - var resultVars map[bool]map[string]string - - router := NewRouter() - router.NewRoute().Host("{var1}.google.com"). - Path("/{var2:[a-z]+}/{var3:[0-9]+}"). - Queries("foo", "bar"). - Methods("GET"). - Schemes("https"). - Headers("x-requested-with", "XMLHttpRequest") - router.NewRoute().Host("www.{var4}.com"). - PathPrefix("/foo/{var5:[a-z]+}/{var6:[0-9]+}"). - Queries("baz", "ding"). - Methods("POST"). - Schemes("http"). - Headers("Content-Type", "application/json") - - reset := func() { - // Everything match. - scheme = "https" - host = "www.google.com" - path = "/product/42" - query = "?foo=bar" - method = "GET" - headers = map[string]string{"X-Requested-With": "XMLHttpRequest"} - resultVars = map[bool]map[string]string{ - true: {"var1": "www", "var2": "product", "var3": "42"}, - false: {}, - } - } - - reset2 := func() { - // Everything match. - scheme = "http" - host = "www.google.com" - path = "/foo/product/42/path/that/is/ignored" - query = "?baz=ding" - method = "POST" - headers = map[string]string{"Content-Type": "application/json"} - resultVars = map[bool]map[string]string{ - true: {"var4": "google", "var5": "product", "var6": "42"}, - false: {}, - } - } - - match := func(shouldMatch bool) { - url := scheme + "://" + host + path + query - request, _ := http.NewRequest(method, url, nil) - for key, value := range headers { - request.Header.Add(key, value) - } - - var routeMatch RouteMatch - matched := router.Match(request, &routeMatch) - if matched != shouldMatch { - // Need better messages. :) - if matched { - t.Errorf("Should match.") - } else { - t.Errorf("Should not match.") - } - } - - if matched { - currentRoute := routeMatch.Route - if currentRoute == nil { - t.Errorf("Expected a current route.") - } - vars := routeMatch.Vars - expectedVars := resultVars[shouldMatch] - if len(vars) != len(expectedVars) { - t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars) - } - for name, value := range vars { - if expectedVars[name] != value { - t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars) - } - } - } - } - - // 1st route -------------------------------------------------------------- - - // Everything match. - reset() - match(true) - - // Scheme doesn't match. - reset() - scheme = "http" - match(false) - - // Host doesn't match. - reset() - host = "www.mygoogle.com" - match(false) - - // Path doesn't match. - reset() - path = "/product/notdigits" - match(false) - - // Query doesn't match. - reset() - query = "?foo=baz" - match(false) - - // Method doesn't match. - reset() - method = "POST" - match(false) - - // Header doesn't match. - reset() - headers = map[string]string{} - match(false) - - // Everything match, again. - reset() - match(true) - - // 2nd route -------------------------------------------------------------- - - // Everything match. - reset2() - match(true) - - // Scheme doesn't match. - reset2() - scheme = "https" - match(false) - - // Host doesn't match. - reset2() - host = "sub.google.com" - match(false) - - // Path doesn't match. - reset2() - path = "/bar/product/42" - match(false) - - // Query doesn't match. - reset2() - query = "?foo=baz" - match(false) - - // Method doesn't match. - reset2() - method = "GET" - match(false) - - // Header doesn't match. - reset2() - headers = map[string]string{} - match(false) - - // Everything match, again. - reset2() - match(true) -} - -type headerMatcherTest struct { - matcher headerMatcher - headers map[string]string - result bool -} - -var headerMatcherTests = []headerMatcherTest{ - { - matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}), - headers: map[string]string{"X-Requested-With": "XMLHttpRequest"}, - result: true, - }, - { - matcher: headerMatcher(map[string]string{"x-requested-with": ""}), - headers: map[string]string{"X-Requested-With": "anything"}, - result: true, - }, - { - matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}), - headers: map[string]string{}, - result: false, - }, -} - -type hostMatcherTest struct { - matcher *Route - url string - vars map[string]string - result bool -} - -var hostMatcherTests = []hostMatcherTest{ - { - matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"), - url: "http://abc.def.ghi/", - vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"}, - result: true, - }, - { - matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"), - url: "http://a.b.c/", - vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"}, - result: false, - }, -} - -type methodMatcherTest struct { - matcher methodMatcher - method string - result bool -} - -var methodMatcherTests = []methodMatcherTest{ - { - matcher: methodMatcher([]string{"GET", "POST", "PUT"}), - method: "GET", - result: true, - }, - { - matcher: methodMatcher([]string{"GET", "POST", "PUT"}), - method: "POST", - result: true, - }, - { - matcher: methodMatcher([]string{"GET", "POST", "PUT"}), - method: "PUT", - result: true, - }, - { - matcher: methodMatcher([]string{"GET", "POST", "PUT"}), - method: "DELETE", - result: false, - }, -} - -type pathMatcherTest struct { - matcher *Route - url string - vars map[string]string - result bool -} - -var pathMatcherTests = []pathMatcherTest{ - { - matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"), - url: "http://localhost:8080/123/456/789", - vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"}, - result: true, - }, - { - matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"), - url: "http://localhost:8080/1/2/3", - vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"}, - result: false, - }, -} - -type schemeMatcherTest struct { - matcher schemeMatcher - url string - result bool -} - -var schemeMatcherTests = []schemeMatcherTest{ - { - matcher: schemeMatcher([]string{"http", "https"}), - url: "http://localhost:8080/", - result: true, - }, - { - matcher: schemeMatcher([]string{"http", "https"}), - url: "https://localhost:8080/", - result: true, - }, - { - matcher: schemeMatcher([]string{"https"}), - url: "http://localhost:8080/", - result: false, - }, - { - matcher: schemeMatcher([]string{"http"}), - url: "https://localhost:8080/", - result: false, - }, -} - -type urlBuildingTest struct { - route *Route - vars []string - url string -} - -var urlBuildingTests = []urlBuildingTest{ - { - route: new(Route).Host("foo.domain.com"), - vars: []string{}, - url: "http://foo.domain.com", - }, - { - route: new(Route).Host("{subdomain}.domain.com"), - vars: []string{"subdomain", "bar"}, - url: "http://bar.domain.com", - }, - { - route: new(Route).Host("foo.domain.com").Path("/articles"), - vars: []string{}, - url: "http://foo.domain.com/articles", - }, - { - route: new(Route).Path("/articles"), - vars: []string{}, - url: "/articles", - }, - { - route: new(Route).Path("/articles/{category}/{id:[0-9]+}"), - vars: []string{"category", "technology", "id", "42"}, - url: "/articles/technology/42", - }, - { - route: new(Route).Host("{subdomain}.domain.com").Path("/articles/{category}/{id:[0-9]+}"), - vars: []string{"subdomain", "foo", "category", "technology", "id", "42"}, - url: "http://foo.domain.com/articles/technology/42", - }, -} - -func TestHeaderMatcher(t *testing.T) { - for _, v := range headerMatcherTests { - request, _ := http.NewRequest("GET", "http://localhost:8080/", nil) - for key, value := range v.headers { - request.Header.Add(key, value) - } - var routeMatch RouteMatch - result := v.matcher.Match(request, &routeMatch) - if result != v.result { - if v.result { - t.Errorf("%#v: should match %v.", v.matcher, request.Header) - } else { - t.Errorf("%#v: should not match %v.", v.matcher, request.Header) - } - } - } -} - -func TestHostMatcher(t *testing.T) { - for _, v := range hostMatcherTests { - request, _ := http.NewRequest("GET", v.url, nil) - var routeMatch RouteMatch - result := v.matcher.Match(request, &routeMatch) - vars := routeMatch.Vars - if result != v.result { - if v.result { - t.Errorf("%#v: should match %v.", v.matcher, v.url) - } else { - t.Errorf("%#v: should not match %v.", v.matcher, v.url) - } - } - if result { - if len(vars) != len(v.vars) { - t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars)) - } - for name, value := range vars { - if v.vars[name] != value { - t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value) - } - } - } else { - if len(vars) != 0 { - t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars)) - } - } - } -} - -func TestMethodMatcher(t *testing.T) { - for _, v := range methodMatcherTests { - request, _ := http.NewRequest(v.method, "http://localhost:8080/", nil) - var routeMatch RouteMatch - result := v.matcher.Match(request, &routeMatch) - if result != v.result { - if v.result { - t.Errorf("%#v: should match %v.", v.matcher, v.method) - } else { - t.Errorf("%#v: should not match %v.", v.matcher, v.method) - } - } - } -} - -func TestPathMatcher(t *testing.T) { - for _, v := range pathMatcherTests { - request, _ := http.NewRequest("GET", v.url, nil) - var routeMatch RouteMatch - result := v.matcher.Match(request, &routeMatch) - vars := routeMatch.Vars - if result != v.result { - if v.result { - t.Errorf("%#v: should match %v.", v.matcher, v.url) - } else { - t.Errorf("%#v: should not match %v.", v.matcher, v.url) - } - } - if result { - if len(vars) != len(v.vars) { - t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars)) - } - for name, value := range vars { - if v.vars[name] != value { - t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value) - } - } - } else { - if len(vars) != 0 { - t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars)) - } - } - } -} - -func TestSchemeMatcher(t *testing.T) { - for _, v := range schemeMatcherTests { - request, _ := http.NewRequest("GET", v.url, nil) - var routeMatch RouteMatch - result := v.matcher.Match(request, &routeMatch) - if result != v.result { - if v.result { - t.Errorf("%#v: should match %v.", v.matcher, v.url) - } else { - t.Errorf("%#v: should not match %v.", v.matcher, v.url) - } - } - } -} - -func TestUrlBuilding(t *testing.T) { - - for _, v := range urlBuildingTests { - u, _ := v.route.URL(v.vars...) - url := u.String() - if url != v.url { - t.Errorf("expected %v, got %v", v.url, url) - /* - reversePath := "" - reverseHost := "" - if v.route.pathTemplate != nil { - reversePath = v.route.pathTemplate.Reverse - } - if v.route.hostTemplate != nil { - reverseHost = v.route.hostTemplate.Reverse - } - - t.Errorf("%#v:\nexpected: %q\ngot: %q\nreverse path: %q\nreverse host: %q", v.route, v.url, url, reversePath, reverseHost) - */ - } - } - - ArticleHandler := func(w http.ResponseWriter, r *http.Request) { - } - - router := NewRouter() - router.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).Name("article") - - url, _ := router.Get("article").URL("category", "technology", "id", "42") - expected := "/articles/technology/42" - if url.String() != expected { - t.Errorf("Expected %v, got %v", expected, url.String()) - } -} - -func TestMatchedRouteName(t *testing.T) { - routeName := "stock" - router := NewRouter() - route := router.NewRoute().Path("/products/").Name(routeName) - - url := "http://www.example.com/products/" - request, _ := http.NewRequest("GET", url, nil) - var rv RouteMatch - ok := router.Match(request, &rv) - - if !ok || rv.Route != route { - t.Errorf("Expected same route, got %+v.", rv.Route) - } - - retName := rv.Route.GetName() - if retName != routeName { - t.Errorf("Expected %q, got %q.", routeName, retName) - } -} - -func TestSubRouting(t *testing.T) { - // Example from docs. - router := NewRouter() - subrouter := router.NewRoute().Host("www.example.com").Subrouter() - route := subrouter.NewRoute().Path("/products/").Name("products") - - url := "http://www.example.com/products/" - request, _ := http.NewRequest("GET", url, nil) - var rv RouteMatch - ok := router.Match(request, &rv) - - if !ok || rv.Route != route { - t.Errorf("Expected same route, got %+v.", rv.Route) - } - - u, _ := router.Get("products").URL() - builtUrl := u.String() - // Yay, subroute aware of the domain when building! - if builtUrl != url { - t.Errorf("Expected %q, got %q.", url, builtUrl) - } -} - -func TestVariableNames(t *testing.T) { - route := new(Route).Host("{arg1}.domain.com").Path("/{arg1}/{arg2:[0-9]+}") - if route.err == nil { - t.Errorf("Expected error for duplicated variable names") - } -} - -func TestRedirectSlash(t *testing.T) { - var route *Route - var routeMatch RouteMatch - r := NewRouter() - - r.StrictSlash(false) - route = r.NewRoute() - if route.strictSlash != false { - t.Errorf("Expected false redirectSlash.") - } - - r.StrictSlash(true) - route = r.NewRoute() - if route.strictSlash != true { - t.Errorf("Expected true redirectSlash.") - } - - route = new(Route) - route.strictSlash = true - route.Path("/{arg1}/{arg2:[0-9]+}/") - request, _ := http.NewRequest("GET", "http://localhost/foo/123", nil) - routeMatch = RouteMatch{} - _ = route.Match(request, &routeMatch) - vars := routeMatch.Vars - if vars["arg1"] != "foo" { - t.Errorf("Expected foo.") - } - if vars["arg2"] != "123" { - t.Errorf("Expected 123.") - } - rsp := NewRecorder() - routeMatch.Handler.ServeHTTP(rsp, request) - if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123/" { - t.Errorf("Expected redirect header.") - } - - route = new(Route) - route.strictSlash = true - route.Path("/{arg1}/{arg2:[0-9]+}") - request, _ = http.NewRequest("GET", "http://localhost/foo/123/", nil) - routeMatch = RouteMatch{} - _ = route.Match(request, &routeMatch) - vars = routeMatch.Vars - if vars["arg1"] != "foo" { - t.Errorf("Expected foo.") - } - if vars["arg2"] != "123" { - t.Errorf("Expected 123.") - } - rsp = NewRecorder() - routeMatch.Handler.ServeHTTP(rsp, request) - if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123" { - t.Errorf("Expected redirect header.") - } -} - -// Test for the new regexp library, still not available in stable Go. -func TestNewRegexp(t *testing.T) { - var p *routeRegexp - var matches []string - - tests := map[string]map[string][]string{ - "/{foo:a{2}}": { - "/a": nil, - "/aa": {"aa"}, - "/aaa": nil, - "/aaaa": nil, - }, - "/{foo:a{2,}}": { - "/a": nil, - "/aa": {"aa"}, - "/aaa": {"aaa"}, - "/aaaa": {"aaaa"}, - }, - "/{foo:a{2,3}}": { - "/a": nil, - "/aa": {"aa"}, - "/aaa": {"aaa"}, - "/aaaa": nil, - }, - "/{foo:[a-z]{3}}/{bar:[a-z]{2}}": { - "/a": nil, - "/ab": nil, - "/abc": nil, - "/abcd": nil, - "/abc/ab": {"abc", "ab"}, - "/abc/abc": nil, - "/abcd/ab": nil, - }, - `/{foo:\w{3,}}/{bar:\d{2,}}`: { - "/a": nil, - "/ab": nil, - "/abc": nil, - "/abc/1": nil, - "/abc/12": {"abc", "12"}, - "/abcd/12": {"abcd", "12"}, - "/abcd/123": {"abcd", "123"}, - }, - } - - for pattern, paths := range tests { - p, _ = newRouteRegexp(pattern, false, false, false, false) - for path, result := range paths { - matches = p.regexp.FindStringSubmatch(path) - if result == nil { - if matches != nil { - t.Errorf("%v should not match %v.", pattern, path) - } - } else { - if len(matches) != len(result)+1 { - t.Errorf("Expected %v matches, got %v.", len(result)+1, len(matches)) - } else { - for k, v := range result { - if matches[k+1] != v { - t.Errorf("Expected %v, got %v.", v, matches[k+1]) - } - } - } - } - } - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/regexp.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/regexp.go deleted file mode 100644 index 06728dd54..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/regexp.go +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright 2012 The Gorilla 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 mux - -import ( - "bytes" - "fmt" - "net/http" - "net/url" - "regexp" - "strconv" - "strings" -) - -// newRouteRegexp parses a route template and returns a routeRegexp, -// used to match a host, a path or a query string. -// -// It will extract named variables, assemble a regexp to be matched, create -// a "reverse" template to build URLs and compile regexps to validate variable -// values used in URL building. -// -// Previously we accepted only Python-like identifiers for variable -// names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that -// name and pattern can't be empty, and names can't contain a colon. -func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash bool) (*routeRegexp, error) { - // Check if it is well-formed. - idxs, errBraces := braceIndices(tpl) - if errBraces != nil { - return nil, errBraces - } - // Backup the original. - template := tpl - // Now let's parse it. - defaultPattern := "[^/]+" - if matchQuery { - defaultPattern = "[^?&]*" - } else if matchHost { - defaultPattern = "[^.]+" - matchPrefix = false - } - // Only match strict slash if not matching - if matchPrefix || matchHost || matchQuery { - strictSlash = false - } - // Set a flag for strictSlash. - endSlash := false - if strictSlash && strings.HasSuffix(tpl, "/") { - tpl = tpl[:len(tpl)-1] - endSlash = true - } - varsN := make([]string, len(idxs)/2) - varsR := make([]*regexp.Regexp, len(idxs)/2) - pattern := bytes.NewBufferString("") - pattern.WriteByte('^') - reverse := bytes.NewBufferString("") - var end int - var err error - for i := 0; i < len(idxs); i += 2 { - // Set all values we are interested in. - raw := tpl[end:idxs[i]] - end = idxs[i+1] - parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2) - name := parts[0] - patt := defaultPattern - if len(parts) == 2 { - patt = parts[1] - } - // Name or pattern can't be empty. - if name == "" || patt == "" { - return nil, fmt.Errorf("mux: missing name or pattern in %q", - tpl[idxs[i]:end]) - } - // Build the regexp pattern. - varIdx := i / 2 - fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(varIdx), patt) - // Build the reverse template. - fmt.Fprintf(reverse, "%s%%s", raw) - - // Append variable name and compiled pattern. - varsN[varIdx] = name - varsR[varIdx], err = regexp.Compile(fmt.Sprintf("^%s$", patt)) - if err != nil { - return nil, err - } - } - // Add the remaining. - raw := tpl[end:] - pattern.WriteString(regexp.QuoteMeta(raw)) - if strictSlash { - pattern.WriteString("[/]?") - } - if matchQuery { - // Add the default pattern if the query value is empty - if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" { - pattern.WriteString(defaultPattern) - } - } - if !matchPrefix { - pattern.WriteByte('$') - } - reverse.WriteString(raw) - if endSlash { - reverse.WriteByte('/') - } - // Compile full regexp. - reg, errCompile := regexp.Compile(pattern.String()) - if errCompile != nil { - return nil, errCompile - } - // Done! - return &routeRegexp{ - template: template, - matchHost: matchHost, - matchQuery: matchQuery, - strictSlash: strictSlash, - regexp: reg, - reverse: reverse.String(), - varsN: varsN, - varsR: varsR, - }, nil -} - -// routeRegexp stores a regexp to match a host or path and information to -// collect and validate route variables. -type routeRegexp struct { - // The unmodified template. - template string - // True for host match, false for path or query string match. - matchHost bool - // True for query string match, false for path and host match. - matchQuery bool - // The strictSlash value defined on the route, but disabled if PathPrefix was used. - strictSlash bool - // Expanded regexp. - regexp *regexp.Regexp - // Reverse template. - reverse string - // Variable names. - varsN []string - // Variable regexps (validators). - varsR []*regexp.Regexp -} - -// Match matches the regexp against the URL host or path. -func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool { - if !r.matchHost { - if r.matchQuery { - return r.matchQueryString(req) - } else { - return r.regexp.MatchString(req.URL.Path) - } - } - return r.regexp.MatchString(getHost(req)) -} - -// url builds a URL part using the given values. -func (r *routeRegexp) url(values map[string]string) (string, error) { - urlValues := make([]interface{}, len(r.varsN)) - for k, v := range r.varsN { - value, ok := values[v] - if !ok { - return "", fmt.Errorf("mux: missing route variable %q", v) - } - urlValues[k] = value - } - rv := fmt.Sprintf(r.reverse, urlValues...) - if !r.regexp.MatchString(rv) { - // The URL is checked against the full regexp, instead of checking - // individual variables. This is faster but to provide a good error - // message, we check individual regexps if the URL doesn't match. - for k, v := range r.varsN { - if !r.varsR[k].MatchString(values[v]) { - return "", fmt.Errorf( - "mux: variable %q doesn't match, expected %q", values[v], - r.varsR[k].String()) - } - } - } - return rv, nil -} - -// getUrlQuery returns a single query parameter from a request URL. -// For a URL with foo=bar&baz=ding, we return only the relevant key -// value pair for the routeRegexp. -func (r *routeRegexp) getUrlQuery(req *http.Request) string { - if !r.matchQuery { - return "" - } - templateKey := strings.SplitN(r.template, "=", 2)[0] - for key, vals := range req.URL.Query() { - if key == templateKey && len(vals) > 0 { - return key + "=" + vals[0] - } - } - return "" -} - -func (r *routeRegexp) matchQueryString(req *http.Request) bool { - return r.regexp.MatchString(r.getUrlQuery(req)) -} - -// braceIndices returns the first level curly brace indices from a string. -// It returns an error in case of unbalanced braces. -func braceIndices(s string) ([]int, error) { - var level, idx int - idxs := make([]int, 0) - for i := 0; i < len(s); i++ { - switch s[i] { - case '{': - if level++; level == 1 { - idx = i - } - case '}': - if level--; level == 0 { - idxs = append(idxs, idx, i+1) - } else if level < 0 { - return nil, fmt.Errorf("mux: unbalanced braces in %q", s) - } - } - } - if level != 0 { - return nil, fmt.Errorf("mux: unbalanced braces in %q", s) - } - return idxs, nil -} - -// varGroupName builds a capturing group name for the indexed variable. -func varGroupName(idx int) string { - return "v" + strconv.Itoa(idx) -} - -// ---------------------------------------------------------------------------- -// routeRegexpGroup -// ---------------------------------------------------------------------------- - -// routeRegexpGroup groups the route matchers that carry variables. -type routeRegexpGroup struct { - host *routeRegexp - path *routeRegexp - queries []*routeRegexp -} - -// setMatch extracts the variables from the URL once a route matches. -func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) { - // Store host variables. - if v.host != nil { - hostVars := v.host.regexp.FindStringSubmatch(getHost(req)) - if hostVars != nil { - subexpNames := v.host.regexp.SubexpNames() - varName := 0 - for i, name := range subexpNames[1:] { - if name != "" && name == varGroupName(varName) { - m.Vars[v.host.varsN[varName]] = hostVars[i+1] - varName++ - } - } - } - } - // Store path variables. - if v.path != nil { - pathVars := v.path.regexp.FindStringSubmatch(req.URL.Path) - if pathVars != nil { - subexpNames := v.path.regexp.SubexpNames() - varName := 0 - for i, name := range subexpNames[1:] { - if name != "" && name == varGroupName(varName) { - m.Vars[v.path.varsN[varName]] = pathVars[i+1] - varName++ - } - } - // Check if we should redirect. - if v.path.strictSlash { - p1 := strings.HasSuffix(req.URL.Path, "/") - p2 := strings.HasSuffix(v.path.template, "/") - if p1 != p2 { - u, _ := url.Parse(req.URL.String()) - if p1 { - u.Path = u.Path[:len(u.Path)-1] - } else { - u.Path += "/" - } - m.Handler = http.RedirectHandler(u.String(), 301) - } - } - } - } - // Store query string variables. - for _, q := range v.queries { - queryVars := q.regexp.FindStringSubmatch(q.getUrlQuery(req)) - if queryVars != nil { - subexpNames := q.regexp.SubexpNames() - varName := 0 - for i, name := range subexpNames[1:] { - if name != "" && name == varGroupName(varName) { - m.Vars[q.varsN[varName]] = queryVars[i+1] - varName++ - } - } - } - } -} - -// getHost tries its best to return the request host. -func getHost(r *http.Request) string { - if r.URL.IsAbs() { - return r.URL.Host - } - host := r.Host - // Slice off any port information. - if i := strings.Index(host, ":"); i != -1 { - host = host[:i] - } - return host - -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/route.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/route.go deleted file mode 100644 index 913432c1c..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/route.go +++ /dev/null @@ -1,595 +0,0 @@ -// Copyright 2012 The Gorilla 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 mux - -import ( - "errors" - "fmt" - "net/http" - "net/url" - "regexp" - "strings" -) - -// Route stores information to match a request and build URLs. -type Route struct { - // Parent where the route was registered (a Router). - parent parentRoute - // Request handler for the route. - handler http.Handler - // List of matchers. - matchers []matcher - // Manager for the variables from host and path. - regexp *routeRegexpGroup - // If true, when the path pattern is "/path/", accessing "/path" will - // redirect to the former and vice versa. - strictSlash bool - // If true, this route never matches: it is only used to build URLs. - buildOnly bool - // The name used to build URLs. - name string - // Error resulted from building a route. - err error - - buildVarsFunc BuildVarsFunc -} - -// Match matches the route against the request. -func (r *Route) Match(req *http.Request, match *RouteMatch) bool { - if r.buildOnly || r.err != nil { - return false - } - // Match everything. - for _, m := range r.matchers { - if matched := m.Match(req, match); !matched { - return false - } - } - // Yay, we have a match. Let's collect some info about it. - if match.Route == nil { - match.Route = r - } - if match.Handler == nil { - match.Handler = r.handler - } - if match.Vars == nil { - match.Vars = make(map[string]string) - } - // Set variables. - if r.regexp != nil { - r.regexp.setMatch(req, match, r) - } - return true -} - -// ---------------------------------------------------------------------------- -// Route attributes -// ---------------------------------------------------------------------------- - -// GetError returns an error resulted from building the route, if any. -func (r *Route) GetError() error { - return r.err -} - -// BuildOnly sets the route to never match: it is only used to build URLs. -func (r *Route) BuildOnly() *Route { - r.buildOnly = true - return r -} - -// Handler -------------------------------------------------------------------- - -// Handler sets a handler for the route. -func (r *Route) Handler(handler http.Handler) *Route { - if r.err == nil { - r.handler = handler - } - return r -} - -// HandlerFunc sets a handler function for the route. -func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route { - return r.Handler(http.HandlerFunc(f)) -} - -// GetHandler returns the handler for the route, if any. -func (r *Route) GetHandler() http.Handler { - return r.handler -} - -// Name ----------------------------------------------------------------------- - -// Name sets the name for the route, used to build URLs. -// If the name was registered already it will be overwritten. -func (r *Route) Name(name string) *Route { - if r.name != "" { - r.err = fmt.Errorf("mux: route already has name %q, can't set %q", - r.name, name) - } - if r.err == nil { - r.name = name - r.getNamedRoutes()[name] = r - } - return r -} - -// GetName returns the name for the route, if any. -func (r *Route) GetName() string { - return r.name -} - -// ---------------------------------------------------------------------------- -// Matchers -// ---------------------------------------------------------------------------- - -// matcher types try to match a request. -type matcher interface { - Match(*http.Request, *RouteMatch) bool -} - -// addMatcher adds a matcher to the route. -func (r *Route) addMatcher(m matcher) *Route { - if r.err == nil { - r.matchers = append(r.matchers, m) - } - return r -} - -// addRegexpMatcher adds a host or path matcher and builder to a route. -func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery bool) error { - if r.err != nil { - return r.err - } - r.regexp = r.getRegexpGroup() - if !matchHost && !matchQuery { - if len(tpl) == 0 || tpl[0] != '/' { - return fmt.Errorf("mux: path must start with a slash, got %q", tpl) - } - if r.regexp.path != nil { - tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl - } - } - rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash) - if err != nil { - return err - } - for _, q := range r.regexp.queries { - if err = uniqueVars(rr.varsN, q.varsN); err != nil { - return err - } - } - if matchHost { - if r.regexp.path != nil { - if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil { - return err - } - } - r.regexp.host = rr - } else { - if r.regexp.host != nil { - if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil { - return err - } - } - if matchQuery { - r.regexp.queries = append(r.regexp.queries, rr) - } else { - r.regexp.path = rr - } - } - r.addMatcher(rr) - return nil -} - -// Headers -------------------------------------------------------------------- - -// headerMatcher matches the request against header values. -type headerMatcher map[string]string - -func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool { - return matchMapWithString(m, r.Header, true) -} - -// Headers adds a matcher for request header values. -// It accepts a sequence of key/value pairs to be matched. For example: -// -// r := mux.NewRouter() -// r.Headers("Content-Type", "application/json", -// "X-Requested-With", "XMLHttpRequest") -// -// The above route will only match if both request header values match. -// If the value is an empty string, it will match any value if the key is set. -func (r *Route) Headers(pairs ...string) *Route { - if r.err == nil { - var headers map[string]string - headers, r.err = mapFromPairsToString(pairs...) - return r.addMatcher(headerMatcher(headers)) - } - return r -} - -// headerRegexMatcher matches the request against the route given a regex for the header -type headerRegexMatcher map[string]*regexp.Regexp - -func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool { - return matchMapWithRegex(m, r.Header, true) -} - -// Regular expressions can be used with headers as well. -// It accepts a sequence of key/value pairs, where the value has regex support. For example -// r := mux.NewRouter() -// r.HeadersRegexp("Content-Type", "application/(text|json)", -// "X-Requested-With", "XMLHttpRequest") -// -// The above route will only match if both the request header matches both regular expressions. -// It the value is an empty string, it will match any value if the key is set. -func (r *Route) HeadersRegexp(pairs ...string) *Route { - if r.err == nil { - var headers map[string]*regexp.Regexp - headers, r.err = mapFromPairsToRegex(pairs...) - return r.addMatcher(headerRegexMatcher(headers)) - } - return r -} - -// Host ----------------------------------------------------------------------- - -// Host adds a matcher for the URL host. -// It accepts a template with zero or more URL variables enclosed by {}. -// Variables can define an optional regexp pattern to be matched: -// -// - {name} matches anything until the next dot. -// -// - {name:pattern} matches the given regexp pattern. -// -// For example: -// -// r := mux.NewRouter() -// r.Host("www.example.com") -// r.Host("{subdomain}.domain.com") -// r.Host("{subdomain:[a-z]+}.domain.com") -// -// Variable names must be unique in a given route. They can be retrieved -// calling mux.Vars(request). -func (r *Route) Host(tpl string) *Route { - r.err = r.addRegexpMatcher(tpl, true, false, false) - return r -} - -// MatcherFunc ---------------------------------------------------------------- - -// MatcherFunc is the function signature used by custom matchers. -type MatcherFunc func(*http.Request, *RouteMatch) bool - -func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool { - return m(r, match) -} - -// MatcherFunc adds a custom function to be used as request matcher. -func (r *Route) MatcherFunc(f MatcherFunc) *Route { - return r.addMatcher(f) -} - -// Methods -------------------------------------------------------------------- - -// methodMatcher matches the request against HTTP methods. -type methodMatcher []string - -func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool { - return matchInArray(m, r.Method) -} - -// Methods adds a matcher for HTTP methods. -// It accepts a sequence of one or more methods to be matched, e.g.: -// "GET", "POST", "PUT". -func (r *Route) Methods(methods ...string) *Route { - for k, v := range methods { - methods[k] = strings.ToUpper(v) - } - return r.addMatcher(methodMatcher(methods)) -} - -// Path ----------------------------------------------------------------------- - -// Path adds a matcher for the URL path. -// It accepts a template with zero or more URL variables enclosed by {}. The -// template must start with a "/". -// Variables can define an optional regexp pattern to be matched: -// -// - {name} matches anything until the next slash. -// -// - {name:pattern} matches the given regexp pattern. -// -// For example: -// -// r := mux.NewRouter() -// r.Path("/products/").Handler(ProductsHandler) -// r.Path("/products/{key}").Handler(ProductsHandler) -// r.Path("/articles/{category}/{id:[0-9]+}"). -// Handler(ArticleHandler) -// -// Variable names must be unique in a given route. They can be retrieved -// calling mux.Vars(request). -func (r *Route) Path(tpl string) *Route { - r.err = r.addRegexpMatcher(tpl, false, false, false) - return r -} - -// PathPrefix ----------------------------------------------------------------- - -// PathPrefix adds a matcher for the URL path prefix. This matches if the given -// template is a prefix of the full URL path. See Route.Path() for details on -// the tpl argument. -// -// Note that it does not treat slashes specially ("/foobar/" will be matched by -// the prefix "/foo") so you may want to use a trailing slash here. -// -// Also note that the setting of Router.StrictSlash() has no effect on routes -// with a PathPrefix matcher. -func (r *Route) PathPrefix(tpl string) *Route { - r.err = r.addRegexpMatcher(tpl, false, true, false) - return r -} - -// Query ---------------------------------------------------------------------- - -// Queries adds a matcher for URL query values. -// It accepts a sequence of key/value pairs. Values may define variables. -// For example: -// -// r := mux.NewRouter() -// r.Queries("foo", "bar", "id", "{id:[0-9]+}") -// -// The above route will only match if the URL contains the defined queries -// values, e.g.: ?foo=bar&id=42. -// -// It the value is an empty string, it will match any value if the key is set. -// -// Variables can define an optional regexp pattern to be matched: -// -// - {name} matches anything until the next slash. -// -// - {name:pattern} matches the given regexp pattern. -func (r *Route) Queries(pairs ...string) *Route { - length := len(pairs) - if length%2 != 0 { - r.err = fmt.Errorf( - "mux: number of parameters must be multiple of 2, got %v", pairs) - return nil - } - for i := 0; i < length; i += 2 { - if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, false, true); r.err != nil { - return r - } - } - - return r -} - -// Schemes -------------------------------------------------------------------- - -// schemeMatcher matches the request against URL schemes. -type schemeMatcher []string - -func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool { - return matchInArray(m, r.URL.Scheme) -} - -// Schemes adds a matcher for URL schemes. -// It accepts a sequence of schemes to be matched, e.g.: "http", "https". -func (r *Route) Schemes(schemes ...string) *Route { - for k, v := range schemes { - schemes[k] = strings.ToLower(v) - } - return r.addMatcher(schemeMatcher(schemes)) -} - -// BuildVarsFunc -------------------------------------------------------------- - -// BuildVarsFunc is the function signature used by custom build variable -// functions (which can modify route variables before a route's URL is built). -type BuildVarsFunc func(map[string]string) map[string]string - -// BuildVarsFunc adds a custom function to be used to modify build variables -// before a route's URL is built. -func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route { - r.buildVarsFunc = f - return r -} - -// Subrouter ------------------------------------------------------------------ - -// Subrouter creates a subrouter for the route. -// -// It will test the inner routes only if the parent route matched. For example: -// -// r := mux.NewRouter() -// s := r.Host("www.example.com").Subrouter() -// s.HandleFunc("/products/", ProductsHandler) -// s.HandleFunc("/products/{key}", ProductHandler) -// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) -// -// Here, the routes registered in the subrouter won't be tested if the host -// doesn't match. -func (r *Route) Subrouter() *Router { - router := &Router{parent: r, strictSlash: r.strictSlash} - r.addMatcher(router) - return router -} - -// ---------------------------------------------------------------------------- -// URL building -// ---------------------------------------------------------------------------- - -// URL builds a URL for the route. -// -// It accepts a sequence of key/value pairs for the route variables. For -// example, given this route: -// -// r := mux.NewRouter() -// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). -// Name("article") -// -// ...a URL for it can be built using: -// -// url, err := r.Get("article").URL("category", "technology", "id", "42") -// -// ...which will return an url.URL with the following path: -// -// "/articles/technology/42" -// -// This also works for host variables: -// -// r := mux.NewRouter() -// r.Host("{subdomain}.domain.com"). -// HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). -// Name("article") -// -// // url.String() will be "http://news.domain.com/articles/technology/42" -// url, err := r.Get("article").URL("subdomain", "news", -// "category", "technology", -// "id", "42") -// -// All variables defined in the route are required, and their values must -// conform to the corresponding patterns. -func (r *Route) URL(pairs ...string) (*url.URL, error) { - if r.err != nil { - return nil, r.err - } - if r.regexp == nil { - return nil, errors.New("mux: route doesn't have a host or path") - } - values, err := r.prepareVars(pairs...) - if err != nil { - return nil, err - } - var scheme, host, path string - if r.regexp.host != nil { - // Set a default scheme. - scheme = "http" - if host, err = r.regexp.host.url(values); err != nil { - return nil, err - } - } - if r.regexp.path != nil { - if path, err = r.regexp.path.url(values); err != nil { - return nil, err - } - } - return &url.URL{ - Scheme: scheme, - Host: host, - Path: path, - }, nil -} - -// URLHost builds the host part of the URL for a route. See Route.URL(). -// -// The route must have a host defined. -func (r *Route) URLHost(pairs ...string) (*url.URL, error) { - if r.err != nil { - return nil, r.err - } - if r.regexp == nil || r.regexp.host == nil { - return nil, errors.New("mux: route doesn't have a host") - } - values, err := r.prepareVars(pairs...) - if err != nil { - return nil, err - } - host, err := r.regexp.host.url(values) - if err != nil { - return nil, err - } - return &url.URL{ - Scheme: "http", - Host: host, - }, nil -} - -// URLPath builds the path part of the URL for a route. See Route.URL(). -// -// The route must have a path defined. -func (r *Route) URLPath(pairs ...string) (*url.URL, error) { - if r.err != nil { - return nil, r.err - } - if r.regexp == nil || r.regexp.path == nil { - return nil, errors.New("mux: route doesn't have a path") - } - values, err := r.prepareVars(pairs...) - if err != nil { - return nil, err - } - path, err := r.regexp.path.url(values) - if err != nil { - return nil, err - } - return &url.URL{ - Path: path, - }, nil -} - -// prepareVars converts the route variable pairs into a map. If the route has a -// BuildVarsFunc, it is invoked. -func (r *Route) prepareVars(pairs ...string) (map[string]string, error) { - m, err := mapFromPairsToString(pairs...) - if err != nil { - return nil, err - } - return r.buildVars(m), nil -} - -func (r *Route) buildVars(m map[string]string) map[string]string { - if r.parent != nil { - m = r.parent.buildVars(m) - } - if r.buildVarsFunc != nil { - m = r.buildVarsFunc(m) - } - return m -} - -// ---------------------------------------------------------------------------- -// parentRoute -// ---------------------------------------------------------------------------- - -// parentRoute allows routes to know about parent host and path definitions. -type parentRoute interface { - getNamedRoutes() map[string]*Route - getRegexpGroup() *routeRegexpGroup - buildVars(map[string]string) map[string]string -} - -// getNamedRoutes returns the map where named routes are registered. -func (r *Route) getNamedRoutes() map[string]*Route { - if r.parent == nil { - // During tests router is not always set. - r.parent = NewRouter() - } - return r.parent.getNamedRoutes() -} - -// getRegexpGroup returns regexp definitions from this route. -func (r *Route) getRegexpGroup() *routeRegexpGroup { - if r.regexp == nil { - if r.parent == nil { - // During tests router is not always set. - r.parent = NewRouter() - } - regexp := r.parent.getRegexpGroup() - if regexp == nil { - r.regexp = new(routeRegexpGroup) - } else { - // Copy. - r.regexp = &routeRegexpGroup{ - host: regexp.host, - path: regexp.path, - queries: regexp.queries, - } - } - } - return r.regexp -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/opencontainers/runc/libcontainer/user/user_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/opencontainers/runc/libcontainer/user/user_test.go deleted file mode 100644 index 53b2289bf..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/opencontainers/runc/libcontainer/user/user_test.go +++ /dev/null @@ -1,472 +0,0 @@ -package user - -import ( - "io" - "reflect" - "sort" - "strconv" - "strings" - "testing" -) - -func TestUserParseLine(t *testing.T) { - var ( - a, b string - c []string - d int - ) - - parseLine("", &a, &b) - if a != "" || b != "" { - t.Fatalf("a and b should be empty ('%v', '%v')", a, b) - } - - parseLine("a", &a, &b) - if a != "a" || b != "" { - t.Fatalf("a should be 'a' and b should be empty ('%v', '%v')", a, b) - } - - parseLine("bad boys:corny cows", &a, &b) - if a != "bad boys" || b != "corny cows" { - t.Fatalf("a should be 'bad boys' and b should be 'corny cows' ('%v', '%v')", a, b) - } - - parseLine("", &c) - if len(c) != 0 { - t.Fatalf("c should be empty (%#v)", c) - } - - parseLine("d,e,f:g:h:i,j,k", &c, &a, &b, &c) - if a != "g" || b != "h" || len(c) != 3 || c[0] != "i" || c[1] != "j" || c[2] != "k" { - t.Fatalf("a should be 'g', b should be 'h', and c should be ['i','j','k'] ('%v', '%v', '%#v')", a, b, c) - } - - parseLine("::::::::::", &a, &b, &c) - if a != "" || b != "" || len(c) != 0 { - t.Fatalf("a, b, and c should all be empty ('%v', '%v', '%#v')", a, b, c) - } - - parseLine("not a number", &d) - if d != 0 { - t.Fatalf("d should be 0 (%v)", d) - } - - parseLine("b:12:c", &a, &d, &b) - if a != "b" || b != "c" || d != 12 { - t.Fatalf("a should be 'b' and b should be 'c', and d should be 12 ('%v', '%v', %v)", a, b, d) - } -} - -func TestUserParsePasswd(t *testing.T) { - users, err := ParsePasswdFilter(strings.NewReader(` -root:x:0:0:root:/root:/bin/bash -adm:x:3:4:adm:/var/adm:/bin/false -this is just some garbage data -`), nil) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if len(users) != 3 { - t.Fatalf("Expected 3 users, got %v", len(users)) - } - if users[0].Uid != 0 || users[0].Name != "root" { - t.Fatalf("Expected users[0] to be 0 - root, got %v - %v", users[0].Uid, users[0].Name) - } - if users[1].Uid != 3 || users[1].Name != "adm" { - t.Fatalf("Expected users[1] to be 3 - adm, got %v - %v", users[1].Uid, users[1].Name) - } -} - -func TestUserParseGroup(t *testing.T) { - groups, err := ParseGroupFilter(strings.NewReader(` -root:x:0:root -adm:x:4:root,adm,daemon -this is just some garbage data -`), nil) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if len(groups) != 3 { - t.Fatalf("Expected 3 groups, got %v", len(groups)) - } - if groups[0].Gid != 0 || groups[0].Name != "root" || len(groups[0].List) != 1 { - t.Fatalf("Expected groups[0] to be 0 - root - 1 member, got %v - %v - %v", groups[0].Gid, groups[0].Name, len(groups[0].List)) - } - if groups[1].Gid != 4 || groups[1].Name != "adm" || len(groups[1].List) != 3 { - t.Fatalf("Expected groups[1] to be 4 - adm - 3 members, got %v - %v - %v", groups[1].Gid, groups[1].Name, len(groups[1].List)) - } -} - -func TestValidGetExecUser(t *testing.T) { - const passwdContent = ` -root:x:0:0:root user:/root:/bin/bash -adm:x:42:43:adm:/var/adm:/bin/false -this is just some garbage data -` - const groupContent = ` -root:x:0:root -adm:x:43: -grp:x:1234:root,adm -this is just some garbage data -` - defaultExecUser := ExecUser{ - Uid: 8888, - Gid: 8888, - Sgids: []int{8888}, - Home: "/8888", - } - - tests := []struct { - ref string - expected ExecUser - }{ - { - ref: "root", - expected: ExecUser{ - Uid: 0, - Gid: 0, - Sgids: []int{0, 1234}, - Home: "/root", - }, - }, - { - ref: "adm", - expected: ExecUser{ - Uid: 42, - Gid: 43, - Sgids: []int{1234}, - Home: "/var/adm", - }, - }, - { - ref: "root:adm", - expected: ExecUser{ - Uid: 0, - Gid: 43, - Sgids: defaultExecUser.Sgids, - Home: "/root", - }, - }, - { - ref: "adm:1234", - expected: ExecUser{ - Uid: 42, - Gid: 1234, - Sgids: defaultExecUser.Sgids, - Home: "/var/adm", - }, - }, - { - ref: "42:1234", - expected: ExecUser{ - Uid: 42, - Gid: 1234, - Sgids: defaultExecUser.Sgids, - Home: "/var/adm", - }, - }, - { - ref: "1337:1234", - expected: ExecUser{ - Uid: 1337, - Gid: 1234, - Sgids: defaultExecUser.Sgids, - Home: defaultExecUser.Home, - }, - }, - { - ref: "1337", - expected: ExecUser{ - Uid: 1337, - Gid: defaultExecUser.Gid, - Sgids: defaultExecUser.Sgids, - Home: defaultExecUser.Home, - }, - }, - { - ref: "", - expected: ExecUser{ - Uid: defaultExecUser.Uid, - Gid: defaultExecUser.Gid, - Sgids: defaultExecUser.Sgids, - Home: defaultExecUser.Home, - }, - }, - } - - for _, test := range tests { - passwd := strings.NewReader(passwdContent) - group := strings.NewReader(groupContent) - - execUser, err := GetExecUser(test.ref, &defaultExecUser, passwd, group) - if err != nil { - t.Logf("got unexpected error when parsing '%s': %s", test.ref, err.Error()) - t.Fail() - continue - } - - if !reflect.DeepEqual(test.expected, *execUser) { - t.Logf("got: %#v", execUser) - t.Logf("expected: %#v", test.expected) - t.Fail() - continue - } - } -} - -func TestInvalidGetExecUser(t *testing.T) { - const passwdContent = ` -root:x:0:0:root user:/root:/bin/bash -adm:x:42:43:adm:/var/adm:/bin/false -this is just some garbage data -` - const groupContent = ` -root:x:0:root -adm:x:43: -grp:x:1234:root,adm -this is just some garbage data -` - - tests := []string{ - // No such user/group. - "notuser", - "notuser:notgroup", - "root:notgroup", - "notuser:adm", - "8888:notgroup", - "notuser:8888", - - // Invalid user/group values. - "-1:0", - "0:-3", - "-5:-2", - } - - for _, test := range tests { - passwd := strings.NewReader(passwdContent) - group := strings.NewReader(groupContent) - - execUser, err := GetExecUser(test, nil, passwd, group) - if err == nil { - t.Logf("got unexpected success when parsing '%s': %#v", test, execUser) - t.Fail() - continue - } - } -} - -func TestGetExecUserNilSources(t *testing.T) { - const passwdContent = ` -root:x:0:0:root user:/root:/bin/bash -adm:x:42:43:adm:/var/adm:/bin/false -this is just some garbage data -` - const groupContent = ` -root:x:0:root -adm:x:43: -grp:x:1234:root,adm -this is just some garbage data -` - - defaultExecUser := ExecUser{ - Uid: 8888, - Gid: 8888, - Sgids: []int{8888}, - Home: "/8888", - } - - tests := []struct { - ref string - passwd, group bool - expected ExecUser - }{ - { - ref: "", - passwd: false, - group: false, - expected: ExecUser{ - Uid: 8888, - Gid: 8888, - Sgids: []int{8888}, - Home: "/8888", - }, - }, - { - ref: "root", - passwd: true, - group: false, - expected: ExecUser{ - Uid: 0, - Gid: 0, - Sgids: []int{8888}, - Home: "/root", - }, - }, - { - ref: "0", - passwd: false, - group: false, - expected: ExecUser{ - Uid: 0, - Gid: 8888, - Sgids: []int{8888}, - Home: "/8888", - }, - }, - { - ref: "0:0", - passwd: false, - group: false, - expected: ExecUser{ - Uid: 0, - Gid: 0, - Sgids: []int{8888}, - Home: "/8888", - }, - }, - } - - for _, test := range tests { - var passwd, group io.Reader - - if test.passwd { - passwd = strings.NewReader(passwdContent) - } - - if test.group { - group = strings.NewReader(groupContent) - } - - execUser, err := GetExecUser(test.ref, &defaultExecUser, passwd, group) - if err != nil { - t.Logf("got unexpected error when parsing '%s': %s", test.ref, err.Error()) - t.Fail() - continue - } - - if !reflect.DeepEqual(test.expected, *execUser) { - t.Logf("got: %#v", execUser) - t.Logf("expected: %#v", test.expected) - t.Fail() - continue - } - } -} - -func TestGetAdditionalGroups(t *testing.T) { - const groupContent = ` -root:x:0:root -adm:x:43: -grp:x:1234:root,adm -adm:x:4343:root,adm-duplicate -this is just some garbage data -` - tests := []struct { - groups []string - expected []int - hasError bool - }{ - { - // empty group - groups: []string{}, - expected: []int{}, - }, - { - // single group - groups: []string{"adm"}, - expected: []int{43}, - }, - { - // multiple groups - groups: []string{"adm", "grp"}, - expected: []int{43, 1234}, - }, - { - // invalid group - groups: []string{"adm", "grp", "not-exist"}, - expected: nil, - hasError: true, - }, - { - // group with numeric id - groups: []string{"43"}, - expected: []int{43}, - }, - { - // group with unknown numeric id - groups: []string{"adm", "10001"}, - expected: []int{43, 10001}, - }, - { - // groups specified twice with numeric and name - groups: []string{"adm", "43"}, - expected: []int{43}, - }, - { - // groups with too small id - groups: []string{"-1"}, - expected: nil, - hasError: true, - }, - { - // groups with too large id - groups: []string{strconv.Itoa(1 << 31)}, - expected: nil, - hasError: true, - }, - } - - for _, test := range tests { - group := strings.NewReader(groupContent) - - gids, err := GetAdditionalGroups(test.groups, group) - if test.hasError && err == nil { - t.Errorf("Parse(%#v) expects error but has none", test) - continue - } - if !test.hasError && err != nil { - t.Errorf("Parse(%#v) has error %v", test, err) - continue - } - sort.Sort(sort.IntSlice(gids)) - if !reflect.DeepEqual(gids, test.expected) { - t.Errorf("Gids(%v), expect %v from groups %v", gids, test.expected, test.groups) - } - } -} - -func TestGetAdditionalGroupsNumeric(t *testing.T) { - tests := []struct { - groups []string - expected []int - hasError bool - }{ - { - // numeric groups only - groups: []string{"1234", "5678"}, - expected: []int{1234, 5678}, - }, - { - // numeric and alphabetic - groups: []string{"1234", "fake"}, - expected: nil, - hasError: true, - }, - } - - for _, test := range tests { - gids, err := GetAdditionalGroups(test.groups, nil) - if test.hasError && err == nil { - t.Errorf("Parse(%#v) expects error but has none", test) - continue - } - if !test.hasError && err != nil { - t.Errorf("Parse(%#v) has error %v", test, err) - continue - } - sort.Sort(sort.IntSlice(gids)) - if !reflect.DeepEqual(gids, test.expected) { - t.Errorf("Gids(%v), expect %v from groups %v", gids, test.expected, test.groups) - } - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/net/context/context_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/net/context/context_test.go deleted file mode 100644 index 05345fc5e..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/net/context/context_test.go +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright 2014 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 context - -import ( - "fmt" - "math/rand" - "runtime" - "strings" - "sync" - "testing" - "time" -) - -// otherContext is a Context that's not one of the types defined in context.go. -// This lets us test code paths that differ based on the underlying type of the -// Context. -type otherContext struct { - Context -} - -func TestBackground(t *testing.T) { - c := Background() - if c == nil { - t.Fatalf("Background returned nil") - } - select { - case x := <-c.Done(): - t.Errorf("<-c.Done() == %v want nothing (it should block)", x) - default: - } - if got, want := fmt.Sprint(c), "context.Background"; got != want { - t.Errorf("Background().String() = %q want %q", got, want) - } -} - -func TestTODO(t *testing.T) { - c := TODO() - if c == nil { - t.Fatalf("TODO returned nil") - } - select { - case x := <-c.Done(): - t.Errorf("<-c.Done() == %v want nothing (it should block)", x) - default: - } - if got, want := fmt.Sprint(c), "context.TODO"; got != want { - t.Errorf("TODO().String() = %q want %q", got, want) - } -} - -func TestWithCancel(t *testing.T) { - c1, cancel := WithCancel(Background()) - - if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want { - t.Errorf("c1.String() = %q want %q", got, want) - } - - o := otherContext{c1} - c2, _ := WithCancel(o) - contexts := []Context{c1, o, c2} - - for i, c := range contexts { - if d := c.Done(); d == nil { - t.Errorf("c[%d].Done() == %v want non-nil", i, d) - } - if e := c.Err(); e != nil { - t.Errorf("c[%d].Err() == %v want nil", i, e) - } - - select { - case x := <-c.Done(): - t.Errorf("<-c.Done() == %v want nothing (it should block)", x) - default: - } - } - - cancel() - time.Sleep(100 * time.Millisecond) // let cancelation propagate - - for i, c := range contexts { - select { - case <-c.Done(): - default: - t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i) - } - if e := c.Err(); e != Canceled { - t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled) - } - } -} - -func TestParentFinishesChild(t *testing.T) { - // Context tree: - // parent -> cancelChild - // parent -> valueChild -> timerChild - parent, cancel := WithCancel(Background()) - cancelChild, stop := WithCancel(parent) - defer stop() - valueChild := WithValue(parent, "key", "value") - timerChild, stop := WithTimeout(valueChild, 10000*time.Hour) - defer stop() - - select { - case x := <-parent.Done(): - t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) - case x := <-cancelChild.Done(): - t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x) - case x := <-timerChild.Done(): - t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x) - case x := <-valueChild.Done(): - t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x) - default: - } - - // The parent's children should contain the two cancelable children. - pc := parent.(*cancelCtx) - cc := cancelChild.(*cancelCtx) - tc := timerChild.(*timerCtx) - pc.mu.Lock() - if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] { - t.Errorf("bad linkage: pc.children = %v, want %v and %v", - pc.children, cc, tc) - } - pc.mu.Unlock() - - if p, ok := parentCancelCtx(cc.Context); !ok || p != pc { - t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc) - } - if p, ok := parentCancelCtx(tc.Context); !ok || p != pc { - t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc) - } - - cancel() - - pc.mu.Lock() - if len(pc.children) != 0 { - t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children) - } - pc.mu.Unlock() - - // parent and children should all be finished. - check := func(ctx Context, name string) { - select { - case <-ctx.Done(): - default: - t.Errorf("<-%s.Done() blocked, but shouldn't have", name) - } - if e := ctx.Err(); e != Canceled { - t.Errorf("%s.Err() == %v want %v", name, e, Canceled) - } - } - check(parent, "parent") - check(cancelChild, "cancelChild") - check(valueChild, "valueChild") - check(timerChild, "timerChild") - - // WithCancel should return a canceled context on a canceled parent. - precanceledChild := WithValue(parent, "key", "value") - select { - case <-precanceledChild.Done(): - default: - t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have") - } - if e := precanceledChild.Err(); e != Canceled { - t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled) - } -} - -func TestChildFinishesFirst(t *testing.T) { - cancelable, stop := WithCancel(Background()) - defer stop() - for _, parent := range []Context{Background(), cancelable} { - child, cancel := WithCancel(parent) - - select { - case x := <-parent.Done(): - t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) - case x := <-child.Done(): - t.Errorf("<-child.Done() == %v want nothing (it should block)", x) - default: - } - - cc := child.(*cancelCtx) - pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background() - if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) { - t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok) - } - - if pcok { - pc.mu.Lock() - if len(pc.children) != 1 || !pc.children[cc] { - t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc) - } - pc.mu.Unlock() - } - - cancel() - - if pcok { - pc.mu.Lock() - if len(pc.children) != 0 { - t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children) - } - pc.mu.Unlock() - } - - // child should be finished. - select { - case <-child.Done(): - default: - t.Errorf("<-child.Done() blocked, but shouldn't have") - } - if e := child.Err(); e != Canceled { - t.Errorf("child.Err() == %v want %v", e, Canceled) - } - - // parent should not be finished. - select { - case x := <-parent.Done(): - t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) - default: - } - if e := parent.Err(); e != nil { - t.Errorf("parent.Err() == %v want nil", e) - } - } -} - -func testDeadline(c Context, wait time.Duration, t *testing.T) { - select { - case <-time.After(wait): - t.Fatalf("context should have timed out") - case <-c.Done(): - } - if e := c.Err(); e != DeadlineExceeded { - t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded) - } -} - -func TestDeadline(t *testing.T) { - c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) - if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { - t.Errorf("c.String() = %q want prefix %q", got, prefix) - } - testDeadline(c, 200*time.Millisecond, t) - - c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) - o := otherContext{c} - testDeadline(o, 200*time.Millisecond, t) - - c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) - o = otherContext{c} - c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond)) - testDeadline(c, 200*time.Millisecond, t) -} - -func TestTimeout(t *testing.T) { - c, _ := WithTimeout(Background(), 100*time.Millisecond) - if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { - t.Errorf("c.String() = %q want prefix %q", got, prefix) - } - testDeadline(c, 200*time.Millisecond, t) - - c, _ = WithTimeout(Background(), 100*time.Millisecond) - o := otherContext{c} - testDeadline(o, 200*time.Millisecond, t) - - c, _ = WithTimeout(Background(), 100*time.Millisecond) - o = otherContext{c} - c, _ = WithTimeout(o, 300*time.Millisecond) - testDeadline(c, 200*time.Millisecond, t) -} - -func TestCanceledTimeout(t *testing.T) { - c, _ := WithTimeout(Background(), 200*time.Millisecond) - o := otherContext{c} - c, cancel := WithTimeout(o, 400*time.Millisecond) - cancel() - time.Sleep(100 * time.Millisecond) // let cancelation propagate - select { - case <-c.Done(): - default: - t.Errorf("<-c.Done() blocked, but shouldn't have") - } - if e := c.Err(); e != Canceled { - t.Errorf("c.Err() == %v want %v", e, Canceled) - } -} - -type key1 int -type key2 int - -var k1 = key1(1) -var k2 = key2(1) // same int as k1, different type -var k3 = key2(3) // same type as k2, different int - -func TestValues(t *testing.T) { - check := func(c Context, nm, v1, v2, v3 string) { - if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 { - t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0) - } - if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 { - t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0) - } - if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 { - t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0) - } - } - - c0 := Background() - check(c0, "c0", "", "", "") - - c1 := WithValue(Background(), k1, "c1k1") - check(c1, "c1", "c1k1", "", "") - - if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want { - t.Errorf("c.String() = %q want %q", got, want) - } - - c2 := WithValue(c1, k2, "c2k2") - check(c2, "c2", "c1k1", "c2k2", "") - - c3 := WithValue(c2, k3, "c3k3") - check(c3, "c2", "c1k1", "c2k2", "c3k3") - - c4 := WithValue(c3, k1, nil) - check(c4, "c4", "", "c2k2", "c3k3") - - o0 := otherContext{Background()} - check(o0, "o0", "", "", "") - - o1 := otherContext{WithValue(Background(), k1, "c1k1")} - check(o1, "o1", "c1k1", "", "") - - o2 := WithValue(o1, k2, "o2k2") - check(o2, "o2", "c1k1", "o2k2", "") - - o3 := otherContext{c4} - check(o3, "o3", "", "c2k2", "c3k3") - - o4 := WithValue(o3, k3, nil) - check(o4, "o4", "", "c2k2", "") -} - -func TestAllocs(t *testing.T) { - bg := Background() - for _, test := range []struct { - desc string - f func() - limit float64 - gccgoLimit float64 - }{ - { - desc: "Background()", - f: func() { Background() }, - limit: 0, - gccgoLimit: 0, - }, - { - desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1), - f: func() { - c := WithValue(bg, k1, nil) - c.Value(k1) - }, - limit: 3, - gccgoLimit: 3, - }, - { - desc: "WithTimeout(bg, 15*time.Millisecond)", - f: func() { - c, _ := WithTimeout(bg, 15*time.Millisecond) - <-c.Done() - }, - limit: 8, - gccgoLimit: 15, - }, - { - desc: "WithCancel(bg)", - f: func() { - c, cancel := WithCancel(bg) - cancel() - <-c.Done() - }, - limit: 5, - gccgoLimit: 8, - }, - { - desc: "WithTimeout(bg, 100*time.Millisecond)", - f: func() { - c, cancel := WithTimeout(bg, 100*time.Millisecond) - cancel() - <-c.Done() - }, - limit: 8, - gccgoLimit: 25, - }, - } { - limit := test.limit - if runtime.Compiler == "gccgo" { - // gccgo does not yet do escape analysis. - // TOOD(iant): Remove this when gccgo does do escape analysis. - limit = test.gccgoLimit - } - if n := testing.AllocsPerRun(100, test.f); n > limit { - t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit)) - } - } -} - -func TestSimultaneousCancels(t *testing.T) { - root, cancel := WithCancel(Background()) - m := map[Context]CancelFunc{root: cancel} - q := []Context{root} - // Create a tree of contexts. - for len(q) != 0 && len(m) < 100 { - parent := q[0] - q = q[1:] - for i := 0; i < 4; i++ { - ctx, cancel := WithCancel(parent) - m[ctx] = cancel - q = append(q, ctx) - } - } - // Start all the cancels in a random order. - var wg sync.WaitGroup - wg.Add(len(m)) - for _, cancel := range m { - go func(cancel CancelFunc) { - cancel() - wg.Done() - }(cancel) - } - // Wait on all the contexts in a random order. - for ctx := range m { - select { - case <-ctx.Done(): - case <-time.After(1 * time.Second): - buf := make([]byte, 10<<10) - n := runtime.Stack(buf, true) - t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n]) - } - } - // Wait for all the cancel functions to return. - done := make(chan struct{}) - go func() { - wg.Wait() - close(done) - }() - select { - case <-done: - case <-time.After(1 * time.Second): - buf := make([]byte, 10<<10) - n := runtime.Stack(buf, true) - t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n]) - } -} - -func TestInterlockedCancels(t *testing.T) { - parent, cancelParent := WithCancel(Background()) - child, cancelChild := WithCancel(parent) - go func() { - parent.Done() - cancelChild() - }() - cancelParent() - select { - case <-child.Done(): - case <-time.After(1 * time.Second): - buf := make([]byte, 10<<10) - n := runtime.Stack(buf, true) - t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n]) - } -} - -func TestLayersCancel(t *testing.T) { - testLayers(t, time.Now().UnixNano(), false) -} - -func TestLayersTimeout(t *testing.T) { - testLayers(t, time.Now().UnixNano(), true) -} - -func testLayers(t *testing.T, seed int64, testTimeout bool) { - rand.Seed(seed) - errorf := func(format string, a ...interface{}) { - t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...) - } - const ( - timeout = 200 * time.Millisecond - minLayers = 30 - ) - type value int - var ( - vals []*value - cancels []CancelFunc - numTimers int - ctx = Background() - ) - for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ { - switch rand.Intn(3) { - case 0: - v := new(value) - ctx = WithValue(ctx, v, v) - vals = append(vals, v) - case 1: - var cancel CancelFunc - ctx, cancel = WithCancel(ctx) - cancels = append(cancels, cancel) - case 2: - var cancel CancelFunc - ctx, cancel = WithTimeout(ctx, timeout) - cancels = append(cancels, cancel) - numTimers++ - } - } - checkValues := func(when string) { - for _, key := range vals { - if val := ctx.Value(key).(*value); key != val { - errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key) - } - } - } - select { - case <-ctx.Done(): - errorf("ctx should not be canceled yet") - default: - } - if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) { - t.Errorf("ctx.String() = %q want prefix %q", s, prefix) - } - t.Log(ctx) - checkValues("before cancel") - if testTimeout { - select { - case <-ctx.Done(): - case <-time.After(timeout + 100*time.Millisecond): - errorf("ctx should have timed out") - } - checkValues("after timeout") - } else { - cancel := cancels[rand.Intn(len(cancels))] - cancel() - select { - case <-ctx.Done(): - default: - errorf("ctx should be canceled") - } - checkValues("after cancel") - } -} - -func TestCancelRemoves(t *testing.T) { - checkChildren := func(when string, ctx Context, want int) { - if got := len(ctx.(*cancelCtx).children); got != want { - t.Errorf("%s: context has %d children, want %d", when, got, want) - } - } - - ctx, _ := WithCancel(Background()) - checkChildren("after creation", ctx, 0) - _, cancel := WithCancel(ctx) - checkChildren("with WithCancel child ", ctx, 1) - cancel() - checkChildren("after cancelling WithCancel child", ctx, 0) - - ctx, _ = WithCancel(Background()) - checkChildren("after creation", ctx, 0) - _, cancel = WithTimeout(ctx, 60*time.Minute) - checkChildren("with WithTimeout child ", ctx, 1) - cancel() - checkChildren("after cancelling WithTimeout child", ctx, 0) -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/net/context/withtimeout_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/net/context/withtimeout_test.go deleted file mode 100644 index 00d5d1ca9..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/net/context/withtimeout_test.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2014 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 context_test - -import ( - "fmt" - "time" - - "github.com/fsouza/go-dockerclient/external/golang.org/x/net/context" -) - -func ExampleWithTimeout() { - // Pass a context with a timeout to tell a blocking function that it - // should abandon its work after the timeout elapses. - ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond) - select { - case <-time.After(200 * time.Millisecond): - fmt.Println("overslept") - case <-ctx.Done(): - fmt.Println(ctx.Err()) // prints "context deadline exceeded" - } - // Output: - // context deadline exceeded -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/creds_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/creds_test.go deleted file mode 100644 index 31cf39b1e..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/creds_test.go +++ /dev/null @@ -1,121 +0,0 @@ -// 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 - -package unix_test - -import ( - "bytes" - "net" - "os" - "syscall" - "testing" - - "github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix" -) - -// TestSCMCredentials tests the sending and receiving of credentials -// (PID, UID, GID) in an ancillary message between two UNIX -// sockets. The SO_PASSCRED socket option is enabled on the sending -// socket for this to work. -func TestSCMCredentials(t *testing.T) { - fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM, 0) - if err != nil { - t.Fatalf("Socketpair: %v", err) - } - defer unix.Close(fds[0]) - defer unix.Close(fds[1]) - - err = unix.SetsockoptInt(fds[0], unix.SOL_SOCKET, unix.SO_PASSCRED, 1) - if err != nil { - t.Fatalf("SetsockoptInt: %v", err) - } - - srvFile := os.NewFile(uintptr(fds[0]), "server") - defer srvFile.Close() - srv, err := net.FileConn(srvFile) - if err != nil { - t.Errorf("FileConn: %v", err) - return - } - defer srv.Close() - - cliFile := os.NewFile(uintptr(fds[1]), "client") - defer cliFile.Close() - cli, err := net.FileConn(cliFile) - if err != nil { - t.Errorf("FileConn: %v", err) - return - } - defer cli.Close() - - var ucred unix.Ucred - if os.Getuid() != 0 { - ucred.Pid = int32(os.Getpid()) - ucred.Uid = 0 - ucred.Gid = 0 - oob := unix.UnixCredentials(&ucred) - _, _, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil) - if op, ok := err.(*net.OpError); ok { - err = op.Err - } - if sys, ok := err.(*os.SyscallError); ok { - err = sys.Err - } - if err != syscall.EPERM { - t.Fatalf("WriteMsgUnix failed with %v, want EPERM", err) - } - } - - ucred.Pid = int32(os.Getpid()) - ucred.Uid = uint32(os.Getuid()) - ucred.Gid = uint32(os.Getgid()) - oob := unix.UnixCredentials(&ucred) - - // this is going to send a dummy byte - n, oobn, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil) - if err != nil { - t.Fatalf("WriteMsgUnix: %v", err) - } - if n != 0 { - t.Fatalf("WriteMsgUnix n = %d, want 0", n) - } - if oobn != len(oob) { - t.Fatalf("WriteMsgUnix oobn = %d, want %d", oobn, len(oob)) - } - - oob2 := make([]byte, 10*len(oob)) - n, oobn2, flags, _, err := srv.(*net.UnixConn).ReadMsgUnix(nil, oob2) - if err != nil { - t.Fatalf("ReadMsgUnix: %v", err) - } - if flags != 0 { - t.Fatalf("ReadMsgUnix flags = 0x%x, want 0", flags) - } - if n != 1 { - t.Fatalf("ReadMsgUnix n = %d, want 1 (dummy byte)", n) - } - if oobn2 != oobn { - // without SO_PASSCRED set on the socket, ReadMsgUnix will - // return zero oob bytes - t.Fatalf("ReadMsgUnix oobn = %d, want %d", oobn2, oobn) - } - oob2 = oob2[:oobn2] - if !bytes.Equal(oob, oob2) { - t.Fatal("ReadMsgUnix oob bytes don't match") - } - - scm, err := unix.ParseSocketControlMessage(oob2) - if err != nil { - t.Fatalf("ParseSocketControlMessage: %v", err) - } - newUcred, err := unix.ParseUnixCredentials(&scm[0]) - if err != nil { - t.Fatalf("ParseUnixCredentials: %v", err) - } - if *newUcred != ucred { - t.Fatalf("ParseUnixCredentials = %+v, want %+v", newUcred, ucred) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/export_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/export_test.go deleted file mode 100644 index b4fdd970b..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/export_test.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2015 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 linux netbsd openbsd solaris - -package unix - -var Itoa = itoa diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/mmap_unix_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/mmap_unix_test.go deleted file mode 100644 index 30aa6fe23..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/mmap_unix_test.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2014 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 linux netbsd openbsd solaris - -package unix_test - -import ( - "testing" - - "github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix" -) - -func TestMmap(t *testing.T) { - b, err := unix.Mmap(-1, 0, unix.Getpagesize(), unix.PROT_NONE, unix.MAP_ANON|unix.MAP_PRIVATE) - if err != nil { - t.Fatalf("Mmap: %v", err) - } - if err := unix.Munmap(b); err != nil { - t.Fatalf("Munmap: %v", err) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_bsd_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_bsd_test.go deleted file mode 100644 index 7cd360647..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_bsd_test.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2014 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 openbsd - -package unix_test - -import ( - "testing" - - "github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix" -) - -const MNT_WAIT = 1 - -func TestGetfsstat(t *testing.T) { - n, err := unix.Getfsstat(nil, MNT_WAIT) - if err != nil { - t.Fatal(err) - } - - data := make([]unix.Statfs_t, n) - n, err = unix.Getfsstat(data, MNT_WAIT) - if err != nil { - t.Fatal(err) - } - - empty := unix.Statfs_t{} - for _, stat := range data { - if stat == empty { - t.Fatal("an empty Statfs_t struct was returned") - } - } -} - -func TestSysctlRaw(t *testing.T) { - _, err := unix.SysctlRaw("kern.proc.pid", unix.Getpid()) - if err != nil { - t.Fatal(err) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_freebsd_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_freebsd_test.go deleted file mode 100644 index 62f8052a8..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_freebsd_test.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2014 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 freebsd - -package unix_test - -import ( - "testing" - - "github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix" -) - -func TestSysctUint64(t *testing.T) { - _, err := unix.SysctlUint64("vm.max_kernel_address") - if err != nil { - t.Fatal(err) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_test.go deleted file mode 100644 index b1b2c23a0..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_test.go +++ /dev/null @@ -1,50 +0,0 @@ -// 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 linux netbsd openbsd solaris - -package unix_test - -import ( - "fmt" - "testing" - - "github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix" -) - -func testSetGetenv(t *testing.T, key, value string) { - err := unix.Setenv(key, value) - if err != nil { - t.Fatalf("Setenv failed to set %q: %v", value, err) - } - newvalue, found := unix.Getenv(key) - if !found { - t.Fatalf("Getenv failed to find %v variable (want value %q)", key, value) - } - if newvalue != value { - t.Fatalf("Getenv(%v) = %q; want %q", key, newvalue, value) - } -} - -func TestEnv(t *testing.T) { - testSetGetenv(t, "TESTENV", "AVALUE") - // make sure TESTENV gets set to "", not deleted - testSetGetenv(t, "TESTENV", "") -} - -func TestItoa(t *testing.T) { - // Make most negative integer: 0x8000... - i := 1 - for i<<1 != 0 { - i <<= 1 - } - if i >= 0 { - t.Fatal("bad math") - } - s := unix.Itoa(i) - f := fmt.Sprint(i) - if s != f { - t.Fatalf("itoa(%d) = %s, want %s", i, s, f) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_unix_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_unix_test.go deleted file mode 100644 index ddad90e5f..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix/syscall_unix_test.go +++ /dev/null @@ -1,318 +0,0 @@ -// 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 linux netbsd openbsd solaris - -package unix_test - -import ( - "flag" - "fmt" - "io/ioutil" - "net" - "os" - "os/exec" - "path/filepath" - "runtime" - "testing" - "time" - - "github.com/fsouza/go-dockerclient/external/golang.org/x/sys/unix" -) - -// Tests that below functions, structures and constants are consistent -// on all Unix-like systems. -func _() { - // program scheduling priority functions and constants - var ( - _ func(int, int, int) error = unix.Setpriority - _ func(int, int) (int, error) = unix.Getpriority - ) - const ( - _ int = unix.PRIO_USER - _ int = unix.PRIO_PROCESS - _ int = unix.PRIO_PGRP - ) - - // termios constants - const ( - _ int = unix.TCIFLUSH - _ int = unix.TCIOFLUSH - _ int = unix.TCOFLUSH - ) - - // fcntl file locking structure and constants - var ( - _ = unix.Flock_t{ - Type: int16(0), - Whence: int16(0), - Start: int64(0), - Len: int64(0), - Pid: int32(0), - } - ) - const ( - _ = unix.F_GETLK - _ = unix.F_SETLK - _ = unix.F_SETLKW - ) -} - -// TestFcntlFlock tests whether the file locking structure matches -// the calling convention of each kernel. -func TestFcntlFlock(t *testing.T) { - name := filepath.Join(os.TempDir(), "TestFcntlFlock") - fd, err := unix.Open(name, unix.O_CREAT|unix.O_RDWR|unix.O_CLOEXEC, 0) - if err != nil { - t.Fatalf("Open failed: %v", err) - } - defer unix.Unlink(name) - defer unix.Close(fd) - flock := unix.Flock_t{ - Type: unix.F_RDLCK, - Start: 0, Len: 0, Whence: 1, - } - if err := unix.FcntlFlock(uintptr(fd), unix.F_GETLK, &flock); err != nil { - t.Fatalf("FcntlFlock failed: %v", err) - } -} - -// TestPassFD tests passing a file descriptor over a Unix socket. -// -// This test involved both a parent and child process. The parent -// process is invoked as a normal test, with "go test", which then -// runs the child process by running the current test binary with args -// "-test.run=^TestPassFD$" and an environment variable used to signal -// that the test should become the child process instead. -func TestPassFD(t *testing.T) { - switch runtime.GOOS { - case "dragonfly": - // TODO(jsing): Figure out why sendmsg is returning EINVAL. - t.Skip("skipping test on dragonfly") - case "solaris": - // TODO(aram): Figure out why ReadMsgUnix is returning empty message. - t.Skip("skipping test on solaris, see issue 7402") - } - if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { - passFDChild() - return - } - - tempDir, err := ioutil.TempDir("", "TestPassFD") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tempDir) - - fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM, 0) - if err != nil { - t.Fatalf("Socketpair: %v", err) - } - defer unix.Close(fds[0]) - defer unix.Close(fds[1]) - writeFile := os.NewFile(uintptr(fds[0]), "child-writes") - readFile := os.NewFile(uintptr(fds[1]), "parent-reads") - defer writeFile.Close() - defer readFile.Close() - - cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", tempDir) - cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} - if lp := os.Getenv("LD_LIBRARY_PATH"); lp != "" { - cmd.Env = append(cmd.Env, "LD_LIBRARY_PATH="+lp) - } - cmd.ExtraFiles = []*os.File{writeFile} - - out, err := cmd.CombinedOutput() - if len(out) > 0 || err != nil { - t.Fatalf("child process: %q, %v", out, err) - } - - c, err := net.FileConn(readFile) - if err != nil { - t.Fatalf("FileConn: %v", err) - } - defer c.Close() - - uc, ok := c.(*net.UnixConn) - if !ok { - t.Fatalf("unexpected FileConn type; expected UnixConn, got %T", c) - } - - buf := make([]byte, 32) // expect 1 byte - oob := make([]byte, 32) // expect 24 bytes - closeUnix := time.AfterFunc(5*time.Second, func() { - t.Logf("timeout reading from unix socket") - uc.Close() - }) - _, oobn, _, _, err := uc.ReadMsgUnix(buf, oob) - closeUnix.Stop() - - scms, err := unix.ParseSocketControlMessage(oob[:oobn]) - if err != nil { - t.Fatalf("ParseSocketControlMessage: %v", err) - } - if len(scms) != 1 { - t.Fatalf("expected 1 SocketControlMessage; got scms = %#v", scms) - } - scm := scms[0] - gotFds, err := unix.ParseUnixRights(&scm) - if err != nil { - t.Fatalf("unix.ParseUnixRights: %v", err) - } - if len(gotFds) != 1 { - t.Fatalf("wanted 1 fd; got %#v", gotFds) - } - - f := os.NewFile(uintptr(gotFds[0]), "fd-from-child") - defer f.Close() - - got, err := ioutil.ReadAll(f) - want := "Hello from child process!\n" - if string(got) != want { - t.Errorf("child process ReadAll: %q, %v; want %q", got, err, want) - } -} - -// passFDChild is the child process used by TestPassFD. -func passFDChild() { - defer os.Exit(0) - - // Look for our fd. It should be fd 3, but we work around an fd leak - // bug here (http://golang.org/issue/2603) to let it be elsewhere. - var uc *net.UnixConn - for fd := uintptr(3); fd <= 10; fd++ { - f := os.NewFile(fd, "unix-conn") - var ok bool - netc, _ := net.FileConn(f) - uc, ok = netc.(*net.UnixConn) - if ok { - break - } - } - if uc == nil { - fmt.Println("failed to find unix fd") - return - } - - // Make a file f to send to our parent process on uc. - // We make it in tempDir, which our parent will clean up. - flag.Parse() - tempDir := flag.Arg(0) - f, err := ioutil.TempFile(tempDir, "") - if err != nil { - fmt.Printf("TempFile: %v", err) - return - } - - f.Write([]byte("Hello from child process!\n")) - f.Seek(0, 0) - - rights := unix.UnixRights(int(f.Fd())) - dummyByte := []byte("x") - n, oobn, err := uc.WriteMsgUnix(dummyByte, rights, nil) - if err != nil { - fmt.Printf("WriteMsgUnix: %v", err) - return - } - if n != 1 || oobn != len(rights) { - fmt.Printf("WriteMsgUnix = %d, %d; want 1, %d", n, oobn, len(rights)) - return - } -} - -// TestUnixRightsRoundtrip tests that UnixRights, ParseSocketControlMessage, -// and ParseUnixRights are able to successfully round-trip lists of file descriptors. -func TestUnixRightsRoundtrip(t *testing.T) { - testCases := [...][][]int{ - {{42}}, - {{1, 2}}, - {{3, 4, 5}}, - {{}}, - {{1, 2}, {3, 4, 5}, {}, {7}}, - } - for _, testCase := range testCases { - b := []byte{} - var n int - for _, fds := range testCase { - // Last assignment to n wins - n = len(b) + unix.CmsgLen(4*len(fds)) - b = append(b, unix.UnixRights(fds...)...) - } - // Truncate b - b = b[:n] - - scms, err := unix.ParseSocketControlMessage(b) - if err != nil { - t.Fatalf("ParseSocketControlMessage: %v", err) - } - if len(scms) != len(testCase) { - t.Fatalf("expected %v SocketControlMessage; got scms = %#v", len(testCase), scms) - } - for i, scm := range scms { - gotFds, err := unix.ParseUnixRights(&scm) - if err != nil { - t.Fatalf("ParseUnixRights: %v", err) - } - wantFds := testCase[i] - if len(gotFds) != len(wantFds) { - t.Fatalf("expected %v fds, got %#v", len(wantFds), gotFds) - } - for j, fd := range gotFds { - if fd != wantFds[j] { - t.Fatalf("expected fd %v, got %v", wantFds[j], fd) - } - } - } - } -} - -func TestRlimit(t *testing.T) { - var rlimit, zero unix.Rlimit - err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit) - if err != nil { - t.Fatalf("Getrlimit: save failed: %v", err) - } - if zero == rlimit { - t.Fatalf("Getrlimit: save failed: got zero value %#v", rlimit) - } - set := rlimit - set.Cur = set.Max - 1 - err = unix.Setrlimit(unix.RLIMIT_NOFILE, &set) - if err != nil { - t.Fatalf("Setrlimit: set failed: %#v %v", set, err) - } - var get unix.Rlimit - err = unix.Getrlimit(unix.RLIMIT_NOFILE, &get) - if err != nil { - t.Fatalf("Getrlimit: get failed: %v", err) - } - set = rlimit - set.Cur = set.Max - 1 - if set != get { - // Seems like Darwin requires some privilege to - // increase the soft limit of rlimit sandbox, though - // Setrlimit never reports an error. - switch runtime.GOOS { - case "darwin": - default: - t.Fatalf("Rlimit: change failed: wanted %#v got %#v", set, get) - } - } - err = unix.Setrlimit(unix.RLIMIT_NOFILE, &rlimit) - if err != nil { - t.Fatalf("Setrlimit: restore failed: %#v %v", rlimit, err) - } -} - -func TestSeekFailure(t *testing.T) { - _, err := unix.Seek(-1, 0, 0) - if err == nil { - t.Fatalf("Seek(-1, 0, 0) did not fail") - } - str := err.Error() // used to crash on Linux - t.Logf("Seek: %v", str) - if str == "" { - t.Fatalf("Seek(-1, 0, 0) return error with empty message") - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image_test.go deleted file mode 100644 index c7544a643..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image_test.go +++ /dev/null @@ -1,1018 +0,0 @@ -// Copyright 2015 go-dockerclient 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 docker - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "io/ioutil" - "net" - "net/http" - "net/url" - "os" - "reflect" - "strings" - "testing" - "time" -) - -func newTestClient(rt *FakeRoundTripper) Client { - endpoint := "http://localhost:4243" - u, _ := parseEndpoint("http://localhost:4243", false) - testAPIVersion, _ := NewAPIVersion("1.17") - client := Client{ - HTTPClient: &http.Client{Transport: rt}, - Dialer: &net.Dialer{}, - endpoint: endpoint, - endpointURL: u, - SkipServerVersionCheck: true, - serverAPIVersion: testAPIVersion, - } - return client -} - -type stdoutMock struct { - *bytes.Buffer -} - -func (m stdoutMock) Close() error { - return nil -} - -type stdinMock struct { - *bytes.Buffer -} - -func (m stdinMock) Close() error { - return nil -} - -func TestListImages(t *testing.T) { - body := `[ - { - "Repository":"base", - "Tag":"ubuntu-12.10", - "Id":"b750fe79269d", - "Created":1364102658 - }, - { - "Repository":"base", - "Tag":"ubuntu-quantal", - "Id":"b750fe79269d", - "Created":1364102658 - }, - { - "RepoTag": [ - "ubuntu:12.04", - "ubuntu:precise", - "ubuntu:latest" - ], - "Id": "8dbd9e392a964c", - "Created": 1365714795, - "Size": 131506275, - "VirtualSize": 131506275 - }, - { - "RepoTag": [ - "ubuntu:12.10", - "ubuntu:quantal" - ], - "ParentId": "27cf784147099545", - "Id": "b750fe79269d2e", - "Created": 1364102658, - "Size": 24653, - "VirtualSize": 180116135 - } -]` - var expected []APIImages - err := json.Unmarshal([]byte(body), &expected) - if err != nil { - t.Fatal(err) - } - client := newTestClient(&FakeRoundTripper{message: body, status: http.StatusOK}) - images, err := client.ListImages(ListImagesOptions{}) - if err != nil { - t.Error(err) - } - if !reflect.DeepEqual(images, expected) { - t.Errorf("ListImages: Wrong return value. Want %#v. Got %#v.", expected, images) - } -} - -func TestListImagesParameters(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "null", status: http.StatusOK} - client := newTestClient(fakeRT) - _, err := client.ListImages(ListImagesOptions{All: false}) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - if req.Method != "GET" { - t.Errorf("ListImages({All: false}: Wrong HTTP method. Want GET. Got %s.", req.Method) - } - if all := req.URL.Query().Get("all"); all != "0" && all != "" { - t.Errorf("ListImages({All: false}): Wrong parameter. Want all=0 or not present at all. Got all=%s", all) - } - fakeRT.Reset() - _, err = client.ListImages(ListImagesOptions{All: true}) - if err != nil { - t.Fatal(err) - } - req = fakeRT.requests[0] - if all := req.URL.Query().Get("all"); all != "1" { - t.Errorf("ListImages({All: true}): Wrong parameter. Want all=1. Got all=%s", all) - } - fakeRT.Reset() - _, err = client.ListImages(ListImagesOptions{Filters: map[string][]string{ - "dangling": {"true"}, - }}) - if err != nil { - t.Fatal(err) - } - req = fakeRT.requests[0] - body := req.URL.Query().Get("filters") - var filters map[string][]string - err = json.Unmarshal([]byte(body), &filters) - if err != nil { - t.Fatal(err) - } - if len(filters["dangling"]) != 1 || filters["dangling"][0] != "true" { - t.Errorf("ListImages(dangling=[true]): Wrong filter map. Want dangling=[true], got dangling=%v", filters["dangling"]) - } -} - -func TestImageHistory(t *testing.T) { - body := `[ - { - "Id": "25daec02219d2d852f7526137213a9b199926b4b24e732eab5b8bc6c49bd470e", - "Tags": [ - "debian:7.6", - "debian:latest", - "debian:7", - "debian:wheezy" - ], - "Created": 1409856216, - "CreatedBy": "/bin/sh -c #(nop) CMD [/bin/bash]" - }, - { - "Id": "41026a5347fb5be6ed16115bf22df8569697139f246186de9ae8d4f67c335dce", - "Created": 1409856213, - "CreatedBy": "/bin/sh -c #(nop) ADD file:1ee9e97209d00e3416a4543b23574cc7259684741a46bbcbc755909b8a053a38 in /", - "Size": 85178663 - }, - { - "Id": "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158", - "Tags": [ - "scratch:latest" - ], - "Created": 1371157430 - } -]` - var expected []ImageHistory - err := json.Unmarshal([]byte(body), &expected) - if err != nil { - t.Fatal(err) - } - client := newTestClient(&FakeRoundTripper{message: body, status: http.StatusOK}) - history, err := client.ImageHistory("debian:latest") - if err != nil { - t.Error(err) - } - if !reflect.DeepEqual(history, expected) { - t.Errorf("ImageHistory: Wrong return value. Want %#v. Got %#v.", expected, history) - } -} - -func TestRemoveImage(t *testing.T) { - name := "test" - fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent} - client := newTestClient(fakeRT) - err := client.RemoveImage(name) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expectedMethod := "DELETE" - if req.Method != expectedMethod { - t.Errorf("RemoveImage(%q): Wrong HTTP method. Want %s. Got %s.", name, expectedMethod, req.Method) - } - u, _ := url.Parse(client.getURL("/images/" + name)) - if req.URL.Path != u.Path { - t.Errorf("RemoveImage(%q): Wrong request path. Want %q. Got %q.", name, u.Path, req.URL.Path) - } -} - -func TestRemoveImageNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such image", status: http.StatusNotFound}) - err := client.RemoveImage("test:") - if err != ErrNoSuchImage { - t.Errorf("RemoveImage: wrong error. Want %#v. Got %#v.", ErrNoSuchImage, err) - } -} - -func TestRemoveImageExtended(t *testing.T) { - name := "test" - fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent} - client := newTestClient(fakeRT) - err := client.RemoveImageExtended(name, RemoveImageOptions{Force: true, NoPrune: true}) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expectedMethod := "DELETE" - if req.Method != expectedMethod { - t.Errorf("RemoveImage(%q): Wrong HTTP method. Want %s. Got %s.", name, expectedMethod, req.Method) - } - u, _ := url.Parse(client.getURL("/images/" + name)) - if req.URL.Path != u.Path { - t.Errorf("RemoveImage(%q): Wrong request path. Want %q. Got %q.", name, u.Path, req.URL.Path) - } - expectedQuery := "force=1&noprune=1" - if query := req.URL.Query().Encode(); query != expectedQuery { - t.Errorf("PushImage: Wrong query string. Want %q. Got %q.", expectedQuery, query) - } -} - -func TestInspectImage(t *testing.T) { - body := `{ - "Id":"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", - "Parent":"27cf784147099545", - "Created":"2013-03-23T22:24:18.818426Z", - "Container":"3d67245a8d72ecf13f33dffac9f79dcdf70f75acb84d308770391510e0c23ad0", - "ContainerConfig":{"Memory":1}, - "VirtualSize":12345 -}` - - created, err := time.Parse(time.RFC3339Nano, "2013-03-23T22:24:18.818426Z") - if err != nil { - t.Fatal(err) - } - - expected := Image{ - ID: "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", - Parent: "27cf784147099545", - Created: created, - Container: "3d67245a8d72ecf13f33dffac9f79dcdf70f75acb84d308770391510e0c23ad0", - ContainerConfig: Config{ - Memory: 1, - }, - VirtualSize: 12345, - } - fakeRT := &FakeRoundTripper{message: body, status: http.StatusOK} - client := newTestClient(fakeRT) - image, err := client.InspectImage(expected.ID) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(*image, expected) { - t.Errorf("InspectImage(%q): Wrong image returned. Want %#v. Got %#v.", expected.ID, expected, *image) - } - req := fakeRT.requests[0] - if req.Method != "GET" { - t.Errorf("InspectImage(%q): Wrong HTTP method. Want GET. Got %s.", expected.ID, req.Method) - } - u, _ := url.Parse(client.getURL("/images/" + expected.ID + "/json")) - if req.URL.Path != u.Path { - t.Errorf("InspectImage(%q): Wrong request URL. Want %q. Got %q.", expected.ID, u.Path, req.URL.Path) - } -} - -func TestInspectImageNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such image", status: http.StatusNotFound}) - name := "test" - image, err := client.InspectImage(name) - if image != nil { - t.Errorf("InspectImage(%q): expected image, got %#v.", name, image) - } - if err != ErrNoSuchImage { - t.Errorf("InspectImage(%q): wrong error. Want %#v. Got %#v.", name, ErrNoSuchImage, err) - } -} - -func TestPushImage(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "Pushing 1/100", status: http.StatusOK} - client := newTestClient(fakeRT) - var buf bytes.Buffer - err := client.PushImage(PushImageOptions{Name: "test", OutputStream: &buf}, AuthConfiguration{}) - if err != nil { - t.Fatal(err) - } - expected := "Pushing 1/100" - if buf.String() != expected { - t.Errorf("PushImage: Wrong output. Want %q. Got %q.", expected, buf.String()) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("PushImage: Wrong HTTP method. Want POST. Got %s.", req.Method) - } - u, _ := url.Parse(client.getURL("/images/test/push")) - if req.URL.Path != u.Path { - t.Errorf("PushImage: Wrong request path. Want %q. Got %q.", u.Path, req.URL.Path) - } - if query := req.URL.Query().Encode(); query != "" { - t.Errorf("PushImage: Wrong query string. Want no parameters, got %q.", query) - } - - auth, err := base64.URLEncoding.DecodeString(req.Header.Get("X-Registry-Auth")) - if err != nil { - t.Errorf("PushImage: caught error decoding auth. %#v", err.Error()) - } - if strings.TrimSpace(string(auth)) != "{}" { - t.Errorf("PushImage: wrong body. Want %q. Got %q.", - base64.URLEncoding.EncodeToString([]byte("{}")), req.Header.Get("X-Registry-Auth")) - } -} - -func TestPushImageWithRawJSON(t *testing.T) { - body := ` - {"status":"Pushing..."} - {"status":"Pushing", "progress":"1/? (n/a)", "progressDetail":{"current":1}}} - {"status":"Image successfully pushed"} - ` - fakeRT := &FakeRoundTripper{ - message: body, - status: http.StatusOK, - header: map[string]string{ - "Content-Type": "application/json", - }, - } - client := newTestClient(fakeRT) - var buf bytes.Buffer - - err := client.PushImage(PushImageOptions{ - Name: "test", - OutputStream: &buf, - RawJSONStream: true, - }, AuthConfiguration{}) - if err != nil { - t.Fatal(err) - } - if buf.String() != body { - t.Errorf("PushImage: Wrong raw output. Want %q. Got %q.", body, buf.String()) - } -} - -func TestPushImageWithAuthentication(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "Pushing 1/100", status: http.StatusOK} - client := newTestClient(fakeRT) - var buf bytes.Buffer - inputAuth := AuthConfiguration{ - Username: "gopher", - Password: "gopher123", - Email: "gopher@tsuru.io", - } - err := client.PushImage(PushImageOptions{Name: "test", OutputStream: &buf}, inputAuth) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - var gotAuth AuthConfiguration - - auth, err := base64.URLEncoding.DecodeString(req.Header.Get("X-Registry-Auth")) - if err != nil { - t.Errorf("PushImage: caught error decoding auth. %#v", err.Error()) - } - - err = json.Unmarshal(auth, &gotAuth) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(gotAuth, inputAuth) { - t.Errorf("PushImage: wrong auth configuration. Want %#v. Got %#v.", inputAuth, gotAuth) - } -} - -func TestPushImageCustomRegistry(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "Pushing 1/100", status: http.StatusOK} - client := newTestClient(fakeRT) - var authConfig AuthConfiguration - var buf bytes.Buffer - opts := PushImageOptions{ - Name: "test", Registry: "docker.tsuru.io", - OutputStream: &buf, - } - err := client.PushImage(opts, authConfig) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expectedQuery := "registry=docker.tsuru.io" - if query := req.URL.Query().Encode(); query != expectedQuery { - t.Errorf("PushImage: Wrong query string. Want %q. Got %q.", expectedQuery, query) - } -} - -func TestPushImageNoName(t *testing.T) { - client := Client{} - err := client.PushImage(PushImageOptions{}, AuthConfiguration{}) - if err != ErrNoSuchImage { - t.Errorf("PushImage: got wrong error. Want %#v. Got %#v.", ErrNoSuchImage, err) - } -} - -func TestPullImage(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "Pulling 1/100", status: http.StatusOK} - client := newTestClient(fakeRT) - var buf bytes.Buffer - err := client.PullImage(PullImageOptions{Repository: "base", OutputStream: &buf}, - AuthConfiguration{}) - if err != nil { - t.Fatal(err) - } - expected := "Pulling 1/100" - if buf.String() != expected { - t.Errorf("PullImage: Wrong output. Want %q. Got %q.", expected, buf.String()) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("PullImage: Wrong HTTP method. Want POST. Got %s.", req.Method) - } - u, _ := url.Parse(client.getURL("/images/create")) - if req.URL.Path != u.Path { - t.Errorf("PullImage: Wrong request path. Want %q. Got %q.", u.Path, req.URL.Path) - } - expectedQuery := "fromImage=base" - if query := req.URL.Query().Encode(); query != expectedQuery { - t.Errorf("PullImage: Wrong query strin. Want %q. Got %q.", expectedQuery, query) - } -} - -func TestPullImageWithRawJSON(t *testing.T) { - body := ` - {"status":"Pulling..."} - {"status":"Pulling", "progress":"1 B/ 100 B", "progressDetail":{"current":1, "total":100}} - ` - fakeRT := &FakeRoundTripper{ - message: body, - status: http.StatusOK, - header: map[string]string{ - "Content-Type": "application/json", - }, - } - client := newTestClient(fakeRT) - var buf bytes.Buffer - err := client.PullImage(PullImageOptions{ - Repository: "base", - OutputStream: &buf, - RawJSONStream: true, - }, AuthConfiguration{}) - if err != nil { - t.Fatal(err) - } - if buf.String() != body { - t.Errorf("PullImage: Wrong raw output. Want %q. Got %q", body, buf.String()) - } -} - -func TestPullImageWithoutOutputStream(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "Pulling 1/100", status: http.StatusOK} - client := newTestClient(fakeRT) - opts := PullImageOptions{ - Repository: "base", - Registry: "docker.tsuru.io", - } - err := client.PullImage(opts, AuthConfiguration{}) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expected := map[string][]string{"fromImage": {"base"}, "registry": {"docker.tsuru.io"}} - got := map[string][]string(req.URL.Query()) - if !reflect.DeepEqual(got, expected) { - t.Errorf("PullImage: wrong query string. Want %#v. Got %#v.", expected, got) - } -} - -func TestPullImageCustomRegistry(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "Pulling 1/100", status: http.StatusOK} - client := newTestClient(fakeRT) - var buf bytes.Buffer - opts := PullImageOptions{ - Repository: "base", - Registry: "docker.tsuru.io", - OutputStream: &buf, - } - err := client.PullImage(opts, AuthConfiguration{}) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expected := map[string][]string{"fromImage": {"base"}, "registry": {"docker.tsuru.io"}} - got := map[string][]string(req.URL.Query()) - if !reflect.DeepEqual(got, expected) { - t.Errorf("PullImage: wrong query string. Want %#v. Got %#v.", expected, got) - } -} - -func TestPullImageTag(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "Pulling 1/100", status: http.StatusOK} - client := newTestClient(fakeRT) - var buf bytes.Buffer - opts := PullImageOptions{ - Repository: "base", - Registry: "docker.tsuru.io", - Tag: "latest", - OutputStream: &buf, - } - err := client.PullImage(opts, AuthConfiguration{}) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expected := map[string][]string{"fromImage": {"base"}, "registry": {"docker.tsuru.io"}, "tag": {"latest"}} - got := map[string][]string(req.URL.Query()) - if !reflect.DeepEqual(got, expected) { - t.Errorf("PullImage: wrong query string. Want %#v. Got %#v.", expected, got) - } -} - -func TestPullImageNoRepository(t *testing.T) { - var opts PullImageOptions - client := Client{} - err := client.PullImage(opts, AuthConfiguration{}) - if err != ErrNoSuchImage { - t.Errorf("PullImage: got wrong error. Want %#v. Got %#v.", ErrNoSuchImage, err) - } -} - -func TestImportImageFromUrl(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - var buf bytes.Buffer - opts := ImportImageOptions{ - Source: "http://mycompany.com/file.tar", - Repository: "testimage", - Tag: "tag", - OutputStream: &buf, - } - err := client.ImportImage(opts) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expected := map[string][]string{"fromSrc": {opts.Source}, "repo": {opts.Repository}, "tag": {opts.Tag}} - got := map[string][]string(req.URL.Query()) - if !reflect.DeepEqual(got, expected) { - t.Errorf("ImportImage: wrong query string. Want %#v. Got %#v.", expected, got) - } -} - -func TestImportImageFromInput(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - in := bytes.NewBufferString("tar content") - var buf bytes.Buffer - opts := ImportImageOptions{ - Source: "-", Repository: "testimage", - InputStream: in, OutputStream: &buf, - Tag: "tag", - } - err := client.ImportImage(opts) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expected := map[string][]string{"fromSrc": {opts.Source}, "repo": {opts.Repository}, "tag": {opts.Tag}} - got := map[string][]string(req.URL.Query()) - if !reflect.DeepEqual(got, expected) { - t.Errorf("ImportImage: wrong query string. Want %#v. Got %#v.", expected, got) - } - body, err := ioutil.ReadAll(req.Body) - if err != nil { - t.Errorf("ImportImage: caugth error while reading body %#v", err.Error()) - } - e := "tar content" - if string(body) != e { - t.Errorf("ImportImage: wrong body. Want %#v. Got %#v.", e, string(body)) - } -} - -func TestImportImageDoesNotPassesInputIfSourceIsNotDash(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - var buf bytes.Buffer - in := bytes.NewBufferString("foo") - opts := ImportImageOptions{ - Source: "http://test.com/container.tar", Repository: "testimage", - InputStream: in, OutputStream: &buf, - } - err := client.ImportImage(opts) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expected := map[string][]string{"fromSrc": {opts.Source}, "repo": {opts.Repository}} - got := map[string][]string(req.URL.Query()) - if !reflect.DeepEqual(got, expected) { - t.Errorf("ImportImage: wrong query string. Want %#v. Got %#v.", expected, got) - } - body, err := ioutil.ReadAll(req.Body) - if err != nil { - t.Errorf("ImportImage: caugth error while reading body %#v", err.Error()) - } - if string(body) != "" { - t.Errorf("ImportImage: wrong body. Want nothing. Got %#v.", string(body)) - } -} - -func TestImportImageShouldPassTarContentToBodyWhenSourceIsFilePath(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - var buf bytes.Buffer - tarPath := "testing/data/container.tar" - opts := ImportImageOptions{ - Source: tarPath, Repository: "testimage", - OutputStream: &buf, - } - err := client.ImportImage(opts) - if err != nil { - t.Fatal(err) - } - tar, err := os.Open(tarPath) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - tarContent, err := ioutil.ReadAll(tar) - body, err := ioutil.ReadAll(req.Body) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(tarContent, body) { - t.Errorf("ImportImage: wrong body. Want %#v content. Got %#v.", tarPath, body) - } -} - -func TestImportImageShouldChangeSourceToDashWhenItsAFilePath(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - var buf bytes.Buffer - tarPath := "testing/data/container.tar" - opts := ImportImageOptions{ - Source: tarPath, Repository: "testimage", - OutputStream: &buf, - } - err := client.ImportImage(opts) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expected := map[string][]string{"fromSrc": {"-"}, "repo": {opts.Repository}} - got := map[string][]string(req.URL.Query()) - if !reflect.DeepEqual(got, expected) { - t.Errorf("ImportImage: wrong query string. Want %#v. Got %#v.", expected, got) - } -} - -func TestBuildImageParameters(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - var buf bytes.Buffer - opts := BuildImageOptions{ - Name: "testImage", - NoCache: true, - SuppressOutput: true, - Pull: true, - RmTmpContainer: true, - ForceRmTmpContainer: true, - Memory: 1024, - Memswap: 2048, - CPUShares: 10, - CPUQuota: 7500, - CPUPeriod: 100000, - CPUSetCPUs: "0-3", - Ulimits: []ULimit{{Name: "nofile", Soft: 100, Hard: 200}}, - InputStream: &buf, - OutputStream: &buf, - } - err := client.BuildImage(opts) - if err != nil && strings.Index(err.Error(), "build image fail") == -1 { - t.Fatal(err) - } - req := fakeRT.requests[0] - expected := map[string][]string{ - "t": {opts.Name}, - "nocache": {"1"}, - "q": {"1"}, - "pull": {"1"}, - "rm": {"1"}, - "forcerm": {"1"}, - "memory": {"1024"}, - "memswap": {"2048"}, - "cpushares": {"10"}, - "cpuquota": {"7500"}, - "cpuperiod": {"100000"}, - "cpusetcpus": {"0-3"}, - "ulimits": {"[{\"Name\":\"nofile\",\"Soft\":100,\"Hard\":200}]"}, - } - got := map[string][]string(req.URL.Query()) - if !reflect.DeepEqual(got, expected) { - t.Errorf("BuildImage: wrong query string. Want %#v. Got %#v.", expected, got) - } -} - -func TestBuildImageParametersForRemoteBuild(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - var buf bytes.Buffer - opts := BuildImageOptions{ - Name: "testImage", - Remote: "testing/data/container.tar", - SuppressOutput: true, - OutputStream: &buf, - } - err := client.BuildImage(opts) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expected := map[string][]string{"t": {opts.Name}, "remote": {opts.Remote}, "q": {"1"}} - got := map[string][]string(req.URL.Query()) - if !reflect.DeepEqual(got, expected) { - t.Errorf("BuildImage: wrong query string. Want %#v. Got %#v.", expected, got) - } -} - -func TestBuildImageMissingRepoAndNilInput(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - var buf bytes.Buffer - opts := BuildImageOptions{ - Name: "testImage", - SuppressOutput: true, - OutputStream: &buf, - } - err := client.BuildImage(opts) - if err != ErrMissingRepo { - t.Errorf("BuildImage: wrong error returned. Want %#v. Got %#v.", ErrMissingRepo, err) - } -} - -func TestBuildImageMissingOutputStream(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - opts := BuildImageOptions{Name: "testImage"} - err := client.BuildImage(opts) - if err != ErrMissingOutputStream { - t.Errorf("BuildImage: wrong error returned. Want %#v. Got %#v.", ErrMissingOutputStream, err) - } -} - -func TestBuildImageWithRawJSON(t *testing.T) { - body := ` - {"stream":"Step 0 : FROM ubuntu:latest\n"} - {"stream":" ---\u003e 4300eb9d3c8d\n"} - {"stream":"Step 1 : MAINTAINER docker \n"} - {"stream":" ---\u003e Using cache\n"} - {"stream":" ---\u003e 3a3ed758c370\n"} - {"stream":"Step 2 : CMD /usr/bin/top\n"} - {"stream":" ---\u003e Running in 36b1479cc2e4\n"} - {"stream":" ---\u003e 4b6188aebe39\n"} - {"stream":"Removing intermediate container 36b1479cc2e4\n"} - {"stream":"Successfully built 4b6188aebe39\n"} - ` - fakeRT := &FakeRoundTripper{ - message: body, - status: http.StatusOK, - header: map[string]string{ - "Content-Type": "application/json", - }, - } - client := newTestClient(fakeRT) - var buf bytes.Buffer - opts := BuildImageOptions{ - Name: "testImage", - RmTmpContainer: true, - InputStream: &buf, - OutputStream: &buf, - RawJSONStream: true, - } - err := client.BuildImage(opts) - if err != nil { - t.Fatal(err) - } - if buf.String() != body { - t.Errorf("BuildImage: Wrong raw output. Want %q. Got %q.", body, buf.String()) - } -} - -func TestBuildImageRemoteWithoutName(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - var buf bytes.Buffer - opts := BuildImageOptions{ - Remote: "testing/data/container.tar", - SuppressOutput: true, - OutputStream: &buf, - } - err := client.BuildImage(opts) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expected := map[string][]string{"t": {opts.Remote}, "remote": {opts.Remote}, "q": {"1"}} - got := map[string][]string(req.URL.Query()) - if !reflect.DeepEqual(got, expected) { - t.Errorf("BuildImage: wrong query string. Want %#v. Got %#v.", expected, got) - } -} - -func TestTagImageParameters(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - opts := TagImageOptions{Repo: "testImage"} - err := client.TagImage("base", opts) - if err != nil && strings.Index(err.Error(), "tag image fail") == -1 { - t.Fatal(err) - } - req := fakeRT.requests[0] - expected := "http://localhost:4243/images/base/tag?repo=testImage" - got := req.URL.String() - if !reflect.DeepEqual(got, expected) { - t.Errorf("TagImage: wrong query string. Want %#v. Got %#v.", expected, got) - } -} - -func TestTagImageMissingRepo(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - opts := TagImageOptions{Repo: "testImage"} - err := client.TagImage("", opts) - if err != ErrNoSuchImage { - t.Errorf("TestTag: wrong error returned. Want %#v. Got %#v.", - ErrNoSuchImage, err) - } -} - -func TestIsUrl(t *testing.T) { - url := "http://foo.bar/" - result := isURL(url) - if !result { - t.Errorf("isURL: wrong match. Expected %#v to be a url. Got %#v.", url, result) - } - url = "/foo/bar.tar" - result = isURL(url) - if result { - t.Errorf("isURL: wrong match. Expected %#v to not be a url. Got %#v", url, result) - } -} - -func TestLoadImage(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - tar, err := os.Open("testing/data/container.tar") - if err != nil { - t.Fatal(err) - } else { - defer tar.Close() - } - opts := LoadImageOptions{InputStream: tar} - err = client.LoadImage(opts) - if nil != err { - t.Error(err) - } - req := fakeRT.requests[0] - if req.Method != "POST" { - t.Errorf("LoadImage: wrong method. Expected %q. Got %q.", "POST", req.Method) - } - if req.URL.Path != "/images/load" { - t.Errorf("LoadImage: wrong URL. Expected %q. Got %q.", "/images/load", req.URL.Path) - } -} - -func TestExportImage(t *testing.T) { - var buf bytes.Buffer - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - opts := ExportImageOptions{Name: "testimage", OutputStream: &buf} - err := client.ExportImage(opts) - if nil != err { - t.Error(err) - } - req := fakeRT.requests[0] - if req.Method != "GET" { - t.Errorf("ExportImage: wrong method. Expected %q. Got %q.", "GET", req.Method) - } - expectedPath := "/images/testimage/get" - if req.URL.Path != expectedPath { - t.Errorf("ExportIMage: wrong path. Expected %q. Got %q.", expectedPath, req.URL.Path) - } -} - -func TestExportImages(t *testing.T) { - var buf bytes.Buffer - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - opts := ExportImagesOptions{Names: []string{"testimage1", "testimage2:latest"}, OutputStream: &buf} - err := client.ExportImages(opts) - if nil != err { - t.Error(err) - } - req := fakeRT.requests[0] - if req.Method != "GET" { - t.Errorf("ExportImage: wrong method. Expected %q. Got %q.", "GET", req.Method) - } - expected := "http://localhost:4243/images/get?names=testimage1&names=testimage2%3Alatest" - got := req.URL.String() - if !reflect.DeepEqual(got, expected) { - t.Errorf("ExportIMage: wrong path. Expected %q. Got %q.", expected, got) - } -} - -func TestExportImagesNoNames(t *testing.T) { - var buf bytes.Buffer - fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK} - client := newTestClient(fakeRT) - opts := ExportImagesOptions{Names: []string{}, OutputStream: &buf} - err := client.ExportImages(opts) - if err == nil { - t.Error("Expected an error") - } - if err != ErrMustSpecifyNames { - t.Error(err) - } -} - -func TestSearchImages(t *testing.T) { - body := `[ - { - "description":"A container with Cassandra 2.0.3", - "is_official":true, - "is_automated":true, - "name":"poklet/cassandra", - "star_count":17 - }, - { - "description":"A container with Cassandra 2.0.3", - "is_official":true, - "is_automated":false, - "name":"poklet/cassandra", - "star_count":17 - } - , - { - "description":"A container with Cassandra 2.0.3", - "is_official":false, - "is_automated":true, - "name":"poklet/cassandra", - "star_count":17 - } -]` - var expected []APIImageSearch - err := json.Unmarshal([]byte(body), &expected) - if err != nil { - t.Fatal(err) - } - client := newTestClient(&FakeRoundTripper{message: body, status: http.StatusOK}) - result, err := client.SearchImages("cassandra") - if err != nil { - t.Error(err) - } - if !reflect.DeepEqual(result, expected) { - t.Errorf("SearchImages: Wrong return value. Want %#v. Got %#v.", expected, result) - } -} - -func TestSearchImagesEx(t *testing.T) { - body := `[ - { - "description":"A container with Cassandra 2.0.3", - "is_official":true, - "is_automated":true, - "name":"poklet/cassandra", - "star_count":17 - }, - { - "description":"A container with Cassandra 2.0.3", - "is_official":true, - "is_automated":false, - "name":"poklet/cassandra", - "star_count":17 - } - , - { - "description":"A container with Cassandra 2.0.3", - "is_official":false, - "is_automated":true, - "name":"poklet/cassandra", - "star_count":17 - } -]` - var expected []APIImageSearch - err := json.Unmarshal([]byte(body), &expected) - if err != nil { - t.Fatal(err) - } - client := newTestClient(&FakeRoundTripper{message: body, status: http.StatusOK}) - auth := AuthConfiguration{} - result, err := client.SearchImagesEx("cassandra", auth) - if err != nil { - t.Error(err) - } - if !reflect.DeepEqual(result, expected) { - t.Errorf("SearchImages: Wrong return value. Want %#v. Got %#v.", expected, result) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/integration_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/integration_test.go deleted file mode 100644 index f5aeea27a..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/integration_test.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2015 go-dockerclient 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 docker_integration - -package docker - -import ( - "bytes" - "os" - "testing" -) - -var dockerEndpoint string - -func init() { - dockerEndpoint = os.Getenv("DOCKER_HOST") - if dockerEndpoint == "" { - dockerEndpoint = "unix:///var/run/docker.sock" - } -} - -func TestIntegrationPullCreateStartLogs(t *testing.T) { - imageName := pullImage(t) - client := getClient() - hostConfig := HostConfig{PublishAllPorts: true} - createOpts := CreateContainerOptions{ - Config: &Config{ - Image: imageName, - Cmd: []string{"cat", "/home/gopher/file.txt"}, - User: "gopher", - }, - HostConfig: &hostConfig, - } - container, err := client.CreateContainer(createOpts) - if err != nil { - t.Fatal(err) - } - err = client.StartContainer(container.ID, &hostConfig) - if err != nil { - t.Fatal(err) - } - status, err := client.WaitContainer(container.ID) - if err != nil { - t.Error(err) - } - if status != 0 { - t.Error("WaitContainer(%q): wrong status. Want 0. Got %d", container.ID, status) - } - var stdout, stderr bytes.Buffer - logsOpts := LogsOptions{ - Container: container.ID, - OutputStream: &stdout, - ErrorStream: &stderr, - Stdout: true, - Stderr: true, - } - err = client.Logs(logsOpts) - if err != nil { - t.Error(err) - } - if stderr.String() != "" { - t.Errorf("Got unexpected stderr from logs: %q", stderr.String()) - } - expected := `Welcome to reality, wake up and rejoice -Welcome to reality, you've made the right choice -Welcome to reality, and let them hear your voice, shout it out! -` - if stdout.String() != expected { - t.Errorf("Got wrong stdout from logs.\nWant:\n%#v.\n\nGot:\n%#v.", expected, stdout.String()) - } -} - -func pullImage(t *testing.T) string { - imageName := "fsouza/go-dockerclient-integration" - var buf bytes.Buffer - pullOpts := PullImageOptions{ - Repository: imageName, - OutputStream: &buf, - } - client := getClient() - err := client.PullImage(pullOpts, AuthConfiguration{}) - if err != nil { - t.Logf("Pull output: %s", buf.String()) - t.Fatal(err) - } - return imageName -} - -func getClient() *Client { - client, _ := NewClient(dockerEndpoint) - return client -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/misc_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/misc_test.go deleted file mode 100644 index ceaf076ed..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/misc_test.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2014 go-dockerclient 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 docker - -import ( - "net/http" - "net/url" - "reflect" - "sort" - "testing" -) - -type DockerVersion struct { - Version string - GitCommit string - GoVersion string -} - -func TestVersion(t *testing.T) { - body := `{ - "Version":"0.2.2", - "GitCommit":"5a2a5cc+CHANGES", - "GoVersion":"go1.0.3" -}` - fakeRT := FakeRoundTripper{message: body, status: http.StatusOK} - client := newTestClient(&fakeRT) - expected := DockerVersion{ - Version: "0.2.2", - GitCommit: "5a2a5cc+CHANGES", - GoVersion: "go1.0.3", - } - version, err := client.Version() - if err != nil { - t.Fatal(err) - } - - if result := version.Get("Version"); result != expected.Version { - t.Errorf("Version(): Wrong result. Want %#v. Got %#v.", expected.Version, version.Get("Version")) - } - if result := version.Get("GitCommit"); result != expected.GitCommit { - t.Errorf("GitCommit(): Wrong result. Want %#v. Got %#v.", expected.GitCommit, version.Get("GitCommit")) - } - if result := version.Get("GoVersion"); result != expected.GoVersion { - t.Errorf("GoVersion(): Wrong result. Want %#v. Got %#v.", expected.GoVersion, version.Get("GoVersion")) - } - req := fakeRT.requests[0] - if req.Method != "GET" { - t.Errorf("Version(): wrong request method. Want GET. Got %s.", req.Method) - } - u, _ := url.Parse(client.getURL("/version")) - if req.URL.Path != u.Path { - t.Errorf("Version(): wrong request path. Want %q. Got %q.", u.Path, req.URL.Path) - } -} - -func TestVersionError(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "internal error", status: http.StatusInternalServerError} - client := newTestClient(fakeRT) - version, err := client.Version() - if version != nil { - t.Errorf("Version(): expected value, got %#v.", version) - } - if err == nil { - t.Error("Version(): unexpected error") - } -} - -func TestInfo(t *testing.T) { - body := `{ - "Containers":11, - "Images":16, - "Debug":0, - "NFd":11, - "NGoroutines":21, - "MemoryLimit":1, - "SwapLimit":0 -}` - fakeRT := FakeRoundTripper{message: body, status: http.StatusOK} - client := newTestClient(&fakeRT) - expected := Env{} - expected.SetInt("Containers", 11) - expected.SetInt("Images", 16) - expected.SetBool("Debug", false) - expected.SetInt("NFd", 11) - expected.SetInt("NGoroutines", 21) - expected.SetBool("MemoryLimit", true) - expected.SetBool("SwapLimit", false) - info, err := client.Info() - if err != nil { - t.Fatal(err) - } - infoSlice := []string(*info) - expectedSlice := []string(expected) - sort.Strings(infoSlice) - sort.Strings(expectedSlice) - if !reflect.DeepEqual(expectedSlice, infoSlice) { - t.Errorf("Info(): Wrong result.\nWant %#v.\nGot %#v.", expected, *info) - } - req := fakeRT.requests[0] - if req.Method != "GET" { - t.Errorf("Info(): Wrong HTTP method. Want GET. Got %s.", req.Method) - } - u, _ := url.Parse(client.getURL("/info")) - if req.URL.Path != u.Path { - t.Errorf("Info(): Wrong request path. Want %q. Got %q.", u.Path, req.URL.Path) - } -} - -func TestInfoError(t *testing.T) { - fakeRT := &FakeRoundTripper{message: "internal error", status: http.StatusInternalServerError} - client := newTestClient(fakeRT) - version, err := client.Info() - if version != nil { - t.Errorf("Info(): expected value, got %#v.", version) - } - if err == nil { - t.Error("Info(): unexpected error") - } -} - -func TestParseRepositoryTag(t *testing.T) { - var tests = []struct { - input string - expectedRepo string - expectedTag string - }{ - { - "localhost.localdomain:5000/samalba/hipache:latest", - "localhost.localdomain:5000/samalba/hipache", - "latest", - }, - { - "localhost.localdomain:5000/samalba/hipache", - "localhost.localdomain:5000/samalba/hipache", - "", - }, - { - "tsuru/python", - "tsuru/python", - "", - }, - { - "tsuru/python:2.7", - "tsuru/python", - "2.7", - }, - } - for _, tt := range tests { - repo, tag := ParseRepositoryTag(tt.input) - if repo != tt.expectedRepo { - t.Errorf("ParseRepositoryTag(%q): wrong repository. Want %q. Got %q", tt.input, tt.expectedRepo, repo) - } - if tag != tt.expectedTag { - t.Errorf("ParseRepositoryTag(%q): wrong tag. Want %q. Got %q", tt.input, tt.expectedTag, tag) - } - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/network_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/network_test.go deleted file mode 100644 index 2bff70fe4..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/network_test.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2015 go-dockerclient 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 docker - -import ( - "encoding/json" - "net/http" - "net/url" - "reflect" - "testing" -) - -func TestListNetworks(t *testing.T) { - jsonNetworks := `[ - { - "ID": "8dfafdbc3a40", - "Name": "blah", - "Type": "bridge", - "Endpoints":[{"ID": "918c11c8288a", "Name": "dsafdsaf", "Network": "8dfafdbc3a40"}] - }, - { - "ID": "9fb1e39c", - "Name": "foo", - "Type": "bridge", - "Endpoints":[{"ID": "c080be979dda", "Name": "lllll2222", "Network": "9fb1e39c"}] - } -]` - var expected []Network - err := json.Unmarshal([]byte(jsonNetworks), &expected) - if err != nil { - t.Fatal(err) - } - client := newTestClient(&FakeRoundTripper{message: jsonNetworks, status: http.StatusOK}) - containers, err := client.ListNetworks() - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(containers, expected) { - t.Errorf("ListNetworks: Expected %#v. Got %#v.", expected, containers) - } -} - -func TestNetworkInfo(t *testing.T) { - jsonNetwork := `{ - "ID": "8dfafdbc3a40", - "Name": "blah", - "Type": "bridge", - "Endpoints":[{"ID": "918c11c8288a", "Name": "dsafdsaf", "Network": "8dfafdbc3a40"}] - }` - var expected Network - err := json.Unmarshal([]byte(jsonNetwork), &expected) - if err != nil { - t.Fatal(err) - } - fakeRT := &FakeRoundTripper{message: jsonNetwork, status: http.StatusOK} - client := newTestClient(fakeRT) - id := "8dfafdbc3a40" - network, err := client.NetworkInfo(id) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(*network, expected) { - t.Errorf("NetworkInfo(%q): Expected %#v. Got %#v.", id, expected, network) - } - expectedURL, _ := url.Parse(client.getURL("/networks/8dfafdbc3a40")) - if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path { - t.Errorf("NetworkInfo(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath) - } -} - -func TestNetworkCreate(t *testing.T) { - jsonID := `{"ID": "8dfafdbc3a40"}` - jsonNetwork := `{ - "ID": "8dfafdbc3a40", - "Name": "foobar", - "Driver": "bridge" - }` - var expected Network - err := json.Unmarshal([]byte(jsonNetwork), &expected) - if err != nil { - t.Fatal(err) - } - - client := newTestClient(&FakeRoundTripper{message: jsonID, status: http.StatusOK}) - opts := CreateNetworkOptions{"foobar", false, "bridge", IPAMOptions{}, nil} - network, err := client.CreateNetwork(opts) - if err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(*network, expected) { - t.Errorf("CreateNetwork: Expected %#v. Got %#v.", expected, network) - } -} - -func TestNetworkRemove(t *testing.T) { - id := "8dfafdbc3a40" - fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent} - client := newTestClient(fakeRT) - err := client.RemoveNetwork(id) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expectedMethod := "DELETE" - if req.Method != expectedMethod { - t.Errorf("RemoveNetwork(%q): Wrong HTTP method. Want %s. Got %s.", id, expectedMethod, req.Method) - } - u, _ := url.Parse(client.getURL("/networks/" + id)) - if req.URL.Path != u.Path { - t.Errorf("RemoveNetwork(%q): Wrong request path. Want %q. Got %q.", id, u.Path, req.URL.Path) - } -} - -func TestNetworkConnect(t *testing.T) { - id := "8dfafdbc3a40" - fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent} - client := newTestClient(fakeRT) - opts := NetworkConnectionOptions{"foobar"} - err := client.ConnectNetwork(id, opts) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expectedMethod := "POST" - if req.Method != expectedMethod { - t.Errorf("ConnectNetwork(%q): Wrong HTTP method. Want %s. Got %s.", id, expectedMethod, req.Method) - } - u, _ := url.Parse(client.getURL("/networks/" + id + "/connect")) - if req.URL.Path != u.Path { - t.Errorf("ConnectNetwork(%q): Wrong request path. Want %q. Got %q.", id, u.Path, req.URL.Path) - } -} - -func TestNetworkConnectNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such network container", status: http.StatusNotFound}) - opts := NetworkConnectionOptions{"foobar"} - err := client.ConnectNetwork("8dfafdbc3a40", opts) - if serr, ok := err.(*NoSuchNetworkOrContainer); !ok { - t.Errorf("ConnectNetwork: wrong error type: %s.", serr) - } -} - -func TestNetworkDisconnect(t *testing.T) { - id := "8dfafdbc3a40" - fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent} - client := newTestClient(fakeRT) - opts := NetworkConnectionOptions{"foobar"} - err := client.DisconnectNetwork(id, opts) - if err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expectedMethod := "POST" - if req.Method != expectedMethod { - t.Errorf("DisconnectNetwork(%q): Wrong HTTP method. Want %s. Got %s.", id, expectedMethod, req.Method) - } - u, _ := url.Parse(client.getURL("/networks/" + id + "/disconnect")) - if req.URL.Path != u.Path { - t.Errorf("DisconnectNetwork(%q): Wrong request path. Want %q. Got %q.", id, u.Path, req.URL.Path) - } -} - -func TestNetworkDisconnectNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such network container", status: http.StatusNotFound}) - opts := NetworkConnectionOptions{"foobar"} - err := client.DisconnectNetwork("8dfafdbc3a40", opts) - if serr, ok := err.(*NoSuchNetworkOrContainer); !ok { - t.Errorf("DisconnectNetwork: wrong error type: %s.", serr) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/.dockerignore b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/.dockerignore deleted file mode 100644 index 027e8c20e..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -container.tar -dockerfile.tar -foofile diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/Dockerfile b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/Dockerfile deleted file mode 100644 index 0948dcfa8..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -# this file describes how to build tsuru python image -# to run it: -# 1- install docker -# 2- run: $ docker build -t tsuru/python https://raw.github.com/tsuru/basebuilder/master/python/Dockerfile - -from base:ubuntu-quantal -run apt-get install wget -y --force-yes -run wget http://github.com/tsuru/basebuilder/tarball/master -O basebuilder.tar.gz --no-check-certificate -run mkdir /var/lib/tsuru -run tar -xvf basebuilder.tar.gz -C /var/lib/tsuru --strip 1 -run cp /var/lib/tsuru/python/deploy /var/lib/tsuru -run cp /var/lib/tsuru/base/restart /var/lib/tsuru -run cp /var/lib/tsuru/base/start /var/lib/tsuru -run /var/lib/tsuru/base/install -run /var/lib/tsuru/base/setup diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/barfile b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/barfile deleted file mode 100644 index e69de29bb..000000000 diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/ca.pem b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/ca.pem deleted file mode 100644 index 8e38bba13..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/ca.pem +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC1TCCAb+gAwIBAgIQJ9MsNxrUxumNbAytGi3GEDALBgkqhkiG9w0BAQswFjEU -MBIGA1UEChMLQm9vdDJEb2NrZXIwHhcNMTQxMDE2MjAyMTM4WhcNMTcwOTMwMjAy -MTM4WjAWMRQwEgYDVQQKEwtCb290MkRvY2tlcjCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBALpFCSARjG+5yXoqr7UMzuE0df7RRZfeRZI06lJ02ZqV4Iii -rgL7ML9yPxX50NbLnjiilSDTUhnyocYFItokzUzz8qpX/nlYhuN2Iqwh4d0aWS8z -f5y248F+H1z+HY2W8NPl/6DVlVwYaNW1/k+RPMlHS0INLR6j+3Ievew7RNE0NnM2 -znELW6NetekDt3GUcz0Z95vDUDfdPnIk1eIFMmYvLxZh23xOca4Q37a3S8F3d+dN -+OOpwjdgY9Qme0NQUaXpgp58jWuQfB8q7mZrdnLlLqRa8gx1HeDSotX7UmWtWPkb -vd9EdlKLYw5PVpxMV1rkwf2t4TdgD5NfkpXlXkkCAwEAAaMjMCEwDgYDVR0PAQH/ -BAQDAgCkMA8GA1UdEwEB/wQFMAMBAf8wCwYJKoZIhvcNAQELA4IBAQBxYjHVSKqE -MJw7CW0GddesULtXXVWGJuZdWJLQlPvPMfIfjIvlcZyS4cdVNiQ3sREFIZz8TpII -CT0/Pg3sgv/FcOQe1CN0xZYZcyiAZHK1z0fJQq2qVpdv7+tJcjI2vvU6NI24iQCo -W1wz25trJz9QbdB2MRLMjyz7TSWuafztIvcfEzaIdQ0Whqund/cSuPGQx5IwF83F -rvlkOyJSH2+VIEBTCIuykJeL0DLTt8cePBQR5L1ISXb4RUMK9ZtqRscBRv8sn7o2 -ixG3wtL0gYF4xLtsQWVxI3iFVrU3WzOH/3c5shVRkWBd+AQRSwCJI4mKH7penJCF -i3/zzlkvOnjV ------END CERTIFICATE----- diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/cert.pem b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/cert.pem deleted file mode 100644 index 5e7244b24..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/cert.pem +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC6DCCAdKgAwIBAgIRANO6ymxQAjp66KmEka1G6b0wCwYJKoZIhvcNAQELMBYx -FDASBgNVBAoTC0Jvb3QyRG9ja2VyMB4XDTE0MTAxNjIwMjE1MloXDTE3MDkzMDIw -MjE1MlowFjEUMBIGA1UEChMLQm9vdDJEb2NrZXIwggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQDGA1mAhSOpZspD1dpZ7qVEQrIJw4Xo8252jHaORnEdDiFm -b6brEmr6jw8t4P3IGxbqBc/TqRV+SSXxwYEVvfpeQKH+SmqStoMNtD3Ura161az4 -V0BcxMtSlsUGpoz+//QCAq8qiaxMwgiyc5253mkQm88anj2cNt7xbewiu/KFWuf7 -BVpNK1+ltpJmlukfcj/G+I1bw7j1KxBjDrFqe5cyDuuZcDL2tmUXP/ZWDyXwSv+H -AOckqn44z6aXlBkVvOXDBZJqY76d/vWVDNCuZeXRnqlhP3t1kH4V0RQXo+JD2tgt -JgdU0unzyoFOSWNUBPm73tqmjUGGAmGHBmeegJr/AgMBAAGjNTAzMA4GA1UdDwEB -/wQEAwIAgDATBgNVHSUEDDAKBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMAsGCSqG -SIb3DQEBCwOCAQEABVTWl5SmBP+j5He5bQsgnIXjviSKqe40/10V4LJAOmilycRF -zLrzM+YMwfjg6PLIs8CldAMWHw9y9ktZY4MxkgCktaiaN/QmMTMwFWEcN4wy5IpM -U5l93eAg7xsnY430h3QBBADujX4wdF3fs8rSL8zAAQFL0ihurwU124K3yXKsrwpb -CiVUGfIN4sPwjy8Ws9oxHFDC9/P8lgjHZ1nBIf8KSHnMzlxDGj7isQfhtH+7mcCL -cM1qO2NirS2v7uaEPPY+MJstAz+W7EJCW9dfMSmHna2SDC37Xkin7uEY9z+qaKFL -8d/XxOB/L8Ucy8VZhdsv0dsBq5KfJntITM0ksQ== ------END CERTIFICATE----- diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/container.tar b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/container.tar deleted file mode 100644 index e4b066e3b6df8cb78ac445a34234f3780d164cf4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2048 zcmeH_Q3``F42FH)DgF~kTC`qZ7s*`9%A^%r$Bu89Fp<6NMew1akmheFe?H>)Y5N#5 z`(UT)m>?q4G^iwZ#(XmAwH8Ujv`|_rQd)Ig3sQ!(szArs+5bAH%#&Di1HU}iJx_zp z+3uU9k~Zgl)J<3?S%)LS_Hgc7e)t4AX&%Rz>>WAcX2Ec>82D}md=O1Y)p%bo=N_rJ OD+CIGLZA@%gTMmt=q{T8 diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/dockerfile.tar b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/dockerfile.tar deleted file mode 100644 index 32c9ce64704835cd096b85ac44c35b5087b5ccdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2560 zcmeHGy>8<$49;3V1%d0TNOs}`$a>xT46-c8LTt+?QB8ACf0XPiQll-h0~9$I?_v`_`p)qp;@ z0OJK)JAmosQD=m*-~y?5ASGvD1{zS;L7n!AYz2z}2Y8%Kb25fgK0fDb5l4UE+{yF$ zXs`{{TG^hbn!J);Cl1>2UV0=k!T8hL+GbhfZ2u5L51|SJ2KFb&fyiW3|3Qw(jvC+i zouk4oz*u9Q((Iyric9uLhPZsmgZ8ANMrS_2p5cn+n!M}dU&=mMrdq8|OlgOvF-oFN zh5A!%9Pk(EcxS4q(c~Z~u-BL7!+gIN2&&-GnGy1YRpY|{e@?X?J9}9;KY_$PxYO}H o;5QJT#=q||{Y*ZuNn-Gk-)jtGb|Y`+PV+v2`vmS2xaA4_1I+dVl>h($ diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/foofile b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/foofile deleted file mode 100644 index e69de29bb..000000000 diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/key.pem b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/key.pem deleted file mode 100644 index a9346bcf4..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAxgNZgIUjqWbKQ9XaWe6lREKyCcOF6PNudox2jkZxHQ4hZm+m -6xJq+o8PLeD9yBsW6gXP06kVfkkl8cGBFb36XkCh/kpqkraDDbQ91K2tetWs+FdA -XMTLUpbFBqaM/v/0AgKvKomsTMIIsnOdud5pEJvPGp49nDbe8W3sIrvyhVrn+wVa -TStfpbaSZpbpH3I/xviNW8O49SsQYw6xanuXMg7rmXAy9rZlFz/2Vg8l8Er/hwDn -JKp+OM+ml5QZFbzlwwWSamO+nf71lQzQrmXl0Z6pYT97dZB+FdEUF6PiQ9rYLSYH -VNLp88qBTkljVAT5u97apo1BhgJhhwZnnoCa/wIDAQABAoIBAQCaGy9EC9pmU95l -DwGh7k5nIrUnTilg1FwLHWSDdCVCZKXv8ENrPelOWZqJrUo1u4eI2L8XTsewgkNq -tJu/DRzWz9yDaO0qg6rZNobMh+K076lvmZA44twOydJLS8H+D7ua+PXU2FLlZjmY -kMyXRJZmW6zCXZc7haTbJx6ZJccoquk/DkS4FcFurJP177u1YrWS9TTw9kensUtU -jQ63uf56UTN1i+0+Rxl7OW1TZlqwlri5I4njg5249+FxwwHzIq8+l7zD7K9pl8c/ -nG1HuulvU2bVlDlRdyslMPAH34vw9Sku1BD8furrJLr1na5lRSLKJODEaIPEsLwv -CdEUwP9JAoGBAO76ZW80RyNB2fA+wbTq70Sr8CwrXxYemXrez5LKDC7SsohKFCPE -IedpO/n+nmymiiJvMm874EExoG6BVrbkWkeb+2vinEfOQNlDMsDx7WLjPekP3t6i -rXHO3CjFooVFq2z3mZa/Nc5NZqu8fNWNCKJxZDJphdoj6sORNJIUvZVjAoGBANQd -++J+ITcu3/+A6JrGcgLunBFQYPqkiItk0J4QKYKuX5ik9rWcQDN8TTtfW2mDuiQ4 -NrCwuVPq1V1kB16JzH017SsYLo9g8I20YjnBZge9pKTeUaLVTb3C50LW8FBylop0 -Bnm597dNbtSjphjoTMg0XyC19o3Esf2YeWG0QNS1AoGAWWDfFRNJU99qIldmXULM -0DM6NVrXSk+ReYnhunXEzrJQwXZrR+EwCPurydk36Uz0NuK9yypquhdUeF/5TZfk -SAoHo5byekyipl9imRUigqyY2BTudvgCxKDoaHtaSFwBPFTyZZYICquaLbrmOXxw -8UhVgCFFRYvPXuts7QHC0h8CgYBWEvy9gfU0kV7wLX02IUTuj6jhFb7ktpN6DSTi -nyhZES1VoctDEu6ydcRZTW6ouH12aSE4Pd5WgTqntQmQgVZrkNB25k8ue2Xh+srJ -KQOgLIJ9LIHwE6KCWG7DnrjRzE3uTPq7to0g4tkQjH/AJ7PQof/gJDayfJjFkXPg -A+cy6QKBgEPbKpiqscm03gT2QanBut5pg4dqPOxp0SlErA3kSFNTRK3oYBQPC+LH -qA5nD5brdkeNBB58Rll8Zpzxiff50bcvLP/7/Sb3NjaXFTEY0gVbdRof3n6N0YP3 -Hu5XDNJ9RNkNzE5RIG1g86KE+aKlcrKMaigqAiuIy2PSnjkQeGk8 ------END RSA PRIVATE KEY----- diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/server.pem b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/server.pem deleted file mode 100644 index 89cc445e1..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/server.pem +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC/DCCAeagAwIBAgIQMUILcXtvmSOK63zEBo0VXzALBgkqhkiG9w0BAQswFjEU -MBIGA1UEChMLQm9vdDJEb2NrZXIwHhcNMTQxMDE2MjAyMTQ2WhcNMTcwOTMwMjAy -MTQ2WjAWMRQwEgYDVQQKEwtCb290MkRvY2tlcjCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBANxUOUhNnqFnrTlLsBYzfFRZWQo268l+4K4lOJCVbfDonP3g -Mz0vGi9fcyFqEWSA8Y+ShXna625HTnReCwFdsu0861qCIq7v95hFFCyOe0iIxpd0 -AKLnl90d+1vonE7andgFgoobbTiMly4UK4H6z8D148fFNIihoteOG3PIF89TFxP7 -CJ/3wXnx/IKpdlO8PAnub3tBPJHvGDj7KORLy4IBxRX5VBAdfGNybE66fcrehEva -rLA4m9pgiaR/Nnr9FdKhPyqYdjflLNvzydxNvMIV4M0hFlhXmYvpMjA5/XsTnsyV -t9JHJa5Upwqsbne08t7rsm7liZNxZlko8xPOTQcCAwEAAaNKMEgwDgYDVR0PAQH/ -BAQDAgCgMAwGA1UdEwEB/wQCMAAwKAYDVR0RBCEwH4ILYm9vdDJkb2NrZXKHBH8A -AAGHBAoAAg+HBMCoO2cwCwYJKoZIhvcNAQELA4IBAQAYoYcDkDWkl73FZ0WnPmAj -LiF7HU95Qg3KyEpFsAJeShSLPPbQntmwhdekEzY4tQ3eKQB/+zHFjzsCr/lmDUmH -Ea/ryQ17C+jyH+Ykg0IWW6L6veZhvRDg6Z9focVtPVBRxPTqC/Qhb54blWRASV+W -UreMuXQ5+1dQptAM7ixOeLVHjBi/bd9TL3jvwBVCr9QedteMjjK4TCF9Tbcou+MF -2w3OJJZMDhcD+YwoK9uJDqlKmcTm/vVMbSsp/pTMcnQ7jxCeR8/XyX+VwTZwaHAa -o92Q/eg3THAiWhvyT/SzyH9dHHBAyXynUwGCggKawHktfvW4QXRPuLxLrJ7iB5cy ------END CERTIFICATE----- diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/serverkey.pem b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/serverkey.pem deleted file mode 100644 index c897e5da5..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/data/serverkey.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEoAIBAAKCAQEA3FQ5SE2eoWetOUuwFjN8VFlZCjbryX7griU4kJVt8Oic/eAz -PS8aL19zIWoRZIDxj5KFedrrbkdOdF4LAV2y7TzrWoIiru/3mEUULI57SIjGl3QA -oueX3R37W+icTtqd2AWCihttOIyXLhQrgfrPwPXjx8U0iKGi144bc8gXz1MXE/sI -n/fBefH8gql2U7w8Ce5ve0E8ke8YOPso5EvLggHFFflUEB18Y3JsTrp9yt6ES9qs -sDib2mCJpH82ev0V0qE/Kph2N+Us2/PJ3E28whXgzSEWWFeZi+kyMDn9exOezJW3 -0kclrlSnCqxud7Ty3uuybuWJk3FmWSjzE85NBwIDAQABAoIBAG0ak+cW8LeShHf7 -3+2Of0GxoOLrAWWdG5uAuPr31CJYve0FybnBimDtDjD8ujIfm/7xmoEWBEFutA3x -x9dcU88gvJbsHEqub9gKVQwfXjMz78tt2SbSMiR/xUnk7QorPcCMMfE71aEMFYzu -1gCed6Rg3vO81t/V0rKVH0j9S7UQz5v/oX15eVDV5LOqyCHwAi6K0eXXbqnbI0TH -SOQ/nexM2msVXWbO9t6ra6f5V7FXziDK5Xi+rPxRbX9mkrDzxDAevfuRqYBx5vtL -W2Q2hKjUAHFgXFniNSZBS7dCdAtz0el/3ct+cNmpuTMhhs7M6wC1CuYiZ/DxLiFh -Si73VckCgYEA+/ceh3+VjtQ0rgEw8sD9bqYEA8IaBiObjneIoFnKBYRG7yZd8JMm -HD4M/aQ1qhcRLPN7GR03YQULgQJURbKSjJHnhfTXHyeHC3NN4gMVHQXewu2MHCh6 -7FCQ9CfK0KcYLgegVVvL3PrF3hyWGnmTu+G0UkDQRYVnaNrB7snrW6UCgYEA39tq -+MCQdu0moJ5szSZf02undg9EeW6isk9qzi7TId3/MLci2eH7PEnipipPUK3+DERq -aba0y0TKgBR2EXvXLFJA/+kfdo2loIEHOfox85HVfxgUaFRti63ZI0uF8D0QT2Yy -oJal+RFghVoSnv4LjhRKEPbIkScTXGjdK+7wFjsCfz79iKRXQQx0ALd/lL0bgkAn -QNmvrNHcFQeI2p8700WNzC39aX67SsvEt3qxkrjzC1gxhpTAuReIK1gVPPwvqHN8 -BmV20FD5kMlMCix2mNCopwgUWvKvLAvoGFTxncKMA39+aJbuXAjiqJTekKgNvOE7 -i9kEWw0GTNPp3JHV6QECgYAPwb0M11kT1euDIMOdyRazpf86kyaJuZzgGjD1ZFxe -JOcigbGFTp/FhZnbglzk2+pm6KXo3QBq0mPCki4hWusxZnTGzpz1VlETNCHTFeZQ -M7KoaIR/N3oie9Et59H8r/+m5xWnMhNqratyl316DX24uXrhKM3DUdHODl+LCR2D -IwKBgE1MbHuwolUPEw3HeO4R7NMFVTFei7E/fpUsimPfArGg8UydwvloNT1myJos -N2JzfGGjN2KPVcBk9fOs71mJ6VcK3C3g5JIccplk6h9VNaw55+zdQvKPTzoBoTvy -A+Fwx2AlF61KeRF87DL2YTRJ6B9MHmWgf7+GVZOxomLgEAcZ ------END RSA PRIVATE KEY----- diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go deleted file mode 100644 index b16e71367..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go +++ /dev/null @@ -1,1246 +0,0 @@ -// Copyright 2015 go-dockerclient 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 testing provides a fake implementation of the Docker API, useful for -// testing purpose. -package testing - -import ( - "archive/tar" - "crypto/rand" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - mathrand "math/rand" - "net" - "net/http" - "regexp" - "strconv" - "strings" - "sync" - "time" - - "github.com/fsouza/go-dockerclient" - "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/stdcopy" - "github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux" -) - -var nameRegexp = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_.-]+$`) - -// DockerServer represents a programmable, concurrent (not much), HTTP server -// implementing a fake version of the Docker remote API. -// -// It can used in standalone mode, listening for connections or as an arbitrary -// HTTP handler. -// -// For more details on the remote API, check http://goo.gl/G3plxW. -type DockerServer struct { - containers []*docker.Container - uploadedFiles map[string]string - execs []*docker.ExecInspect - execMut sync.RWMutex - cMut sync.RWMutex - images []docker.Image - iMut sync.RWMutex - imgIDs map[string]string - networks []*docker.Network - netMut sync.RWMutex - listener net.Listener - mux *mux.Router - hook func(*http.Request) - failures map[string]string - multiFailures []map[string]string - execCallbacks map[string]func() - statsCallbacks map[string]func(string) docker.Stats - customHandlers map[string]http.Handler - handlerMutex sync.RWMutex - cChan chan<- *docker.Container - volStore map[string]*volumeCounter - volMut sync.RWMutex -} - -type volumeCounter struct { - volume docker.Volume - count int -} - -// NewServer returns a new instance of the fake server, in standalone mode. Use -// the method URL to get the URL of the server. -// -// It receives the bind address (use 127.0.0.1:0 for getting an available port -// on the host), a channel of containers and a hook function, that will be -// called on every request. -// -// The fake server will send containers in the channel whenever the container -// changes its state, via the HTTP API (i.e.: create, start and stop). This -// channel may be nil, which means that the server won't notify on state -// changes. -func NewServer(bind string, containerChan chan<- *docker.Container, hook func(*http.Request)) (*DockerServer, error) { - listener, err := net.Listen("tcp", bind) - if err != nil { - return nil, err - } - server := DockerServer{ - listener: listener, - imgIDs: make(map[string]string), - hook: hook, - failures: make(map[string]string), - execCallbacks: make(map[string]func()), - statsCallbacks: make(map[string]func(string) docker.Stats), - customHandlers: make(map[string]http.Handler), - uploadedFiles: make(map[string]string), - cChan: containerChan, - } - server.buildMuxer() - go http.Serve(listener, &server) - return &server, nil -} - -func (s *DockerServer) notify(container *docker.Container) { - if s.cChan != nil { - s.cChan <- container - } -} - -func (s *DockerServer) buildMuxer() { - s.mux = mux.NewRouter() - s.mux.Path("/commit").Methods("POST").HandlerFunc(s.handlerWrapper(s.commitContainer)) - s.mux.Path("/containers/json").Methods("GET").HandlerFunc(s.handlerWrapper(s.listContainers)) - s.mux.Path("/containers/create").Methods("POST").HandlerFunc(s.handlerWrapper(s.createContainer)) - s.mux.Path("/containers/{id:.*}/json").Methods("GET").HandlerFunc(s.handlerWrapper(s.inspectContainer)) - s.mux.Path("/containers/{id:.*}/rename").Methods("POST").HandlerFunc(s.handlerWrapper(s.renameContainer)) - s.mux.Path("/containers/{id:.*}/top").Methods("GET").HandlerFunc(s.handlerWrapper(s.topContainer)) - s.mux.Path("/containers/{id:.*}/start").Methods("POST").HandlerFunc(s.handlerWrapper(s.startContainer)) - s.mux.Path("/containers/{id:.*}/kill").Methods("POST").HandlerFunc(s.handlerWrapper(s.stopContainer)) - s.mux.Path("/containers/{id:.*}/stop").Methods("POST").HandlerFunc(s.handlerWrapper(s.stopContainer)) - s.mux.Path("/containers/{id:.*}/pause").Methods("POST").HandlerFunc(s.handlerWrapper(s.pauseContainer)) - s.mux.Path("/containers/{id:.*}/unpause").Methods("POST").HandlerFunc(s.handlerWrapper(s.unpauseContainer)) - s.mux.Path("/containers/{id:.*}/wait").Methods("POST").HandlerFunc(s.handlerWrapper(s.waitContainer)) - s.mux.Path("/containers/{id:.*}/attach").Methods("POST").HandlerFunc(s.handlerWrapper(s.attachContainer)) - s.mux.Path("/containers/{id:.*}").Methods("DELETE").HandlerFunc(s.handlerWrapper(s.removeContainer)) - s.mux.Path("/containers/{id:.*}/exec").Methods("POST").HandlerFunc(s.handlerWrapper(s.createExecContainer)) - s.mux.Path("/containers/{id:.*}/stats").Methods("GET").HandlerFunc(s.handlerWrapper(s.statsContainer)) - s.mux.Path("/containers/{id:.*}/archive").Methods("PUT").HandlerFunc(s.handlerWrapper(s.uploadToContainer)) - s.mux.Path("/exec/{id:.*}/resize").Methods("POST").HandlerFunc(s.handlerWrapper(s.resizeExecContainer)) - s.mux.Path("/exec/{id:.*}/start").Methods("POST").HandlerFunc(s.handlerWrapper(s.startExecContainer)) - s.mux.Path("/exec/{id:.*}/json").Methods("GET").HandlerFunc(s.handlerWrapper(s.inspectExecContainer)) - s.mux.Path("/images/create").Methods("POST").HandlerFunc(s.handlerWrapper(s.pullImage)) - s.mux.Path("/build").Methods("POST").HandlerFunc(s.handlerWrapper(s.buildImage)) - s.mux.Path("/images/json").Methods("GET").HandlerFunc(s.handlerWrapper(s.listImages)) - s.mux.Path("/images/{id:.*}").Methods("DELETE").HandlerFunc(s.handlerWrapper(s.removeImage)) - s.mux.Path("/images/{name:.*}/json").Methods("GET").HandlerFunc(s.handlerWrapper(s.inspectImage)) - s.mux.Path("/images/{name:.*}/push").Methods("POST").HandlerFunc(s.handlerWrapper(s.pushImage)) - s.mux.Path("/images/{name:.*}/tag").Methods("POST").HandlerFunc(s.handlerWrapper(s.tagImage)) - s.mux.Path("/events").Methods("GET").HandlerFunc(s.listEvents) - s.mux.Path("/_ping").Methods("GET").HandlerFunc(s.handlerWrapper(s.pingDocker)) - s.mux.Path("/images/load").Methods("POST").HandlerFunc(s.handlerWrapper(s.loadImage)) - s.mux.Path("/images/{id:.*}/get").Methods("GET").HandlerFunc(s.handlerWrapper(s.getImage)) - s.mux.Path("/networks").Methods("GET").HandlerFunc(s.handlerWrapper(s.listNetworks)) - s.mux.Path("/networks/{id:.*}").Methods("GET").HandlerFunc(s.handlerWrapper(s.networkInfo)) - s.mux.Path("/networks").Methods("POST").HandlerFunc(s.handlerWrapper(s.createNetwork)) - s.mux.Path("/volumes").Methods("GET").HandlerFunc(s.handlerWrapper(s.listVolumes)) - s.mux.Path("/volumes/create").Methods("POST").HandlerFunc(s.handlerWrapper(s.createVolume)) - s.mux.Path("/volumes/{name:.*}").Methods("GET").HandlerFunc(s.handlerWrapper(s.inspectVolume)) - s.mux.Path("/volumes/{name:.*}").Methods("DELETE").HandlerFunc(s.handlerWrapper(s.removeVolume)) -} - -// SetHook changes the hook function used by the server. -// -// The hook function is a function called on every request. -func (s *DockerServer) SetHook(hook func(*http.Request)) { - s.hook = hook -} - -// PrepareExec adds a callback to a container exec in the fake server. -// -// This function will be called whenever the given exec id is started, and the -// given exec id will remain in the "Running" start while the function is -// running, so it's useful for emulating an exec that runs for two seconds, for -// example: -// -// opts := docker.CreateExecOptions{ -// AttachStdin: true, -// AttachStdout: true, -// AttachStderr: true, -// Tty: true, -// Cmd: []string{"/bin/bash", "-l"}, -// } -// // Client points to a fake server. -// exec, err := client.CreateExec(opts) -// // handle error -// server.PrepareExec(exec.ID, func() {time.Sleep(2 * time.Second)}) -// err = client.StartExec(exec.ID, docker.StartExecOptions{Tty: true}) // will block for 2 seconds -// // handle error -func (s *DockerServer) PrepareExec(id string, callback func()) { - s.execCallbacks[id] = callback -} - -// PrepareStats adds a callback that will be called for each container stats -// call. -// -// This callback function will be called multiple times if stream is set to -// true when stats is called. -func (s *DockerServer) PrepareStats(id string, callback func(string) docker.Stats) { - s.statsCallbacks[id] = callback -} - -// PrepareFailure adds a new expected failure based on a URL regexp it receives -// an id for the failure. -func (s *DockerServer) PrepareFailure(id string, urlRegexp string) { - s.failures[id] = urlRegexp -} - -// PrepareMultiFailures enqueues a new expected failure based on a URL regexp -// it receives an id for the failure. -func (s *DockerServer) PrepareMultiFailures(id string, urlRegexp string) { - s.multiFailures = append(s.multiFailures, map[string]string{"error": id, "url": urlRegexp}) -} - -// ResetFailure removes an expected failure identified by the given id. -func (s *DockerServer) ResetFailure(id string) { - delete(s.failures, id) -} - -// ResetMultiFailures removes all enqueued failures. -func (s *DockerServer) ResetMultiFailures() { - s.multiFailures = []map[string]string{} -} - -// CustomHandler registers a custom handler for a specific path. -// -// For example: -// -// server.CustomHandler("/containers/json", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { -// http.Error(w, "Something wrong is not right", http.StatusInternalServerError) -// })) -func (s *DockerServer) CustomHandler(path string, handler http.Handler) { - s.handlerMutex.Lock() - s.customHandlers[path] = handler - s.handlerMutex.Unlock() -} - -// MutateContainer changes the state of a container, returning an error if the -// given id does not match to any container "running" in the server. -func (s *DockerServer) MutateContainer(id string, state docker.State) error { - for _, container := range s.containers { - if container.ID == id { - container.State = state - return nil - } - } - return errors.New("container not found") -} - -// Stop stops the server. -func (s *DockerServer) Stop() { - if s.listener != nil { - s.listener.Close() - } -} - -// URL returns the HTTP URL of the server. -func (s *DockerServer) URL() string { - if s.listener == nil { - return "" - } - return "http://" + s.listener.Addr().String() + "/" -} - -// ServeHTTP handles HTTP requests sent to the server. -func (s *DockerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - s.handlerMutex.RLock() - defer s.handlerMutex.RUnlock() - for re, handler := range s.customHandlers { - if m, _ := regexp.MatchString(re, r.URL.Path); m { - handler.ServeHTTP(w, r) - return - } - } - s.mux.ServeHTTP(w, r) - if s.hook != nil { - s.hook(r) - } -} - -// DefaultHandler returns default http.Handler mux, it allows customHandlers to -// call the default behavior if wanted. -func (s *DockerServer) DefaultHandler() http.Handler { - return s.mux -} - -func (s *DockerServer) handlerWrapper(f func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - for errorID, urlRegexp := range s.failures { - matched, err := regexp.MatchString(urlRegexp, r.URL.Path) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if !matched { - continue - } - http.Error(w, errorID, http.StatusBadRequest) - return - } - for i, failure := range s.multiFailures { - matched, err := regexp.MatchString(failure["url"], r.URL.Path) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if !matched { - continue - } - http.Error(w, failure["error"], http.StatusBadRequest) - s.multiFailures = append(s.multiFailures[:i], s.multiFailures[i+1:]...) - return - } - f(w, r) - } -} - -func (s *DockerServer) listContainers(w http.ResponseWriter, r *http.Request) { - all := r.URL.Query().Get("all") - s.cMut.RLock() - result := make([]docker.APIContainers, 0, len(s.containers)) - for _, container := range s.containers { - if all == "1" || container.State.Running { - result = append(result, docker.APIContainers{ - ID: container.ID, - Image: container.Image, - Command: fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " ")), - Created: container.Created.Unix(), - Status: container.State.String(), - Ports: container.NetworkSettings.PortMappingAPI(), - Names: []string{fmt.Sprintf("/%s", container.Name)}, - }) - } - } - s.cMut.RUnlock() - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(result) -} - -func (s *DockerServer) listImages(w http.ResponseWriter, r *http.Request) { - s.cMut.RLock() - result := make([]docker.APIImages, len(s.images)) - for i, image := range s.images { - result[i] = docker.APIImages{ - ID: image.ID, - Created: image.Created.Unix(), - } - for tag, id := range s.imgIDs { - if id == image.ID { - result[i].RepoTags = append(result[i].RepoTags, tag) - } - } - } - s.cMut.RUnlock() - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(result) -} - -func (s *DockerServer) findImage(id string) (string, error) { - s.iMut.RLock() - defer s.iMut.RUnlock() - image, ok := s.imgIDs[id] - if ok { - return image, nil - } - image, _, err := s.findImageByID(id) - return image, err -} - -func (s *DockerServer) findImageByID(id string) (string, int, error) { - s.iMut.RLock() - defer s.iMut.RUnlock() - for i, image := range s.images { - if image.ID == id { - return image.ID, i, nil - } - } - return "", -1, errors.New("No such image") -} - -func (s *DockerServer) createContainer(w http.ResponseWriter, r *http.Request) { - var config struct { - *docker.Config - HostConfig *docker.HostConfig - } - defer r.Body.Close() - err := json.NewDecoder(r.Body).Decode(&config) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - name := r.URL.Query().Get("name") - if name != "" && !nameRegexp.MatchString(name) { - http.Error(w, "Invalid container name", http.StatusInternalServerError) - return - } - if _, err := s.findImage(config.Image); err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - ports := map[docker.Port][]docker.PortBinding{} - for port := range config.ExposedPorts { - ports[port] = []docker.PortBinding{{ - HostIP: "0.0.0.0", - HostPort: strconv.Itoa(mathrand.Int() % 0xffff), - }} - } - - //the container may not have cmd when using a Dockerfile - var path string - var args []string - if len(config.Cmd) == 1 { - path = config.Cmd[0] - } else if len(config.Cmd) > 1 { - path = config.Cmd[0] - args = config.Cmd[1:] - } - - generatedID := s.generateID() - config.Config.Hostname = generatedID[:12] - container := docker.Container{ - Name: name, - ID: generatedID, - Created: time.Now(), - Path: path, - Args: args, - Config: config.Config, - HostConfig: config.HostConfig, - State: docker.State{ - Running: false, - Pid: mathrand.Int() % 50000, - ExitCode: 0, - StartedAt: time.Now(), - }, - Image: config.Image, - NetworkSettings: &docker.NetworkSettings{ - IPAddress: fmt.Sprintf("172.16.42.%d", mathrand.Int()%250+2), - IPPrefixLen: 24, - Gateway: "172.16.42.1", - Bridge: "docker0", - Ports: ports, - }, - } - s.cMut.Lock() - if container.Name != "" { - for _, c := range s.containers { - if c.Name == container.Name { - defer s.cMut.Unlock() - http.Error(w, "there's already a container with this name", http.StatusConflict) - return - } - } - } - s.containers = append(s.containers, &container) - s.cMut.Unlock() - w.WriteHeader(http.StatusCreated) - s.notify(&container) - - json.NewEncoder(w).Encode(container) -} - -func (s *DockerServer) generateID() string { - var buf [16]byte - rand.Read(buf[:]) - return fmt.Sprintf("%x", buf) -} - -func (s *DockerServer) renameContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - container, index, err := s.findContainer(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - copy := *container - copy.Name = r.URL.Query().Get("name") - s.cMut.Lock() - defer s.cMut.Unlock() - if s.containers[index].ID == copy.ID { - s.containers[index] = © - } - w.WriteHeader(http.StatusNoContent) -} - -func (s *DockerServer) inspectContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - container, _, err := s.findContainer(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(container) -} - -func (s *DockerServer) statsContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - _, _, err := s.findContainer(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - stream, _ := strconv.ParseBool(r.URL.Query().Get("stream")) - callback := s.statsCallbacks[id] - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - encoder := json.NewEncoder(w) - for { - var stats docker.Stats - if callback != nil { - stats = callback(id) - } - encoder.Encode(stats) - if !stream { - break - } - } -} - -func (s *DockerServer) uploadToContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - container, _, err := s.findContainer(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - if !container.State.Running { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Container %s is not running", id) - return - } - path := r.URL.Query().Get("path") - s.uploadedFiles[id] = path - w.WriteHeader(http.StatusOK) -} - -func (s *DockerServer) topContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - container, _, err := s.findContainer(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - if !container.State.Running { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Container %s is not running", id) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - result := docker.TopResult{ - Titles: []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"}, - Processes: [][]string{ - {"root", "7535", "7516", "0", "03:20", "?", "00:00:00", container.Path + " " + strings.Join(container.Args, " ")}, - }, - } - json.NewEncoder(w).Encode(result) -} - -func (s *DockerServer) startContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - container, _, err := s.findContainer(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - s.cMut.Lock() - defer s.cMut.Unlock() - defer r.Body.Close() - var hostConfig docker.HostConfig - err = json.NewDecoder(r.Body).Decode(&hostConfig) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - container.HostConfig = &hostConfig - if len(hostConfig.PortBindings) > 0 { - ports := map[docker.Port][]docker.PortBinding{} - for key, items := range hostConfig.PortBindings { - bindings := make([]docker.PortBinding, len(items)) - for i := range items { - binding := docker.PortBinding{ - HostIP: items[i].HostIP, - HostPort: items[i].HostPort, - } - if binding.HostIP == "" { - binding.HostIP = "0.0.0.0" - } - if binding.HostPort == "" { - binding.HostPort = strconv.Itoa(mathrand.Int() % 0xffff) - } - bindings[i] = binding - } - ports[key] = bindings - } - container.NetworkSettings.Ports = ports - } - if container.State.Running { - http.Error(w, "", http.StatusNotModified) - return - } - container.State.Running = true - s.notify(container) -} - -func (s *DockerServer) stopContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - container, _, err := s.findContainer(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - s.cMut.Lock() - defer s.cMut.Unlock() - if !container.State.Running { - http.Error(w, "Container not running", http.StatusBadRequest) - return - } - w.WriteHeader(http.StatusNoContent) - container.State.Running = false - s.notify(container) -} - -func (s *DockerServer) pauseContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - container, _, err := s.findContainer(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - s.cMut.Lock() - defer s.cMut.Unlock() - if container.State.Paused { - http.Error(w, "Container already paused", http.StatusBadRequest) - return - } - w.WriteHeader(http.StatusNoContent) - container.State.Paused = true -} - -func (s *DockerServer) unpauseContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - container, _, err := s.findContainer(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - s.cMut.Lock() - defer s.cMut.Unlock() - if !container.State.Paused { - http.Error(w, "Container not paused", http.StatusBadRequest) - return - } - w.WriteHeader(http.StatusNoContent) - container.State.Paused = false -} - -func (s *DockerServer) attachContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - container, _, err := s.findContainer(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - hijacker, ok := w.(http.Hijacker) - if !ok { - http.Error(w, "cannot hijack connection", http.StatusInternalServerError) - return - } - w.Header().Set("Content-Type", "application/vnd.docker.raw-stream") - w.WriteHeader(http.StatusOK) - conn, _, err := hijacker.Hijack() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - wg := sync.WaitGroup{} - if r.URL.Query().Get("stdin") == "1" { - wg.Add(1) - go func() { - ioutil.ReadAll(conn) - wg.Done() - }() - } - outStream := stdcopy.NewStdWriter(conn, stdcopy.Stdout) - if container.State.Running { - fmt.Fprintf(outStream, "Container is running\n") - } else { - fmt.Fprintf(outStream, "Container is not running\n") - } - fmt.Fprintln(outStream, "What happened?") - fmt.Fprintln(outStream, "Something happened") - wg.Wait() - if r.URL.Query().Get("stream") == "1" { - for { - time.Sleep(1e6) - s.cMut.RLock() - if !container.State.Running { - s.cMut.RUnlock() - break - } - s.cMut.RUnlock() - } - } - conn.Close() -} - -func (s *DockerServer) waitContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - container, _, err := s.findContainer(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - for { - time.Sleep(1e6) - s.cMut.RLock() - if !container.State.Running { - s.cMut.RUnlock() - break - } - s.cMut.RUnlock() - } - result := map[string]int{"StatusCode": container.State.ExitCode} - json.NewEncoder(w).Encode(result) -} - -func (s *DockerServer) removeContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - force := r.URL.Query().Get("force") - container, index, err := s.findContainer(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - if container.State.Running && force != "1" { - msg := "Error: API error (406): Impossible to remove a running container, please stop it first" - http.Error(w, msg, http.StatusInternalServerError) - return - } - w.WriteHeader(http.StatusNoContent) - s.cMut.Lock() - defer s.cMut.Unlock() - if s.containers[index].ID == id || s.containers[index].Name == id { - s.containers[index] = s.containers[len(s.containers)-1] - s.containers = s.containers[:len(s.containers)-1] - } -} - -func (s *DockerServer) commitContainer(w http.ResponseWriter, r *http.Request) { - id := r.URL.Query().Get("container") - container, _, err := s.findContainer(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - var config *docker.Config - runConfig := r.URL.Query().Get("run") - if runConfig != "" { - config = new(docker.Config) - err = json.Unmarshal([]byte(runConfig), config) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - } - w.WriteHeader(http.StatusOK) - image := docker.Image{ - ID: "img-" + container.ID, - Parent: container.Image, - Container: container.ID, - Comment: r.URL.Query().Get("m"), - Author: r.URL.Query().Get("author"), - Config: config, - } - repository := r.URL.Query().Get("repo") - tag := r.URL.Query().Get("tag") - s.iMut.Lock() - s.images = append(s.images, image) - if repository != "" { - if tag != "" { - repository += ":" + tag - } - s.imgIDs[repository] = image.ID - } - s.iMut.Unlock() - fmt.Fprintf(w, `{"ID":%q}`, image.ID) -} - -func (s *DockerServer) findContainer(idOrName string) (*docker.Container, int, error) { - s.cMut.RLock() - defer s.cMut.RUnlock() - for i, container := range s.containers { - if container.ID == idOrName || container.Name == idOrName { - return container, i, nil - } - } - return nil, -1, errors.New("No such container") -} - -func (s *DockerServer) buildImage(w http.ResponseWriter, r *http.Request) { - if ct := r.Header.Get("Content-Type"); ct == "application/tar" { - gotDockerFile := false - tr := tar.NewReader(r.Body) - for { - header, err := tr.Next() - if err != nil { - break - } - if header.Name == "Dockerfile" { - gotDockerFile = true - } - } - if !gotDockerFile { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("miss Dockerfile")) - return - } - } - //we did not use that Dockerfile to build image cause we are a fake Docker daemon - image := docker.Image{ - ID: s.generateID(), - Created: time.Now(), - } - - query := r.URL.Query() - repository := image.ID - if t := query.Get("t"); t != "" { - repository = t - } - s.iMut.Lock() - s.images = append(s.images, image) - s.imgIDs[repository] = image.ID - s.iMut.Unlock() - w.Write([]byte(fmt.Sprintf("Successfully built %s", image.ID))) -} - -func (s *DockerServer) pullImage(w http.ResponseWriter, r *http.Request) { - fromImageName := r.URL.Query().Get("fromImage") - tag := r.URL.Query().Get("tag") - image := docker.Image{ - ID: s.generateID(), - } - s.iMut.Lock() - s.images = append(s.images, image) - if fromImageName != "" { - if tag != "" { - fromImageName = fmt.Sprintf("%s:%s", fromImageName, tag) - } - s.imgIDs[fromImageName] = image.ID - } - s.iMut.Unlock() -} - -func (s *DockerServer) pushImage(w http.ResponseWriter, r *http.Request) { - name := mux.Vars(r)["name"] - tag := r.URL.Query().Get("tag") - if tag != "" { - name += ":" + tag - } - s.iMut.RLock() - if _, ok := s.imgIDs[name]; !ok { - s.iMut.RUnlock() - http.Error(w, "No such image", http.StatusNotFound) - return - } - s.iMut.RUnlock() - fmt.Fprintln(w, "Pushing...") - fmt.Fprintln(w, "Pushed") -} - -func (s *DockerServer) tagImage(w http.ResponseWriter, r *http.Request) { - name := mux.Vars(r)["name"] - s.iMut.RLock() - if _, ok := s.imgIDs[name]; !ok { - s.iMut.RUnlock() - http.Error(w, "No such image", http.StatusNotFound) - return - } - s.iMut.RUnlock() - s.iMut.Lock() - defer s.iMut.Unlock() - newRepo := r.URL.Query().Get("repo") - newTag := r.URL.Query().Get("tag") - if newTag != "" { - newRepo += ":" + newTag - } - s.imgIDs[newRepo] = s.imgIDs[name] - w.WriteHeader(http.StatusCreated) -} - -func (s *DockerServer) removeImage(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - s.iMut.RLock() - var tag string - if img, ok := s.imgIDs[id]; ok { - id, tag = img, id - } - var tags []string - for tag, taggedID := range s.imgIDs { - if taggedID == id { - tags = append(tags, tag) - } - } - s.iMut.RUnlock() - _, index, err := s.findImageByID(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - w.WriteHeader(http.StatusNoContent) - s.iMut.Lock() - defer s.iMut.Unlock() - if len(tags) < 2 { - s.images[index] = s.images[len(s.images)-1] - s.images = s.images[:len(s.images)-1] - } - if tag != "" { - delete(s.imgIDs, tag) - } -} - -func (s *DockerServer) inspectImage(w http.ResponseWriter, r *http.Request) { - name := mux.Vars(r)["name"] - s.iMut.RLock() - defer s.iMut.RUnlock() - if id, ok := s.imgIDs[name]; ok { - for _, img := range s.images { - if img.ID == id { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(img) - return - } - } - } - http.Error(w, "not found", http.StatusNotFound) -} - -func (s *DockerServer) listEvents(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - var events [][]byte - count := mathrand.Intn(20) - for i := 0; i < count; i++ { - data, err := json.Marshal(s.generateEvent()) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - events = append(events, data) - } - w.WriteHeader(http.StatusOK) - for _, d := range events { - fmt.Fprintln(w, d) - time.Sleep(time.Duration(mathrand.Intn(200)) * time.Millisecond) - } -} - -func (s *DockerServer) pingDocker(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) -} - -func (s *DockerServer) generateEvent() *docker.APIEvents { - var eventType string - switch mathrand.Intn(4) { - case 0: - eventType = "create" - case 1: - eventType = "start" - case 2: - eventType = "stop" - case 3: - eventType = "destroy" - } - return &docker.APIEvents{ - ID: s.generateID(), - Status: eventType, - From: "mybase:latest", - Time: time.Now().Unix(), - } -} - -func (s *DockerServer) loadImage(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) -} - -func (s *DockerServer) getImage(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "application/tar") -} - -func (s *DockerServer) createExecContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - container, _, err := s.findContainer(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - - execID := s.generateID() - container.ExecIDs = append(container.ExecIDs, execID) - - exec := docker.ExecInspect{ - ID: execID, - Container: *container, - } - - var params docker.CreateExecOptions - err = json.NewDecoder(r.Body).Decode(¶ms) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if len(params.Cmd) > 0 { - exec.ProcessConfig.EntryPoint = params.Cmd[0] - if len(params.Cmd) > 1 { - exec.ProcessConfig.Arguments = params.Cmd[1:] - } - } - - exec.ProcessConfig.User = params.User - exec.ProcessConfig.Tty = params.Tty - - s.execMut.Lock() - s.execs = append(s.execs, &exec) - s.execMut.Unlock() - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(map[string]string{"Id": exec.ID}) -} - -func (s *DockerServer) startExecContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - if exec, err := s.getExec(id, false); err == nil { - s.execMut.Lock() - exec.Running = true - s.execMut.Unlock() - if callback, ok := s.execCallbacks[id]; ok { - callback() - delete(s.execCallbacks, id) - } else if callback, ok := s.execCallbacks["*"]; ok { - callback() - delete(s.execCallbacks, "*") - } - s.execMut.Lock() - exec.Running = false - s.execMut.Unlock() - w.WriteHeader(http.StatusOK) - return - } - w.WriteHeader(http.StatusNotFound) -} - -func (s *DockerServer) resizeExecContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - if _, err := s.getExec(id, false); err == nil { - w.WriteHeader(http.StatusOK) - return - } - w.WriteHeader(http.StatusNotFound) -} - -func (s *DockerServer) inspectExecContainer(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - if exec, err := s.getExec(id, true); err == nil { - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(exec) - return - } - w.WriteHeader(http.StatusNotFound) -} - -func (s *DockerServer) getExec(id string, copy bool) (*docker.ExecInspect, error) { - s.execMut.RLock() - defer s.execMut.RUnlock() - for _, exec := range s.execs { - if exec.ID == id { - if copy { - cp := *exec - exec = &cp - } - return exec, nil - } - } - return nil, errors.New("exec not found") -} - -func (s *DockerServer) findNetwork(idOrName string) (*docker.Network, int, error) { - s.netMut.RLock() - defer s.netMut.RUnlock() - for i, network := range s.networks { - if network.ID == idOrName || network.Name == idOrName { - return network, i, nil - } - } - return nil, -1, errors.New("No such network") -} - -func (s *DockerServer) listNetworks(w http.ResponseWriter, r *http.Request) { - s.netMut.RLock() - result := make([]docker.Network, 0, len(s.networks)) - for _, network := range s.networks { - result = append(result, *network) - } - s.netMut.RUnlock() - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(result) -} - -func (s *DockerServer) networkInfo(w http.ResponseWriter, r *http.Request) { - id := mux.Vars(r)["id"] - network, _, err := s.findNetwork(id) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(network) -} - -// isValidName validates configuration objects supported by libnetwork -func isValidName(name string) bool { - if name == "" || strings.Contains(name, ".") { - return false - } - return true -} - -func (s *DockerServer) createNetwork(w http.ResponseWriter, r *http.Request) { - var config *docker.CreateNetworkOptions - defer r.Body.Close() - err := json.NewDecoder(r.Body).Decode(&config) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if !isValidName(config.Name) { - http.Error(w, "Invalid network name", http.StatusBadRequest) - return - } - if n, _, _ := s.findNetwork(config.Name); n != nil { - http.Error(w, "network already exists", http.StatusForbidden) - return - } - - generatedID := s.generateID() - network := docker.Network{ - Name: config.Name, - ID: generatedID, - Driver: config.Driver, - } - s.netMut.Lock() - s.networks = append(s.networks, &network) - s.netMut.Unlock() - w.WriteHeader(http.StatusCreated) - var c = struct{ ID string }{ID: network.ID} - json.NewEncoder(w).Encode(c) -} - -func (s *DockerServer) listVolumes(w http.ResponseWriter, r *http.Request) { - s.volMut.RLock() - result := make([]docker.Volume, 0, len(s.volStore)) - for _, volumeCounter := range s.volStore { - result = append(result, volumeCounter.volume) - } - s.volMut.RUnlock() - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(result) -} - -func (s *DockerServer) createVolume(w http.ResponseWriter, r *http.Request) { - var data struct { - *docker.CreateVolumeOptions - } - defer r.Body.Close() - err := json.NewDecoder(r.Body).Decode(&data) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - volume := &docker.Volume{ - Name: data.CreateVolumeOptions.Name, - Driver: data.CreateVolumeOptions.Driver, - } - // If the name is not specified, generate one. Just using generateID for now - if len(volume.Name) == 0 { - volume.Name = s.generateID() - } - // If driver is not specified, use local - if len(volume.Driver) == 0 { - volume.Driver = "local" - } - // Mount point is a default one with name - volume.Mountpoint = "/var/lib/docker/volumes/" + volume.Name - - // If the volume already exists, don't re-add it. - exists := false - s.volMut.Lock() - if s.volStore != nil { - _, exists = s.volStore[volume.Name] - } else { - // No volumes, create volStore - s.volStore = make(map[string]*volumeCounter) - } - if !exists { - s.volStore[volume.Name] = &volumeCounter{ - volume: *volume, - count: 0, - } - } - s.volMut.Unlock() - w.WriteHeader(http.StatusCreated) - json.NewEncoder(w).Encode(volume) -} - -func (s *DockerServer) inspectVolume(w http.ResponseWriter, r *http.Request) { - s.volMut.RLock() - defer s.volMut.RUnlock() - name := mux.Vars(r)["name"] - vol, err := s.findVolume(name) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(vol.volume) -} - -func (s *DockerServer) findVolume(name string) (*volumeCounter, error) { - vol, ok := s.volStore[name] - if !ok { - return nil, errors.New("no such volume") - } - return vol, nil -} - -func (s *DockerServer) removeVolume(w http.ResponseWriter, r *http.Request) { - s.volMut.Lock() - defer s.volMut.Unlock() - name := mux.Vars(r)["name"] - vol, err := s.findVolume(name) - if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - if vol.count != 0 { - http.Error(w, "volume in use and cannot be removed", http.StatusConflict) - return - } - s.volStore[vol.volume.Name] = nil - w.WriteHeader(http.StatusNoContent) -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go deleted file mode 100644 index ddadcaab5..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go +++ /dev/null @@ -1,2103 +0,0 @@ -// Copyright 2015 go-dockerclient 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 testing - -import ( - "bufio" - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "math/rand" - "net" - "net/http" - "net/http/httptest" - "os" - "reflect" - "strings" - "sync" - "testing" - "time" - - "github.com/fsouza/go-dockerclient" -) - -func TestNewServer(t *testing.T) { - server, err := NewServer("127.0.0.1:0", nil, nil) - if err != nil { - t.Fatal(err) - } - defer server.listener.Close() - conn, err := net.Dial("tcp", server.listener.Addr().String()) - if err != nil { - t.Fatal(err) - } - conn.Close() -} - -func TestServerStop(t *testing.T) { - server, err := NewServer("127.0.0.1:0", nil, nil) - if err != nil { - t.Fatal(err) - } - server.Stop() - _, err = net.Dial("tcp", server.listener.Addr().String()) - if err == nil { - t.Error("Unexpected error when dialing to stopped server") - } -} - -func TestServerStopNoListener(t *testing.T) { - server := DockerServer{} - server.Stop() -} - -func TestServerURL(t *testing.T) { - server, err := NewServer("127.0.0.1:0", nil, nil) - if err != nil { - t.Fatal(err) - } - defer server.Stop() - url := server.URL() - if expected := "http://" + server.listener.Addr().String() + "/"; url != expected { - t.Errorf("DockerServer.URL(): Want %q. Got %q.", expected, url) - } -} - -func TestServerURLNoListener(t *testing.T) { - server := DockerServer{} - url := server.URL() - if url != "" { - t.Errorf("DockerServer.URL(): Expected empty URL on handler mode, got %q.", url) - } -} - -func TestHandleWithHook(t *testing.T) { - var called bool - server, _ := NewServer("127.0.0.1:0", nil, func(*http.Request) { called = true }) - defer server.Stop() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/containers/json?all=1", nil) - server.ServeHTTP(recorder, request) - if !called { - t.Error("ServeHTTP did not call the hook function.") - } -} - -func TestSetHook(t *testing.T) { - var called bool - server, _ := NewServer("127.0.0.1:0", nil, nil) - defer server.Stop() - server.SetHook(func(*http.Request) { called = true }) - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/containers/json?all=1", nil) - server.ServeHTTP(recorder, request) - if !called { - t.Error("ServeHTTP did not call the hook function.") - } -} - -func TestCustomHandler(t *testing.T) { - var called bool - server, _ := NewServer("127.0.0.1:0", nil, nil) - addContainers(server, 2) - server.CustomHandler("/containers/json", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - called = true - fmt.Fprint(w, "Hello world") - })) - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/containers/json?all=1", nil) - server.ServeHTTP(recorder, request) - if !called { - t.Error("Did not call the custom handler") - } - if got := recorder.Body.String(); got != "Hello world" { - t.Errorf("Wrong output for custom handler: want %q. Got %q.", "Hello world", got) - } -} - -func TestCustomHandlerRegexp(t *testing.T) { - var called bool - server, _ := NewServer("127.0.0.1:0", nil, nil) - addContainers(server, 2) - server.CustomHandler("/containers/.*/json", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - called = true - fmt.Fprint(w, "Hello world") - })) - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/containers/.*/json?all=1", nil) - server.ServeHTTP(recorder, request) - if !called { - t.Error("Did not call the custom handler") - } - if got := recorder.Body.String(); got != "Hello world" { - t.Errorf("Wrong output for custom handler: want %q. Got %q.", "Hello world", got) - } -} - -func TestListContainers(t *testing.T) { - server := DockerServer{} - addContainers(&server, 2) - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/containers/json?all=1", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("ListContainers: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - expected := make([]docker.APIContainers, 2) - for i, container := range server.containers { - expected[i] = docker.APIContainers{ - ID: container.ID, - Image: container.Image, - Command: strings.Join(container.Config.Cmd, " "), - Created: container.Created.Unix(), - Status: container.State.String(), - Ports: container.NetworkSettings.PortMappingAPI(), - Names: []string{"/" + container.Name}, - } - } - var got []docker.APIContainers - err := json.NewDecoder(recorder.Body).Decode(&got) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(got, expected) { - t.Errorf("ListContainers. Want %#v. Got %#v.", expected, got) - } -} - -func TestListRunningContainers(t *testing.T) { - server := DockerServer{} - addContainers(&server, 2) - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/containers/json?all=0", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("ListRunningContainers: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - var got []docker.APIContainers - err := json.NewDecoder(recorder.Body).Decode(&got) - if err != nil { - t.Fatal(err) - } - if len(got) != 0 { - t.Errorf("ListRunningContainers: Want 0. Got %d.", len(got)) - } -} - -func TestCreateContainer(t *testing.T) { - server := DockerServer{} - server.imgIDs = map[string]string{"base": "a1234"} - server.buildMuxer() - recorder := httptest.NewRecorder() - body := `{"Hostname":"", "User":"ubuntu", "Memory":0, "MemorySwap":0, "AttachStdin":false, "AttachStdout":true, "AttachStderr":true, -"PortSpecs":null, "Tty":false, "OpenStdin":false, "StdinOnce":false, "Env":null, "Cmd":["date"], "Image":"base", "Volumes":{}, "VolumesFrom":"","HostConfig":{"Binds":["/var/run/docker.sock:/var/run/docker.sock:rw"]}}` - request, _ := http.NewRequest("POST", "/containers/create", strings.NewReader(body)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusCreated { - t.Errorf("CreateContainer: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code) - } - var returned docker.Container - err := json.NewDecoder(recorder.Body).Decode(&returned) - if err != nil { - t.Fatal(err) - } - stored := server.containers[0] - if returned.ID != stored.ID { - t.Errorf("CreateContainer: ID mismatch. Stored: %q. Returned: %q.", stored.ID, returned.ID) - } - if stored.State.Running { - t.Errorf("CreateContainer should not set container to running state.") - } - if stored.Config.User != "ubuntu" { - t.Errorf("CreateContainer: wrong config. Expected: %q. Returned: %q.", "ubuntu", stored.Config.User) - } - if stored.Config.Hostname != returned.ID[:12] { - t.Errorf("CreateContainer: wrong hostname. Expected: %q. Returned: %q.", returned.ID[:12], stored.Config.Hostname) - } - expectedBind := []string{"/var/run/docker.sock:/var/run/docker.sock:rw"} - if !reflect.DeepEqual(stored.HostConfig.Binds, expectedBind) { - t.Errorf("CreateContainer: wrong host config. Expected: %v. Returned %v.", expectedBind, stored.HostConfig.Binds) - } -} - -func TestCreateContainerWithNotifyChannel(t *testing.T) { - ch := make(chan *docker.Container, 1) - server := DockerServer{} - server.imgIDs = map[string]string{"base": "a1234"} - server.cChan = ch - server.buildMuxer() - recorder := httptest.NewRecorder() - body := `{"Hostname":"", "User":"", "Memory":0, "MemorySwap":0, "AttachStdin":false, "AttachStdout":true, "AttachStderr":true, -"PortSpecs":null, "Tty":false, "OpenStdin":false, "StdinOnce":false, "Env":null, "Cmd":["date"], "Image":"base", "Volumes":{}, "VolumesFrom":""}` - request, _ := http.NewRequest("POST", "/containers/create", strings.NewReader(body)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusCreated { - t.Errorf("CreateContainer: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code) - } - if notified := <-ch; notified != server.containers[0] { - t.Errorf("CreateContainer: did not notify the proper container. Want %q. Got %q.", server.containers[0].ID, notified.ID) - } -} - -func TestCreateContainerInvalidBody(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/containers/create", strings.NewReader("whaaaaaat---")) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusBadRequest { - t.Errorf("CreateContainer: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code) - } -} - -func TestCreateContainerDuplicateName(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - server.imgIDs = map[string]string{"base": "a1234"} - addContainers(&server, 1) - server.containers[0].Name = "mycontainer" - recorder := httptest.NewRecorder() - body := `{"Hostname":"", "User":"ubuntu", "Memory":0, "MemorySwap":0, "AttachStdin":false, "AttachStdout":true, "AttachStderr":true, -"PortSpecs":null, "Tty":false, "OpenStdin":false, "StdinOnce":false, "Env":null, "Cmd":["date"], "Image":"base", "Volumes":{}, "VolumesFrom":"","HostConfig":{"Binds":["/var/run/docker.sock:/var/run/docker.sock:rw"]}}` - request, _ := http.NewRequest("POST", "/containers/create?name=mycontainer", strings.NewReader(body)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusConflict { - t.Errorf("CreateContainer: wrong status. Want %d. Got %d.", http.StatusConflict, recorder.Code) - } -} - -func TestCreateMultipleContainersEmptyName(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - server.imgIDs = map[string]string{"base": "a1234"} - addContainers(&server, 1) - server.containers[0].Name = "" - recorder := httptest.NewRecorder() - body := `{"Hostname":"", "User":"ubuntu", "Memory":0, "MemorySwap":0, "AttachStdin":false, "AttachStdout":true, "AttachStderr":true, -"PortSpecs":null, "Tty":false, "OpenStdin":false, "StdinOnce":false, "Env":null, "Cmd":["date"], "Image":"base", "Volumes":{}, "VolumesFrom":"","HostConfig":{"Binds":["/var/run/docker.sock:/var/run/docker.sock:rw"]}}` - request, _ := http.NewRequest("POST", "/containers/create", strings.NewReader(body)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusCreated { - t.Errorf("CreateContainer: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code) - } - var returned docker.Container - err := json.NewDecoder(recorder.Body).Decode(&returned) - if err != nil { - t.Fatal(err) - } - stored := server.containers[1] - if returned.ID != stored.ID { - t.Errorf("CreateContainer: ID mismatch. Stored: %q. Returned: %q.", stored.ID, returned.ID) - } - if stored.State.Running { - t.Errorf("CreateContainer should not set container to running state.") - } - if stored.Config.User != "ubuntu" { - t.Errorf("CreateContainer: wrong config. Expected: %q. Returned: %q.", "ubuntu", stored.Config.User) - } - expectedBind := []string{"/var/run/docker.sock:/var/run/docker.sock:rw"} - if !reflect.DeepEqual(stored.HostConfig.Binds, expectedBind) { - t.Errorf("CreateContainer: wrong host config. Expected: %v. Returned %v.", expectedBind, stored.HostConfig.Binds) - } -} - -func TestCreateContainerInvalidName(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - body := `{"Hostname":"", "User":"", "Memory":0, "MemorySwap":0, "AttachStdin":false, "AttachStdout":true, "AttachStderr":true, -"PortSpecs":null, "Tty":false, "OpenStdin":false, "StdinOnce":false, "Env":null, "Cmd":["date"], -"Image":"base", "Volumes":{}, "VolumesFrom":""}` - request, _ := http.NewRequest("POST", "/containers/create?name=myapp/container1", strings.NewReader(body)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusInternalServerError { - t.Errorf("CreateContainer: wrong status. Want %d. Got %d.", http.StatusInternalServerError, recorder.Code) - } - expectedBody := "Invalid container name\n" - if got := recorder.Body.String(); got != expectedBody { - t.Errorf("CreateContainer: wrong body. Want %q. Got %q.", expectedBody, got) - } -} - -func TestCreateContainerImageNotFound(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - body := `{"Hostname":"", "User":"", "Memory":0, "MemorySwap":0, "AttachStdin":false, "AttachStdout":true, "AttachStderr":true, -"PortSpecs":null, "Tty":false, "OpenStdin":false, "StdinOnce":false, "Env":null, "Cmd":["date"], -"Image":"base", "Volumes":{}, "VolumesFrom":""}` - request, _ := http.NewRequest("POST", "/containers/create", strings.NewReader(body)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("CreateContainer: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -func TestRenameContainer(t *testing.T) { - server := DockerServer{} - addContainers(&server, 2) - server.buildMuxer() - recorder := httptest.NewRecorder() - newName := server.containers[0].Name + "abc" - path := fmt.Sprintf("/containers/%s/rename?name=%s", server.containers[0].ID, newName) - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNoContent { - t.Errorf("RenameContainer: wrong status. Want %d. Got %d.", http.StatusNoContent, recorder.Code) - } - container := server.containers[0] - if container.Name != newName { - t.Errorf("RenameContainer: did not rename the container. Want %q. Got %q.", newName, container.Name) - } -} - -func TestRenameContainerNotFound(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/containers/blabla/rename?name=something", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("RenameContainer: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -func TestCommitContainer(t *testing.T) { - server := DockerServer{} - addContainers(&server, 2) - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/commit?container="+server.containers[0].ID, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("CommitContainer: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - expected := fmt.Sprintf(`{"ID":"%s"}`, server.images[0].ID) - if got := recorder.Body.String(); got != expected { - t.Errorf("CommitContainer: wrong response body. Want %q. Got %q.", expected, got) - } -} - -func TestCommitContainerComplete(t *testing.T) { - server := DockerServer{} - server.imgIDs = make(map[string]string) - addContainers(&server, 2) - server.buildMuxer() - recorder := httptest.NewRecorder() - queryString := "container=" + server.containers[0].ID + "&repo=tsuru/python&m=saving&author=developers" - queryString += `&run={"Cmd": ["cat", "/world"],"PortSpecs":["22"]}` - request, _ := http.NewRequest("POST", "/commit?"+queryString, nil) - server.ServeHTTP(recorder, request) - image := server.images[0] - if image.Parent != server.containers[0].Image { - t.Errorf("CommitContainer: wrong parent image. Want %q. Got %q.", server.containers[0].Image, image.Parent) - } - if image.Container != server.containers[0].ID { - t.Errorf("CommitContainer: wrong container. Want %q. Got %q.", server.containers[0].ID, image.Container) - } - message := "saving" - if image.Comment != message { - t.Errorf("CommitContainer: wrong comment (commit message). Want %q. Got %q.", message, image.Comment) - } - author := "developers" - if image.Author != author { - t.Errorf("CommitContainer: wrong author. Want %q. Got %q.", author, image.Author) - } - if id := server.imgIDs["tsuru/python"]; id != image.ID { - t.Errorf("CommitContainer: wrong ID saved for repository. Want %q. Got %q.", image.ID, id) - } - portSpecs := []string{"22"} - if !reflect.DeepEqual(image.Config.PortSpecs, portSpecs) { - t.Errorf("CommitContainer: wrong port spec in config. Want %#v. Got %#v.", portSpecs, image.Config.PortSpecs) - } - cmd := []string{"cat", "/world"} - if !reflect.DeepEqual(image.Config.Cmd, cmd) { - t.Errorf("CommitContainer: wrong cmd in config. Want %#v. Got %#v.", cmd, image.Config.Cmd) - } -} - -func TestCommitContainerWithTag(t *testing.T) { - server := DockerServer{} - server.imgIDs = make(map[string]string) - addContainers(&server, 2) - server.buildMuxer() - recorder := httptest.NewRecorder() - queryString := "container=" + server.containers[0].ID + "&repo=tsuru/python&tag=v1" - request, _ := http.NewRequest("POST", "/commit?"+queryString, nil) - server.ServeHTTP(recorder, request) - image := server.images[0] - if image.Parent != server.containers[0].Image { - t.Errorf("CommitContainer: wrong parent image. Want %q. Got %q.", server.containers[0].Image, image.Parent) - } - if image.Container != server.containers[0].ID { - t.Errorf("CommitContainer: wrong container. Want %q. Got %q.", server.containers[0].ID, image.Container) - } - if id := server.imgIDs["tsuru/python:v1"]; id != image.ID { - t.Errorf("CommitContainer: wrong ID saved for repository. Want %q. Got %q.", image.ID, id) - } -} - -func TestCommitContainerInvalidRun(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/commit?container="+server.containers[0].ID+"&run=abc---", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusBadRequest { - t.Errorf("CommitContainer. Wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code) - } -} - -func TestCommitContainerNotFound(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/commit?container=abc123", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("CommitContainer. Wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -func TestInspectContainer(t *testing.T) { - server := DockerServer{} - addContainers(&server, 2) - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/json", server.containers[0].ID) - request, _ := http.NewRequest("GET", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("InspectContainer: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - expected := server.containers[0] - var got docker.Container - err := json.NewDecoder(recorder.Body).Decode(&got) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(got.Config, expected.Config) { - t.Errorf("InspectContainer: wrong value. Want %#v. Got %#v.", *expected, got) - } - if !reflect.DeepEqual(got.NetworkSettings, expected.NetworkSettings) { - t.Errorf("InspectContainer: wrong value. Want %#v. Got %#v.", *expected, got) - } - got.State.StartedAt = expected.State.StartedAt - got.State.FinishedAt = expected.State.FinishedAt - got.Config = expected.Config - got.Created = expected.Created - got.NetworkSettings = expected.NetworkSettings - if !reflect.DeepEqual(got, *expected) { - t.Errorf("InspectContainer: wrong value. Want %#v. Got %#v.", *expected, got) - } -} - -func TestInspectContainerNotFound(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/containers/abc123/json", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("InspectContainer: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -func TestTopContainer(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.containers[0].State.Running = true - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/top", server.containers[0].ID) - request, _ := http.NewRequest("GET", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("TopContainer: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - var got docker.TopResult - err := json.NewDecoder(recorder.Body).Decode(&got) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(got.Titles, []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"}) { - t.Fatalf("TopContainer: Unexpected titles, got: %#v", got.Titles) - } - if len(got.Processes) != 1 { - t.Fatalf("TopContainer: Unexpected process len, got: %d", len(got.Processes)) - } - if got.Processes[0][len(got.Processes[0])-1] != "ls -la .." { - t.Fatalf("TopContainer: Unexpected command name, got: %s", got.Processes[0][len(got.Processes[0])-1]) - } -} - -func TestTopContainerNotFound(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/containers/xyz/top", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("TopContainer: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -func TestTopContainerStopped(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/top", server.containers[0].ID) - request, _ := http.NewRequest("GET", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusInternalServerError { - t.Errorf("TopContainer: wrong status. Want %d. Got %d.", http.StatusInternalServerError, recorder.Code) - } -} - -func TestStartContainer(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.buildMuxer() - memory := int64(536870912) - hostConfig := docker.HostConfig{Memory: memory} - configBytes, err := json.Marshal(hostConfig) - if err != nil { - t.Fatal(err) - } - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/start", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, bytes.NewBuffer(configBytes)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - if !server.containers[0].State.Running { - t.Error("StartContainer: did not set the container to running state") - } - if gotMemory := server.containers[0].HostConfig.Memory; gotMemory != memory { - t.Errorf("StartContainer: wrong HostConfig. Wants %d of memory. Got %d", memory, gotMemory) - } -} - -func TestStartContainerChangeNetwork(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.buildMuxer() - hostConfig := docker.HostConfig{ - PortBindings: map[docker.Port][]docker.PortBinding{ - "8888/tcp": {{HostIP: "", HostPort: "12345"}}, - }, - } - configBytes, err := json.Marshal(hostConfig) - if err != nil { - t.Fatal(err) - } - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/start", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, bytes.NewBuffer(configBytes)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - if !server.containers[0].State.Running { - t.Error("StartContainer: did not set the container to running state") - } - portMapping := server.containers[0].NetworkSettings.Ports["8888/tcp"] - expected := []docker.PortBinding{{HostIP: "0.0.0.0", HostPort: "12345"}} - if !reflect.DeepEqual(portMapping, expected) { - t.Errorf("StartContainer: network not updated. Wants %#v ports. Got %#v", expected, portMapping) - } -} - -func TestStartContainerWithNotifyChannel(t *testing.T) { - ch := make(chan *docker.Container, 1) - server := DockerServer{} - server.cChan = ch - addContainers(&server, 1) - addContainers(&server, 1) - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/start", server.containers[1].ID) - request, _ := http.NewRequest("POST", path, bytes.NewBuffer([]byte("{}"))) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - if notified := <-ch; notified != server.containers[1] { - t.Errorf("StartContainer: did not notify the proper container. Want %q. Got %q.", server.containers[1].ID, notified.ID) - } -} - -func TestStartContainerNotFound(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - path := "/containers/abc123/start" - request, _ := http.NewRequest("POST", path, bytes.NewBuffer([]byte("null"))) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -func TestStartContainerAlreadyRunning(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.containers[0].State.Running = true - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/start", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, bytes.NewBuffer([]byte("null"))) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotModified { - t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusNotModified, recorder.Code) - } -} - -func TestStopContainer(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.containers[0].State.Running = true - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/stop", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNoContent { - t.Errorf("StopContainer: wrong status code. Want %d. Got %d.", http.StatusNoContent, recorder.Code) - } - if server.containers[0].State.Running { - t.Error("StopContainer: did not stop the container") - } -} - -func TestKillContainer(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.containers[0].State.Running = true - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/kill", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNoContent { - t.Errorf("KillContainer: wrong status code. Want %d. Got %d.", http.StatusNoContent, recorder.Code) - } - if server.containers[0].State.Running { - t.Error("KillContainer: did not stop the container") - } -} - -func TestStopContainerWithNotifyChannel(t *testing.T) { - ch := make(chan *docker.Container, 1) - server := DockerServer{} - server.cChan = ch - addContainers(&server, 1) - addContainers(&server, 1) - server.containers[1].State.Running = true - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/stop", server.containers[1].ID) - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNoContent { - t.Errorf("StopContainer: wrong status code. Want %d. Got %d.", http.StatusNoContent, recorder.Code) - } - if notified := <-ch; notified != server.containers[1] { - t.Errorf("StopContainer: did not notify the proper container. Want %q. Got %q.", server.containers[1].ID, notified.ID) - } -} - -func TestStopContainerNotFound(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - path := "/containers/abc123/stop" - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("StopContainer: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -func TestStopContainerNotRunning(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/stop", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusBadRequest { - t.Errorf("StopContainer: wrong status code. Want %d. Got %d.", http.StatusBadRequest, recorder.Code) - } -} - -func TestPauseContainer(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/pause", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNoContent { - t.Errorf("PauseContainer: wrong status code. Want %d. Got %d.", http.StatusNoContent, recorder.Code) - } - if !server.containers[0].State.Paused { - t.Error("PauseContainer: did not pause the container") - } -} - -func TestPauseContainerAlreadyPaused(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.containers[0].State.Paused = true - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/pause", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusBadRequest { - t.Errorf("PauseContainer: wrong status code. Want %d. Got %d.", http.StatusBadRequest, recorder.Code) - } -} - -func TestPauseContainerNotFound(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - path := "/containers/abc123/pause" - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("PauseContainer: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -func TestUnpauseContainer(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.containers[0].State.Paused = true - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/unpause", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNoContent { - t.Errorf("UnpauseContainer: wrong status code. Want %d. Got %d.", http.StatusNoContent, recorder.Code) - } - if server.containers[0].State.Paused { - t.Error("UnpauseContainer: did not unpause the container") - } -} - -func TestUnpauseContainerNotPaused(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/unpause", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusBadRequest { - t.Errorf("UnpauseContainer: wrong status code. Want %d. Got %d.", http.StatusBadRequest, recorder.Code) - } -} - -func TestUnpauseContainerNotFound(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - path := "/containers/abc123/unpause" - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("UnpauseContainer: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -func TestWaitContainer(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.containers[0].State.Running = true - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/wait", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, nil) - go func() { - server.cMut.Lock() - server.containers[0].State.Running = false - server.cMut.Unlock() - }() - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("WaitContainer: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - expected := `{"StatusCode":0}` + "\n" - if body := recorder.Body.String(); body != expected { - t.Errorf("WaitContainer: wrong body. Want %q. Got %q.", expected, body) - } -} - -func TestWaitContainerStatus(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.buildMuxer() - server.containers[0].State.ExitCode = 63 - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/wait", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("WaitContainer: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - expected := `{"StatusCode":63}` + "\n" - if body := recorder.Body.String(); body != expected { - t.Errorf("WaitContainer: wrong body. Want %q. Got %q.", expected, body) - } -} - -func TestWaitContainerNotFound(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - path := "/containers/abc123/wait" - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("WaitContainer: wrong status code. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -type HijackableResponseRecorder struct { - httptest.ResponseRecorder - readCh chan []byte -} - -func (r *HijackableResponseRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) { - myConn, otherConn := net.Pipe() - r.readCh = make(chan []byte) - go func() { - data, _ := ioutil.ReadAll(myConn) - r.readCh <- data - }() - return otherConn, nil, nil -} - -func (r *HijackableResponseRecorder) HijackBuffer() string { - return string(<-r.readCh) -} - -func TestAttachContainer(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.containers[0].State.Running = true - server.buildMuxer() - recorder := &HijackableResponseRecorder{} - path := fmt.Sprintf("/containers/%s/attach?logs=1", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - lines := []string{ - "\x01\x00\x00\x00\x00\x00\x00\x15Container is running", - "\x01\x00\x00\x00\x00\x00\x00\x0fWhat happened?", - "\x01\x00\x00\x00\x00\x00\x00\x13Something happened", - } - expected := strings.Join(lines, "\n") + "\n" - if body := recorder.HijackBuffer(); body != expected { - t.Errorf("AttachContainer: wrong body. Want %q. Got %q.", expected, body) - } -} - -func TestAttachContainerNotFound(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := &HijackableResponseRecorder{} - path := "/containers/abc123/attach?logs=1" - request, _ := http.NewRequest("POST", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("AttachContainer: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -func TestAttachContainerWithStreamBlocks(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.containers[0].State.Running = true - server.buildMuxer() - path := fmt.Sprintf("/containers/%s/attach?logs=1&stdout=1&stream=1", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, nil) - done := make(chan string) - go func() { - recorder := &HijackableResponseRecorder{} - server.ServeHTTP(recorder, request) - done <- recorder.HijackBuffer() - }() - select { - case <-done: - t.Fatalf("attach stream returned before container is stopped") - case <-time.After(500 * time.Millisecond): - } - server.cMut.Lock() - server.containers[0].State.Running = false - server.cMut.Unlock() - var body string - select { - case body = <-done: - case <-time.After(5 * time.Second): - t.Fatalf("timed out waiting for attach to finish") - } - lines := []string{ - "\x01\x00\x00\x00\x00\x00\x00\x15Container is running", - "\x01\x00\x00\x00\x00\x00\x00\x0fWhat happened?", - "\x01\x00\x00\x00\x00\x00\x00\x13Something happened", - } - expected := strings.Join(lines, "\n") + "\n" - if body != expected { - t.Errorf("AttachContainer: wrong body. Want %q. Got %q.", expected, body) - } -} - -func TestRemoveContainer(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s", server.containers[0].ID) - request, _ := http.NewRequest("DELETE", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNoContent { - t.Errorf("RemoveContainer: wrong status. Want %d. Got %d.", http.StatusNoContent, recorder.Code) - } - if len(server.containers) > 0 { - t.Error("RemoveContainer: did not remove the container.") - } -} - -func TestRemoveContainerByName(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s", server.containers[0].Name) - request, _ := http.NewRequest("DELETE", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNoContent { - t.Errorf("RemoveContainer: wrong status. Want %d. Got %d.", http.StatusNoContent, recorder.Code) - } - if len(server.containers) > 0 { - t.Error("RemoveContainer: did not remove the container.") - } -} - -func TestRemoveContainerNotFound(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/abc123") - request, _ := http.NewRequest("DELETE", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("RemoveContainer: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -func TestRemoveContainerRunning(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.containers[0].State.Running = true - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s", server.containers[0].ID) - request, _ := http.NewRequest("DELETE", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusInternalServerError { - t.Errorf("RemoveContainer: wrong status. Want %d. Got %d.", http.StatusInternalServerError, recorder.Code) - } - if len(server.containers) < 1 { - t.Error("RemoveContainer: should not remove the container.") - } -} - -func TestRemoveContainerRunningForce(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.containers[0].State.Running = true - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s?%s", server.containers[0].ID, "force=1") - request, _ := http.NewRequest("DELETE", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNoContent { - t.Errorf("RemoveContainer: wrong status. Want %d. Got %d.", http.StatusNoContent, recorder.Code) - } - if len(server.containers) > 0 { - t.Error("RemoveContainer: did not remove the container.") - } -} - -func TestPullImage(t *testing.T) { - server := DockerServer{imgIDs: make(map[string]string)} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/images/create?fromImage=base", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("PullImage: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - if len(server.images) != 1 { - t.Errorf("PullImage: Want 1 image. Got %d.", len(server.images)) - } - if _, ok := server.imgIDs["base"]; !ok { - t.Error("PullImage: Repository should not be empty.") - } -} - -func TestPullImageWithTag(t *testing.T) { - server := DockerServer{imgIDs: make(map[string]string)} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/images/create?fromImage=base&tag=tag", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("PullImage: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - if len(server.images) != 1 { - t.Errorf("PullImage: Want 1 image. Got %d.", len(server.images)) - } - if _, ok := server.imgIDs["base:tag"]; !ok { - t.Error("PullImage: Repository should not be empty.") - } -} - -func TestPushImage(t *testing.T) { - server := DockerServer{imgIDs: map[string]string{"tsuru/python": "a123"}} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/images/tsuru/python/push", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("PushImage: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } -} - -func TestPushImageWithTag(t *testing.T) { - server := DockerServer{imgIDs: map[string]string{"tsuru/python:v1": "a123"}} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/images/tsuru/python/push?tag=v1", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("PushImage: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } -} - -func TestPushImageNotFound(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/images/tsuru/python/push", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("PushImage: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -func TestTagImage(t *testing.T) { - server := DockerServer{imgIDs: map[string]string{"tsuru/python": "a123"}} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/images/tsuru/python/tag?repo=tsuru/new-python", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusCreated { - t.Errorf("TagImage: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code) - } - if server.imgIDs["tsuru/python"] != server.imgIDs["tsuru/new-python"] { - t.Errorf("TagImage: did not tag the image") - } -} - -func TestTagImageWithRepoAndTag(t *testing.T) { - server := DockerServer{imgIDs: map[string]string{"tsuru/python": "a123"}} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/images/tsuru/python/tag?repo=tsuru/new-python&tag=v1", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusCreated { - t.Errorf("TagImage: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code) - } - if server.imgIDs["tsuru/python"] != server.imgIDs["tsuru/new-python:v1"] { - t.Errorf("TagImage: did not tag the image") - } -} - -func TestTagImageNotFound(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/images/tsuru/python/tag", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("TagImage: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -func addContainers(server *DockerServer, n int) { - server.cMut.Lock() - defer server.cMut.Unlock() - for i := 0; i < n; i++ { - date := time.Now().Add(time.Duration((rand.Int() % (i + 1))) * time.Hour) - container := docker.Container{ - Name: fmt.Sprintf("%x", rand.Int()%10000), - ID: fmt.Sprintf("%x", rand.Int()%10000), - Created: date, - Path: "ls", - Args: []string{"-la", ".."}, - Config: &docker.Config{ - Hostname: fmt.Sprintf("docker-%d", i), - AttachStdout: true, - AttachStderr: true, - Env: []string{"ME=you", fmt.Sprintf("NUMBER=%d", i)}, - Cmd: []string{"ls", "-la", ".."}, - Image: "base", - }, - State: docker.State{ - Running: false, - Pid: 400 + i, - ExitCode: 0, - StartedAt: date, - }, - Image: "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", - NetworkSettings: &docker.NetworkSettings{ - IPAddress: fmt.Sprintf("10.10.10.%d", i+2), - IPPrefixLen: 24, - Gateway: "10.10.10.1", - Bridge: "docker0", - PortMapping: map[string]docker.PortMapping{ - "Tcp": {"8888": fmt.Sprintf("%d", 49600+i)}, - }, - Ports: map[docker.Port][]docker.PortBinding{ - "8888/tcp": { - {HostIP: "0.0.0.0", HostPort: fmt.Sprintf("%d", 49600+i)}, - }, - }, - }, - ResolvConfPath: "/etc/resolv.conf", - } - server.containers = append(server.containers, &container) - } -} - -func addImages(server *DockerServer, n int, repo bool) { - server.iMut.Lock() - defer server.iMut.Unlock() - if server.imgIDs == nil { - server.imgIDs = make(map[string]string) - } - for i := 0; i < n; i++ { - date := time.Now().Add(time.Duration((rand.Int() % (i + 1))) * time.Hour) - image := docker.Image{ - ID: fmt.Sprintf("%x", rand.Int()%10000), - Created: date, - } - server.images = append(server.images, image) - if repo { - repo := "docker/python-" + image.ID - server.imgIDs[repo] = image.ID - } - } -} - -func TestListImages(t *testing.T) { - server := DockerServer{} - addImages(&server, 2, true) - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/images/json?all=1", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("ListImages: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - expected := make([]docker.APIImages, 2) - for i, image := range server.images { - expected[i] = docker.APIImages{ - ID: image.ID, - Created: image.Created.Unix(), - RepoTags: []string{"docker/python-" + image.ID}, - } - } - var got []docker.APIImages - err := json.NewDecoder(recorder.Body).Decode(&got) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(got, expected) { - t.Errorf("ListImages. Want %#v. Got %#v.", expected, got) - } -} - -func TestRemoveImage(t *testing.T) { - server := DockerServer{} - addImages(&server, 1, false) - server.buildMuxer() - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/images/%s", server.images[0].ID) - request, _ := http.NewRequest("DELETE", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNoContent { - t.Errorf("RemoveImage: wrong status. Want %d. Got %d.", http.StatusNoContent, recorder.Code) - } - if len(server.images) > 0 { - t.Error("RemoveImage: did not remove the image.") - } -} - -func TestRemoveImageByName(t *testing.T) { - server := DockerServer{} - addImages(&server, 1, true) - server.buildMuxer() - recorder := httptest.NewRecorder() - imgName := "docker/python-" + server.images[0].ID - path := "/images/" + imgName - request, _ := http.NewRequest("DELETE", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNoContent { - t.Errorf("RemoveImage: wrong status. Want %d. Got %d.", http.StatusNoContent, recorder.Code) - } - if len(server.images) > 0 { - t.Error("RemoveImage: did not remove the image.") - } - _, ok := server.imgIDs[imgName] - if ok { - t.Error("RemoveImage: did not remove image tag name.") - } -} - -func TestRemoveImageWithMultipleTags(t *testing.T) { - server := DockerServer{} - addImages(&server, 1, true) - server.buildMuxer() - imgID := server.images[0].ID - imgName := "docker/python-" + imgID - server.imgIDs["docker/python-wat"] = imgID - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/images/%s", imgName) - request, _ := http.NewRequest("DELETE", path, nil) - server.ServeHTTP(recorder, request) - _, ok := server.imgIDs[imgName] - if ok { - t.Error("RemoveImage: did not remove image tag name.") - } - id, ok := server.imgIDs["docker/python-wat"] - if !ok { - t.Error("RemoveImage: removed the wrong tag name.") - } - if id != imgID { - t.Error("RemoveImage: disassociated the wrong ID from the tag") - } - if len(server.images) < 1 { - t.Fatal("RemoveImage: removed the image, but should keep it") - } - if server.images[0].ID != imgID { - t.Error("RemoveImage: changed the ID of the image!") - } -} - -func TestPrepareFailure(t *testing.T) { - server := DockerServer{failures: make(map[string]string)} - server.buildMuxer() - errorID := "my_error" - server.PrepareFailure(errorID, "containers/json") - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/containers/json?all=1", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusBadRequest { - t.Errorf("PrepareFailure: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code) - } - if recorder.Body.String() != errorID+"\n" { - t.Errorf("PrepareFailure: wrong message. Want %s. Got %s.", errorID, recorder.Body.String()) - } -} - -func TestPrepareMultiFailures(t *testing.T) { - server := DockerServer{multiFailures: []map[string]string{}} - server.buildMuxer() - errorID := "multi error" - server.PrepareMultiFailures(errorID, "containers/json") - server.PrepareMultiFailures(errorID, "containers/json") - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/containers/json?all=1", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusBadRequest { - t.Errorf("PrepareFailure: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code) - } - if recorder.Body.String() != errorID+"\n" { - t.Errorf("PrepareFailure: wrong message. Want %s. Got %s.", errorID, recorder.Body.String()) - } - recorder = httptest.NewRecorder() - request, _ = http.NewRequest("GET", "/containers/json?all=1", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusBadRequest { - t.Errorf("PrepareFailure: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code) - } - if recorder.Body.String() != errorID+"\n" { - t.Errorf("PrepareFailure: wrong message. Want %s. Got %s.", errorID, recorder.Body.String()) - } - recorder = httptest.NewRecorder() - request, _ = http.NewRequest("GET", "/containers/json?all=1", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("PrepareFailure: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - if recorder.Body.String() == errorID+"\n" { - t.Errorf("PrepareFailure: wrong message. Want %s. Got %s.", errorID, recorder.Body.String()) - } -} - -func TestRemoveFailure(t *testing.T) { - server := DockerServer{failures: make(map[string]string)} - server.buildMuxer() - errorID := "my_error" - server.PrepareFailure(errorID, "containers/json") - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/containers/json?all=1", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusBadRequest { - t.Errorf("PrepareFailure: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code) - } - server.ResetFailure(errorID) - recorder = httptest.NewRecorder() - request, _ = http.NewRequest("GET", "/containers/json?all=1", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("RemoveFailure: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } -} - -func TestResetMultiFailures(t *testing.T) { - server := DockerServer{multiFailures: []map[string]string{}} - server.buildMuxer() - errorID := "multi error" - server.PrepareMultiFailures(errorID, "containers/json") - server.PrepareMultiFailures(errorID, "containers/json") - if len(server.multiFailures) != 2 { - t.Errorf("PrepareMultiFailures: error adding multi failures.") - } - server.ResetMultiFailures() - if len(server.multiFailures) != 0 { - t.Errorf("ResetMultiFailures: error reseting multi failures.") - } -} - -func TestMutateContainer(t *testing.T) { - server := DockerServer{failures: make(map[string]string)} - server.buildMuxer() - server.containers = append(server.containers, &docker.Container{ID: "id123"}) - state := docker.State{Running: false, ExitCode: 1} - err := server.MutateContainer("id123", state) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(server.containers[0].State, state) { - t.Errorf("Wrong state after mutation.\nWant %#v.\nGot %#v.", - state, server.containers[0].State) - } -} - -func TestMutateContainerNotFound(t *testing.T) { - server := DockerServer{failures: make(map[string]string)} - server.buildMuxer() - state := docker.State{Running: false, ExitCode: 1} - err := server.MutateContainer("id123", state) - if err == nil { - t.Error("Unexpected error") - } - if err.Error() != "container not found" { - t.Errorf("wrong error message. Want %q. Got %q.", "container not found", err) - } -} - -func TestBuildImageWithContentTypeTar(t *testing.T) { - server := DockerServer{imgIDs: make(map[string]string)} - imageName := "teste" - recorder := httptest.NewRecorder() - tarFile, err := os.Open("data/dockerfile.tar") - if err != nil { - t.Fatal(err) - } - defer tarFile.Close() - request, _ := http.NewRequest("POST", "/build?t=teste", tarFile) - request.Header.Add("Content-Type", "application/tar") - server.buildImage(recorder, request) - if recorder.Body.String() == "miss Dockerfile" { - t.Errorf("BuildImage: miss Dockerfile") - return - } - if _, ok := server.imgIDs[imageName]; ok == false { - t.Errorf("BuildImage: image %s not builded", imageName) - } -} - -func TestBuildImageWithRemoteDockerfile(t *testing.T) { - server := DockerServer{imgIDs: make(map[string]string)} - imageName := "teste" - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/build?t=teste&remote=http://localhost/Dockerfile", nil) - server.buildImage(recorder, request) - if _, ok := server.imgIDs[imageName]; ok == false { - t.Errorf("BuildImage: image %s not builded", imageName) - } -} - -func TestPing(t *testing.T) { - server := DockerServer{} - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/_ping", nil) - server.pingDocker(recorder, request) - if recorder.Body.String() != "" { - t.Errorf("Ping: Unexpected body: %s", recorder.Body.String()) - } - if recorder.Code != http.StatusOK { - t.Errorf("Ping: Expected code %d, got: %d", http.StatusOK, recorder.Code) - } -} - -func TestDefaultHandler(t *testing.T) { - server, err := NewServer("127.0.0.1:0", nil, nil) - if err != nil { - t.Fatal(err) - } - defer server.listener.Close() - if server.mux != server.DefaultHandler() { - t.Fatalf("DefaultHandler: Expected to return server.mux, got: %#v", server.DefaultHandler()) - } -} - -func TestCreateExecContainer(t *testing.T) { - server := DockerServer{} - addContainers(&server, 2) - server.buildMuxer() - recorder := httptest.NewRecorder() - body := `{"Cmd": ["bash", "-c", "ls"]}` - path := fmt.Sprintf("/containers/%s/exec", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, strings.NewReader(body)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Fatalf("CreateExec: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - serverExec := server.execs[0] - var got docker.Exec - err := json.NewDecoder(recorder.Body).Decode(&got) - if err != nil { - t.Fatal(err) - } - if got.ID != serverExec.ID { - t.Errorf("CreateExec: wrong value. Want %#v. Got %#v.", serverExec.ID, got.ID) - } - - expected := docker.ExecInspect{ - ID: got.ID, - ProcessConfig: docker.ExecProcessConfig{ - EntryPoint: "bash", - Arguments: []string{"-c", "ls"}, - }, - Container: *server.containers[0], - } - - if !reflect.DeepEqual(*serverExec, expected) { - t.Errorf("InspectContainer: wrong value. Want:\n%#v\nGot:\n%#v\n", expected, *serverExec) - } -} - -func TestInspectExecContainer(t *testing.T) { - server := DockerServer{} - addContainers(&server, 1) - server.buildMuxer() - recorder := httptest.NewRecorder() - body := `{"Cmd": ["bash", "-c", "ls"]}` - path := fmt.Sprintf("/containers/%s/exec", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, strings.NewReader(body)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Fatalf("CreateExec: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - var got docker.Exec - err := json.NewDecoder(recorder.Body).Decode(&got) - if err != nil { - t.Fatal(err) - } - path = fmt.Sprintf("/exec/%s/json", got.ID) - request, _ = http.NewRequest("GET", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Fatalf("CreateExec: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - var got2 docker.ExecInspect - err = json.NewDecoder(recorder.Body).Decode(&got2) - if err != nil { - t.Fatal(err) - } - expected := docker.ExecInspect{ - ID: got.ID, - ProcessConfig: docker.ExecProcessConfig{ - EntryPoint: "bash", - Arguments: []string{"-c", "ls"}, - }, - Container: *server.containers[0], - } - got2.Container.State.StartedAt = expected.Container.State.StartedAt - got2.Container.State.FinishedAt = expected.Container.State.FinishedAt - got2.Container.Config = expected.Container.Config - got2.Container.Created = expected.Container.Created - got2.Container.NetworkSettings = expected.Container.NetworkSettings - got2.Container.ExecIDs = expected.Container.ExecIDs - - if !reflect.DeepEqual(got2, expected) { - t.Errorf("InspectContainer: wrong value. Want:\n%#v\nGot:\n%#v\n", expected, got2) - } -} - -func TestStartExecContainer(t *testing.T) { - server, _ := NewServer("127.0.0.1:0", nil, nil) - addContainers(server, 1) - server.buildMuxer() - recorder := httptest.NewRecorder() - body := `{"Cmd": ["bash", "-c", "ls"]}` - path := fmt.Sprintf("/containers/%s/exec", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, strings.NewReader(body)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Fatalf("CreateExec: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - var exec docker.Exec - err := json.NewDecoder(recorder.Body).Decode(&exec) - if err != nil { - t.Fatal(err) - } - unleash := make(chan bool) - server.PrepareExec(exec.ID, func() { - <-unleash - }) - codes := make(chan int, 1) - sent := make(chan bool) - go func() { - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/exec/%s/start", exec.ID) - body := `{"Tty":true}` - request, _ := http.NewRequest("POST", path, strings.NewReader(body)) - close(sent) - server.ServeHTTP(recorder, request) - codes <- recorder.Code - }() - <-sent - execInfo, err := waitExec(server.URL(), exec.ID, true, 5) - if err != nil { - t.Fatal(err) - } - if !execInfo.Running { - t.Error("StartExec: expected exec to be running, but it's not running") - } - close(unleash) - if code := <-codes; code != http.StatusOK { - t.Errorf("StartExec: wrong status. Want %d. Got %d.", http.StatusOK, code) - } - execInfo, err = waitExec(server.URL(), exec.ID, false, 5) - if err != nil { - t.Fatal(err) - } - if execInfo.Running { - t.Error("StartExec: expected exec to be not running after start returns, but it's running") - } -} - -func TestStartExecContainerWildcardCallback(t *testing.T) { - server, _ := NewServer("127.0.0.1:0", nil, nil) - addContainers(server, 1) - server.buildMuxer() - recorder := httptest.NewRecorder() - body := `{"Cmd": ["bash", "-c", "ls"]}` - path := fmt.Sprintf("/containers/%s/exec", server.containers[0].ID) - request, _ := http.NewRequest("POST", path, strings.NewReader(body)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Fatalf("CreateExec: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - unleash := make(chan bool) - server.PrepareExec("*", func() { - <-unleash - }) - var exec docker.Exec - err := json.NewDecoder(recorder.Body).Decode(&exec) - if err != nil { - t.Fatal(err) - } - codes := make(chan int, 1) - sent := make(chan bool) - go func() { - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/exec/%s/start", exec.ID) - body := `{"Tty":true}` - request, _ := http.NewRequest("POST", path, strings.NewReader(body)) - close(sent) - server.ServeHTTP(recorder, request) - codes <- recorder.Code - }() - <-sent - execInfo, err := waitExec(server.URL(), exec.ID, true, 5) - if err != nil { - t.Fatal(err) - } - if !execInfo.Running { - t.Error("StartExec: expected exec to be running, but it's not running") - } - close(unleash) - if code := <-codes; code != http.StatusOK { - t.Errorf("StartExec: wrong status. Want %d. Got %d.", http.StatusOK, code) - } - execInfo, err = waitExec(server.URL(), exec.ID, false, 5) - if err != nil { - t.Fatal(err) - } - if execInfo.Running { - t.Error("StartExec: expected exec to be not running after start returns, but it's running") - } -} - -func TestStartExecContainerNotFound(t *testing.T) { - server, _ := NewServer("127.0.0.1:0", nil, nil) - addContainers(server, 1) - server.buildMuxer() - recorder := httptest.NewRecorder() - body := `{"Tty":true}` - request, _ := http.NewRequest("POST", "/exec/something-wat/start", strings.NewReader(body)) - server.ServeHTTP(recorder, request) -} - -func waitExec(url, execID string, running bool, maxTry int) (*docker.ExecInspect, error) { - client, err := docker.NewClient(url) - if err != nil { - return nil, err - } - exec, err := client.InspectExec(execID) - for i := 0; i < maxTry && exec.Running != running && err == nil; i++ { - time.Sleep(100e6) - exec, err = client.InspectExec(exec.ID) - } - return exec, err -} - -func TestStatsContainer(t *testing.T) { - server, err := NewServer("127.0.0.1:0", nil, nil) - if err != nil { - t.Fatal(err) - } - defer server.Stop() - addContainers(server, 2) - server.buildMuxer() - expected := docker.Stats{} - expected.CPUStats.CPUUsage.TotalUsage = 20 - server.PrepareStats(server.containers[0].ID, func(id string) docker.Stats { - return expected - }) - recorder := httptest.NewRecorder() - path := fmt.Sprintf("/containers/%s/stats?stream=false", server.containers[0].ID) - request, _ := http.NewRequest("GET", path, nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("StatsContainer: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - body := recorder.Body.Bytes() - var got docker.Stats - err = json.Unmarshal(body, &got) - if err != nil { - t.Fatal(err) - } - got.Read = time.Time{} - if !reflect.DeepEqual(got, expected) { - t.Errorf("StatsContainer: wrong value. Want %#v. Got %#v.", expected, got) - } -} - -type safeWriter struct { - sync.Mutex - *httptest.ResponseRecorder -} - -func (w *safeWriter) Write(buf []byte) (int, error) { - w.Lock() - defer w.Unlock() - return w.ResponseRecorder.Write(buf) -} - -func TestStatsContainerStream(t *testing.T) { - server, err := NewServer("127.0.0.1:0", nil, nil) - if err != nil { - t.Fatal(err) - } - defer server.Stop() - addContainers(server, 2) - server.buildMuxer() - expected := docker.Stats{} - expected.CPUStats.CPUUsage.TotalUsage = 20 - server.PrepareStats(server.containers[0].ID, func(id string) docker.Stats { - time.Sleep(50 * time.Millisecond) - return expected - }) - recorder := &safeWriter{ - ResponseRecorder: httptest.NewRecorder(), - } - path := fmt.Sprintf("/containers/%s/stats?stream=true", server.containers[0].ID) - request, _ := http.NewRequest("GET", path, nil) - go func() { - server.ServeHTTP(recorder, request) - }() - time.Sleep(200 * time.Millisecond) - recorder.Lock() - defer recorder.Unlock() - body := recorder.Body.Bytes() - parts := bytes.Split(body, []byte("\n")) - if len(parts) < 2 { - t.Errorf("StatsContainer: wrong number of parts. Want at least 2. Got %#v.", len(parts)) - } - var got docker.Stats - err = json.Unmarshal(parts[0], &got) - if err != nil { - t.Fatal(err) - } - got.Read = time.Time{} - if !reflect.DeepEqual(got, expected) { - t.Errorf("StatsContainer: wrong value. Want %#v. Got %#v.", expected, got) - } -} - -func addNetworks(server *DockerServer, n int) { - server.netMut.Lock() - defer server.netMut.Unlock() - for i := 0; i < n; i++ { - netid := fmt.Sprintf("%x", rand.Int()%10000) - network := docker.Network{ - Name: netid, - ID: fmt.Sprintf("%x", rand.Int()%10000), - Driver: "bridge", - Containers: map[string]docker.Endpoint{ - "blah": { - Name: "blah", - ID: fmt.Sprintf("%x", rand.Int()%10000), - }, - }, - } - server.networks = append(server.networks, &network) - } -} - -func TestListNetworks(t *testing.T) { - server := DockerServer{} - addNetworks(&server, 2) - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/networks", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("ListNetworks: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } - expected := make([]docker.Network, 2) - for i, network := range server.networks { - expected[i] = docker.Network{ - ID: network.ID, - Name: network.Name, - Driver: network.Driver, - Containers: network.Containers, - } - } - var got []docker.Network - err := json.NewDecoder(recorder.Body).Decode(&got) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(got, expected) { - t.Errorf("ListNetworks. Want %#v. Got %#v.", expected, got) - } -} - -type createNetworkResponse struct { - ID string `json:"ID"` -} - -func TestCreateNetwork(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - netid := fmt.Sprintf("%x", rand.Int()%10000) - netname := fmt.Sprintf("%x", rand.Int()%10000) - body := fmt.Sprintf(`{"ID": "%s", "Name": "%s", "Type": "bridge" }`, netid, netname) - request, _ := http.NewRequest("POST", "/networks", strings.NewReader(body)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusCreated { - t.Errorf("CreateNetwork: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code) - } - - var returned createNetworkResponse - err := json.NewDecoder(recorder.Body).Decode(&returned) - if err != nil { - t.Fatal(err) - } - stored := server.networks[0] - if returned.ID != stored.ID { - t.Errorf("CreateNetwork: ID mismatch. Stored: %q. Returned: %q.", stored.ID, returned) - } -} - -func TestCreateNetworkInvalidBody(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/networks", strings.NewReader("whaaaaaat---")) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusBadRequest { - t.Errorf("CreateNetwork: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code) - } -} - -func TestCreateNetworkDuplicateName(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - addNetworks(&server, 1) - server.networks[0].Name = "mynetwork" - recorder := httptest.NewRecorder() - body := fmt.Sprintf(`{"ID": "%s", "Name": "mynetwork", "Type": "bridge" }`, fmt.Sprintf("%x", rand.Int()%10000)) - request, _ := http.NewRequest("POST", "/networks", strings.NewReader(body)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusForbidden { - t.Errorf("CreateNetwork: wrong status. Want %d. Got %d.", http.StatusForbidden, recorder.Code) - } -} - -func TestListVolumes(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - expected := []docker.Volume{{ - Name: "test-vol-1", - Driver: "local", - Mountpoint: "/var/lib/docker/volumes/test-vol-1", - }} - server.volStore = make(map[string]*volumeCounter) - for _, vol := range expected { - server.volStore[vol.Name] = &volumeCounter{ - volume: vol, - count: 0, - } - } - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/volumes", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("ListVolumes: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code) - } - var got []docker.Volume - err := json.NewDecoder(recorder.Body).Decode(&got) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(got, expected) { - t.Errorf("ListVolumes. Want %#v. Got %#v.", expected, got) - } -} - -func TestCreateVolume(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - body := `{"Name":"test-volume"}` - request, _ := http.NewRequest("POST", "/volumes/create", strings.NewReader(body)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusCreated { - t.Errorf("CreateVolume: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code) - } - var returned docker.Volume - err := json.NewDecoder(recorder.Body).Decode(&returned) - if err != nil { - t.Error(err) - } - if returned.Name != "test-volume" { - t.Errorf("CreateVolume: Name mismatch. Expected: test-volume. Returned %q.", returned.Name) - } - if returned.Driver != "local" { - t.Errorf("CreateVolume: Driver mismatch. Expected: local. Returned: %q", returned.Driver) - } - if returned.Mountpoint != "/var/lib/docker/volumes/test-volume" { - t.Errorf("CreateVolume: Mountpoint mismatch. Expected: /var/lib/docker/volumes/test-volume. Returned: %q.", returned.Mountpoint) - } -} - -func TestCreateVolumeAlreadExists(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - server.volStore = make(map[string]*volumeCounter) - server.volStore["test-volume"] = &volumeCounter{ - volume: docker.Volume{ - Name: "test-volume", - Driver: "local", - Mountpoint: "/var/lib/docker/volumes/test-volume", - }, - count: 0, - } - body := `{"Name":"test-volume"}` - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("POST", "/volumes/create", strings.NewReader(body)) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusCreated { - t.Errorf("CreateVolumeAlreadExists: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code) - } - var returned docker.Volume - err := json.NewDecoder(recorder.Body).Decode(&returned) - if err != nil { - t.Error(err) - } - if returned.Name != "test-volume" { - t.Errorf("CreateVolumeAlreadExists: Name mismatch. Expected: test-volume. Returned %q.", returned.Name) - } - if returned.Driver != "local" { - t.Errorf("CreateVolumeAlreadExists: Driver mismatch. Expected: local. Returned: %q", returned.Driver) - } - if returned.Mountpoint != "/var/lib/docker/volumes/test-volume" { - t.Errorf("CreateVolumeAlreadExists: Mountpoint mismatch. Expected: /var/lib/docker/volumes/test-volume. Returned: %q.", returned.Mountpoint) - } -} - -func TestInspectVolume(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - expected := docker.Volume{ - Name: "test-volume", - Driver: "local", - Mountpoint: "/var/lib/docker/volumes/test-volume", - } - volC := &volumeCounter{ - volume: expected, - count: 0, - } - volStore := make(map[string]*volumeCounter) - volStore["test-volume"] = volC - server.volStore = volStore - request, _ := http.NewRequest("GET", "/volumes/test-volume", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("InspectVolume: wrong status. Want %d. God %d.", http.StatusOK, recorder.Code) - } - var returned docker.Volume - err := json.NewDecoder(recorder.Body).Decode(&returned) - if err != nil { - t.Error(err) - } - if returned.Name != "test-volume" { - t.Errorf("InspectVolume: Name mismatch. Expected: test-volume. Returned %q.", returned.Name) - } - if returned.Driver != "local" { - t.Errorf("InspectVolume: Driver mismatch. Expected: local. Returned: %q", returned.Driver) - } - if returned.Mountpoint != "/var/lib/docker/volumes/test-volume" { - t.Errorf("InspectVolume: Mountpoint mismatch. Expected: /var/lib/docker/volumes/test-volume. Returned: %q.", returned.Mountpoint) - } -} - -func TestInspectVolumeNotFound(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("GET", "/volumes/test-volume", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("RemoveMissingVolume: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -func TestRemoveVolume(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - server.volStore = make(map[string]*volumeCounter) - server.volStore["test-volume"] = &volumeCounter{ - volume: docker.Volume{ - Name: "test-volume", - Driver: "local", - Mountpoint: "/var/lib/docker/volumes/test-volume", - }, - count: 0, - } - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("DELETE", "/volumes/test-volume", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNoContent { - t.Errorf("RemoveVolume: wrong status. Want %d. Got %d.", http.StatusNoContent, recorder.Code) - } -} - -func TestRemoveMissingVolume(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("DELETE", "/volumes/test-volume", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("RemoveMissingVolume: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} - -func TestRemoveVolumeInuse(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - server.volStore = make(map[string]*volumeCounter) - server.volStore["test-volume"] = &volumeCounter{ - volume: docker.Volume{ - Name: "test-volume", - Driver: "local", - Mountpoint: "/var/lib/docker/volumes/test-volume", - }, - count: 1, - } - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("DELETE", "/volumes/test-volume", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusConflict { - t.Errorf("RemoveVolume: wrong status. Want %d. Got %d.", http.StatusConflict, recorder.Code) - } -} - -func TestUploadToContainer(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - cont := &docker.Container{ - ID: "id123", - State: docker.State{ - Running: true, - ExitCode: 0, - }, - } - server.containers = append(server.containers, cont) - server.uploadedFiles = make(map[string]string) - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("PUT", fmt.Sprintf("/containers/%s/archive?path=abcd", cont.ID), nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusOK { - t.Errorf("UploadToContainer: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code) - } -} - -func TestUploadToContainerMissingContainer(t *testing.T) { - server := DockerServer{} - server.buildMuxer() - recorder := httptest.NewRecorder() - request, _ := http.NewRequest("PUT", "/containers/missing-container/archive?path=abcd", nil) - server.ServeHTTP(recorder, request) - if recorder.Code != http.StatusNotFound { - t.Errorf("UploadToContainer: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code) - } -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/volume_test.go b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/volume_test.go deleted file mode 100644 index e6bcca95f..000000000 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/volume_test.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2015 go-dockerclient 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 docker - -import ( - "encoding/json" - "net/http" - "net/url" - "reflect" - "testing" -) - -func TestListVolumes(t *testing.T) { - volumesData := `[ - { - "Name": "tardis", - "Driver": "local", - "Mountpoint": "/var/lib/docker/volumes/tardis" - }, - { - "Name": "foo", - "Driver": "bar", - "Mountpoint": "/var/lib/docker/volumes/bar" - } -]` - body := `{ "Volumes": ` + volumesData + ` }` - var expected []Volume - if err := json.Unmarshal([]byte(volumesData), &expected); err != nil { - t.Fatal(err) - } - client := newTestClient(&FakeRoundTripper{message: body, status: http.StatusOK}) - volumes, err := client.ListVolumes(ListVolumesOptions{}) - if err != nil { - t.Error(err) - } - if !reflect.DeepEqual(volumes, expected) { - t.Errorf("ListVolumes: Wrong return value. Want %#v. Got %#v.", expected, volumes) - } -} - -func TestCreateVolume(t *testing.T) { - body := `{ - "Name": "tardis", - "Driver": "local", - "Mountpoint": "/var/lib/docker/volumes/tardis" - }` - var expected Volume - if err := json.Unmarshal([]byte(body), &expected); err != nil { - t.Fatal(err) - } - fakeRT := &FakeRoundTripper{message: body, status: http.StatusOK} - client := newTestClient(fakeRT) - volume, err := client.CreateVolume( - CreateVolumeOptions{ - Name: "tardis", - Driver: "local", - DriverOpts: map[string]string{ - "foo": "bar", - }, - }, - ) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(volume, &expected) { - t.Errorf("CreateVolume: Wrong return value. Want %#v. Got %#v.", expected, volume) - } - req := fakeRT.requests[0] - expectedMethod := "POST" - if req.Method != expectedMethod { - t.Errorf("CreateVolume(): Wrong HTTP method. Want %s. Got %s.", expectedMethod, req.Method) - } - u, _ := url.Parse(client.getURL("/volumes/create")) - if req.URL.Path != u.Path { - t.Errorf("CreateVolume(): Wrong request path. Want %q. Got %q.", u.Path, req.URL.Path) - } -} - -func TestInspectVolume(t *testing.T) { - body := `{ - "Name": "tardis", - "Driver": "local", - "Mountpoint": "/var/lib/docker/volumes/tardis" - }` - var expected Volume - if err := json.Unmarshal([]byte(body), &expected); err != nil { - t.Fatal(err) - } - fakeRT := &FakeRoundTripper{message: body, status: http.StatusOK} - client := newTestClient(fakeRT) - name := "tardis" - volume, err := client.InspectVolume(name) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(volume, &expected) { - t.Errorf("InspectVolume: Wrong return value. Want %#v. Got %#v.", expected, volume) - } - req := fakeRT.requests[0] - expectedMethod := "GET" - if req.Method != expectedMethod { - t.Errorf("InspectVolume(%q): Wrong HTTP method. Want %s. Got %s.", name, expectedMethod, req.Method) - } - u, _ := url.Parse(client.getURL("/volumes/" + name)) - if req.URL.Path != u.Path { - t.Errorf("CreateVolume(%q): Wrong request path. Want %q. Got %q.", name, u.Path, req.URL.Path) - } -} - -func TestRemoveVolume(t *testing.T) { - name := "test" - fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent} - client := newTestClient(fakeRT) - if err := client.RemoveVolume(name); err != nil { - t.Fatal(err) - } - req := fakeRT.requests[0] - expectedMethod := "DELETE" - if req.Method != expectedMethod { - t.Errorf("RemoveVolume(%q): Wrong HTTP method. Want %s. Got %s.", name, expectedMethod, req.Method) - } - u, _ := url.Parse(client.getURL("/volumes/" + name)) - if req.URL.Path != u.Path { - t.Errorf("RemoveVolume(%q): Wrong request path. Want %q. Got %q.", name, u.Path, req.URL.Path) - } -} - -func TestRemoveVolumeNotFound(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "no such volume", status: http.StatusNotFound}) - if err := client.RemoveVolume("test:"); err != ErrNoSuchVolume { - t.Errorf("RemoveVolume: wrong error. Want %#v. Got %#v.", ErrNoSuchVolume, err) - } -} - -func TestRemoveVolumeInUse(t *testing.T) { - client := newTestClient(&FakeRoundTripper{message: "volume in use and cannot be removed", status: http.StatusConflict}) - if err := client.RemoveVolume("test:"); err != ErrVolumeInUse { - t.Errorf("RemoveVolume: wrong error. Want %#v. Got %#v.", ErrVolumeInUse, err) - } -} diff --git a/Godeps/_workspace/src/github.com/ghodss/yaml/yaml_test.go b/Godeps/_workspace/src/github.com/ghodss/yaml/yaml_test.go deleted file mode 100644 index 0ae0954e9..000000000 --- a/Godeps/_workspace/src/github.com/ghodss/yaml/yaml_test.go +++ /dev/null @@ -1,271 +0,0 @@ -package yaml - -import ( - "fmt" - "math" - "reflect" - "strconv" - "testing" -) - -type MarshalTest struct { - A string - B int64 - // Would like to test float64, but it's not supported in go-yaml. - // (See https://github.com/go-yaml/yaml/issues/83.) - C float32 -} - -func TestMarshal(t *testing.T) { - f32String := strconv.FormatFloat(math.MaxFloat32, 'g', -1, 32) - s := MarshalTest{"a", math.MaxInt64, math.MaxFloat32} - e := []byte(fmt.Sprintf("A: a\nB: %d\nC: %s\n", math.MaxInt64, f32String)) - - y, err := Marshal(s) - if err != nil { - t.Errorf("error marshaling YAML: %v", err) - } - - if !reflect.DeepEqual(y, e) { - t.Errorf("marshal YAML was unsuccessful, expected: %#v, got: %#v", - string(e), string(y)) - } -} - -type UnmarshalString struct { - A string - True string -} - -type UnmarshalStringMap struct { - A map[string]string -} - -type UnmarshalNestedString struct { - A NestedString -} - -type NestedString struct { - A string -} - -type UnmarshalSlice struct { - A []NestedSlice -} - -type NestedSlice struct { - B string - C *string -} - -func TestUnmarshal(t *testing.T) { - y := []byte("a: 1") - s1 := UnmarshalString{} - e1 := UnmarshalString{A: "1"} - unmarshal(t, y, &s1, &e1) - - y = []byte("a: true") - s1 = UnmarshalString{} - e1 = UnmarshalString{A: "true"} - unmarshal(t, y, &s1, &e1) - - y = []byte("true: 1") - s1 = UnmarshalString{} - e1 = UnmarshalString{True: "1"} - unmarshal(t, y, &s1, &e1) - - y = []byte("a:\n a: 1") - s2 := UnmarshalNestedString{} - e2 := UnmarshalNestedString{NestedString{"1"}} - unmarshal(t, y, &s2, &e2) - - y = []byte("a:\n - b: abc\n c: def\n - b: 123\n c: 456\n") - s3 := UnmarshalSlice{} - e3 := UnmarshalSlice{[]NestedSlice{NestedSlice{"abc", strPtr("def")}, NestedSlice{"123", strPtr("456")}}} - unmarshal(t, y, &s3, &e3) - - y = []byte("a:\n b: 1") - s4 := UnmarshalStringMap{} - e4 := UnmarshalStringMap{map[string]string{"b": "1"}} - unmarshal(t, y, &s4, &e4) -} - -func unmarshal(t *testing.T, y []byte, s, e interface{}) { - err := Unmarshal(y, s) - if err != nil { - t.Errorf("error unmarshaling YAML: %v", err) - } - - if !reflect.DeepEqual(s, e) { - t.Errorf("unmarshal YAML was unsuccessful, expected: %+#v, got: %+#v", - e, s) - } -} - -type Case struct { - input string - output string - // By default we test that reversing the output == input. But if there is a - // difference in the reversed output, you can optionally specify it here. - reverse *string -} - -type RunType int - -const ( - RunTypeJSONToYAML RunType = iota - RunTypeYAMLToJSON -) - -func TestJSONToYAML(t *testing.T) { - cases := []Case{ - { - `{"t":"a"}`, - "t: a\n", - nil, - }, { - `{"t":null}`, - "t: null\n", - nil, - }, - } - - runCases(t, RunTypeJSONToYAML, cases) -} - -func TestYAMLToJSON(t *testing.T) { - cases := []Case{ - { - "t: a\n", - `{"t":"a"}`, - nil, - }, { - "t: \n", - `{"t":null}`, - strPtr("t: null\n"), - }, { - "t: null\n", - `{"t":null}`, - nil, - }, { - "1: a\n", - `{"1":"a"}`, - strPtr("\"1\": a\n"), - }, { - "1000000000000000000000000000000000000: a\n", - `{"1e+36":"a"}`, - strPtr("\"1e+36\": a\n"), - }, { - "1e+36: a\n", - `{"1e+36":"a"}`, - strPtr("\"1e+36\": a\n"), - }, { - "\"1e+36\": a\n", - `{"1e+36":"a"}`, - nil, - }, { - "\"1.2\": a\n", - `{"1.2":"a"}`, - nil, - }, { - "- t: a\n", - `[{"t":"a"}]`, - nil, - }, { - "- t: a\n" + - "- t:\n" + - " b: 1\n" + - " c: 2\n", - `[{"t":"a"},{"t":{"b":1,"c":2}}]`, - nil, - }, { - `[{t: a}, {t: {b: 1, c: 2}}]`, - `[{"t":"a"},{"t":{"b":1,"c":2}}]`, - strPtr("- t: a\n" + - "- t:\n" + - " b: 1\n" + - " c: 2\n"), - }, { - "- t: \n", - `[{"t":null}]`, - strPtr("- t: null\n"), - }, { - "- t: null\n", - `[{"t":null}]`, - nil, - }, - } - - // Cases that should produce errors. - _ = []Case{ - { - "~: a", - `{"null":"a"}`, - nil, - }, { - "a: !!binary gIGC\n", - "{\"a\":\"\x80\x81\x82\"}", - nil, - }, - } - - runCases(t, RunTypeYAMLToJSON, cases) -} - -func runCases(t *testing.T, runType RunType, cases []Case) { - var f func([]byte) ([]byte, error) - var invF func([]byte) ([]byte, error) - var msg string - var invMsg string - if runType == RunTypeJSONToYAML { - f = JSONToYAML - invF = YAMLToJSON - msg = "JSON to YAML" - invMsg = "YAML back to JSON" - } else { - f = YAMLToJSON - invF = JSONToYAML - msg = "YAML to JSON" - invMsg = "JSON back to YAML" - } - - for _, c := range cases { - // Convert the string. - t.Logf("converting %s\n", c.input) - output, err := f([]byte(c.input)) - if err != nil { - t.Errorf("Failed to convert %s, input: `%s`, err: %v", msg, c.input, err) - } - - // Check it against the expected output. - if string(output) != c.output { - t.Errorf("Failed to convert %s, input: `%s`, expected `%s`, got `%s`", - msg, c.input, c.output, string(output)) - } - - // Set the string that we will compare the reversed output to. - reverse := c.input - // If a special reverse string was specified, use that instead. - if c.reverse != nil { - reverse = *c.reverse - } - - // Reverse the output. - input, err := invF(output) - if err != nil { - t.Errorf("Failed to convert %s, input: `%s`, err: %v", invMsg, string(output), err) - } - - // Check the reverse is equal to the input (or to *c.reverse). - if string(input) != reverse { - t.Errorf("Failed to convert %s, input: `%s`, expected `%s`, got `%s`", - invMsg, string(output), reverse, string(input)) - } - } - -} - -// To be able to easily fill in the *Case.reverse string above. -func strPtr(s string) *string { - return &s -} diff --git a/Godeps/_workspace/src/github.com/golang/glog/glog_test.go b/Godeps/_workspace/src/github.com/golang/glog/glog_test.go deleted file mode 100644 index 0fb376e1f..000000000 --- a/Godeps/_workspace/src/github.com/golang/glog/glog_test.go +++ /dev/null @@ -1,415 +0,0 @@ -// 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 - -import ( - "bytes" - "fmt" - stdLog "log" - "path/filepath" - "runtime" - "strconv" - "strings" - "testing" - "time" -) - -// Test that shortHostname works as advertised. -func TestShortHostname(t *testing.T) { - for hostname, expect := range map[string]string{ - "": "", - "host": "host", - "host.google.com": "host", - } { - if got := shortHostname(hostname); expect != got { - t.Errorf("shortHostname(%q): expected %q, got %q", hostname, expect, got) - } - } -} - -// flushBuffer wraps a bytes.Buffer to satisfy flushSyncWriter. -type flushBuffer struct { - bytes.Buffer -} - -func (f *flushBuffer) Flush() error { - return nil -} - -func (f *flushBuffer) Sync() error { - return nil -} - -// swap sets the log writers and returns the old array. -func (l *loggingT) swap(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) { - l.mu.Lock() - defer l.mu.Unlock() - old = l.file - for i, w := range writers { - logging.file[i] = w - } - return -} - -// newBuffers sets the log writers to all new byte buffers and returns the old array. -func (l *loggingT) newBuffers() [numSeverity]flushSyncWriter { - return l.swap([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)}) -} - -// contents returns the specified log value as a string. -func contents(s severity) string { - return logging.file[s].(*flushBuffer).String() -} - -// contains reports whether the string is contained in the log. -func contains(s severity, str string, t *testing.T) bool { - return strings.Contains(contents(s), str) -} - -// setFlags configures the logging flags how the test expects them. -func setFlags() { - logging.toStderr = false -} - -// Test that Info works as advertised. -func TestInfo(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - Info("test") - if !contains(infoLog, "I", t) { - t.Errorf("Info has wrong character: %q", contents(infoLog)) - } - if !contains(infoLog, "test", t) { - t.Error("Info failed") - } -} - -func TestInfoDepth(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - - f := func() { InfoDepth(1, "depth-test1") } - - // The next three lines must stay together - _, _, wantLine, _ := runtime.Caller(0) - InfoDepth(0, "depth-test0") - f() - - msgs := strings.Split(strings.TrimSuffix(contents(infoLog), "\n"), "\n") - if len(msgs) != 2 { - t.Fatalf("Got %d lines, expected 2", len(msgs)) - } - - for i, m := range msgs { - if !strings.HasPrefix(m, "I") { - t.Errorf("InfoDepth[%d] has wrong character: %q", i, m) - } - w := fmt.Sprintf("depth-test%d", i) - if !strings.Contains(m, w) { - t.Errorf("InfoDepth[%d] missing %q: %q", i, w, m) - } - - // pull out the line number (between : and ]) - msg := m[strings.LastIndex(m, ":")+1:] - x := strings.Index(msg, "]") - if x < 0 { - t.Errorf("InfoDepth[%d]: missing ']': %q", i, m) - continue - } - line, err := strconv.Atoi(msg[:x]) - if err != nil { - t.Errorf("InfoDepth[%d]: bad line number: %q", i, m) - continue - } - wantLine++ - if wantLine != line { - t.Errorf("InfoDepth[%d]: got line %d, want %d", i, line, wantLine) - } - } -} - -func init() { - CopyStandardLogTo("INFO") -} - -// Test that CopyStandardLogTo panics on bad input. -func TestCopyStandardLogToPanic(t *testing.T) { - defer func() { - if s, ok := recover().(string); !ok || !strings.Contains(s, "LOG") { - t.Errorf(`CopyStandardLogTo("LOG") should have panicked: %v`, s) - } - }() - CopyStandardLogTo("LOG") -} - -// Test that using the standard log package logs to INFO. -func TestStandardLog(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - stdLog.Print("test") - if !contains(infoLog, "I", t) { - t.Errorf("Info has wrong character: %q", contents(infoLog)) - } - if !contains(infoLog, "test", t) { - t.Error("Info failed") - } -} - -// Test that the header has the correct format. -func TestHeader(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - defer func(previous func() time.Time) { timeNow = previous }(timeNow) - timeNow = func() time.Time { - return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local) - } - pid = 1234 - Info("test") - var line int - format := "I0102 15:04:05.067890 1234 glog_test.go:%d] test\n" - n, err := fmt.Sscanf(contents(infoLog), format, &line) - if n != 1 || err != nil { - t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog)) - } - // Scanf treats multiple spaces as equivalent to a single space, - // so check for correct space-padding also. - want := fmt.Sprintf(format, line) - if contents(infoLog) != want { - t.Errorf("log format error: got:\n\t%q\nwant:\t%q", contents(infoLog), want) - } -} - -// Test that an Error log goes to Warning and Info. -// Even in the Info log, the source character will be E, so the data should -// all be identical. -func TestError(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - Error("test") - if !contains(errorLog, "E", t) { - t.Errorf("Error has wrong character: %q", contents(errorLog)) - } - if !contains(errorLog, "test", t) { - t.Error("Error failed") - } - str := contents(errorLog) - if !contains(warningLog, str, t) { - t.Error("Warning failed") - } - if !contains(infoLog, str, t) { - t.Error("Info failed") - } -} - -// Test that a Warning log goes to Info. -// Even in the Info log, the source character will be W, so the data should -// all be identical. -func TestWarning(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - Warning("test") - if !contains(warningLog, "W", t) { - t.Errorf("Warning has wrong character: %q", contents(warningLog)) - } - if !contains(warningLog, "test", t) { - t.Error("Warning failed") - } - str := contents(warningLog) - if !contains(infoLog, str, t) { - t.Error("Info failed") - } -} - -// Test that a V log goes to Info. -func TestV(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - logging.verbosity.Set("2") - defer logging.verbosity.Set("0") - V(2).Info("test") - if !contains(infoLog, "I", t) { - t.Errorf("Info has wrong character: %q", contents(infoLog)) - } - if !contains(infoLog, "test", t) { - t.Error("Info failed") - } -} - -// Test that a vmodule enables a log in this file. -func TestVmoduleOn(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - logging.vmodule.Set("glog_test=2") - defer logging.vmodule.Set("") - if !V(1) { - t.Error("V not enabled for 1") - } - if !V(2) { - t.Error("V not enabled for 2") - } - if V(3) { - t.Error("V enabled for 3") - } - V(2).Info("test") - if !contains(infoLog, "I", t) { - t.Errorf("Info has wrong character: %q", contents(infoLog)) - } - if !contains(infoLog, "test", t) { - t.Error("Info failed") - } -} - -// Test that a vmodule of another file does not enable a log in this file. -func TestVmoduleOff(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - logging.vmodule.Set("notthisfile=2") - defer logging.vmodule.Set("") - for i := 1; i <= 3; i++ { - if V(Level(i)) { - t.Errorf("V enabled for %d", i) - } - } - V(2).Info("test") - if contents(infoLog) != "" { - t.Error("V logged incorrectly") - } -} - -// vGlobs are patterns that match/don't match this file at V=2. -var vGlobs = map[string]bool{ - // Easy to test the numeric match here. - "glog_test=1": false, // If -vmodule sets V to 1, V(2) will fail. - "glog_test=2": true, - "glog_test=3": true, // If -vmodule sets V to 1, V(3) will succeed. - // These all use 2 and check the patterns. All are true. - "*=2": true, - "?l*=2": true, - "????_*=2": true, - "??[mno]?_*t=2": true, - // These all use 2 and check the patterns. All are false. - "*x=2": false, - "m*=2": false, - "??_*=2": false, - "?[abc]?_*t=2": false, -} - -// Test that vmodule globbing works as advertised. -func testVmoduleGlob(pat string, match bool, t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - defer logging.vmodule.Set("") - logging.vmodule.Set(pat) - if V(2) != Verbose(match) { - t.Errorf("incorrect match for %q: got %t expected %t", pat, V(2), match) - } -} - -// Test that a vmodule globbing works as advertised. -func TestVmoduleGlob(t *testing.T) { - for glob, match := range vGlobs { - testVmoduleGlob(glob, match, t) - } -} - -func TestRollover(t *testing.T) { - setFlags() - var err error - defer func(previous func(error)) { logExitFunc = previous }(logExitFunc) - logExitFunc = func(e error) { - err = e - } - defer func(previous uint64) { MaxSize = previous }(MaxSize) - MaxSize = 512 - - Info("x") // Be sure we have a file. - info, ok := logging.file[infoLog].(*syncBuffer) - if !ok { - t.Fatal("info wasn't created") - } - if err != nil { - t.Fatalf("info has initial error: %v", err) - } - fname0 := info.file.Name() - Info(strings.Repeat("x", int(MaxSize))) // force a rollover - if err != nil { - t.Fatalf("info has error after big write: %v", err) - } - - // Make sure the next log file gets a file name with a different - // time stamp. - // - // TODO: determine whether we need to support subsecond log - // rotation. C++ does not appear to handle this case (nor does it - // handle Daylight Savings Time properly). - time.Sleep(1 * time.Second) - - Info("x") // create a new file - if err != nil { - t.Fatalf("error after rotation: %v", err) - } - fname1 := info.file.Name() - if fname0 == fname1 { - t.Errorf("info.f.Name did not change: %v", fname0) - } - if info.nbytes >= MaxSize { - t.Errorf("file size was not reset: %d", info.nbytes) - } -} - -func TestLogBacktraceAt(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - // The peculiar style of this code simplifies line counting and maintenance of the - // tracing block below. - var infoLine string - setTraceLocation := func(file string, line int, ok bool, delta int) { - if !ok { - t.Fatal("could not get file:line") - } - _, file = filepath.Split(file) - infoLine = fmt.Sprintf("%s:%d", file, line+delta) - err := logging.traceLocation.Set(infoLine) - if err != nil { - t.Fatal("error setting log_backtrace_at: ", err) - } - } - { - // Start of tracing block. These lines know about each other's relative position. - _, file, line, ok := runtime.Caller(0) - setTraceLocation(file, line, ok, +2) // Two lines between Caller and Info calls. - Info("we want a stack trace here") - } - numAppearances := strings.Count(contents(infoLog), infoLine) - if numAppearances < 2 { - // Need 2 appearances, one in the log header and one in the trace: - // log_test.go:281: I0511 16:36:06.952398 02238 log_test.go:280] we want a stack trace here - // ... - // github.com/glog/glog_test.go:280 (0x41ba91) - // ... - // We could be more precise but that would require knowing the details - // of the traceback format, which may not be dependable. - t.Fatal("got no trace back; log is ", contents(infoLog)) - } -} - -func BenchmarkHeader(b *testing.B) { - for i := 0; i < b.N; i++ { - buf, _, _ := logging.header(infoLog, 0) - logging.putBuffer(buf) - } -} diff --git a/Godeps/_workspace/src/github.com/golang/groupcache/LICENSE b/Godeps/_workspace/src/github.com/golang/groupcache/LICENSE new file mode 100644 index 000000000..37ec93a14 --- /dev/null +++ b/Godeps/_workspace/src/github.com/golang/groupcache/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/Godeps/_workspace/src/github.com/golang/groupcache/lru/lru_test.go b/Godeps/_workspace/src/github.com/golang/groupcache/lru/lru_test.go deleted file mode 100644 index 98a2656e8..000000000 --- a/Godeps/_workspace/src/github.com/golang/groupcache/lru/lru_test.go +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright 2013 Google 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 lru - -import ( - "testing" -) - -type simpleStruct struct { - int - string -} - -type complexStruct struct { - int - simpleStruct -} - -var getTests = []struct { - name string - keyToAdd interface{} - keyToGet interface{} - expectedOk bool -}{ - {"string_hit", "myKey", "myKey", true}, - {"string_miss", "myKey", "nonsense", false}, - {"simple_struct_hit", simpleStruct{1, "two"}, simpleStruct{1, "two"}, true}, - {"simeple_struct_miss", simpleStruct{1, "two"}, simpleStruct{0, "noway"}, false}, - {"complex_struct_hit", complexStruct{1, simpleStruct{2, "three"}}, - complexStruct{1, simpleStruct{2, "three"}}, true}, -} - -func TestGet(t *testing.T) { - for _, tt := range getTests { - lru := New(0) - lru.Add(tt.keyToAdd, 1234) - val, ok := lru.Get(tt.keyToGet) - if ok != tt.expectedOk { - t.Fatalf("%s: cache hit = %v; want %v", tt.name, ok, !ok) - } else if ok && val != 1234 { - t.Fatalf("%s expected get to return 1234 but got %v", tt.name, val) - } - } -} - -func TestRemove(t *testing.T) { - lru := New(0) - lru.Add("myKey", 1234) - if val, ok := lru.Get("myKey"); !ok { - t.Fatal("TestRemove returned no match") - } else if val != 1234 { - t.Fatalf("TestRemove failed. Expected %d, got %v", 1234, val) - } - - lru.Remove("myKey") - if _, ok := lru.Get("myKey"); ok { - t.Fatal("TestRemove returned a removed entry") - } -} diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/LICENSE b/Godeps/_workspace/src/github.com/golang/protobuf/LICENSE new file mode 100644 index 000000000..1b1b1921e --- /dev/null +++ b/Godeps/_workspace/src/github.com/golang/protobuf/LICENSE @@ -0,0 +1,31 @@ +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. + diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/Makefile b/Godeps/_workspace/src/github.com/golang/protobuf/proto/Makefile index fb838ed2d..f1f06564a 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/Makefile +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/Makefile @@ -39,5 +39,5 @@ test: install generate-test-pbs generate-test-pbs: make install make -C testdata - make -C proto3_proto + protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata:. proto3_proto/proto3.proto make diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/all_test.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/all_test.go deleted file mode 100644 index 3fade175f..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/all_test.go +++ /dev/null @@ -1,2059 +0,0 @@ -// 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. - -package proto_test - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "math" - "math/rand" - "reflect" - "runtime/debug" - "strings" - "testing" - "time" - - . "./testdata" - . "github.com/golang/protobuf/proto" -) - -var globalO *Buffer - -func old() *Buffer { - if globalO == nil { - globalO = NewBuffer(nil) - } - globalO.Reset() - return globalO -} - -func equalbytes(b1, b2 []byte, t *testing.T) { - if len(b1) != len(b2) { - t.Errorf("wrong lengths: 2*%d != %d", len(b1), len(b2)) - return - } - for i := 0; i < len(b1); i++ { - if b1[i] != b2[i] { - t.Errorf("bad byte[%d]:%x %x: %s %s", i, b1[i], b2[i], b1, b2) - } - } -} - -func initGoTestField() *GoTestField { - f := new(GoTestField) - f.Label = String("label") - f.Type = String("type") - return f -} - -// These are all structurally equivalent but the tag numbers differ. -// (It's remarkable that required, optional, and repeated all have -// 8 letters.) -func initGoTest_RequiredGroup() *GoTest_RequiredGroup { - return &GoTest_RequiredGroup{ - RequiredField: String("required"), - } -} - -func initGoTest_OptionalGroup() *GoTest_OptionalGroup { - return &GoTest_OptionalGroup{ - RequiredField: String("optional"), - } -} - -func initGoTest_RepeatedGroup() *GoTest_RepeatedGroup { - return &GoTest_RepeatedGroup{ - RequiredField: String("repeated"), - } -} - -func initGoTest(setdefaults bool) *GoTest { - pb := new(GoTest) - if setdefaults { - pb.F_BoolDefaulted = Bool(Default_GoTest_F_BoolDefaulted) - pb.F_Int32Defaulted = Int32(Default_GoTest_F_Int32Defaulted) - pb.F_Int64Defaulted = Int64(Default_GoTest_F_Int64Defaulted) - pb.F_Fixed32Defaulted = Uint32(Default_GoTest_F_Fixed32Defaulted) - pb.F_Fixed64Defaulted = Uint64(Default_GoTest_F_Fixed64Defaulted) - pb.F_Uint32Defaulted = Uint32(Default_GoTest_F_Uint32Defaulted) - pb.F_Uint64Defaulted = Uint64(Default_GoTest_F_Uint64Defaulted) - pb.F_FloatDefaulted = Float32(Default_GoTest_F_FloatDefaulted) - pb.F_DoubleDefaulted = Float64(Default_GoTest_F_DoubleDefaulted) - pb.F_StringDefaulted = String(Default_GoTest_F_StringDefaulted) - pb.F_BytesDefaulted = Default_GoTest_F_BytesDefaulted - pb.F_Sint32Defaulted = Int32(Default_GoTest_F_Sint32Defaulted) - pb.F_Sint64Defaulted = Int64(Default_GoTest_F_Sint64Defaulted) - } - - pb.Kind = GoTest_TIME.Enum() - pb.RequiredField = initGoTestField() - pb.F_BoolRequired = Bool(true) - pb.F_Int32Required = Int32(3) - pb.F_Int64Required = Int64(6) - pb.F_Fixed32Required = Uint32(32) - pb.F_Fixed64Required = Uint64(64) - pb.F_Uint32Required = Uint32(3232) - pb.F_Uint64Required = Uint64(6464) - pb.F_FloatRequired = Float32(3232) - pb.F_DoubleRequired = Float64(6464) - pb.F_StringRequired = String("string") - pb.F_BytesRequired = []byte("bytes") - pb.F_Sint32Required = Int32(-32) - pb.F_Sint64Required = Int64(-64) - pb.Requiredgroup = initGoTest_RequiredGroup() - - return pb -} - -func fail(msg string, b *bytes.Buffer, s string, t *testing.T) { - data := b.Bytes() - ld := len(data) - ls := len(s) / 2 - - fmt.Printf("fail %s ld=%d ls=%d\n", msg, ld, ls) - - // find the interesting spot - n - n := ls - if ld < ls { - n = ld - } - j := 0 - for i := 0; i < n; i++ { - bs := hex(s[j])*16 + hex(s[j+1]) - j += 2 - if data[i] == bs { - continue - } - n = i - break - } - l := n - 10 - if l < 0 { - l = 0 - } - h := n + 10 - - // find the interesting spot - n - fmt.Printf("is[%d]:", l) - for i := l; i < h; i++ { - if i >= ld { - fmt.Printf(" --") - continue - } - fmt.Printf(" %.2x", data[i]) - } - fmt.Printf("\n") - - fmt.Printf("sb[%d]:", l) - for i := l; i < h; i++ { - if i >= ls { - fmt.Printf(" --") - continue - } - bs := hex(s[j])*16 + hex(s[j+1]) - j += 2 - fmt.Printf(" %.2x", bs) - } - fmt.Printf("\n") - - t.Fail() - - // t.Errorf("%s: \ngood: %s\nbad: %x", msg, s, b.Bytes()) - // Print the output in a partially-decoded format; can - // be helpful when updating the test. It produces the output - // that is pasted, with minor edits, into the argument to verify(). - // data := b.Bytes() - // nesting := 0 - // for b.Len() > 0 { - // start := len(data) - b.Len() - // var u uint64 - // u, err := DecodeVarint(b) - // if err != nil { - // fmt.Printf("decode error on varint:", err) - // return - // } - // wire := u & 0x7 - // tag := u >> 3 - // switch wire { - // case WireVarint: - // v, err := DecodeVarint(b) - // if err != nil { - // fmt.Printf("decode error on varint:", err) - // return - // } - // fmt.Printf("\t\t\"%x\" // field %d, encoding %d, value %d\n", - // data[start:len(data)-b.Len()], tag, wire, v) - // case WireFixed32: - // v, err := DecodeFixed32(b) - // if err != nil { - // fmt.Printf("decode error on fixed32:", err) - // return - // } - // fmt.Printf("\t\t\"%x\" // field %d, encoding %d, value %d\n", - // data[start:len(data)-b.Len()], tag, wire, v) - // case WireFixed64: - // v, err := DecodeFixed64(b) - // if err != nil { - // fmt.Printf("decode error on fixed64:", err) - // return - // } - // fmt.Printf("\t\t\"%x\" // field %d, encoding %d, value %d\n", - // data[start:len(data)-b.Len()], tag, wire, v) - // case WireBytes: - // nb, err := DecodeVarint(b) - // if err != nil { - // fmt.Printf("decode error on bytes:", err) - // return - // } - // after_tag := len(data) - b.Len() - // str := make([]byte, nb) - // _, err = b.Read(str) - // if err != nil { - // fmt.Printf("decode error on bytes:", err) - // return - // } - // fmt.Printf("\t\t\"%x\" \"%x\" // field %d, encoding %d (FIELD)\n", - // data[start:after_tag], str, tag, wire) - // case WireStartGroup: - // nesting++ - // fmt.Printf("\t\t\"%x\"\t\t// start group field %d level %d\n", - // data[start:len(data)-b.Len()], tag, nesting) - // case WireEndGroup: - // fmt.Printf("\t\t\"%x\"\t\t// end group field %d level %d\n", - // data[start:len(data)-b.Len()], tag, nesting) - // nesting-- - // default: - // fmt.Printf("unrecognized wire type %d\n", wire) - // return - // } - // } -} - -func hex(c uint8) uint8 { - if '0' <= c && c <= '9' { - return c - '0' - } - if 'a' <= c && c <= 'f' { - return 10 + c - 'a' - } - if 'A' <= c && c <= 'F' { - return 10 + c - 'A' - } - return 0 -} - -func equal(b []byte, s string, t *testing.T) bool { - if 2*len(b) != len(s) { - // fail(fmt.Sprintf("wrong lengths: 2*%d != %d", len(b), len(s)), b, s, t) - fmt.Printf("wrong lengths: 2*%d != %d\n", len(b), len(s)) - return false - } - for i, j := 0, 0; i < len(b); i, j = i+1, j+2 { - x := hex(s[j])*16 + hex(s[j+1]) - if b[i] != x { - // fail(fmt.Sprintf("bad byte[%d]:%x %x", i, b[i], x), b, s, t) - fmt.Printf("bad byte[%d]:%x %x", i, b[i], x) - return false - } - } - return true -} - -func overify(t *testing.T, pb *GoTest, expected string) { - o := old() - err := o.Marshal(pb) - if err != nil { - fmt.Printf("overify marshal-1 err = %v", err) - o.DebugPrint("", o.Bytes()) - t.Fatalf("expected = %s", expected) - } - if !equal(o.Bytes(), expected, t) { - o.DebugPrint("overify neq 1", o.Bytes()) - t.Fatalf("expected = %s", expected) - } - - // Now test Unmarshal by recreating the original buffer. - pbd := new(GoTest) - err = o.Unmarshal(pbd) - if err != nil { - t.Fatalf("overify unmarshal err = %v", err) - o.DebugPrint("", o.Bytes()) - t.Fatalf("string = %s", expected) - } - o.Reset() - err = o.Marshal(pbd) - if err != nil { - t.Errorf("overify marshal-2 err = %v", err) - o.DebugPrint("", o.Bytes()) - t.Fatalf("string = %s", expected) - } - if !equal(o.Bytes(), expected, t) { - o.DebugPrint("overify neq 2", o.Bytes()) - t.Fatalf("string = %s", expected) - } -} - -// Simple tests for numeric encode/decode primitives (varint, etc.) -func TestNumericPrimitives(t *testing.T) { - for i := uint64(0); i < 1e6; i += 111 { - o := old() - if o.EncodeVarint(i) != nil { - t.Error("EncodeVarint") - break - } - x, e := o.DecodeVarint() - if e != nil { - t.Fatal("DecodeVarint") - } - if x != i { - t.Fatal("varint decode fail:", i, x) - } - - o = old() - if o.EncodeFixed32(i) != nil { - t.Fatal("encFixed32") - } - x, e = o.DecodeFixed32() - if e != nil { - t.Fatal("decFixed32") - } - if x != i { - t.Fatal("fixed32 decode fail:", i, x) - } - - o = old() - if o.EncodeFixed64(i*1234567) != nil { - t.Error("encFixed64") - break - } - x, e = o.DecodeFixed64() - if e != nil { - t.Error("decFixed64") - break - } - if x != i*1234567 { - t.Error("fixed64 decode fail:", i*1234567, x) - break - } - - o = old() - i32 := int32(i - 12345) - if o.EncodeZigzag32(uint64(i32)) != nil { - t.Fatal("EncodeZigzag32") - } - x, e = o.DecodeZigzag32() - if e != nil { - t.Fatal("DecodeZigzag32") - } - if x != uint64(uint32(i32)) { - t.Fatal("zigzag32 decode fail:", i32, x) - } - - o = old() - i64 := int64(i - 12345) - if o.EncodeZigzag64(uint64(i64)) != nil { - t.Fatal("EncodeZigzag64") - } - x, e = o.DecodeZigzag64() - if e != nil { - t.Fatal("DecodeZigzag64") - } - if x != uint64(i64) { - t.Fatal("zigzag64 decode fail:", i64, x) - } - } -} - -// fakeMarshaler is a simple struct implementing Marshaler and Message interfaces. -type fakeMarshaler struct { - b []byte - err error -} - -func (f fakeMarshaler) Marshal() ([]byte, error) { - return f.b, f.err -} - -func (f fakeMarshaler) String() string { - return fmt.Sprintf("Bytes: %v Error: %v", f.b, f.err) -} - -func (f fakeMarshaler) ProtoMessage() {} - -func (f fakeMarshaler) Reset() {} - -// Simple tests for proto messages that implement the Marshaler interface. -func TestMarshalerEncoding(t *testing.T) { - tests := []struct { - name string - m Message - want []byte - wantErr error - }{ - { - name: "Marshaler that fails", - m: fakeMarshaler{ - err: errors.New("some marshal err"), - b: []byte{5, 6, 7}, - }, - // Since there's an error, nothing should be written to buffer. - want: nil, - wantErr: errors.New("some marshal err"), - }, - { - name: "Marshaler that succeeds", - m: fakeMarshaler{ - b: []byte{0, 1, 2, 3, 4, 127, 255}, - }, - want: []byte{0, 1, 2, 3, 4, 127, 255}, - wantErr: nil, - }, - } - for _, test := range tests { - b := NewBuffer(nil) - err := b.Marshal(test.m) - if !reflect.DeepEqual(test.wantErr, err) { - t.Errorf("%s: got err %v wanted %v", test.name, err, test.wantErr) - } - if !reflect.DeepEqual(test.want, b.Bytes()) { - t.Errorf("%s: got bytes %v wanted %v", test.name, b.Bytes(), test.want) - } - } -} - -// Simple tests for bytes -func TestBytesPrimitives(t *testing.T) { - o := old() - bytes := []byte{'n', 'o', 'w', ' ', 'i', 's', ' ', 't', 'h', 'e', ' ', 't', 'i', 'm', 'e'} - if o.EncodeRawBytes(bytes) != nil { - t.Error("EncodeRawBytes") - } - decb, e := o.DecodeRawBytes(false) - if e != nil { - t.Error("DecodeRawBytes") - } - equalbytes(bytes, decb, t) -} - -// Simple tests for strings -func TestStringPrimitives(t *testing.T) { - o := old() - s := "now is the time" - if o.EncodeStringBytes(s) != nil { - t.Error("enc_string") - } - decs, e := o.DecodeStringBytes() - if e != nil { - t.Error("dec_string") - } - if s != decs { - t.Error("string encode/decode fail:", s, decs) - } -} - -// Do we catch the "required bit not set" case? -func TestRequiredBit(t *testing.T) { - o := old() - pb := new(GoTest) - err := o.Marshal(pb) - if err == nil { - t.Error("did not catch missing required fields") - } else if strings.Index(err.Error(), "Kind") < 0 { - t.Error("wrong error type:", err) - } -} - -// Check that all fields are nil. -// Clearly silly, and a residue from a more interesting test with an earlier, -// different initialization property, but it once caught a compiler bug so -// it lives. -func checkInitialized(pb *GoTest, t *testing.T) { - if pb.F_BoolDefaulted != nil { - t.Error("New or Reset did not set boolean:", *pb.F_BoolDefaulted) - } - if pb.F_Int32Defaulted != nil { - t.Error("New or Reset did not set int32:", *pb.F_Int32Defaulted) - } - if pb.F_Int64Defaulted != nil { - t.Error("New or Reset did not set int64:", *pb.F_Int64Defaulted) - } - if pb.F_Fixed32Defaulted != nil { - t.Error("New or Reset did not set fixed32:", *pb.F_Fixed32Defaulted) - } - if pb.F_Fixed64Defaulted != nil { - t.Error("New or Reset did not set fixed64:", *pb.F_Fixed64Defaulted) - } - if pb.F_Uint32Defaulted != nil { - t.Error("New or Reset did not set uint32:", *pb.F_Uint32Defaulted) - } - if pb.F_Uint64Defaulted != nil { - t.Error("New or Reset did not set uint64:", *pb.F_Uint64Defaulted) - } - if pb.F_FloatDefaulted != nil { - t.Error("New or Reset did not set float:", *pb.F_FloatDefaulted) - } - if pb.F_DoubleDefaulted != nil { - t.Error("New or Reset did not set double:", *pb.F_DoubleDefaulted) - } - if pb.F_StringDefaulted != nil { - t.Error("New or Reset did not set string:", *pb.F_StringDefaulted) - } - if pb.F_BytesDefaulted != nil { - t.Error("New or Reset did not set bytes:", string(pb.F_BytesDefaulted)) - } - if pb.F_Sint32Defaulted != nil { - t.Error("New or Reset did not set int32:", *pb.F_Sint32Defaulted) - } - if pb.F_Sint64Defaulted != nil { - t.Error("New or Reset did not set int64:", *pb.F_Sint64Defaulted) - } -} - -// Does Reset() reset? -func TestReset(t *testing.T) { - pb := initGoTest(true) - // muck with some values - pb.F_BoolDefaulted = Bool(false) - pb.F_Int32Defaulted = Int32(237) - pb.F_Int64Defaulted = Int64(12346) - pb.F_Fixed32Defaulted = Uint32(32000) - pb.F_Fixed64Defaulted = Uint64(666) - pb.F_Uint32Defaulted = Uint32(323232) - pb.F_Uint64Defaulted = nil - pb.F_FloatDefaulted = nil - pb.F_DoubleDefaulted = Float64(0) - pb.F_StringDefaulted = String("gotcha") - pb.F_BytesDefaulted = []byte("asdfasdf") - pb.F_Sint32Defaulted = Int32(123) - pb.F_Sint64Defaulted = Int64(789) - pb.Reset() - checkInitialized(pb, t) -} - -// All required fields set, no defaults provided. -func TestEncodeDecode1(t *testing.T) { - pb := initGoTest(false) - overify(t, pb, - "0807"+ // field 1, encoding 0, value 7 - "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField) - "5001"+ // field 10, encoding 0, value 1 - "5803"+ // field 11, encoding 0, value 3 - "6006"+ // field 12, encoding 0, value 6 - "6d20000000"+ // field 13, encoding 5, value 0x20 - "714000000000000000"+ // field 14, encoding 1, value 0x40 - "78a019"+ // field 15, encoding 0, value 0xca0 = 3232 - "8001c032"+ // field 16, encoding 0, value 0x1940 = 6464 - "8d0100004a45"+ // field 17, encoding 5, value 3232.0 - "9101000000000040b940"+ // field 18, encoding 1, value 6464.0 - "9a0106"+"737472696e67"+ // field 19, encoding 2, string "string" - "b304"+ // field 70, encoding 3, start group - "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required" - "b404"+ // field 70, encoding 4, end group - "aa0605"+"6279746573"+ // field 101, encoding 2, string "bytes" - "b0063f"+ // field 102, encoding 0, 0x3f zigzag32 - "b8067f") // field 103, encoding 0, 0x7f zigzag64 -} - -// All required fields set, defaults provided. -func TestEncodeDecode2(t *testing.T) { - pb := initGoTest(true) - overify(t, pb, - "0807"+ // field 1, encoding 0, value 7 - "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField) - "5001"+ // field 10, encoding 0, value 1 - "5803"+ // field 11, encoding 0, value 3 - "6006"+ // field 12, encoding 0, value 6 - "6d20000000"+ // field 13, encoding 5, value 32 - "714000000000000000"+ // field 14, encoding 1, value 64 - "78a019"+ // field 15, encoding 0, value 3232 - "8001c032"+ // field 16, encoding 0, value 6464 - "8d0100004a45"+ // field 17, encoding 5, value 3232.0 - "9101000000000040b940"+ // field 18, encoding 1, value 6464.0 - "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string" - "c00201"+ // field 40, encoding 0, value 1 - "c80220"+ // field 41, encoding 0, value 32 - "d00240"+ // field 42, encoding 0, value 64 - "dd0240010000"+ // field 43, encoding 5, value 320 - "e1028002000000000000"+ // field 44, encoding 1, value 640 - "e8028019"+ // field 45, encoding 0, value 3200 - "f0028032"+ // field 46, encoding 0, value 6400 - "fd02e0659948"+ // field 47, encoding 5, value 314159.0 - "81030000000050971041"+ // field 48, encoding 1, value 271828.0 - "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n" - "b304"+ // start group field 70 level 1 - "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required" - "b404"+ // end group field 70 level 1 - "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes" - "b0063f"+ // field 102, encoding 0, 0x3f zigzag32 - "b8067f"+ // field 103, encoding 0, 0x7f zigzag64 - "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose" - "90193f"+ // field 402, encoding 0, value 63 - "98197f") // field 403, encoding 0, value 127 - -} - -// All default fields set to their default value by hand -func TestEncodeDecode3(t *testing.T) { - pb := initGoTest(false) - pb.F_BoolDefaulted = Bool(true) - pb.F_Int32Defaulted = Int32(32) - pb.F_Int64Defaulted = Int64(64) - pb.F_Fixed32Defaulted = Uint32(320) - pb.F_Fixed64Defaulted = Uint64(640) - pb.F_Uint32Defaulted = Uint32(3200) - pb.F_Uint64Defaulted = Uint64(6400) - pb.F_FloatDefaulted = Float32(314159) - pb.F_DoubleDefaulted = Float64(271828) - pb.F_StringDefaulted = String("hello, \"world!\"\n") - pb.F_BytesDefaulted = []byte("Bignose") - pb.F_Sint32Defaulted = Int32(-32) - pb.F_Sint64Defaulted = Int64(-64) - - overify(t, pb, - "0807"+ // field 1, encoding 0, value 7 - "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField) - "5001"+ // field 10, encoding 0, value 1 - "5803"+ // field 11, encoding 0, value 3 - "6006"+ // field 12, encoding 0, value 6 - "6d20000000"+ // field 13, encoding 5, value 32 - "714000000000000000"+ // field 14, encoding 1, value 64 - "78a019"+ // field 15, encoding 0, value 3232 - "8001c032"+ // field 16, encoding 0, value 6464 - "8d0100004a45"+ // field 17, encoding 5, value 3232.0 - "9101000000000040b940"+ // field 18, encoding 1, value 6464.0 - "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string" - "c00201"+ // field 40, encoding 0, value 1 - "c80220"+ // field 41, encoding 0, value 32 - "d00240"+ // field 42, encoding 0, value 64 - "dd0240010000"+ // field 43, encoding 5, value 320 - "e1028002000000000000"+ // field 44, encoding 1, value 640 - "e8028019"+ // field 45, encoding 0, value 3200 - "f0028032"+ // field 46, encoding 0, value 6400 - "fd02e0659948"+ // field 47, encoding 5, value 314159.0 - "81030000000050971041"+ // field 48, encoding 1, value 271828.0 - "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n" - "b304"+ // start group field 70 level 1 - "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required" - "b404"+ // end group field 70 level 1 - "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes" - "b0063f"+ // field 102, encoding 0, 0x3f zigzag32 - "b8067f"+ // field 103, encoding 0, 0x7f zigzag64 - "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose" - "90193f"+ // field 402, encoding 0, value 63 - "98197f") // field 403, encoding 0, value 127 - -} - -// All required fields set, defaults provided, all non-defaulted optional fields have values. -func TestEncodeDecode4(t *testing.T) { - pb := initGoTest(true) - pb.Table = String("hello") - pb.Param = Int32(7) - pb.OptionalField = initGoTestField() - pb.F_BoolOptional = Bool(true) - pb.F_Int32Optional = Int32(32) - pb.F_Int64Optional = Int64(64) - pb.F_Fixed32Optional = Uint32(3232) - pb.F_Fixed64Optional = Uint64(6464) - pb.F_Uint32Optional = Uint32(323232) - pb.F_Uint64Optional = Uint64(646464) - pb.F_FloatOptional = Float32(32.) - pb.F_DoubleOptional = Float64(64.) - pb.F_StringOptional = String("hello") - pb.F_BytesOptional = []byte("Bignose") - pb.F_Sint32Optional = Int32(-32) - pb.F_Sint64Optional = Int64(-64) - pb.Optionalgroup = initGoTest_OptionalGroup() - - overify(t, pb, - "0807"+ // field 1, encoding 0, value 7 - "1205"+"68656c6c6f"+ // field 2, encoding 2, string "hello" - "1807"+ // field 3, encoding 0, value 7 - "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField) - "320d"+"0a056c6162656c120474797065"+ // field 6, encoding 2 (GoTestField) - "5001"+ // field 10, encoding 0, value 1 - "5803"+ // field 11, encoding 0, value 3 - "6006"+ // field 12, encoding 0, value 6 - "6d20000000"+ // field 13, encoding 5, value 32 - "714000000000000000"+ // field 14, encoding 1, value 64 - "78a019"+ // field 15, encoding 0, value 3232 - "8001c032"+ // field 16, encoding 0, value 6464 - "8d0100004a45"+ // field 17, encoding 5, value 3232.0 - "9101000000000040b940"+ // field 18, encoding 1, value 6464.0 - "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string" - "f00101"+ // field 30, encoding 0, value 1 - "f80120"+ // field 31, encoding 0, value 32 - "800240"+ // field 32, encoding 0, value 64 - "8d02a00c0000"+ // field 33, encoding 5, value 3232 - "91024019000000000000"+ // field 34, encoding 1, value 6464 - "9802a0dd13"+ // field 35, encoding 0, value 323232 - "a002c0ba27"+ // field 36, encoding 0, value 646464 - "ad0200000042"+ // field 37, encoding 5, value 32.0 - "b1020000000000005040"+ // field 38, encoding 1, value 64.0 - "ba0205"+"68656c6c6f"+ // field 39, encoding 2, string "hello" - "c00201"+ // field 40, encoding 0, value 1 - "c80220"+ // field 41, encoding 0, value 32 - "d00240"+ // field 42, encoding 0, value 64 - "dd0240010000"+ // field 43, encoding 5, value 320 - "e1028002000000000000"+ // field 44, encoding 1, value 640 - "e8028019"+ // field 45, encoding 0, value 3200 - "f0028032"+ // field 46, encoding 0, value 6400 - "fd02e0659948"+ // field 47, encoding 5, value 314159.0 - "81030000000050971041"+ // field 48, encoding 1, value 271828.0 - "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n" - "b304"+ // start group field 70 level 1 - "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required" - "b404"+ // end group field 70 level 1 - "d305"+ // start group field 90 level 1 - "da0508"+"6f7074696f6e616c"+ // field 91, encoding 2, string "optional" - "d405"+ // end group field 90 level 1 - "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes" - "b0063f"+ // field 102, encoding 0, 0x3f zigzag32 - "b8067f"+ // field 103, encoding 0, 0x7f zigzag64 - "ea1207"+"4269676e6f7365"+ // field 301, encoding 2, string "Bignose" - "f0123f"+ // field 302, encoding 0, value 63 - "f8127f"+ // field 303, encoding 0, value 127 - "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose" - "90193f"+ // field 402, encoding 0, value 63 - "98197f") // field 403, encoding 0, value 127 - -} - -// All required fields set, defaults provided, all repeated fields given two values. -func TestEncodeDecode5(t *testing.T) { - pb := initGoTest(true) - pb.RepeatedField = []*GoTestField{initGoTestField(), initGoTestField()} - pb.F_BoolRepeated = []bool{false, true} - pb.F_Int32Repeated = []int32{32, 33} - pb.F_Int64Repeated = []int64{64, 65} - pb.F_Fixed32Repeated = []uint32{3232, 3333} - pb.F_Fixed64Repeated = []uint64{6464, 6565} - pb.F_Uint32Repeated = []uint32{323232, 333333} - pb.F_Uint64Repeated = []uint64{646464, 656565} - pb.F_FloatRepeated = []float32{32., 33.} - pb.F_DoubleRepeated = []float64{64., 65.} - pb.F_StringRepeated = []string{"hello", "sailor"} - pb.F_BytesRepeated = [][]byte{[]byte("big"), []byte("nose")} - pb.F_Sint32Repeated = []int32{32, -32} - pb.F_Sint64Repeated = []int64{64, -64} - pb.Repeatedgroup = []*GoTest_RepeatedGroup{initGoTest_RepeatedGroup(), initGoTest_RepeatedGroup()} - - overify(t, pb, - "0807"+ // field 1, encoding 0, value 7 - "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField) - "2a0d"+"0a056c6162656c120474797065"+ // field 5, encoding 2 (GoTestField) - "2a0d"+"0a056c6162656c120474797065"+ // field 5, encoding 2 (GoTestField) - "5001"+ // field 10, encoding 0, value 1 - "5803"+ // field 11, encoding 0, value 3 - "6006"+ // field 12, encoding 0, value 6 - "6d20000000"+ // field 13, encoding 5, value 32 - "714000000000000000"+ // field 14, encoding 1, value 64 - "78a019"+ // field 15, encoding 0, value 3232 - "8001c032"+ // field 16, encoding 0, value 6464 - "8d0100004a45"+ // field 17, encoding 5, value 3232.0 - "9101000000000040b940"+ // field 18, encoding 1, value 6464.0 - "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string" - "a00100"+ // field 20, encoding 0, value 0 - "a00101"+ // field 20, encoding 0, value 1 - "a80120"+ // field 21, encoding 0, value 32 - "a80121"+ // field 21, encoding 0, value 33 - "b00140"+ // field 22, encoding 0, value 64 - "b00141"+ // field 22, encoding 0, value 65 - "bd01a00c0000"+ // field 23, encoding 5, value 3232 - "bd01050d0000"+ // field 23, encoding 5, value 3333 - "c1014019000000000000"+ // field 24, encoding 1, value 6464 - "c101a519000000000000"+ // field 24, encoding 1, value 6565 - "c801a0dd13"+ // field 25, encoding 0, value 323232 - "c80195ac14"+ // field 25, encoding 0, value 333333 - "d001c0ba27"+ // field 26, encoding 0, value 646464 - "d001b58928"+ // field 26, encoding 0, value 656565 - "dd0100000042"+ // field 27, encoding 5, value 32.0 - "dd0100000442"+ // field 27, encoding 5, value 33.0 - "e1010000000000005040"+ // field 28, encoding 1, value 64.0 - "e1010000000000405040"+ // field 28, encoding 1, value 65.0 - "ea0105"+"68656c6c6f"+ // field 29, encoding 2, string "hello" - "ea0106"+"7361696c6f72"+ // field 29, encoding 2, string "sailor" - "c00201"+ // field 40, encoding 0, value 1 - "c80220"+ // field 41, encoding 0, value 32 - "d00240"+ // field 42, encoding 0, value 64 - "dd0240010000"+ // field 43, encoding 5, value 320 - "e1028002000000000000"+ // field 44, encoding 1, value 640 - "e8028019"+ // field 45, encoding 0, value 3200 - "f0028032"+ // field 46, encoding 0, value 6400 - "fd02e0659948"+ // field 47, encoding 5, value 314159.0 - "81030000000050971041"+ // field 48, encoding 1, value 271828.0 - "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n" - "b304"+ // start group field 70 level 1 - "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required" - "b404"+ // end group field 70 level 1 - "8305"+ // start group field 80 level 1 - "8a0508"+"7265706561746564"+ // field 81, encoding 2, string "repeated" - "8405"+ // end group field 80 level 1 - "8305"+ // start group field 80 level 1 - "8a0508"+"7265706561746564"+ // field 81, encoding 2, string "repeated" - "8405"+ // end group field 80 level 1 - "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes" - "b0063f"+ // field 102, encoding 0, 0x3f zigzag32 - "b8067f"+ // field 103, encoding 0, 0x7f zigzag64 - "ca0c03"+"626967"+ // field 201, encoding 2, string "big" - "ca0c04"+"6e6f7365"+ // field 201, encoding 2, string "nose" - "d00c40"+ // field 202, encoding 0, value 32 - "d00c3f"+ // field 202, encoding 0, value -32 - "d80c8001"+ // field 203, encoding 0, value 64 - "d80c7f"+ // field 203, encoding 0, value -64 - "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose" - "90193f"+ // field 402, encoding 0, value 63 - "98197f") // field 403, encoding 0, value 127 - -} - -// All required fields set, all packed repeated fields given two values. -func TestEncodeDecode6(t *testing.T) { - pb := initGoTest(false) - pb.F_BoolRepeatedPacked = []bool{false, true} - pb.F_Int32RepeatedPacked = []int32{32, 33} - pb.F_Int64RepeatedPacked = []int64{64, 65} - pb.F_Fixed32RepeatedPacked = []uint32{3232, 3333} - pb.F_Fixed64RepeatedPacked = []uint64{6464, 6565} - pb.F_Uint32RepeatedPacked = []uint32{323232, 333333} - pb.F_Uint64RepeatedPacked = []uint64{646464, 656565} - pb.F_FloatRepeatedPacked = []float32{32., 33.} - pb.F_DoubleRepeatedPacked = []float64{64., 65.} - pb.F_Sint32RepeatedPacked = []int32{32, -32} - pb.F_Sint64RepeatedPacked = []int64{64, -64} - - overify(t, pb, - "0807"+ // field 1, encoding 0, value 7 - "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField) - "5001"+ // field 10, encoding 0, value 1 - "5803"+ // field 11, encoding 0, value 3 - "6006"+ // field 12, encoding 0, value 6 - "6d20000000"+ // field 13, encoding 5, value 32 - "714000000000000000"+ // field 14, encoding 1, value 64 - "78a019"+ // field 15, encoding 0, value 3232 - "8001c032"+ // field 16, encoding 0, value 6464 - "8d0100004a45"+ // field 17, encoding 5, value 3232.0 - "9101000000000040b940"+ // field 18, encoding 1, value 6464.0 - "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string" - "9203020001"+ // field 50, encoding 2, 2 bytes, value 0, value 1 - "9a03022021"+ // field 51, encoding 2, 2 bytes, value 32, value 33 - "a203024041"+ // field 52, encoding 2, 2 bytes, value 64, value 65 - "aa0308"+ // field 53, encoding 2, 8 bytes - "a00c0000050d0000"+ // value 3232, value 3333 - "b20310"+ // field 54, encoding 2, 16 bytes - "4019000000000000a519000000000000"+ // value 6464, value 6565 - "ba0306"+ // field 55, encoding 2, 6 bytes - "a0dd1395ac14"+ // value 323232, value 333333 - "c20306"+ // field 56, encoding 2, 6 bytes - "c0ba27b58928"+ // value 646464, value 656565 - "ca0308"+ // field 57, encoding 2, 8 bytes - "0000004200000442"+ // value 32.0, value 33.0 - "d20310"+ // field 58, encoding 2, 16 bytes - "00000000000050400000000000405040"+ // value 64.0, value 65.0 - "b304"+ // start group field 70 level 1 - "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required" - "b404"+ // end group field 70 level 1 - "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes" - "b0063f"+ // field 102, encoding 0, 0x3f zigzag32 - "b8067f"+ // field 103, encoding 0, 0x7f zigzag64 - "b21f02"+ // field 502, encoding 2, 2 bytes - "403f"+ // value 32, value -32 - "ba1f03"+ // field 503, encoding 2, 3 bytes - "80017f") // value 64, value -64 -} - -// Test that we can encode empty bytes fields. -func TestEncodeDecodeBytes1(t *testing.T) { - pb := initGoTest(false) - - // Create our bytes - pb.F_BytesRequired = []byte{} - pb.F_BytesRepeated = [][]byte{{}} - pb.F_BytesOptional = []byte{} - - d, err := Marshal(pb) - if err != nil { - t.Error(err) - } - - pbd := new(GoTest) - if err := Unmarshal(d, pbd); err != nil { - t.Error(err) - } - - if pbd.F_BytesRequired == nil || len(pbd.F_BytesRequired) != 0 { - t.Error("required empty bytes field is incorrect") - } - if pbd.F_BytesRepeated == nil || len(pbd.F_BytesRepeated) == 1 && pbd.F_BytesRepeated[0] == nil { - t.Error("repeated empty bytes field is incorrect") - } - if pbd.F_BytesOptional == nil || len(pbd.F_BytesOptional) != 0 { - t.Error("optional empty bytes field is incorrect") - } -} - -// Test that we encode nil-valued fields of a repeated bytes field correctly. -// Since entries in a repeated field cannot be nil, nil must mean empty value. -func TestEncodeDecodeBytes2(t *testing.T) { - pb := initGoTest(false) - - // Create our bytes - pb.F_BytesRepeated = [][]byte{nil} - - d, err := Marshal(pb) - if err != nil { - t.Error(err) - } - - pbd := new(GoTest) - if err := Unmarshal(d, pbd); err != nil { - t.Error(err) - } - - if len(pbd.F_BytesRepeated) != 1 || pbd.F_BytesRepeated[0] == nil { - t.Error("Unexpected value for repeated bytes field") - } -} - -// All required fields set, defaults provided, all repeated fields given two values. -func TestSkippingUnrecognizedFields(t *testing.T) { - o := old() - pb := initGoTestField() - - // Marshal it normally. - o.Marshal(pb) - - // Now new a GoSkipTest record. - skip := &GoSkipTest{ - SkipInt32: Int32(32), - SkipFixed32: Uint32(3232), - SkipFixed64: Uint64(6464), - SkipString: String("skipper"), - Skipgroup: &GoSkipTest_SkipGroup{ - GroupInt32: Int32(75), - GroupString: String("wxyz"), - }, - } - - // Marshal it into same buffer. - o.Marshal(skip) - - pbd := new(GoTestField) - o.Unmarshal(pbd) - - // The __unrecognized field should be a marshaling of GoSkipTest - skipd := new(GoSkipTest) - - o.SetBuf(pbd.XXX_unrecognized) - o.Unmarshal(skipd) - - if *skipd.SkipInt32 != *skip.SkipInt32 { - t.Error("skip int32", skipd.SkipInt32) - } - if *skipd.SkipFixed32 != *skip.SkipFixed32 { - t.Error("skip fixed32", skipd.SkipFixed32) - } - if *skipd.SkipFixed64 != *skip.SkipFixed64 { - t.Error("skip fixed64", skipd.SkipFixed64) - } - if *skipd.SkipString != *skip.SkipString { - t.Error("skip string", *skipd.SkipString) - } - if *skipd.Skipgroup.GroupInt32 != *skip.Skipgroup.GroupInt32 { - t.Error("skip group int32", skipd.Skipgroup.GroupInt32) - } - if *skipd.Skipgroup.GroupString != *skip.Skipgroup.GroupString { - t.Error("skip group string", *skipd.Skipgroup.GroupString) - } -} - -// Check that unrecognized fields of a submessage are preserved. -func TestSubmessageUnrecognizedFields(t *testing.T) { - nm := &NewMessage{ - Nested: &NewMessage_Nested{ - Name: String("Nigel"), - FoodGroup: String("carbs"), - }, - } - b, err := Marshal(nm) - if err != nil { - t.Fatalf("Marshal of NewMessage: %v", err) - } - - // Unmarshal into an OldMessage. - om := new(OldMessage) - if err := Unmarshal(b, om); err != nil { - t.Fatalf("Unmarshal to OldMessage: %v", err) - } - exp := &OldMessage{ - Nested: &OldMessage_Nested{ - Name: String("Nigel"), - // normal protocol buffer users should not do this - XXX_unrecognized: []byte("\x12\x05carbs"), - }, - } - if !Equal(om, exp) { - t.Errorf("om = %v, want %v", om, exp) - } - - // Clone the OldMessage. - om = Clone(om).(*OldMessage) - if !Equal(om, exp) { - t.Errorf("Clone(om) = %v, want %v", om, exp) - } - - // Marshal the OldMessage, then unmarshal it into an empty NewMessage. - if b, err = Marshal(om); err != nil { - t.Fatalf("Marshal of OldMessage: %v", err) - } - t.Logf("Marshal(%v) -> %q", om, b) - nm2 := new(NewMessage) - if err := Unmarshal(b, nm2); err != nil { - t.Fatalf("Unmarshal to NewMessage: %v", err) - } - if !Equal(nm, nm2) { - t.Errorf("NewMessage round-trip: %v => %v", nm, nm2) - } -} - -// Check that an int32 field can be upgraded to an int64 field. -func TestNegativeInt32(t *testing.T) { - om := &OldMessage{ - Num: Int32(-1), - } - b, err := Marshal(om) - if err != nil { - t.Fatalf("Marshal of OldMessage: %v", err) - } - - // Check the size. It should be 11 bytes; - // 1 for the field/wire type, and 10 for the negative number. - if len(b) != 11 { - t.Errorf("%v marshaled as %q, wanted 11 bytes", om, b) - } - - // Unmarshal into a NewMessage. - nm := new(NewMessage) - if err := Unmarshal(b, nm); err != nil { - t.Fatalf("Unmarshal to NewMessage: %v", err) - } - want := &NewMessage{ - Num: Int64(-1), - } - if !Equal(nm, want) { - t.Errorf("nm = %v, want %v", nm, want) - } -} - -// Check that we can grow an array (repeated field) to have many elements. -// This test doesn't depend only on our encoding; for variety, it makes sure -// we create, encode, and decode the correct contents explicitly. It's therefore -// a bit messier. -// This test also uses (and hence tests) the Marshal/Unmarshal functions -// instead of the methods. -func TestBigRepeated(t *testing.T) { - pb := initGoTest(true) - - // Create the arrays - const N = 50 // Internally the library starts much smaller. - pb.Repeatedgroup = make([]*GoTest_RepeatedGroup, N) - pb.F_Sint64Repeated = make([]int64, N) - pb.F_Sint32Repeated = make([]int32, N) - pb.F_BytesRepeated = make([][]byte, N) - pb.F_StringRepeated = make([]string, N) - pb.F_DoubleRepeated = make([]float64, N) - pb.F_FloatRepeated = make([]float32, N) - pb.F_Uint64Repeated = make([]uint64, N) - pb.F_Uint32Repeated = make([]uint32, N) - pb.F_Fixed64Repeated = make([]uint64, N) - pb.F_Fixed32Repeated = make([]uint32, N) - pb.F_Int64Repeated = make([]int64, N) - pb.F_Int32Repeated = make([]int32, N) - pb.F_BoolRepeated = make([]bool, N) - pb.RepeatedField = make([]*GoTestField, N) - - // Fill in the arrays with checkable values. - igtf := initGoTestField() - igtrg := initGoTest_RepeatedGroup() - for i := 0; i < N; i++ { - pb.Repeatedgroup[i] = igtrg - pb.F_Sint64Repeated[i] = int64(i) - pb.F_Sint32Repeated[i] = int32(i) - s := fmt.Sprint(i) - pb.F_BytesRepeated[i] = []byte(s) - pb.F_StringRepeated[i] = s - pb.F_DoubleRepeated[i] = float64(i) - pb.F_FloatRepeated[i] = float32(i) - pb.F_Uint64Repeated[i] = uint64(i) - pb.F_Uint32Repeated[i] = uint32(i) - pb.F_Fixed64Repeated[i] = uint64(i) - pb.F_Fixed32Repeated[i] = uint32(i) - pb.F_Int64Repeated[i] = int64(i) - pb.F_Int32Repeated[i] = int32(i) - pb.F_BoolRepeated[i] = i%2 == 0 - pb.RepeatedField[i] = igtf - } - - // Marshal. - buf, _ := Marshal(pb) - - // Now test Unmarshal by recreating the original buffer. - pbd := new(GoTest) - Unmarshal(buf, pbd) - - // Check the checkable values - for i := uint64(0); i < N; i++ { - if pbd.Repeatedgroup[i] == nil { // TODO: more checking? - t.Error("pbd.Repeatedgroup bad") - } - var x uint64 - x = uint64(pbd.F_Sint64Repeated[i]) - if x != i { - t.Error("pbd.F_Sint64Repeated bad", x, i) - } - x = uint64(pbd.F_Sint32Repeated[i]) - if x != i { - t.Error("pbd.F_Sint32Repeated bad", x, i) - } - s := fmt.Sprint(i) - equalbytes(pbd.F_BytesRepeated[i], []byte(s), t) - if pbd.F_StringRepeated[i] != s { - t.Error("pbd.F_Sint32Repeated bad", pbd.F_StringRepeated[i], i) - } - x = uint64(pbd.F_DoubleRepeated[i]) - if x != i { - t.Error("pbd.F_DoubleRepeated bad", x, i) - } - x = uint64(pbd.F_FloatRepeated[i]) - if x != i { - t.Error("pbd.F_FloatRepeated bad", x, i) - } - x = pbd.F_Uint64Repeated[i] - if x != i { - t.Error("pbd.F_Uint64Repeated bad", x, i) - } - x = uint64(pbd.F_Uint32Repeated[i]) - if x != i { - t.Error("pbd.F_Uint32Repeated bad", x, i) - } - x = pbd.F_Fixed64Repeated[i] - if x != i { - t.Error("pbd.F_Fixed64Repeated bad", x, i) - } - x = uint64(pbd.F_Fixed32Repeated[i]) - if x != i { - t.Error("pbd.F_Fixed32Repeated bad", x, i) - } - x = uint64(pbd.F_Int64Repeated[i]) - if x != i { - t.Error("pbd.F_Int64Repeated bad", x, i) - } - x = uint64(pbd.F_Int32Repeated[i]) - if x != i { - t.Error("pbd.F_Int32Repeated bad", x, i) - } - if pbd.F_BoolRepeated[i] != (i%2 == 0) { - t.Error("pbd.F_BoolRepeated bad", x, i) - } - if pbd.RepeatedField[i] == nil { // TODO: more checking? - t.Error("pbd.RepeatedField bad") - } - } -} - -// Verify we give a useful message when decoding to the wrong structure type. -func TestTypeMismatch(t *testing.T) { - pb1 := initGoTest(true) - - // Marshal - o := old() - o.Marshal(pb1) - - // Now Unmarshal it to the wrong type. - pb2 := initGoTestField() - err := o.Unmarshal(pb2) - if err == nil { - t.Error("expected error, got no error") - } else if !strings.Contains(err.Error(), "bad wiretype") { - t.Error("expected bad wiretype error, got", err) - } -} - -func encodeDecode(t *testing.T, in, out Message, msg string) { - buf, err := Marshal(in) - if err != nil { - t.Fatalf("failed marshaling %v: %v", msg, err) - } - if err := Unmarshal(buf, out); err != nil { - t.Fatalf("failed unmarshaling %v: %v", msg, err) - } -} - -func TestPackedNonPackedDecoderSwitching(t *testing.T) { - np, p := new(NonPackedTest), new(PackedTest) - - // non-packed -> packed - np.A = []int32{0, 1, 1, 2, 3, 5} - encodeDecode(t, np, p, "non-packed -> packed") - if !reflect.DeepEqual(np.A, p.B) { - t.Errorf("failed non-packed -> packed; np.A=%+v, p.B=%+v", np.A, p.B) - } - - // packed -> non-packed - np.Reset() - p.B = []int32{3, 1, 4, 1, 5, 9} - encodeDecode(t, p, np, "packed -> non-packed") - if !reflect.DeepEqual(p.B, np.A) { - t.Errorf("failed packed -> non-packed; p.B=%+v, np.A=%+v", p.B, np.A) - } -} - -func TestProto1RepeatedGroup(t *testing.T) { - pb := &MessageList{ - Message: []*MessageList_Message{ - { - Name: String("blah"), - Count: Int32(7), - }, - // NOTE: pb.Message[1] is a nil - nil, - }, - } - - o := old() - if err := o.Marshal(pb); err != ErrRepeatedHasNil { - t.Fatalf("unexpected or no error when marshaling: %v", err) - } -} - -// Test that enums work. Checks for a bug introduced by making enums -// named types instead of int32: newInt32FromUint64 would crash with -// a type mismatch in reflect.PointTo. -func TestEnum(t *testing.T) { - pb := new(GoEnum) - pb.Foo = FOO_FOO1.Enum() - o := old() - if err := o.Marshal(pb); err != nil { - t.Fatal("error encoding enum:", err) - } - pb1 := new(GoEnum) - if err := o.Unmarshal(pb1); err != nil { - t.Fatal("error decoding enum:", err) - } - if *pb1.Foo != FOO_FOO1 { - t.Error("expected 7 but got ", *pb1.Foo) - } -} - -// Enum types have String methods. Check that enum fields can be printed. -// We don't care what the value actually is, just as long as it doesn't crash. -func TestPrintingNilEnumFields(t *testing.T) { - pb := new(GoEnum) - fmt.Sprintf("%+v", pb) -} - -// Verify that absent required fields cause Marshal/Unmarshal to return errors. -func TestRequiredFieldEnforcement(t *testing.T) { - pb := new(GoTestField) - _, err := Marshal(pb) - if err == nil { - t.Error("marshal: expected error, got nil") - } else if strings.Index(err.Error(), "Label") < 0 { - t.Errorf("marshal: bad error type: %v", err) - } - - // A slightly sneaky, yet valid, proto. It encodes the same required field twice, - // so simply counting the required fields is insufficient. - // field 1, encoding 2, value "hi" - buf := []byte("\x0A\x02hi\x0A\x02hi") - err = Unmarshal(buf, pb) - if err == nil { - t.Error("unmarshal: expected error, got nil") - } else if strings.Index(err.Error(), "{Unknown}") < 0 { - t.Errorf("unmarshal: bad error type: %v", err) - } -} - -func TestTypedNilMarshal(t *testing.T) { - // A typed nil should return ErrNil and not crash. - _, err := Marshal((*GoEnum)(nil)) - if err != ErrNil { - t.Errorf("Marshal: got err %v, want ErrNil", err) - } -} - -// A type that implements the Marshaler interface, but is not nillable. -type nonNillableInt uint64 - -func (nni nonNillableInt) Marshal() ([]byte, error) { - return EncodeVarint(uint64(nni)), nil -} - -type NNIMessage struct { - nni nonNillableInt -} - -func (*NNIMessage) Reset() {} -func (*NNIMessage) String() string { return "" } -func (*NNIMessage) ProtoMessage() {} - -// A type that implements the Marshaler interface and is nillable. -type nillableMessage struct { - x uint64 -} - -func (nm *nillableMessage) Marshal() ([]byte, error) { - return EncodeVarint(nm.x), nil -} - -type NMMessage struct { - nm *nillableMessage -} - -func (*NMMessage) Reset() {} -func (*NMMessage) String() string { return "" } -func (*NMMessage) ProtoMessage() {} - -// Verify a type that uses the Marshaler interface, but has a nil pointer. -func TestNilMarshaler(t *testing.T) { - // Try a struct with a Marshaler field that is nil. - // It should be directly marshable. - nmm := new(NMMessage) - if _, err := Marshal(nmm); err != nil { - t.Error("unexpected error marshaling nmm: ", err) - } - - // Try a struct with a Marshaler field that is not nillable. - nnim := new(NNIMessage) - nnim.nni = 7 - var _ Marshaler = nnim.nni // verify it is truly a Marshaler - if _, err := Marshal(nnim); err != nil { - t.Error("unexpected error marshaling nnim: ", err) - } -} - -func TestAllSetDefaults(t *testing.T) { - // Exercise SetDefaults with all scalar field types. - m := &Defaults{ - // NaN != NaN, so override that here. - F_Nan: Float32(1.7), - } - expected := &Defaults{ - F_Bool: Bool(true), - F_Int32: Int32(32), - F_Int64: Int64(64), - F_Fixed32: Uint32(320), - F_Fixed64: Uint64(640), - F_Uint32: Uint32(3200), - F_Uint64: Uint64(6400), - F_Float: Float32(314159), - F_Double: Float64(271828), - F_String: String(`hello, "world!"` + "\n"), - F_Bytes: []byte("Bignose"), - F_Sint32: Int32(-32), - F_Sint64: Int64(-64), - F_Enum: Defaults_GREEN.Enum(), - F_Pinf: Float32(float32(math.Inf(1))), - F_Ninf: Float32(float32(math.Inf(-1))), - F_Nan: Float32(1.7), - StrZero: String(""), - } - SetDefaults(m) - if !Equal(m, expected) { - t.Errorf("SetDefaults failed\n got %v\nwant %v", m, expected) - } -} - -func TestSetDefaultsWithSetField(t *testing.T) { - // Check that a set value is not overridden. - m := &Defaults{ - F_Int32: Int32(12), - } - SetDefaults(m) - if v := m.GetF_Int32(); v != 12 { - t.Errorf("m.FInt32 = %v, want 12", v) - } -} - -func TestSetDefaultsWithSubMessage(t *testing.T) { - m := &OtherMessage{ - Key: Int64(123), - Inner: &InnerMessage{ - Host: String("gopher"), - }, - } - expected := &OtherMessage{ - Key: Int64(123), - Inner: &InnerMessage{ - Host: String("gopher"), - Port: Int32(4000), - }, - } - SetDefaults(m) - if !Equal(m, expected) { - t.Errorf("\n got %v\nwant %v", m, expected) - } -} - -func TestSetDefaultsWithRepeatedSubMessage(t *testing.T) { - m := &MyMessage{ - RepInner: []*InnerMessage{{}}, - } - expected := &MyMessage{ - RepInner: []*InnerMessage{{ - Port: Int32(4000), - }}, - } - SetDefaults(m) - if !Equal(m, expected) { - t.Errorf("\n got %v\nwant %v", m, expected) - } -} - -func TestMaximumTagNumber(t *testing.T) { - m := &MaxTag{ - LastField: String("natural goat essence"), - } - buf, err := Marshal(m) - if err != nil { - t.Fatalf("proto.Marshal failed: %v", err) - } - m2 := new(MaxTag) - if err := Unmarshal(buf, m2); err != nil { - t.Fatalf("proto.Unmarshal failed: %v", err) - } - if got, want := m2.GetLastField(), *m.LastField; got != want { - t.Errorf("got %q, want %q", got, want) - } -} - -func TestJSON(t *testing.T) { - m := &MyMessage{ - Count: Int32(4), - Pet: []string{"bunny", "kitty"}, - Inner: &InnerMessage{ - Host: String("cauchy"), - }, - Bikeshed: MyMessage_GREEN.Enum(), - } - const expected = `{"count":4,"pet":["bunny","kitty"],"inner":{"host":"cauchy"},"bikeshed":1}` - - b, err := json.Marshal(m) - if err != nil { - t.Fatalf("json.Marshal failed: %v", err) - } - s := string(b) - if s != expected { - t.Errorf("got %s\nwant %s", s, expected) - } - - received := new(MyMessage) - if err := json.Unmarshal(b, received); err != nil { - t.Fatalf("json.Unmarshal failed: %v", err) - } - if !Equal(received, m) { - t.Fatalf("got %s, want %s", received, m) - } - - // Test unmarshalling of JSON with symbolic enum name. - const old = `{"count":4,"pet":["bunny","kitty"],"inner":{"host":"cauchy"},"bikeshed":"GREEN"}` - received.Reset() - if err := json.Unmarshal([]byte(old), received); err != nil { - t.Fatalf("json.Unmarshal failed: %v", err) - } - if !Equal(received, m) { - t.Fatalf("got %s, want %s", received, m) - } -} - -func TestBadWireType(t *testing.T) { - b := []byte{7<<3 | 6} // field 7, wire type 6 - pb := new(OtherMessage) - if err := Unmarshal(b, pb); err == nil { - t.Errorf("Unmarshal did not fail") - } else if !strings.Contains(err.Error(), "unknown wire type") { - t.Errorf("wrong error: %v", err) - } -} - -func TestBytesWithInvalidLength(t *testing.T) { - // If a byte sequence has an invalid (negative) length, Unmarshal should not panic. - b := []byte{2<<3 | WireBytes, 0xff, 0xff, 0xff, 0xff, 0xff, 0} - Unmarshal(b, new(MyMessage)) -} - -func TestLengthOverflow(t *testing.T) { - // Overflowing a length should not panic. - b := []byte{2<<3 | WireBytes, 1, 1, 3<<3 | WireBytes, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x01} - Unmarshal(b, new(MyMessage)) -} - -func TestVarintOverflow(t *testing.T) { - // Overflowing a 64-bit length should not be allowed. - b := []byte{1<<3 | WireVarint, 0x01, 3<<3 | WireBytes, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01} - if err := Unmarshal(b, new(MyMessage)); err == nil { - t.Fatalf("Overflowed uint64 length without error") - } -} - -func TestUnmarshalFuzz(t *testing.T) { - const N = 1000 - seed := time.Now().UnixNano() - t.Logf("RNG seed is %d", seed) - rng := rand.New(rand.NewSource(seed)) - buf := make([]byte, 20) - for i := 0; i < N; i++ { - for j := range buf { - buf[j] = byte(rng.Intn(256)) - } - fuzzUnmarshal(t, buf) - } -} - -func TestMergeMessages(t *testing.T) { - pb := &MessageList{Message: []*MessageList_Message{{Name: String("x"), Count: Int32(1)}}} - data, err := Marshal(pb) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - - pb1 := new(MessageList) - if err := Unmarshal(data, pb1); err != nil { - t.Fatalf("first Unmarshal: %v", err) - } - if err := Unmarshal(data, pb1); err != nil { - t.Fatalf("second Unmarshal: %v", err) - } - if len(pb1.Message) != 1 { - t.Errorf("two Unmarshals produced %d Messages, want 1", len(pb1.Message)) - } - - pb2 := new(MessageList) - if err := UnmarshalMerge(data, pb2); err != nil { - t.Fatalf("first UnmarshalMerge: %v", err) - } - if err := UnmarshalMerge(data, pb2); err != nil { - t.Fatalf("second UnmarshalMerge: %v", err) - } - if len(pb2.Message) != 2 { - t.Errorf("two UnmarshalMerges produced %d Messages, want 2", len(pb2.Message)) - } -} - -func TestExtensionMarshalOrder(t *testing.T) { - m := &MyMessage{Count: Int(123)} - if err := SetExtension(m, E_Ext_More, &Ext{Data: String("alpha")}); err != nil { - t.Fatalf("SetExtension: %v", err) - } - if err := SetExtension(m, E_Ext_Text, String("aleph")); err != nil { - t.Fatalf("SetExtension: %v", err) - } - if err := SetExtension(m, E_Ext_Number, Int32(1)); err != nil { - t.Fatalf("SetExtension: %v", err) - } - - // Serialize m several times, and check we get the same bytes each time. - var orig []byte - for i := 0; i < 100; i++ { - b, err := Marshal(m) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - if i == 0 { - orig = b - continue - } - if !bytes.Equal(b, orig) { - t.Errorf("Bytes differ on attempt #%d", i) - } - } -} - -// Many extensions, because small maps might not iterate differently on each iteration. -var exts = []*ExtensionDesc{ - E_X201, - E_X202, - E_X203, - E_X204, - E_X205, - E_X206, - E_X207, - E_X208, - E_X209, - E_X210, - E_X211, - E_X212, - E_X213, - E_X214, - E_X215, - E_X216, - E_X217, - E_X218, - E_X219, - E_X220, - E_X221, - E_X222, - E_X223, - E_X224, - E_X225, - E_X226, - E_X227, - E_X228, - E_X229, - E_X230, - E_X231, - E_X232, - E_X233, - E_X234, - E_X235, - E_X236, - E_X237, - E_X238, - E_X239, - E_X240, - E_X241, - E_X242, - E_X243, - E_X244, - E_X245, - E_X246, - E_X247, - E_X248, - E_X249, - E_X250, -} - -func TestMessageSetMarshalOrder(t *testing.T) { - m := &MyMessageSet{} - for _, x := range exts { - if err := SetExtension(m, x, &Empty{}); err != nil { - t.Fatalf("SetExtension: %v", err) - } - } - - buf, err := Marshal(m) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - - // Serialize m several times, and check we get the same bytes each time. - for i := 0; i < 10; i++ { - b1, err := Marshal(m) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - if !bytes.Equal(b1, buf) { - t.Errorf("Bytes differ on re-Marshal #%d", i) - } - - m2 := &MyMessageSet{} - if err := Unmarshal(buf, m2); err != nil { - t.Errorf("Unmarshal: %v", err) - } - b2, err := Marshal(m2) - if err != nil { - t.Errorf("re-Marshal: %v", err) - } - if !bytes.Equal(b2, buf) { - t.Errorf("Bytes differ on round-trip #%d", i) - } - } -} - -func TestUnmarshalMergesMessages(t *testing.T) { - // If a nested message occurs twice in the input, - // the fields should be merged when decoding. - a := &OtherMessage{ - Key: Int64(123), - Inner: &InnerMessage{ - Host: String("polhode"), - Port: Int32(1234), - }, - } - aData, err := Marshal(a) - if err != nil { - t.Fatalf("Marshal(a): %v", err) - } - b := &OtherMessage{ - Weight: Float32(1.2), - Inner: &InnerMessage{ - Host: String("herpolhode"), - Connected: Bool(true), - }, - } - bData, err := Marshal(b) - if err != nil { - t.Fatalf("Marshal(b): %v", err) - } - want := &OtherMessage{ - Key: Int64(123), - Weight: Float32(1.2), - Inner: &InnerMessage{ - Host: String("herpolhode"), - Port: Int32(1234), - Connected: Bool(true), - }, - } - got := new(OtherMessage) - if err := Unmarshal(append(aData, bData...), got); err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if !Equal(got, want) { - t.Errorf("\n got %v\nwant %v", got, want) - } -} - -func TestEncodingSizes(t *testing.T) { - tests := []struct { - m Message - n int - }{ - {&Defaults{F_Int32: Int32(math.MaxInt32)}, 6}, - {&Defaults{F_Int32: Int32(math.MinInt32)}, 11}, - {&Defaults{F_Uint32: Uint32(uint32(math.MaxInt32) + 1)}, 6}, - {&Defaults{F_Uint32: Uint32(math.MaxUint32)}, 6}, - } - for _, test := range tests { - b, err := Marshal(test.m) - if err != nil { - t.Errorf("Marshal(%v): %v", test.m, err) - continue - } - if len(b) != test.n { - t.Errorf("Marshal(%v) yielded %d bytes, want %d bytes", test.m, len(b), test.n) - } - } -} - -func TestRequiredNotSetError(t *testing.T) { - pb := initGoTest(false) - pb.RequiredField.Label = nil - pb.F_Int32Required = nil - pb.F_Int64Required = nil - - expected := "0807" + // field 1, encoding 0, value 7 - "2206" + "120474797065" + // field 4, encoding 2 (GoTestField) - "5001" + // field 10, encoding 0, value 1 - "6d20000000" + // field 13, encoding 5, value 0x20 - "714000000000000000" + // field 14, encoding 1, value 0x40 - "78a019" + // field 15, encoding 0, value 0xca0 = 3232 - "8001c032" + // field 16, encoding 0, value 0x1940 = 6464 - "8d0100004a45" + // field 17, encoding 5, value 3232.0 - "9101000000000040b940" + // field 18, encoding 1, value 6464.0 - "9a0106" + "737472696e67" + // field 19, encoding 2, string "string" - "b304" + // field 70, encoding 3, start group - "ba0408" + "7265717569726564" + // field 71, encoding 2, string "required" - "b404" + // field 70, encoding 4, end group - "aa0605" + "6279746573" + // field 101, encoding 2, string "bytes" - "b0063f" + // field 102, encoding 0, 0x3f zigzag32 - "b8067f" // field 103, encoding 0, 0x7f zigzag64 - - o := old() - bytes, err := Marshal(pb) - if _, ok := err.(*RequiredNotSetError); !ok { - fmt.Printf("marshal-1 err = %v, want *RequiredNotSetError", err) - o.DebugPrint("", bytes) - t.Fatalf("expected = %s", expected) - } - if strings.Index(err.Error(), "RequiredField.Label") < 0 { - t.Errorf("marshal-1 wrong err msg: %v", err) - } - if !equal(bytes, expected, t) { - o.DebugPrint("neq 1", bytes) - t.Fatalf("expected = %s", expected) - } - - // Now test Unmarshal by recreating the original buffer. - pbd := new(GoTest) - err = Unmarshal(bytes, pbd) - if _, ok := err.(*RequiredNotSetError); !ok { - t.Fatalf("unmarshal err = %v, want *RequiredNotSetError", err) - o.DebugPrint("", bytes) - t.Fatalf("string = %s", expected) - } - if strings.Index(err.Error(), "RequiredField.{Unknown}") < 0 { - t.Errorf("unmarshal wrong err msg: %v", err) - } - bytes, err = Marshal(pbd) - if _, ok := err.(*RequiredNotSetError); !ok { - t.Errorf("marshal-2 err = %v, want *RequiredNotSetError", err) - o.DebugPrint("", bytes) - t.Fatalf("string = %s", expected) - } - if strings.Index(err.Error(), "RequiredField.Label") < 0 { - t.Errorf("marshal-2 wrong err msg: %v", err) - } - if !equal(bytes, expected, t) { - o.DebugPrint("neq 2", bytes) - t.Fatalf("string = %s", expected) - } -} - -func fuzzUnmarshal(t *testing.T, data []byte) { - defer func() { - if e := recover(); e != nil { - t.Errorf("These bytes caused a panic: %+v", data) - t.Logf("Stack:\n%s", debug.Stack()) - t.FailNow() - } - }() - - pb := new(MyMessage) - Unmarshal(data, pb) -} - -func TestMapFieldMarshal(t *testing.T) { - m := &MessageWithMap{ - NameMapping: map[int32]string{ - 1: "Rob", - 4: "Ian", - 8: "Dave", - }, - } - b, err := Marshal(m) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - - // b should be the concatenation of these three byte sequences in some order. - parts := []string{ - "\n\a\b\x01\x12\x03Rob", - "\n\a\b\x04\x12\x03Ian", - "\n\b\b\x08\x12\x04Dave", - } - ok := false - for i := range parts { - for j := range parts { - if j == i { - continue - } - for k := range parts { - if k == i || k == j { - continue - } - try := parts[i] + parts[j] + parts[k] - if bytes.Equal(b, []byte(try)) { - ok = true - break - } - } - } - } - if !ok { - t.Fatalf("Incorrect Marshal output.\n got %q\nwant %q (or a permutation of that)", b, parts[0]+parts[1]+parts[2]) - } - t.Logf("FYI b: %q", b) - - (new(Buffer)).DebugPrint("Dump of b", b) -} - -func TestMapFieldRoundTrips(t *testing.T) { - m := &MessageWithMap{ - NameMapping: map[int32]string{ - 1: "Rob", - 4: "Ian", - 8: "Dave", - }, - MsgMapping: map[int64]*FloatingPoint{ - 0x7001: &FloatingPoint{F: Float64(2.0)}, - }, - ByteMapping: map[bool][]byte{ - false: []byte("that's not right!"), - true: []byte("aye, 'tis true!"), - }, - } - b, err := Marshal(m) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - t.Logf("FYI b: %q", b) - m2 := new(MessageWithMap) - if err := Unmarshal(b, m2); err != nil { - t.Fatalf("Unmarshal: %v", err) - } - for _, pair := range [][2]interface{}{ - {m.NameMapping, m2.NameMapping}, - {m.MsgMapping, m2.MsgMapping}, - {m.ByteMapping, m2.ByteMapping}, - } { - if !reflect.DeepEqual(pair[0], pair[1]) { - t.Errorf("Map did not survive a round trip.\ninitial: %v\n final: %v", pair[0], pair[1]) - } - } -} - -// Benchmarks - -func testMsg() *GoTest { - pb := initGoTest(true) - const N = 1000 // Internally the library starts much smaller. - pb.F_Int32Repeated = make([]int32, N) - pb.F_DoubleRepeated = make([]float64, N) - for i := 0; i < N; i++ { - pb.F_Int32Repeated[i] = int32(i) - pb.F_DoubleRepeated[i] = float64(i) - } - return pb -} - -func bytesMsg() *GoTest { - pb := initGoTest(true) - buf := make([]byte, 4000) - for i := range buf { - buf[i] = byte(i) - } - pb.F_BytesDefaulted = buf - return pb -} - -func benchmarkMarshal(b *testing.B, pb Message, marshal func(Message) ([]byte, error)) { - d, _ := marshal(pb) - b.SetBytes(int64(len(d))) - b.ResetTimer() - for i := 0; i < b.N; i++ { - marshal(pb) - } -} - -func benchmarkBufferMarshal(b *testing.B, pb Message) { - p := NewBuffer(nil) - benchmarkMarshal(b, pb, func(pb0 Message) ([]byte, error) { - p.Reset() - err := p.Marshal(pb0) - return p.Bytes(), err - }) -} - -func benchmarkSize(b *testing.B, pb Message) { - benchmarkMarshal(b, pb, func(pb0 Message) ([]byte, error) { - Size(pb) - return nil, nil - }) -} - -func newOf(pb Message) Message { - in := reflect.ValueOf(pb) - if in.IsNil() { - return pb - } - return reflect.New(in.Type().Elem()).Interface().(Message) -} - -func benchmarkUnmarshal(b *testing.B, pb Message, unmarshal func([]byte, Message) error) { - d, _ := Marshal(pb) - b.SetBytes(int64(len(d))) - pbd := newOf(pb) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - unmarshal(d, pbd) - } -} - -func benchmarkBufferUnmarshal(b *testing.B, pb Message) { - p := NewBuffer(nil) - benchmarkUnmarshal(b, pb, func(d []byte, pb0 Message) error { - p.SetBuf(d) - return p.Unmarshal(pb0) - }) -} - -// Benchmark{Marshal,BufferMarshal,Size,Unmarshal,BufferUnmarshal}{,Bytes} - -func BenchmarkMarshal(b *testing.B) { - benchmarkMarshal(b, testMsg(), Marshal) -} - -func BenchmarkBufferMarshal(b *testing.B) { - benchmarkBufferMarshal(b, testMsg()) -} - -func BenchmarkSize(b *testing.B) { - benchmarkSize(b, testMsg()) -} - -func BenchmarkUnmarshal(b *testing.B) { - benchmarkUnmarshal(b, testMsg(), Unmarshal) -} - -func BenchmarkBufferUnmarshal(b *testing.B) { - benchmarkBufferUnmarshal(b, testMsg()) -} - -func BenchmarkMarshalBytes(b *testing.B) { - benchmarkMarshal(b, bytesMsg(), Marshal) -} - -func BenchmarkBufferMarshalBytes(b *testing.B) { - benchmarkBufferMarshal(b, bytesMsg()) -} - -func BenchmarkSizeBytes(b *testing.B) { - benchmarkSize(b, bytesMsg()) -} - -func BenchmarkUnmarshalBytes(b *testing.B) { - benchmarkUnmarshal(b, bytesMsg(), Unmarshal) -} - -func BenchmarkBufferUnmarshalBytes(b *testing.B) { - benchmarkBufferUnmarshal(b, bytesMsg()) -} - -func BenchmarkUnmarshalUnrecognizedFields(b *testing.B) { - b.StopTimer() - pb := initGoTestField() - skip := &GoSkipTest{ - SkipInt32: Int32(32), - SkipFixed32: Uint32(3232), - SkipFixed64: Uint64(6464), - SkipString: String("skipper"), - Skipgroup: &GoSkipTest_SkipGroup{ - GroupInt32: Int32(75), - GroupString: String("wxyz"), - }, - } - - pbd := new(GoTestField) - p := NewBuffer(nil) - p.Marshal(pb) - p.Marshal(skip) - p2 := NewBuffer(nil) - - b.StartTimer() - for i := 0; i < b.N; i++ { - p2.SetBuf(p.Bytes()) - p2.Unmarshal(pbd) - } -} diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone.go index ae276fd77..e98ddec98 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone.go @@ -29,8 +29,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Protocol buffer deep copy. -// TODO: MessageSet and RawMessage. +// Protocol buffer deep copy and merge. +// TODO: RawMessage. package proto @@ -75,12 +75,13 @@ func Merge(dst, src Message) { } func mergeStruct(out, in reflect.Value) { + sprop := GetProperties(in.Type()) for i := 0; i < in.NumField(); i++ { f := in.Type().Field(i) if strings.HasPrefix(f.Name, "XXX_") { continue } - mergeAny(out.Field(i), in.Field(i)) + mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i]) } if emIn, ok := in.Addr().Interface().(extendableProto); ok { @@ -98,7 +99,10 @@ func mergeStruct(out, in reflect.Value) { } } -func mergeAny(out, in reflect.Value) { +// mergeAny performs a merge between two values of the same type. +// viaPtr indicates whether the values were indirected through a pointer (implying proto2). +// prop is set if this is a struct field (it may be nil). +func mergeAny(out, in reflect.Value, viaPtr bool, prop *Properties) { if in.Type() == protoMessageType { if !in.IsNil() { if out.IsNil() { @@ -112,7 +116,21 @@ func mergeAny(out, in reflect.Value) { switch in.Kind() { case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, reflect.String, reflect.Uint32, reflect.Uint64: + if !viaPtr && isProto3Zero(in) { + return + } out.Set(in) + case reflect.Interface: + // Probably a oneof field; copy non-nil values. + if in.IsNil() { + return + } + // Allocate destination if it is not set, or set to a different type. + // Otherwise we will merge as normal. + if out.IsNil() || out.Elem().Type() != in.Elem().Type() { + out.Set(reflect.New(in.Elem().Elem().Type())) // interface -> *T -> T -> new(T) + } + mergeAny(out.Elem(), in.Elem(), false, nil) case reflect.Map: if in.Len() == 0 { return @@ -127,7 +145,7 @@ func mergeAny(out, in reflect.Value) { switch elemKind { case reflect.Ptr: val = reflect.New(in.Type().Elem().Elem()) - mergeAny(val, in.MapIndex(key)) + mergeAny(val, in.MapIndex(key), false, nil) case reflect.Slice: val = in.MapIndex(key) val = reflect.ValueOf(append([]byte{}, val.Bytes()...)) @@ -143,13 +161,21 @@ func mergeAny(out, in reflect.Value) { if out.IsNil() { out.Set(reflect.New(in.Elem().Type())) } - mergeAny(out.Elem(), in.Elem()) + mergeAny(out.Elem(), in.Elem(), true, nil) case reflect.Slice: if in.IsNil() { return } if in.Type().Elem().Kind() == reflect.Uint8 { // []byte is a scalar bytes field, not a repeated field. + + // Edge case: if this is in a proto3 message, a zero length + // bytes field is considered the zero value, and should not + // be merged. + if prop != nil && prop.proto3 && in.Len() == 0 { + return + } + // Make a deep copy. // Append to []byte{} instead of []byte(nil) so that we never end up // with a nil result. @@ -167,7 +193,7 @@ func mergeAny(out, in reflect.Value) { default: for i := 0; i < n; i++ { x := reflect.Indirect(reflect.New(in.Type().Elem())) - mergeAny(x, in.Index(i)) + mergeAny(x, in.Index(i), false, nil) out.Set(reflect.Append(out, x)) } } @@ -184,7 +210,7 @@ func mergeExtension(out, in map[int32]Extension) { eOut := Extension{desc: eIn.desc} if eIn.value != nil { v := reflect.New(reflect.TypeOf(eIn.value)).Elem() - mergeAny(v, reflect.ValueOf(eIn.value)) + mergeAny(v, reflect.ValueOf(eIn.value), false, nil) eOut.value = v.Interface() } if eIn.enc != nil { diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone_test.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone_test.go deleted file mode 100644 index 1ac177d21..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone_test.go +++ /dev/null @@ -1,227 +0,0 @@ -// Go support for Protocol Buffers - Google's data interchange format -// -// Copyright 2011 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 proto_test - -import ( - "testing" - - "github.com/golang/protobuf/proto" - - pb "./testdata" -) - -var cloneTestMessage = &pb.MyMessage{ - Count: proto.Int32(42), - Name: proto.String("Dave"), - Pet: []string{"bunny", "kitty", "horsey"}, - Inner: &pb.InnerMessage{ - Host: proto.String("niles"), - Port: proto.Int32(9099), - Connected: proto.Bool(true), - }, - Others: []*pb.OtherMessage{ - { - Value: []byte("some bytes"), - }, - }, - Somegroup: &pb.MyMessage_SomeGroup{ - GroupField: proto.Int32(6), - }, - RepBytes: [][]byte{[]byte("sham"), []byte("wow")}, -} - -func init() { - ext := &pb.Ext{ - Data: proto.String("extension"), - } - if err := proto.SetExtension(cloneTestMessage, pb.E_Ext_More, ext); err != nil { - panic("SetExtension: " + err.Error()) - } -} - -func TestClone(t *testing.T) { - m := proto.Clone(cloneTestMessage).(*pb.MyMessage) - if !proto.Equal(m, cloneTestMessage) { - t.Errorf("Clone(%v) = %v", cloneTestMessage, m) - } - - // Verify it was a deep copy. - *m.Inner.Port++ - if proto.Equal(m, cloneTestMessage) { - t.Error("Mutating clone changed the original") - } - // Byte fields and repeated fields should be copied. - if &m.Pet[0] == &cloneTestMessage.Pet[0] { - t.Error("Pet: repeated field not copied") - } - if &m.Others[0] == &cloneTestMessage.Others[0] { - t.Error("Others: repeated field not copied") - } - if &m.Others[0].Value[0] == &cloneTestMessage.Others[0].Value[0] { - t.Error("Others[0].Value: bytes field not copied") - } - if &m.RepBytes[0] == &cloneTestMessage.RepBytes[0] { - t.Error("RepBytes: repeated field not copied") - } - if &m.RepBytes[0][0] == &cloneTestMessage.RepBytes[0][0] { - t.Error("RepBytes[0]: bytes field not copied") - } -} - -func TestCloneNil(t *testing.T) { - var m *pb.MyMessage - if c := proto.Clone(m); !proto.Equal(m, c) { - t.Errorf("Clone(%v) = %v", m, c) - } -} - -var mergeTests = []struct { - src, dst, want proto.Message -}{ - { - src: &pb.MyMessage{ - Count: proto.Int32(42), - }, - dst: &pb.MyMessage{ - Name: proto.String("Dave"), - }, - want: &pb.MyMessage{ - Count: proto.Int32(42), - Name: proto.String("Dave"), - }, - }, - { - src: &pb.MyMessage{ - Inner: &pb.InnerMessage{ - Host: proto.String("hey"), - Connected: proto.Bool(true), - }, - Pet: []string{"horsey"}, - Others: []*pb.OtherMessage{ - { - Value: []byte("some bytes"), - }, - }, - }, - dst: &pb.MyMessage{ - Inner: &pb.InnerMessage{ - Host: proto.String("niles"), - Port: proto.Int32(9099), - }, - Pet: []string{"bunny", "kitty"}, - Others: []*pb.OtherMessage{ - { - Key: proto.Int64(31415926535), - }, - { - // Explicitly test a src=nil field - Inner: nil, - }, - }, - }, - want: &pb.MyMessage{ - Inner: &pb.InnerMessage{ - Host: proto.String("hey"), - Connected: proto.Bool(true), - Port: proto.Int32(9099), - }, - Pet: []string{"bunny", "kitty", "horsey"}, - Others: []*pb.OtherMessage{ - { - Key: proto.Int64(31415926535), - }, - {}, - { - Value: []byte("some bytes"), - }, - }, - }, - }, - { - src: &pb.MyMessage{ - RepBytes: [][]byte{[]byte("wow")}, - }, - dst: &pb.MyMessage{ - Somegroup: &pb.MyMessage_SomeGroup{ - GroupField: proto.Int32(6), - }, - RepBytes: [][]byte{[]byte("sham")}, - }, - want: &pb.MyMessage{ - Somegroup: &pb.MyMessage_SomeGroup{ - GroupField: proto.Int32(6), - }, - RepBytes: [][]byte{[]byte("sham"), []byte("wow")}, - }, - }, - // Check that a scalar bytes field replaces rather than appends. - { - src: &pb.OtherMessage{Value: []byte("foo")}, - dst: &pb.OtherMessage{Value: []byte("bar")}, - want: &pb.OtherMessage{Value: []byte("foo")}, - }, - { - src: &pb.MessageWithMap{ - NameMapping: map[int32]string{6: "Nigel"}, - MsgMapping: map[int64]*pb.FloatingPoint{ - 0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)}, - }, - ByteMapping: map[bool][]byte{true: []byte("wowsa")}, - }, - dst: &pb.MessageWithMap{ - NameMapping: map[int32]string{ - 6: "Bruce", // should be overwritten - 7: "Andrew", - }, - }, - want: &pb.MessageWithMap{ - NameMapping: map[int32]string{ - 6: "Nigel", - 7: "Andrew", - }, - MsgMapping: map[int64]*pb.FloatingPoint{ - 0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)}, - }, - ByteMapping: map[bool][]byte{true: []byte("wowsa")}, - }, - }, -} - -func TestMerge(t *testing.T) { - for _, m := range mergeTests { - got := proto.Clone(m.dst) - proto.Merge(got, m.src) - if !proto.Equal(got, m.want) { - t.Errorf("Merge(%v, %v)\n got %v\nwant %v\n", m.dst, m.src, got, m.want) - } - } -} diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/decode.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/decode.go index 88622c305..5810782fd 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/decode.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/decode.go @@ -46,6 +46,10 @@ import ( // errOverflow is returned when an integer is too large to be represented. var errOverflow = errors.New("proto: integer overflow") +// ErrInternalBadWireType is returned by generated code when an incorrect +// wire type is encountered. It does not get returned to user code. +var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof") + // The fundamental decoders that interpret bytes on the wire. // Those that take integer types all return uint64 and are // therefore of type valueDecoder. @@ -314,6 +318,24 @@ func UnmarshalMerge(buf []byte, pb Message) error { return NewBuffer(buf).Unmarshal(pb) } +// DecodeMessage reads a count-delimited message from the Buffer. +func (p *Buffer) DecodeMessage(pb Message) error { + enc, err := p.DecodeRawBytes(false) + if err != nil { + return err + } + return NewBuffer(enc).Unmarshal(pb) +} + +// DecodeGroup reads a tag-delimited group from the Buffer. +func (p *Buffer) DecodeGroup(pb Message) error { + typ, base, err := getbase(pb) + if err != nil { + return err + } + return p.unmarshalType(typ.Elem(), GetProperties(typ.Elem()), true, base) +} + // Unmarshal parses the protocol buffer representation in the // Buffer and places the decoded result in pb. If the struct // underlying pb does not match the data in the buffer, the results can be @@ -377,6 +399,20 @@ func (o *Buffer) unmarshalType(st reflect.Type, prop *StructProperties, is_group continue } } + // Maybe it's a oneof? + if prop.oneofUnmarshaler != nil { + m := structPointer_Interface(base, st).(Message) + // First return value indicates whether tag is a oneof field. + ok, err = prop.oneofUnmarshaler(m, tag, wire, o) + if err == ErrInternalBadWireType { + // Map the error to something more descriptive. + // Do the formatting here to save generated code space. + err = fmt.Errorf("bad wiretype for oneof field in %T", m) + } + if ok { + continue + } + } err = o.skipAndSave(st, tag, wire, base, prop.unrecField) continue } @@ -518,9 +554,7 @@ func (o *Buffer) dec_string(p *Properties, base structPointer) error { if err != nil { return err } - sp := new(string) - *sp = s - *structPointer_String(base, p.field) = sp + *structPointer_String(base, p.field) = &s return nil } @@ -563,9 +597,13 @@ func (o *Buffer) dec_slice_packed_bool(p *Properties, base structPointer) error return err } nb := int(nn) // number of bytes of encoded bools + fin := o.index + nb + if fin < o.index { + return errOverflow + } y := *v - for i := 0; i < nb; i++ { + for o.index < fin { u, err := p.valDec(o) if err != nil { return err @@ -677,7 +715,7 @@ func (o *Buffer) dec_new_map(p *Properties, base structPointer) error { oi := o.index // index at the end of this map entry o.index -= len(raw) // move buffer back to start of map entry - mptr := structPointer_Map(base, p.field, p.mtype) // *map[K]V + mptr := structPointer_NewAt(base, p.field, p.mtype) // *map[K]V if mptr.Elem().IsNil() { mptr.Elem().Set(reflect.MakeMap(mptr.Type().Elem())) } @@ -729,8 +767,14 @@ func (o *Buffer) dec_new_map(p *Properties, base structPointer) error { return fmt.Errorf("proto: bad map data tag %d", raw[0]) } } + keyelem, valelem := keyptr.Elem(), valptr.Elem() + if !keyelem.IsValid() || !valelem.IsValid() { + // We did not decode the key or the value in the map entry. + // Either way, it's an invalid map entry. + return fmt.Errorf("proto: bad map data: missing key/val") + } - v.SetMapIndex(keyptr.Elem(), valptr.Elem()) + v.SetMapIndex(keyelem, valelem) return nil } diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/encode.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/encode.go index 1512d605b..231b07401 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/encode.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/encode.go @@ -60,9 +60,9 @@ func (e *RequiredNotSetError) Error() string { } var ( - // ErrRepeatedHasNil is the error returned if Marshal is called with + // errRepeatedHasNil is the error returned if Marshal is called with // a struct with a repeated field containing a nil element. - ErrRepeatedHasNil = errors.New("proto: repeated field has nil element") + errRepeatedHasNil = errors.New("proto: repeated field has nil element") // ErrNil is the error returned if Marshal is called with nil. ErrNil = errors.New("proto: Marshal called with nil") @@ -105,6 +105,11 @@ func (p *Buffer) EncodeVarint(x uint64) error { return nil } +// SizeVarint returns the varint encoding size of an integer. +func SizeVarint(x uint64) int { + return sizeVarint(x) +} + func sizeVarint(x uint64) (n int) { for { n++ @@ -228,6 +233,20 @@ func Marshal(pb Message) ([]byte, error) { return p.buf, err } +// EncodeMessage writes the protocol buffer to the Buffer, +// prefixed by a varint-encoded length. +func (p *Buffer) EncodeMessage(pb Message) error { + t, base, err := getbase(pb) + if structPointer_IsNil(base) { + return ErrNil + } + if err == nil { + var state errorState + err = p.enc_len_struct(GetProperties(t.Elem()), base, &state) + } + return err +} + // Marshal takes the protocol buffer // and encodes it into the wire format, writing the result to the // Buffer. @@ -318,7 +337,7 @@ func size_bool(p *Properties, base structPointer) int { func size_proto3_bool(p *Properties, base structPointer) int { v := *structPointer_BoolVal(base, p.field) - if !v { + if !v && !p.oneof { return 0 } return len(p.tagcode) + 1 // each bool takes exactly one byte @@ -361,7 +380,7 @@ func size_int32(p *Properties, base structPointer) (n int) { func size_proto3_int32(p *Properties, base structPointer) (n int) { v := structPointer_Word32Val(base, p.field) x := int32(word32Val_Get(v)) // permit sign extension to use full 64-bit range - if x == 0 { + if x == 0 && !p.oneof { return 0 } n += len(p.tagcode) @@ -407,7 +426,7 @@ func size_uint32(p *Properties, base structPointer) (n int) { func size_proto3_uint32(p *Properties, base structPointer) (n int) { v := structPointer_Word32Val(base, p.field) x := word32Val_Get(v) - if x == 0 { + if x == 0 && !p.oneof { return 0 } n += len(p.tagcode) @@ -452,7 +471,7 @@ func size_int64(p *Properties, base structPointer) (n int) { func size_proto3_int64(p *Properties, base structPointer) (n int) { v := structPointer_Word64Val(base, p.field) x := word64Val_Get(v) - if x == 0 { + if x == 0 && !p.oneof { return 0 } n += len(p.tagcode) @@ -495,7 +514,7 @@ func size_string(p *Properties, base structPointer) (n int) { func size_proto3_string(p *Properties, base structPointer) (n int) { v := *structPointer_StringVal(base, p.field) - if v == "" { + if v == "" && !p.oneof { return 0 } n += len(p.tagcode) @@ -529,7 +548,7 @@ func (o *Buffer) enc_struct_message(p *Properties, base structPointer) error { } o.buf = append(o.buf, p.tagcode...) o.EncodeRawBytes(data) - return nil + return state.err } o.buf = append(o.buf, p.tagcode...) @@ -667,7 +686,7 @@ func (o *Buffer) enc_proto3_slice_byte(p *Properties, base structPointer) error func size_slice_byte(p *Properties, base structPointer) (n int) { s := *structPointer_Bytes(base, p.field) - if s == nil { + if s == nil && !p.oneof { return 0 } n += len(p.tagcode) @@ -677,7 +696,7 @@ func size_slice_byte(p *Properties, base structPointer) (n int) { func size_proto3_slice_byte(p *Properties, base structPointer) (n int) { s := *structPointer_Bytes(base, p.field) - if len(s) == 0 { + if len(s) == 0 && !p.oneof { return 0 } n += len(p.tagcode) @@ -939,7 +958,7 @@ func (o *Buffer) enc_slice_struct_message(p *Properties, base structPointer) err for i := 0; i < l; i++ { structp := s.Index(i) if structPointer_IsNil(structp) { - return ErrRepeatedHasNil + return errRepeatedHasNil } // Can the object marshal itself? @@ -958,7 +977,7 @@ func (o *Buffer) enc_slice_struct_message(p *Properties, base structPointer) err err := o.enc_len_struct(p.sprop, structp, &state) if err != nil && !state.shouldContinue(err, nil) { if err == ErrNil { - return ErrRepeatedHasNil + return errRepeatedHasNil } return err } @@ -1001,7 +1020,7 @@ func (o *Buffer) enc_slice_struct_group(p *Properties, base structPointer) error for i := 0; i < l; i++ { b := s.Index(i) if structPointer_IsNil(b) { - return ErrRepeatedHasNil + return errRepeatedHasNil } o.EncodeVarint(uint64((p.Tag << 3) | WireStartGroup)) @@ -1010,7 +1029,7 @@ func (o *Buffer) enc_slice_struct_group(p *Properties, base structPointer) error if err != nil && !state.shouldContinue(err, nil) { if err == ErrNil { - return ErrRepeatedHasNil + return errRepeatedHasNil } return err } @@ -1084,7 +1103,7 @@ func (o *Buffer) enc_new_map(p *Properties, base structPointer) error { repeated MapFieldEntry map_field = N; */ - v := structPointer_Map(base, p.field, p.mtype).Elem() // map[K]V + v := structPointer_NewAt(base, p.field, p.mtype).Elem() // map[K]V if v.Len() == 0 { return nil } @@ -1101,11 +1120,15 @@ func (o *Buffer) enc_new_map(p *Properties, base structPointer) error { return nil } - keys := v.MapKeys() - sort.Sort(mapKeys(keys)) - for _, key := range keys { + // Don't sort map keys. It is not required by the spec, and C++ doesn't do it. + for _, key := range v.MapKeys() { val := v.MapIndex(key) + // The only illegal map entry values are nil message pointers. + if val.Kind() == reflect.Ptr && val.IsNil() { + return errors.New("proto: map has nil element") + } + keycopy.Set(key) valcopy.Set(val) @@ -1118,7 +1141,7 @@ func (o *Buffer) enc_new_map(p *Properties, base structPointer) error { } func size_new_map(p *Properties, base structPointer) int { - v := structPointer_Map(base, p.field, p.mtype).Elem() // map[K]V + v := structPointer_NewAt(base, p.field, p.mtype).Elem() // map[K]V keycopy, valcopy, keybase, valbase := mapEncodeScratch(p.mtype) @@ -1128,10 +1151,12 @@ func size_new_map(p *Properties, base structPointer) int { keycopy.Set(key) valcopy.Set(val) - // Tag codes are two bytes per map entry. - n += 2 - n += p.mkeyprop.size(p.mkeyprop, keybase) - n += p.mvalprop.size(p.mvalprop, valbase) + // Tag codes for key and val are the responsibility of the sub-sizer. + keysize := p.mkeyprop.size(p.mkeyprop, keybase) + valsize := p.mvalprop.size(p.mvalprop, valbase) + entry := keysize + valsize + // Add on tag code and length of map entry itself. + n += len(p.tagcode) + sizeVarint(uint64(entry)) + entry } return n } @@ -1184,6 +1209,9 @@ func (o *Buffer) enc_struct(prop *StructProperties, base structPointer) error { if p.Required && state.err == nil { state.err = &RequiredNotSetError{p.Name} } + } else if err == errRepeatedHasNil { + // Give more context to nil values in repeated fields. + return errors.New("repeated field " + p.OrigName + " has nil element") } else if !state.shouldContinue(err, p) { return err } @@ -1191,6 +1219,14 @@ func (o *Buffer) enc_struct(prop *StructProperties, base structPointer) error { } } + // Do oneof fields. + if prop.oneofMarshaler != nil { + m := structPointer_Interface(base, prop.stype).(Message) + if err := prop.oneofMarshaler(m, o); err != nil { + return err + } + } + // Add unrecognized fields at the end. if prop.unrecField.IsValid() { v := *structPointer_Bytes(base, prop.unrecField) @@ -1216,6 +1252,12 @@ func size_struct(prop *StructProperties, base structPointer) (n int) { n += len(v) } + // Factor in any oneof fields. + if prop.oneofSizer != nil { + m := structPointer_Interface(base, prop.stype).(Message) + n += prop.oneofSizer(m) + } + return } diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal.go index d8673a3e9..f5db1def3 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal.go @@ -30,7 +30,6 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Protocol buffer comparison. -// TODO: MessageSet. package proto @@ -51,7 +50,9 @@ Equality is defined in this way: are equal, and extensions sets are equal. - Two set scalar fields are equal iff their values are equal. If the fields are of a floating-point type, remember that - NaN != x for all x, including NaN. + NaN != x for all x, including NaN. If the message is defined + in a proto3 .proto file, fields are not "set"; specifically, + zero length proto3 "bytes" fields are equal (nil == {}). - Two repeated fields are equal iff their lengths are the same, and their corresponding elements are equal (a "bytes" field, although represented by []byte, is not a repeated field) @@ -89,6 +90,7 @@ func Equal(a, b Message) bool { // v1 and v2 are known to have the same type. func equalStruct(v1, v2 reflect.Value) bool { + sprop := GetProperties(v1.Type()) for i := 0; i < v1.NumField(); i++ { f := v1.Type().Field(i) if strings.HasPrefix(f.Name, "XXX_") { @@ -114,7 +116,7 @@ func equalStruct(v1, v2 reflect.Value) bool { } f1, f2 = f1.Elem(), f2.Elem() } - if !equalAny(f1, f2) { + if !equalAny(f1, f2, sprop.Prop[i]) { return false } } @@ -141,7 +143,8 @@ func equalStruct(v1, v2 reflect.Value) bool { } // v1 and v2 are known to have the same type. -func equalAny(v1, v2 reflect.Value) bool { +// prop may be nil. +func equalAny(v1, v2 reflect.Value, prop *Properties) bool { if v1.Type() == protoMessageType { m1, _ := v1.Interface().(Message) m2, _ := v2.Interface().(Message) @@ -154,6 +157,17 @@ func equalAny(v1, v2 reflect.Value) bool { return v1.Float() == v2.Float() case reflect.Int32, reflect.Int64: return v1.Int() == v2.Int() + case reflect.Interface: + // Probably a oneof field; compare the inner values. + n1, n2 := v1.IsNil(), v2.IsNil() + if n1 || n2 { + return n1 == n2 + } + e1, e2 := v1.Elem(), v2.Elem() + if e1.Type() != e2.Type() { + return false + } + return equalAny(e1, e2, nil) case reflect.Map: if v1.Len() != v2.Len() { return false @@ -164,16 +178,22 @@ func equalAny(v1, v2 reflect.Value) bool { // This key was not found in the second map. return false } - if !equalAny(v1.MapIndex(key), val2) { + if !equalAny(v1.MapIndex(key), val2, nil) { return false } } return true case reflect.Ptr: - return equalAny(v1.Elem(), v2.Elem()) + return equalAny(v1.Elem(), v2.Elem(), prop) case reflect.Slice: if v1.Type().Elem().Kind() == reflect.Uint8 { // short circuit: []byte + + // Edge case: if this is in a proto3 message, a zero length + // bytes field is considered the zero value. + if prop != nil && prop.proto3 && v1.Len() == 0 && v2.Len() == 0 { + return true + } if v1.IsNil() != v2.IsNil() { return false } @@ -184,7 +204,7 @@ func equalAny(v1, v2 reflect.Value) bool { return false } for i := 0; i < v1.Len(); i++ { - if !equalAny(v1.Index(i), v2.Index(i)) { + if !equalAny(v1.Index(i), v2.Index(i), prop) { return false } } @@ -219,7 +239,7 @@ func equalExtensions(base reflect.Type, em1, em2 map[int32]Extension) bool { if m1 != nil && m2 != nil { // Both are unencoded. - if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2)) { + if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) { return false } continue @@ -247,7 +267,7 @@ func equalExtensions(base reflect.Type, em1, em2 map[int32]Extension) bool { log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err) return false } - if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2)) { + if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) { return false } } diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal_test.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal_test.go deleted file mode 100644 index cc25833ca..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal_test.go +++ /dev/null @@ -1,191 +0,0 @@ -// Go support for Protocol Buffers - Google's data interchange format -// -// Copyright 2011 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 proto_test - -import ( - "testing" - - pb "./testdata" - . "github.com/golang/protobuf/proto" -) - -// Four identical base messages. -// The init function adds extensions to some of them. -var messageWithoutExtension = &pb.MyMessage{Count: Int32(7)} -var messageWithExtension1a = &pb.MyMessage{Count: Int32(7)} -var messageWithExtension1b = &pb.MyMessage{Count: Int32(7)} -var messageWithExtension2 = &pb.MyMessage{Count: Int32(7)} - -// Two messages with non-message extensions. -var messageWithInt32Extension1 = &pb.MyMessage{Count: Int32(8)} -var messageWithInt32Extension2 = &pb.MyMessage{Count: Int32(8)} - -func init() { - ext1 := &pb.Ext{Data: String("Kirk")} - ext2 := &pb.Ext{Data: String("Picard")} - - // messageWithExtension1a has ext1, but never marshals it. - if err := SetExtension(messageWithExtension1a, pb.E_Ext_More, ext1); err != nil { - panic("SetExtension on 1a failed: " + err.Error()) - } - - // messageWithExtension1b is the unmarshaled form of messageWithExtension1a. - if err := SetExtension(messageWithExtension1b, pb.E_Ext_More, ext1); err != nil { - panic("SetExtension on 1b failed: " + err.Error()) - } - buf, err := Marshal(messageWithExtension1b) - if err != nil { - panic("Marshal of 1b failed: " + err.Error()) - } - messageWithExtension1b.Reset() - if err := Unmarshal(buf, messageWithExtension1b); err != nil { - panic("Unmarshal of 1b failed: " + err.Error()) - } - - // messageWithExtension2 has ext2. - if err := SetExtension(messageWithExtension2, pb.E_Ext_More, ext2); err != nil { - panic("SetExtension on 2 failed: " + err.Error()) - } - - if err := SetExtension(messageWithInt32Extension1, pb.E_Ext_Number, Int32(23)); err != nil { - panic("SetExtension on Int32-1 failed: " + err.Error()) - } - if err := SetExtension(messageWithInt32Extension1, pb.E_Ext_Number, Int32(24)); err != nil { - panic("SetExtension on Int32-2 failed: " + err.Error()) - } -} - -var EqualTests = []struct { - desc string - a, b Message - exp bool -}{ - {"different types", &pb.GoEnum{}, &pb.GoTestField{}, false}, - {"equal empty", &pb.GoEnum{}, &pb.GoEnum{}, true}, - {"nil vs nil", nil, nil, true}, - {"typed nil vs typed nil", (*pb.GoEnum)(nil), (*pb.GoEnum)(nil), true}, - {"typed nil vs empty", (*pb.GoEnum)(nil), &pb.GoEnum{}, false}, - {"different typed nil", (*pb.GoEnum)(nil), (*pb.GoTestField)(nil), false}, - - {"one set field, one unset field", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{}, false}, - {"one set field zero, one unset field", &pb.GoTest{Param: Int32(0)}, &pb.GoTest{}, false}, - {"different set fields", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{Label: String("bar")}, false}, - {"equal set", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{Label: String("foo")}, true}, - - {"repeated, one set", &pb.GoTest{F_Int32Repeated: []int32{2, 3}}, &pb.GoTest{}, false}, - {"repeated, different length", &pb.GoTest{F_Int32Repeated: []int32{2, 3}}, &pb.GoTest{F_Int32Repeated: []int32{2}}, false}, - {"repeated, different value", &pb.GoTest{F_Int32Repeated: []int32{2}}, &pb.GoTest{F_Int32Repeated: []int32{3}}, false}, - {"repeated, equal", &pb.GoTest{F_Int32Repeated: []int32{2, 4}}, &pb.GoTest{F_Int32Repeated: []int32{2, 4}}, true}, - {"repeated, nil equal nil", &pb.GoTest{F_Int32Repeated: nil}, &pb.GoTest{F_Int32Repeated: nil}, true}, - {"repeated, nil equal empty", &pb.GoTest{F_Int32Repeated: nil}, &pb.GoTest{F_Int32Repeated: []int32{}}, true}, - {"repeated, empty equal nil", &pb.GoTest{F_Int32Repeated: []int32{}}, &pb.GoTest{F_Int32Repeated: nil}, true}, - - { - "nested, different", - &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("foo")}}, - &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("bar")}}, - false, - }, - { - "nested, equal", - &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("wow")}}, - &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("wow")}}, - true, - }, - - {"bytes", &pb.OtherMessage{Value: []byte("foo")}, &pb.OtherMessage{Value: []byte("foo")}, true}, - {"bytes, empty", &pb.OtherMessage{Value: []byte{}}, &pb.OtherMessage{Value: []byte{}}, true}, - {"bytes, empty vs nil", &pb.OtherMessage{Value: []byte{}}, &pb.OtherMessage{Value: nil}, false}, - { - "repeated bytes", - &pb.MyMessage{RepBytes: [][]byte{[]byte("sham"), []byte("wow")}}, - &pb.MyMessage{RepBytes: [][]byte{[]byte("sham"), []byte("wow")}}, - true, - }, - - {"extension vs. no extension", messageWithoutExtension, messageWithExtension1a, false}, - {"extension vs. same extension", messageWithExtension1a, messageWithExtension1b, true}, - {"extension vs. different extension", messageWithExtension1a, messageWithExtension2, false}, - - {"int32 extension vs. itself", messageWithInt32Extension1, messageWithInt32Extension1, true}, - {"int32 extension vs. a different int32", messageWithInt32Extension1, messageWithInt32Extension2, false}, - - { - "message with group", - &pb.MyMessage{ - Count: Int32(1), - Somegroup: &pb.MyMessage_SomeGroup{ - GroupField: Int32(5), - }, - }, - &pb.MyMessage{ - Count: Int32(1), - Somegroup: &pb.MyMessage_SomeGroup{ - GroupField: Int32(5), - }, - }, - true, - }, - - { - "map same", - &pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}}, - &pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}}, - true, - }, - { - "map different entry", - &pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}}, - &pb.MessageWithMap{NameMapping: map[int32]string{2: "Rob"}}, - false, - }, - { - "map different key only", - &pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}}, - &pb.MessageWithMap{NameMapping: map[int32]string{2: "Ken"}}, - false, - }, - { - "map different value only", - &pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}}, - &pb.MessageWithMap{NameMapping: map[int32]string{1: "Rob"}}, - false, - }, -} - -func TestEqual(t *testing.T) { - for _, tc := range EqualTests { - if res := Equal(tc.a, tc.b); res != tc.exp { - t.Errorf("%v: Equal(%v, %v) = %v, want %v", tc.desc, tc.a, tc.b, res, tc.exp) - } - } -} diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions.go index f7667fab4..054f4f1df 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions.go @@ -37,6 +37,7 @@ package proto import ( "errors" + "fmt" "reflect" "strconv" "sync" @@ -221,7 +222,7 @@ func ClearExtension(pb extendableProto, extension *ExtensionDesc) { } // GetExtension parses and returns the given extension of pb. -// If the extension is not present it returns ErrMissingExtension. +// If the extension is not present and has no default value it returns ErrMissingExtension. func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, error) { if err := checkExtensionTypes(pb, extension); err != nil { return nil, err @@ -230,8 +231,11 @@ func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, er emap := pb.ExtensionMap() e, ok := emap[extension.Field] if !ok { - return nil, ErrMissingExtension + // defaultExtensionValue returns the default value or + // ErrMissingExtension if there is no default. + return defaultExtensionValue(extension) } + if e.value != nil { // Already decoded. Check the descriptor, though. if e.desc != extension { @@ -257,12 +261,46 @@ func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, er return e.value, nil } +// defaultExtensionValue returns the default value for extension. +// If no default for an extension is defined ErrMissingExtension is returned. +func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) { + t := reflect.TypeOf(extension.ExtensionType) + props := extensionProperties(extension) + + sf, _, err := fieldDefault(t, props) + if err != nil { + return nil, err + } + + if sf == nil || sf.value == nil { + // There is no default value. + return nil, ErrMissingExtension + } + + if t.Kind() != reflect.Ptr { + // We do not need to return a Ptr, we can directly return sf.value. + return sf.value, nil + } + + // We need to return an interface{} that is a pointer to sf.value. + value := reflect.New(t).Elem() + value.Set(reflect.New(value.Type().Elem())) + if sf.kind == reflect.Int32 { + // We may have an int32 or an enum, but the underlying data is int32. + // Since we can't set an int32 into a non int32 reflect.value directly + // set it as a int32. + value.Elem().SetInt(int64(sf.value.(int32))) + } else { + value.Elem().Set(reflect.ValueOf(sf.value)) + } + return value.Interface(), nil +} + // decodeExtension decodes an extension encoded in b. func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) { o := NewBuffer(b) t := reflect.TypeOf(extension.ExtensionType) - rep := extension.repeated() props := extensionProperties(extension) @@ -284,7 +322,7 @@ func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) { return nil, err } - if !rep || o.index >= len(o.buf) { + if o.index >= len(o.buf) { break } } @@ -321,6 +359,14 @@ func SetExtension(pb extendableProto, extension *ExtensionDesc, value interface{ if typ != reflect.TypeOf(value) { return errors.New("proto: bad extension value type") } + // nil extension values need to be caught early, because the + // encoder can't distinguish an ErrNil due to a nil extension + // from an ErrNil due to a missing field. Extensions are + // always optional, so the encoder would just swallow the error + // and drop all the extensions from the encoded message. + if reflect.ValueOf(value).IsNil() { + return fmt.Errorf("proto: SetExtension called with nil value of type %T", value) + } pb.ExtensionMap()[extension.Field] = Extension{desc: extension, value: value} return nil diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions_test.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions_test.go deleted file mode 100644 index 451ad871a..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions_test.go +++ /dev/null @@ -1,137 +0,0 @@ -// Go support for Protocol Buffers - Google's data interchange format -// -// Copyright 2014 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 proto_test - -import ( - "testing" - - pb "./testdata" - "github.com/golang/protobuf/proto" -) - -func TestGetExtensionsWithMissingExtensions(t *testing.T) { - msg := &pb.MyMessage{} - ext1 := &pb.Ext{} - if err := proto.SetExtension(msg, pb.E_Ext_More, ext1); err != nil { - t.Fatalf("Could not set ext1: %s", ext1) - } - exts, err := proto.GetExtensions(msg, []*proto.ExtensionDesc{ - pb.E_Ext_More, - pb.E_Ext_Text, - }) - if err != nil { - t.Fatalf("GetExtensions() failed: %s", err) - } - if exts[0] != ext1 { - t.Errorf("ext1 not in returned extensions: %T %v", exts[0], exts[0]) - } - if exts[1] != nil { - t.Errorf("ext2 in returned extensions: %T %v", exts[1], exts[1]) - } -} - -func TestGetExtensionStability(t *testing.T) { - check := func(m *pb.MyMessage) bool { - ext1, err := proto.GetExtension(m, pb.E_Ext_More) - if err != nil { - t.Fatalf("GetExtension() failed: %s", err) - } - ext2, err := proto.GetExtension(m, pb.E_Ext_More) - if err != nil { - t.Fatalf("GetExtension() failed: %s", err) - } - return ext1 == ext2 - } - msg := &pb.MyMessage{Count: proto.Int32(4)} - ext0 := &pb.Ext{} - if err := proto.SetExtension(msg, pb.E_Ext_More, ext0); err != nil { - t.Fatalf("Could not set ext1: %s", ext0) - } - if !check(msg) { - t.Errorf("GetExtension() not stable before marshaling") - } - bb, err := proto.Marshal(msg) - if err != nil { - t.Fatalf("Marshal() failed: %s", err) - } - msg1 := &pb.MyMessage{} - err = proto.Unmarshal(bb, msg1) - if err != nil { - t.Fatalf("Unmarshal() failed: %s", err) - } - if !check(msg1) { - t.Errorf("GetExtension() not stable after unmarshaling") - } -} - -func TestExtensionsRoundTrip(t *testing.T) { - msg := &pb.MyMessage{} - ext1 := &pb.Ext{ - Data: proto.String("hi"), - } - ext2 := &pb.Ext{ - Data: proto.String("there"), - } - exists := proto.HasExtension(msg, pb.E_Ext_More) - if exists { - t.Error("Extension More present unexpectedly") - } - if err := proto.SetExtension(msg, pb.E_Ext_More, ext1); err != nil { - t.Error(err) - } - if err := proto.SetExtension(msg, pb.E_Ext_More, ext2); err != nil { - t.Error(err) - } - e, err := proto.GetExtension(msg, pb.E_Ext_More) - if err != nil { - t.Error(err) - } - x, ok := e.(*pb.Ext) - if !ok { - t.Errorf("e has type %T, expected testdata.Ext", e) - } else if *x.Data != "there" { - t.Errorf("SetExtension failed to overwrite, got %+v, not 'there'", x) - } - proto.ClearExtension(msg, pb.E_Ext_More) - if _, err = proto.GetExtension(msg, pb.E_Ext_More); err != proto.ErrMissingExtension { - t.Errorf("got %v, expected ErrMissingExtension", e) - } - if _, err := proto.GetExtension(msg, pb.E_X215); err == nil { - t.Error("expected bad extension error, got nil") - } - if err := proto.SetExtension(msg, pb.E_X215, 12); err == nil { - t.Error("expected extension err") - } - if err := proto.SetExtension(msg, pb.E_Ext_More, 12); err == nil { - t.Error("expected some sort of type mismatch error, got nil") - } -} diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/lib.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/lib.go index 87c6b9d1a..0de8f8dff 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/lib.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/lib.go @@ -30,171 +30,237 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* - Package proto converts data structures to and from the wire format of - protocol buffers. It works in concert with the Go source code generated - for .proto files by the protocol compiler. +Package proto converts data structures to and from the wire format of +protocol buffers. It works in concert with the Go source code generated +for .proto files by the protocol compiler. - A summary of the properties of the protocol buffer interface - for a protocol buffer variable v: +A summary of the properties of the protocol buffer interface +for a protocol buffer variable v: - - Names are turned from camel_case to CamelCase for export. - - There are no methods on v to set fields; just treat - them as structure fields. - - There are getters that return a field's value if set, - and return the field's default value if unset. - The getters work even if the receiver is a nil message. - - The zero value for a struct is its correct initialization state. - All desired fields must be set before marshaling. - - A Reset() method will restore a protobuf struct to its zero state. - - Non-repeated fields are pointers to the values; nil means unset. - That is, optional or required field int32 f becomes F *int32. - - Repeated fields are slices. - - Helper functions are available to aid the setting of fields. - Helpers for getting values are superseded by the - GetFoo methods and their use is deprecated. - msg.Foo = proto.String("hello") // set field - - Constants are defined to hold the default values of all fields that - have them. They have the form Default_StructName_FieldName. - Because the getter methods handle defaulted values, - direct use of these constants should be rare. - - Enums are given type names and maps from names to values. - Enum values are prefixed with the enum's type name. Enum types have - a String method, and a Enum method to assist in message construction. - - Nested groups and enums have type names prefixed with the name of - the surrounding message type. - - Extensions are given descriptor names that start with E_, - followed by an underscore-delimited list of the nested messages - that contain it (if any) followed by the CamelCased name of the - extension field itself. HasExtension, ClearExtension, GetExtension - and SetExtension are functions for manipulating extensions. - - Marshal and Unmarshal are functions to encode and decode the wire format. + - Names are turned from camel_case to CamelCase for export. + - There are no methods on v to set fields; just treat + them as structure fields. + - There are getters that return a field's value if set, + and return the field's default value if unset. + The getters work even if the receiver is a nil message. + - The zero value for a struct is its correct initialization state. + All desired fields must be set before marshaling. + - A Reset() method will restore a protobuf struct to its zero state. + - Non-repeated fields are pointers to the values; nil means unset. + That is, optional or required field int32 f becomes F *int32. + - Repeated fields are slices. + - Helper functions are available to aid the setting of fields. + msg.Foo = proto.String("hello") // set field + - Constants are defined to hold the default values of all fields that + have them. They have the form Default_StructName_FieldName. + Because the getter methods handle defaulted values, + direct use of these constants should be rare. + - Enums are given type names and maps from names to values. + Enum values are prefixed by the enclosing message's name, or by the + enum's type name if it is a top-level enum. Enum types have a String + method, and a Enum method to assist in message construction. + - Nested messages, groups and enums have type names prefixed with the name of + the surrounding message type. + - Extensions are given descriptor names that start with E_, + followed by an underscore-delimited list of the nested messages + that contain it (if any) followed by the CamelCased name of the + extension field itself. HasExtension, ClearExtension, GetExtension + and SetExtension are functions for manipulating extensions. + - Oneof field sets are given a single field in their message, + with distinguished wrapper types for each possible field value. + - Marshal and Unmarshal are functions to encode and decode the wire format. - The simplest way to describe this is to see an example. - Given file test.proto, containing +When the .proto file specifies `syntax="proto3"`, there are some differences: - package example; + - Non-repeated fields of non-message type are values instead of pointers. + - Getters are only generated for message and oneof fields. + - Enum types do not get an Enum method. - enum FOO { X = 17; }; +The simplest way to describe this is to see an example. +Given file test.proto, containing - message Test { - required string label = 1; - optional int32 type = 2 [default=77]; - repeated int64 reps = 3; - optional group OptionalGroup = 4 { - required string RequiredField = 5; - } + package example; + + enum FOO { X = 17; } + + message Test { + required string label = 1; + optional int32 type = 2 [default=77]; + repeated int64 reps = 3; + optional group OptionalGroup = 4 { + required string RequiredField = 5; + } + oneof union { + int32 number = 6; + string name = 7; + } + } + +The resulting file, test.pb.go, is: + + package example + + import proto "github.com/golang/protobuf/proto" + import math "math" + + type FOO int32 + const ( + FOO_X FOO = 17 + ) + var FOO_name = map[int32]string{ + 17: "X", + } + var FOO_value = map[string]int32{ + "X": 17, + } + + func (x FOO) Enum() *FOO { + p := new(FOO) + *p = x + return p + } + func (x FOO) String() string { + return proto.EnumName(FOO_name, int32(x)) + } + func (x *FOO) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(FOO_value, data) + if err != nil { + return err } + *x = FOO(value) + return nil + } - The resulting file, test.pb.go, is: + type Test struct { + Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"` + Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"` + Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"` + Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"` + // Types that are valid to be assigned to Union: + // *Test_Number + // *Test_Name + Union isTest_Union `protobuf_oneof:"union"` + XXX_unrecognized []byte `json:"-"` + } + func (m *Test) Reset() { *m = Test{} } + func (m *Test) String() string { return proto.CompactTextString(m) } + func (*Test) ProtoMessage() {} - package example + type isTest_Union interface { + isTest_Union() + } - import "github.com/golang/protobuf/proto" + type Test_Number struct { + Number int32 `protobuf:"varint,6,opt,name=number"` + } + type Test_Name struct { + Name string `protobuf:"bytes,7,opt,name=name"` + } - type FOO int32 - const ( - FOO_X FOO = 17 - ) - var FOO_name = map[int32]string{ - 17: "X", + func (*Test_Number) isTest_Union() {} + func (*Test_Name) isTest_Union() {} + + func (m *Test) GetUnion() isTest_Union { + if m != nil { + return m.Union } - var FOO_value = map[string]int32{ - "X": 17, + return nil + } + const Default_Test_Type int32 = 77 + + func (m *Test) GetLabel() string { + if m != nil && m.Label != nil { + return *m.Label } + return "" + } - func (x FOO) Enum() *FOO { - p := new(FOO) - *p = x - return p + func (m *Test) GetType() int32 { + if m != nil && m.Type != nil { + return *m.Type } - func (x FOO) String() string { - return proto.EnumName(FOO_name, int32(x)) + return Default_Test_Type + } + + func (m *Test) GetOptionalgroup() *Test_OptionalGroup { + if m != nil { + return m.Optionalgroup } + return nil + } - type Test struct { - Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"` - Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"` - Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"` - Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"` - XXX_unrecognized []byte `json:"-"` + type Test_OptionalGroup struct { + RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"` + } + func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} } + func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) } + + func (m *Test_OptionalGroup) GetRequiredField() string { + if m != nil && m.RequiredField != nil { + return *m.RequiredField } - func (this *Test) Reset() { *this = Test{} } - func (this *Test) String() string { return proto.CompactTextString(this) } - const Default_Test_Type int32 = 77 + return "" + } - func (this *Test) GetLabel() string { - if this != nil && this.Label != nil { - return *this.Label - } - return "" + func (m *Test) GetNumber() int32 { + if x, ok := m.GetUnion().(*Test_Number); ok { + return x.Number } + return 0 + } - func (this *Test) GetType() int32 { - if this != nil && this.Type != nil { - return *this.Type - } - return Default_Test_Type + func (m *Test) GetName() string { + if x, ok := m.GetUnion().(*Test_Name); ok { + return x.Name } + return "" + } - func (this *Test) GetOptionalgroup() *Test_OptionalGroup { - if this != nil { - return this.Optionalgroup - } - return nil + func init() { + proto.RegisterEnum("example.FOO", FOO_name, FOO_value) + } + +To create and play with a Test object: + + package main + + import ( + "log" + + "github.com/golang/protobuf/proto" + pb "./example.pb" + ) + + func main() { + test := &pb.Test{ + Label: proto.String("hello"), + Type: proto.Int32(17), + Reps: []int64{1, 2, 3}, + Optionalgroup: &pb.Test_OptionalGroup{ + RequiredField: proto.String("good bye"), + }, + Union: &pb.Test_Name{"fred"}, } - - type Test_OptionalGroup struct { - RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"` - XXX_unrecognized []byte `json:"-"` + data, err := proto.Marshal(test) + if err != nil { + log.Fatal("marshaling error: ", err) } - func (this *Test_OptionalGroup) Reset() { *this = Test_OptionalGroup{} } - func (this *Test_OptionalGroup) String() string { return proto.CompactTextString(this) } - - func (this *Test_OptionalGroup) GetRequiredField() string { - if this != nil && this.RequiredField != nil { - return *this.RequiredField - } - return "" + newTest := &pb.Test{} + err = proto.Unmarshal(data, newTest) + if err != nil { + log.Fatal("unmarshaling error: ", err) } - - func init() { - proto.RegisterEnum("example.FOO", FOO_name, FOO_value) + // Now test and newTest contain the same data. + if test.GetLabel() != newTest.GetLabel() { + log.Fatalf("data mismatch %q != %q", test.GetLabel(), newTest.GetLabel()) } - - To create and play with a Test object: - - package main - - import ( - "log" - - "github.com/golang/protobuf/proto" - "./example.pb" - ) - - func main() { - test := &example.Test{ - Label: proto.String("hello"), - Type: proto.Int32(17), - Optionalgroup: &example.Test_OptionalGroup{ - RequiredField: proto.String("good bye"), - }, - } - data, err := proto.Marshal(test) - if err != nil { - log.Fatal("marshaling error: ", err) - } - newTest := new(example.Test) - err = proto.Unmarshal(data, newTest) - if err != nil { - log.Fatal("unmarshaling error: ", err) - } - // Now test and newTest contain the same data. - if test.GetLabel() != newTest.GetLabel() { - log.Fatalf("data mismatch %q != %q", test.GetLabel(), newTest.GetLabel()) - } - // etc. + // Use a type switch to determine which oneof was set. + switch u := test.Union.(type) { + case *pb.Test_Number: // u.Number contains the number. + case *pb.Test_Name: // u.Name contains the string. } + // etc. + } */ package proto @@ -203,6 +269,7 @@ import ( "fmt" "log" "reflect" + "sort" "strconv" "sync" ) @@ -377,13 +444,13 @@ func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, // DebugPrint dumps the encoded data in b in a debugging format with a header // including the string s. Used in testing but made available for general debugging. -func (o *Buffer) DebugPrint(s string, b []byte) { +func (p *Buffer) DebugPrint(s string, b []byte) { var u uint64 - obuf := o.buf - index := o.index - o.buf = b - o.index = 0 + obuf := p.buf + index := p.index + p.buf = b + p.index = 0 depth := 0 fmt.Printf("\n--- %s ---\n", s) @@ -394,12 +461,12 @@ out: fmt.Print(" ") } - index := o.index - if index == len(o.buf) { + index := p.index + if index == len(p.buf) { break } - op, err := o.DecodeVarint() + op, err := p.DecodeVarint() if err != nil { fmt.Printf("%3d: fetching op err %v\n", index, err) break out @@ -416,7 +483,7 @@ out: case WireBytes: var r []byte - r, err = o.DecodeRawBytes(false) + r, err = p.DecodeRawBytes(false) if err != nil { break out } @@ -437,7 +504,7 @@ out: fmt.Printf("\n") case WireFixed32: - u, err = o.DecodeFixed32() + u, err = p.DecodeFixed32() if err != nil { fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err) break out @@ -445,16 +512,15 @@ out: fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u) case WireFixed64: - u, err = o.DecodeFixed64() + u, err = p.DecodeFixed64() if err != nil { fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err) break out } fmt.Printf("%3d: t=%3d fix64 %d\n", index, tag, u) - break case WireVarint: - u, err = o.DecodeVarint() + u, err = p.DecodeVarint() if err != nil { fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err) break out @@ -462,30 +528,22 @@ out: fmt.Printf("%3d: t=%3d varint %d\n", index, tag, u) case WireStartGroup: - if err != nil { - fmt.Printf("%3d: t=%3d start err %v\n", index, tag, err) - break out - } fmt.Printf("%3d: t=%3d start\n", index, tag) depth++ case WireEndGroup: depth-- - if err != nil { - fmt.Printf("%3d: t=%3d end err %v\n", index, tag, err) - break out - } fmt.Printf("%3d: t=%3d end\n", index, tag) } } if depth != 0 { - fmt.Printf("%3d: start-end not balanced %d\n", o.index, depth) + fmt.Printf("%3d: start-end not balanced %d\n", p.index, depth) } fmt.Printf("\n") - o.buf = obuf - o.index = index + p.buf = obuf + p.index = index } // SetDefaults sets unset protocol buffer fields to their default values. @@ -599,13 +657,15 @@ func setDefaults(v reflect.Value, recur, zeros bool) { for _, ni := range dm.nested { f := v.Field(ni) - if f.IsNil() { - continue - } - // f is *T or []*T - if f.Kind() == reflect.Ptr { + // f is *T or []*T or map[T]*T + switch f.Kind() { + case reflect.Ptr: + if f.IsNil() { + continue + } setDefaults(f, recur, zeros) - } else { + + case reflect.Slice: for i := 0; i < f.Len(); i++ { e := f.Index(i) if e.IsNil() { @@ -613,6 +673,15 @@ func setDefaults(v reflect.Value, recur, zeros bool) { } setDefaults(e, recur, zeros) } + + case reflect.Map: + for _, k := range f.MapKeys() { + e := f.MapIndex(k) + if e.IsNil() { + continue + } + setDefaults(e, recur, zeros) + } } } } @@ -638,10 +707,6 @@ type scalarField struct { value interface{} // the proto-declared default value, or nil } -func ptrToStruct(t reflect.Type) bool { - return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct -} - // t is a struct type. func buildDefaultMessage(t reflect.Type) (dm defaultMessage) { sprop := GetProperties(t) @@ -653,99 +718,177 @@ func buildDefaultMessage(t reflect.Type) (dm defaultMessage) { } ft := t.Field(fi).Type - // nested messages - if ptrToStruct(ft) || (ft.Kind() == reflect.Slice && ptrToStruct(ft.Elem())) { + sf, nested, err := fieldDefault(ft, prop) + switch { + case err != nil: + log.Print(err) + case nested: dm.nested = append(dm.nested, fi) - continue + case sf != nil: + sf.index = fi + dm.scalars = append(dm.scalars, *sf) } - - sf := scalarField{ - index: fi, - kind: ft.Elem().Kind(), - } - - // scalar fields without defaults - if !prop.HasDefault { - dm.scalars = append(dm.scalars, sf) - continue - } - - // a scalar field: either *T or []byte - switch ft.Elem().Kind() { - case reflect.Bool: - x, err := strconv.ParseBool(prop.Default) - if err != nil { - log.Printf("proto: bad default bool %q: %v", prop.Default, err) - continue - } - sf.value = x - case reflect.Float32: - x, err := strconv.ParseFloat(prop.Default, 32) - if err != nil { - log.Printf("proto: bad default float32 %q: %v", prop.Default, err) - continue - } - sf.value = float32(x) - case reflect.Float64: - x, err := strconv.ParseFloat(prop.Default, 64) - if err != nil { - log.Printf("proto: bad default float64 %q: %v", prop.Default, err) - continue - } - sf.value = x - case reflect.Int32: - x, err := strconv.ParseInt(prop.Default, 10, 32) - if err != nil { - log.Printf("proto: bad default int32 %q: %v", prop.Default, err) - continue - } - sf.value = int32(x) - case reflect.Int64: - x, err := strconv.ParseInt(prop.Default, 10, 64) - if err != nil { - log.Printf("proto: bad default int64 %q: %v", prop.Default, err) - continue - } - sf.value = x - case reflect.String: - sf.value = prop.Default - case reflect.Uint8: - // []byte (not *uint8) - sf.value = []byte(prop.Default) - case reflect.Uint32: - x, err := strconv.ParseUint(prop.Default, 10, 32) - if err != nil { - log.Printf("proto: bad default uint32 %q: %v", prop.Default, err) - continue - } - sf.value = uint32(x) - case reflect.Uint64: - x, err := strconv.ParseUint(prop.Default, 10, 64) - if err != nil { - log.Printf("proto: bad default uint64 %q: %v", prop.Default, err) - continue - } - sf.value = x - default: - log.Printf("proto: unhandled def kind %v", ft.Elem().Kind()) - continue - } - - dm.scalars = append(dm.scalars, sf) } return dm } +// fieldDefault returns the scalarField for field type ft. +// sf will be nil if the field can not have a default. +// nestedMessage will be true if this is a nested message. +// Note that sf.index is not set on return. +func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) { + var canHaveDefault bool + switch ft.Kind() { + case reflect.Ptr: + if ft.Elem().Kind() == reflect.Struct { + nestedMessage = true + } else { + canHaveDefault = true // proto2 scalar field + } + + case reflect.Slice: + switch ft.Elem().Kind() { + case reflect.Ptr: + nestedMessage = true // repeated message + case reflect.Uint8: + canHaveDefault = true // bytes field + } + + case reflect.Map: + if ft.Elem().Kind() == reflect.Ptr { + nestedMessage = true // map with message values + } + } + + if !canHaveDefault { + if nestedMessage { + return nil, true, nil + } + return nil, false, nil + } + + // We now know that ft is a pointer or slice. + sf = &scalarField{kind: ft.Elem().Kind()} + + // scalar fields without defaults + if !prop.HasDefault { + return sf, false, nil + } + + // a scalar field: either *T or []byte + switch ft.Elem().Kind() { + case reflect.Bool: + x, err := strconv.ParseBool(prop.Default) + if err != nil { + return nil, false, fmt.Errorf("proto: bad default bool %q: %v", prop.Default, err) + } + sf.value = x + case reflect.Float32: + x, err := strconv.ParseFloat(prop.Default, 32) + if err != nil { + return nil, false, fmt.Errorf("proto: bad default float32 %q: %v", prop.Default, err) + } + sf.value = float32(x) + case reflect.Float64: + x, err := strconv.ParseFloat(prop.Default, 64) + if err != nil { + return nil, false, fmt.Errorf("proto: bad default float64 %q: %v", prop.Default, err) + } + sf.value = x + case reflect.Int32: + x, err := strconv.ParseInt(prop.Default, 10, 32) + if err != nil { + return nil, false, fmt.Errorf("proto: bad default int32 %q: %v", prop.Default, err) + } + sf.value = int32(x) + case reflect.Int64: + x, err := strconv.ParseInt(prop.Default, 10, 64) + if err != nil { + return nil, false, fmt.Errorf("proto: bad default int64 %q: %v", prop.Default, err) + } + sf.value = x + case reflect.String: + sf.value = prop.Default + case reflect.Uint8: + // []byte (not *uint8) + sf.value = []byte(prop.Default) + case reflect.Uint32: + x, err := strconv.ParseUint(prop.Default, 10, 32) + if err != nil { + return nil, false, fmt.Errorf("proto: bad default uint32 %q: %v", prop.Default, err) + } + sf.value = uint32(x) + case reflect.Uint64: + x, err := strconv.ParseUint(prop.Default, 10, 64) + if err != nil { + return nil, false, fmt.Errorf("proto: bad default uint64 %q: %v", prop.Default, err) + } + sf.value = x + default: + return nil, false, fmt.Errorf("proto: unhandled def kind %v", ft.Elem().Kind()) + } + + return sf, false, nil +} + // Map fields may have key types of non-float scalars, strings and enums. // The easiest way to sort them in some deterministic order is to use fmt. // If this turns out to be inefficient we can always consider other options, // such as doing a Schwartzian transform. -type mapKeys []reflect.Value +func mapKeys(vs []reflect.Value) sort.Interface { + s := mapKeySorter{ + vs: vs, + // default Less function: textual comparison + less: func(a, b reflect.Value) bool { + return fmt.Sprint(a.Interface()) < fmt.Sprint(b.Interface()) + }, + } -func (s mapKeys) Len() int { return len(s) } -func (s mapKeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s mapKeys) Less(i, j int) bool { - return fmt.Sprint(s[i].Interface()) < fmt.Sprint(s[j].Interface()) + // Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps; + // numeric keys are sorted numerically. + if len(vs) == 0 { + return s + } + switch vs[0].Kind() { + case reflect.Int32, reflect.Int64: + s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() } + case reflect.Uint32, reflect.Uint64: + s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() } + } + + return s } + +type mapKeySorter struct { + vs []reflect.Value + less func(a, b reflect.Value) bool +} + +func (s mapKeySorter) Len() int { return len(s.vs) } +func (s mapKeySorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] } +func (s mapKeySorter) Less(i, j int) bool { + return s.less(s.vs[i], s.vs[j]) +} + +// isProto3Zero reports whether v is a zero proto3 value. +func isProto3Zero(v reflect.Value) bool { + switch v.Kind() { + case reflect.Bool: + return !v.Bool() + case reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint32, reflect.Uint64: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.String: + return v.String() == "" + } + return false +} + +// ProtoPackageIsVersion1 is referenced from generated protocol buffer files +// to assert that that code is compatible with this version of the proto package. +const ProtoPackageIsVersion1 = true diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set.go index 9d912bce1..e25e01e63 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set.go @@ -44,11 +44,11 @@ import ( "sort" ) -// ErrNoMessageTypeId occurs when a protocol buffer does not have a message type ID. +// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID. // A message type ID is required for storing a protocol buffer in a message set. -var ErrNoMessageTypeId = errors.New("proto does not have a message type ID") +var errNoMessageTypeID = errors.New("proto does not have a message type ID") -// The first two types (_MessageSet_Item and MessageSet) +// The first two types (_MessageSet_Item and messageSet) // model what the protocol compiler produces for the following protocol message: // message MessageSet { // repeated group Item = 1 { @@ -58,27 +58,20 @@ var ErrNoMessageTypeId = errors.New("proto does not have a message type ID") // } // That is the MessageSet wire format. We can't use a proto to generate these // because that would introduce a circular dependency between it and this package. -// -// When a proto1 proto has a field that looks like: -// optional message info = 3; -// the protocol compiler produces a field in the generated struct that looks like: -// Info *_proto_.MessageSet `protobuf:"bytes,3,opt,name=info"` -// The package is automatically inserted so there is no need for that proto file to -// import this package. type _MessageSet_Item struct { TypeId *int32 `protobuf:"varint,2,req,name=type_id"` Message []byte `protobuf:"bytes,3,req,name=message"` } -type MessageSet struct { +type messageSet struct { Item []*_MessageSet_Item `protobuf:"group,1,rep"` XXX_unrecognized []byte // TODO: caching? } -// Make sure MessageSet is a Message. -var _ Message = (*MessageSet)(nil) +// Make sure messageSet is a Message. +var _ Message = (*messageSet)(nil) // messageTypeIder is an interface satisfied by a protocol buffer type // that may be stored in a MessageSet. @@ -86,7 +79,7 @@ type messageTypeIder interface { MessageTypeId() int32 } -func (ms *MessageSet) find(pb Message) *_MessageSet_Item { +func (ms *messageSet) find(pb Message) *_MessageSet_Item { mti, ok := pb.(messageTypeIder) if !ok { return nil @@ -100,24 +93,24 @@ func (ms *MessageSet) find(pb Message) *_MessageSet_Item { return nil } -func (ms *MessageSet) Has(pb Message) bool { +func (ms *messageSet) Has(pb Message) bool { if ms.find(pb) != nil { return true } return false } -func (ms *MessageSet) Unmarshal(pb Message) error { +func (ms *messageSet) Unmarshal(pb Message) error { if item := ms.find(pb); item != nil { return Unmarshal(item.Message, pb) } if _, ok := pb.(messageTypeIder); !ok { - return ErrNoMessageTypeId + return errNoMessageTypeID } return nil // TODO: return error instead? } -func (ms *MessageSet) Marshal(pb Message) error { +func (ms *messageSet) Marshal(pb Message) error { msg, err := Marshal(pb) if err != nil { return err @@ -130,7 +123,7 @@ func (ms *MessageSet) Marshal(pb Message) error { mti, ok := pb.(messageTypeIder) if !ok { - return ErrNoMessageTypeId + return errNoMessageTypeID } mtid := mti.MessageTypeId() @@ -141,9 +134,9 @@ func (ms *MessageSet) Marshal(pb Message) error { return nil } -func (ms *MessageSet) Reset() { *ms = MessageSet{} } -func (ms *MessageSet) String() string { return CompactTextString(ms) } -func (*MessageSet) ProtoMessage() {} +func (ms *messageSet) Reset() { *ms = messageSet{} } +func (ms *messageSet) String() string { return CompactTextString(ms) } +func (*messageSet) ProtoMessage() {} // Support for the message_set_wire_format message option. @@ -169,7 +162,7 @@ func MarshalMessageSet(m map[int32]Extension) ([]byte, error) { } sort.Ints(ids) - ms := &MessageSet{Item: make([]*_MessageSet_Item, 0, len(m))} + ms := &messageSet{Item: make([]*_MessageSet_Item, 0, len(m))} for _, id := range ids { e := m[int32(id)] // Remove the wire type and field number varint, as well as the length varint. @@ -186,7 +179,7 @@ func MarshalMessageSet(m map[int32]Extension) ([]byte, error) { // UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format. // It is called by generated Unmarshal methods on protocol buffer messages with the message_set_wire_format option. func UnmarshalMessageSet(buf []byte, m map[int32]Extension) error { - ms := new(MessageSet) + ms := new(messageSet) if err := Unmarshal(buf, ms); err != nil { return err } diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set_test.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set_test.go deleted file mode 100644 index 7c29bccf4..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Go support for Protocol Buffers - Google's data interchange format -// -// Copyright 2014 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 proto - -import ( - "bytes" - "testing" -) - -func TestUnmarshalMessageSetWithDuplicate(t *testing.T) { - // Check that a repeated message set entry will be concatenated. - in := &MessageSet{ - Item: []*_MessageSet_Item{ - {TypeId: Int32(12345), Message: []byte("hoo")}, - {TypeId: Int32(12345), Message: []byte("hah")}, - }, - } - b, err := Marshal(in) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - t.Logf("Marshaled bytes: %q", b) - - m := make(map[int32]Extension) - if err := UnmarshalMessageSet(b, m); err != nil { - t.Fatalf("UnmarshalMessageSet: %v", err) - } - ext, ok := m[12345] - if !ok { - t.Fatalf("Didn't retrieve extension 12345; map is %v", m) - } - // Skip wire type/field number and length varints. - got := skipVarint(skipVarint(ext.enc)) - if want := []byte("hoohah"); !bytes.Equal(got, want) { - t.Errorf("Combined extension is %q, want %q", got, want) - } -} diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_reflect.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_reflect.go index 93259a3d6..749919d25 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_reflect.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_reflect.go @@ -29,7 +29,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// +build appengine,!appenginevm +// +build appengine // This file contains an implementation of proto field accesses using package reflect. // It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can @@ -144,8 +144,8 @@ func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension { return structPointer_ifield(p, f).(*map[int32]Extension) } -// Map returns the reflect.Value for the address of a map field in the struct. -func structPointer_Map(p structPointer, f field, typ reflect.Type) reflect.Value { +// NewAt returns the reflect.Value for a pointer to a field in the struct. +func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value { return structPointer_field(p, f).Addr() } diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_unsafe.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_unsafe.go index c52db1ca5..e9be0fe92 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_unsafe.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_unsafe.go @@ -29,7 +29,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// +build !appengine appenginevm +// +build !appengine // This file contains the implementation of the proto field accesses using package unsafe. @@ -130,8 +130,8 @@ func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension { return (*map[int32]Extension)(unsafe.Pointer(uintptr(p) + uintptr(f))) } -// Map returns the reflect.Value for the address of a map field in the struct. -func structPointer_Map(p structPointer, f field, typ reflect.Type) reflect.Value { +// NewAt returns the reflect.Value for a pointer to a field in the struct. +func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value { return reflect.NewAt(typ, unsafe.Pointer(uintptr(p)+uintptr(f))) } diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/properties.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/properties.go index 730a59579..d4531c056 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/properties.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/properties.go @@ -37,6 +37,7 @@ package proto import ( "fmt" + "log" "os" "reflect" "sort" @@ -84,6 +85,15 @@ type decoder func(p *Buffer, prop *Properties, base structPointer) error // A valueDecoder decodes a single integer in a particular encoding. type valueDecoder func(o *Buffer) (x uint64, err error) +// A oneofMarshaler does the marshaling for all oneof fields in a message. +type oneofMarshaler func(Message, *Buffer) error + +// A oneofUnmarshaler does the unmarshaling for a oneof field in a message. +type oneofUnmarshaler func(Message, int, int, *Buffer) (bool, error) + +// A oneofSizer does the sizing for all oneof fields in a message. +type oneofSizer func(Message) int + // tagMap is an optimization over map[int]int for typical protocol buffer // use-cases. Encoded protocol buffers are often in tag order with small tag // numbers. @@ -132,6 +142,22 @@ type StructProperties struct { order []int // list of struct field numbers in tag order unrecField field // field id of the XXX_unrecognized []byte field extendable bool // is this an extendable proto + + oneofMarshaler oneofMarshaler + oneofUnmarshaler oneofUnmarshaler + oneofSizer oneofSizer + stype reflect.Type + + // OneofTypes contains information about the oneof fields in this message. + // It is keyed by the original name of a field. + OneofTypes map[string]*OneofProperties +} + +// OneofProperties represents information about a specific field in a oneof. +type OneofProperties struct { + Type reflect.Type // pointer to generated struct type for this oneof field + Field int // struct field number of the containing oneof in the message + Prop *Properties } // Implement the sorting interface so we can sort the fields in tag order, as recommended by the spec. @@ -156,6 +182,7 @@ type Properties struct { Packed bool // relevant for repeated primitives only Enum string // set for enum types only proto3 bool // whether this is known to be a proto3 field; set for []byte only + oneof bool // whether this is a oneof field Default string // default value HasDefault bool // whether an explicit default was provided @@ -208,6 +235,9 @@ func (p *Properties) String() string { if p.proto3 { s += ",proto3" } + if p.oneof { + s += ",oneof" + } if len(p.Enum) > 0 { s += ",enum=" + p.Enum } @@ -284,6 +314,8 @@ func (p *Properties) Parse(s string) { p.Enum = f[5:] case f == "proto3": p.proto3 = true + case f == "oneof": + p.oneof = true case strings.HasPrefix(f, "def="): p.HasDefault = true p.Default = f[4:] // rest of string @@ -440,7 +472,12 @@ func (p *Properties) setEncAndDec(typ reflect.Type, f *reflect.StructField, lock p.enc = (*Buffer).enc_slice_byte p.dec = (*Buffer).dec_slice_byte p.size = size_slice_byte - if p.proto3 { + // This is a []byte, which is either a bytes field, + // or the value of a map field. In the latter case, + // we always encode an empty []byte, so we should not + // use the proto3 enc/size funcs. + // f == nil iff this is the key/value of a map field. + if p.proto3 && f != nil { p.enc = (*Buffer).enc_proto3_slice_byte p.size = size_proto3_slice_byte } @@ -595,7 +632,7 @@ func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructF } var ( - mutex sync.Mutex + propertiesMu sync.RWMutex propertiesMap = make(map[reflect.Type]*StructProperties) ) @@ -605,13 +642,26 @@ func GetProperties(t reflect.Type) *StructProperties { if t.Kind() != reflect.Struct { panic("proto: type must have kind struct") } - mutex.Lock() - sprop := getPropertiesLocked(t) - mutex.Unlock() + + // Most calls to GetProperties in a long-running program will be + // retrieving details for types we have seen before. + propertiesMu.RLock() + sprop, ok := propertiesMap[t] + propertiesMu.RUnlock() + if ok { + if collectStats { + stats.Chit++ + } + return sprop + } + + propertiesMu.Lock() + sprop = getPropertiesLocked(t) + propertiesMu.Unlock() return sprop } -// getPropertiesLocked requires that mutex is held. +// getPropertiesLocked requires that propertiesMu is held. func getPropertiesLocked(t reflect.Type) *StructProperties { if prop, ok := propertiesMap[t]; ok { if collectStats { @@ -647,6 +697,7 @@ func getPropertiesLocked(t reflect.Type) *StructProperties { if f.Name == "XXX_unrecognized" { // special case prop.unrecField = toField(&f) } + oneof := f.Tag.Get("protobuf_oneof") != "" // special case prop.Prop[i] = p prop.order[i] = i if debug { @@ -656,7 +707,7 @@ func getPropertiesLocked(t reflect.Type) *StructProperties { } print("\n") } - if p.enc == nil && !strings.HasPrefix(f.Name, "XXX_") { + if p.enc == nil && !strings.HasPrefix(f.Name, "XXX_") && !oneof { fmt.Fprintln(os.Stderr, "proto: no encoder for", f.Name, f.Type.String(), "[GetProperties]") } } @@ -664,6 +715,41 @@ func getPropertiesLocked(t reflect.Type) *StructProperties { // Re-order prop.order. sort.Sort(prop) + type oneofMessage interface { + XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{}) + } + if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok { + var oots []interface{} + prop.oneofMarshaler, prop.oneofUnmarshaler, prop.oneofSizer, oots = om.XXX_OneofFuncs() + prop.stype = t + + // Interpret oneof metadata. + prop.OneofTypes = make(map[string]*OneofProperties) + for _, oot := range oots { + oop := &OneofProperties{ + Type: reflect.ValueOf(oot).Type(), // *T + Prop: new(Properties), + } + sft := oop.Type.Elem().Field(0) + oop.Prop.Name = sft.Name + oop.Prop.Parse(sft.Tag.Get("protobuf")) + // There will be exactly one interface field that + // this new value is assignable to. + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if f.Type.Kind() != reflect.Interface { + continue + } + if !oop.Type.AssignableTo(f.Type) { + continue + } + oop.Field = i + break + } + prop.OneofTypes[oop.Prop.OrigName] = oop + } + } + // build required counts // build tags reqCount := 0 @@ -722,3 +808,35 @@ func RegisterEnum(typeName string, unusedNameMap map[int32]string, valueMap map[ } enumValueMaps[typeName] = valueMap } + +// EnumValueMap returns the mapping from names to integers of the +// enum type enumType, or a nil if not found. +func EnumValueMap(enumType string) map[string]int32 { + return enumValueMaps[enumType] +} + +// A registry of all linked message types. +// The string is a fully-qualified proto name ("pkg.Message"). +var ( + protoTypes = make(map[string]reflect.Type) + revProtoTypes = make(map[reflect.Type]string) +) + +// RegisterType is called from generated code and maps from the fully qualified +// proto name to the type (pointer to struct) of the protocol buffer. +func RegisterType(x Message, name string) { + if _, ok := protoTypes[name]; ok { + // TODO: Some day, make this a panic. + log.Printf("proto: duplicate proto type registered: %s", name) + return + } + t := reflect.TypeOf(x) + protoTypes[name] = t + revProtoTypes[t] = name +} + +// MessageName returns the fully-qualified proto name for the given message type. +func MessageName(x Message) string { return revProtoTypes[reflect.TypeOf(x)] } + +// MessageType returns the message type (pointer to struct) for a named message. +func MessageType(name string) reflect.Type { return protoTypes[name] } diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/Makefile b/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/Makefile deleted file mode 100644 index 75144b582..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -# Go support for Protocol Buffers - Google's data interchange format -# -# Copyright 2014 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. - - -include ../../Make.protobuf - -all: regenerate - -regenerate: - rm -f proto3.pb.go - make proto3.pb.go - -# The following rules are just aids to development. Not needed for typical testing. - -diff: regenerate - git diff proto3.pb.go diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/proto3.proto b/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/proto3.proto deleted file mode 100644 index 3e327ded1..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/proto3.proto +++ /dev/null @@ -1,58 +0,0 @@ -// Go support for Protocol Buffers - Google's data interchange format -// -// Copyright 2014 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. - -syntax = "proto3"; - -package proto3_proto; - -message Message { - enum Humour { - UNKNOWN = 0; - PUNS = 1; - SLAPSTICK = 2; - BILL_BAILEY = 3; - } - - string name = 1; - Humour hilarity = 2; - uint32 height_in_cm = 3; - bytes data = 4; - int64 result_count = 7; - bool true_scotsman = 8; - float score = 9; - - repeated uint64 key = 5; - Nested nested = 6; -} - -message Nested { - string bunny = 1; -} diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_test.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_test.go deleted file mode 100644 index d4c96a9e7..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_test.go +++ /dev/null @@ -1,93 +0,0 @@ -// Go support for Protocol Buffers - Google's data interchange format -// -// Copyright 2014 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 proto_test - -import ( - "testing" - - pb "./proto3_proto" - "github.com/golang/protobuf/proto" -) - -func TestProto3ZeroValues(t *testing.T) { - tests := []struct { - desc string - m proto.Message - }{ - {"zero message", &pb.Message{}}, - {"empty bytes field", &pb.Message{Data: []byte{}}}, - } - for _, test := range tests { - b, err := proto.Marshal(test.m) - if err != nil { - t.Errorf("%s: proto.Marshal: %v", test.desc, err) - continue - } - if len(b) > 0 { - t.Errorf("%s: Encoding is non-empty: %q", test.desc, b) - } - } -} - -func TestRoundTripProto3(t *testing.T) { - m := &pb.Message{ - Name: "David", // (2 | 1<<3): 0x0a 0x05 "David" - Hilarity: pb.Message_PUNS, // (0 | 2<<3): 0x10 0x01 - HeightInCm: 178, // (0 | 3<<3): 0x18 0xb2 0x01 - Data: []byte("roboto"), // (2 | 4<<3): 0x20 0x06 "roboto" - ResultCount: 47, // (0 | 7<<3): 0x38 0x2f - TrueScotsman: true, // (0 | 8<<3): 0x40 0x01 - Score: 8.1, // (5 | 9<<3): 0x4d <8.1> - - Key: []uint64{1, 0xdeadbeef}, - Nested: &pb.Nested{ - Bunny: "Monty", - }, - } - t.Logf(" m: %v", m) - - b, err := proto.Marshal(m) - if err != nil { - t.Fatalf("proto.Marshal: %v", err) - } - t.Logf(" b: %q", b) - - m2 := new(pb.Message) - if err := proto.Unmarshal(b, m2); err != nil { - t.Fatalf("proto.Unmarshal: %v", err) - } - t.Logf("m2: %v", m2) - - if !proto.Equal(m, m2) { - t.Errorf("proto.Equal returned false:\n m: %v\nm2: %v", m, m2) - } -} diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/size2_test.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/size2_test.go deleted file mode 100644 index a2729c39a..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/size2_test.go +++ /dev/null @@ -1,63 +0,0 @@ -// Go support for Protocol Buffers - Google's data interchange format -// -// Copyright 2012 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 proto - -import ( - "testing" -) - -// This is a separate file and package from size_test.go because that one uses -// generated messages and thus may not be in package proto without having a circular -// dependency, whereas this file tests unexported details of size.go. - -func TestVarintSize(t *testing.T) { - // Check the edge cases carefully. - testCases := []struct { - n uint64 - size int - }{ - {0, 1}, - {1, 1}, - {127, 1}, - {128, 2}, - {16383, 2}, - {16384, 3}, - {1<<63 - 1, 9}, - {1 << 63, 10}, - } - for _, tc := range testCases { - size := sizeVarint(tc.n) - if size != tc.size { - t.Errorf("sizeVarint(%d) = %d, want %d", tc.n, size, tc.size) - } - } -} diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/size_test.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/size_test.go deleted file mode 100644 index e5f92d6b9..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/size_test.go +++ /dev/null @@ -1,135 +0,0 @@ -// Go support for Protocol Buffers - Google's data interchange format -// -// Copyright 2012 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 proto_test - -import ( - "log" - "testing" - - proto3pb "./proto3_proto" - pb "./testdata" - . "github.com/golang/protobuf/proto" -) - -var messageWithExtension1 = &pb.MyMessage{Count: Int32(7)} - -// messageWithExtension2 is in equal_test.go. -var messageWithExtension3 = &pb.MyMessage{Count: Int32(8)} - -func init() { - if err := SetExtension(messageWithExtension1, pb.E_Ext_More, &pb.Ext{Data: String("Abbott")}); err != nil { - log.Panicf("SetExtension: %v", err) - } - if err := SetExtension(messageWithExtension3, pb.E_Ext_More, &pb.Ext{Data: String("Costello")}); err != nil { - log.Panicf("SetExtension: %v", err) - } - - // Force messageWithExtension3 to have the extension encoded. - Marshal(messageWithExtension3) - -} - -var SizeTests = []struct { - desc string - pb Message -}{ - {"empty", &pb.OtherMessage{}}, - // Basic types. - {"bool", &pb.Defaults{F_Bool: Bool(true)}}, - {"int32", &pb.Defaults{F_Int32: Int32(12)}}, - {"negative int32", &pb.Defaults{F_Int32: Int32(-1)}}, - {"small int64", &pb.Defaults{F_Int64: Int64(1)}}, - {"big int64", &pb.Defaults{F_Int64: Int64(1 << 20)}}, - {"negative int64", &pb.Defaults{F_Int64: Int64(-1)}}, - {"fixed32", &pb.Defaults{F_Fixed32: Uint32(71)}}, - {"fixed64", &pb.Defaults{F_Fixed64: Uint64(72)}}, - {"uint32", &pb.Defaults{F_Uint32: Uint32(123)}}, - {"uint64", &pb.Defaults{F_Uint64: Uint64(124)}}, - {"float", &pb.Defaults{F_Float: Float32(12.6)}}, - {"double", &pb.Defaults{F_Double: Float64(13.9)}}, - {"string", &pb.Defaults{F_String: String("niles")}}, - {"bytes", &pb.Defaults{F_Bytes: []byte("wowsa")}}, - {"bytes, empty", &pb.Defaults{F_Bytes: []byte{}}}, - {"sint32", &pb.Defaults{F_Sint32: Int32(65)}}, - {"sint64", &pb.Defaults{F_Sint64: Int64(67)}}, - {"enum", &pb.Defaults{F_Enum: pb.Defaults_BLUE.Enum()}}, - // Repeated. - {"empty repeated bool", &pb.MoreRepeated{Bools: []bool{}}}, - {"repeated bool", &pb.MoreRepeated{Bools: []bool{false, true, true, false}}}, - {"packed repeated bool", &pb.MoreRepeated{BoolsPacked: []bool{false, true, true, false, true, true, true}}}, - {"repeated int32", &pb.MoreRepeated{Ints: []int32{1, 12203, 1729, -1}}}, - {"repeated int32 packed", &pb.MoreRepeated{IntsPacked: []int32{1, 12203, 1729}}}, - {"repeated int64 packed", &pb.MoreRepeated{Int64SPacked: []int64{ - // Need enough large numbers to verify that the header is counting the number of bytes - // for the field, not the number of elements. - 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, - 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, - }}}, - {"repeated string", &pb.MoreRepeated{Strings: []string{"r", "ken", "gri"}}}, - {"repeated fixed", &pb.MoreRepeated{Fixeds: []uint32{1, 2, 3, 4}}}, - // Nested. - {"nested", &pb.OldMessage{Nested: &pb.OldMessage_Nested{Name: String("whatever")}}}, - {"group", &pb.GroupOld{G: &pb.GroupOld_G{X: Int32(12345)}}}, - // Other things. - {"unrecognized", &pb.MoreRepeated{XXX_unrecognized: []byte{13<<3 | 0, 4}}}, - {"extension (unencoded)", messageWithExtension1}, - {"extension (encoded)", messageWithExtension3}, - // proto3 message - {"proto3 empty", &proto3pb.Message{}}, - {"proto3 bool", &proto3pb.Message{TrueScotsman: true}}, - {"proto3 int64", &proto3pb.Message{ResultCount: 1}}, - {"proto3 uint32", &proto3pb.Message{HeightInCm: 123}}, - {"proto3 float", &proto3pb.Message{Score: 12.6}}, - {"proto3 string", &proto3pb.Message{Name: "Snezana"}}, - {"proto3 bytes", &proto3pb.Message{Data: []byte("wowsa")}}, - {"proto3 bytes, empty", &proto3pb.Message{Data: []byte{}}}, - {"proto3 enum", &proto3pb.Message{Hilarity: proto3pb.Message_PUNS}}, - - {"map field", &pb.MessageWithMap{NameMapping: map[int32]string{1: "Rob", 7: "Andrew"}}}, - {"map field with message", &pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{0x7001: &pb.FloatingPoint{F: Float64(2.0)}}}}, - {"map field with bytes", &pb.MessageWithMap{ByteMapping: map[bool][]byte{true: []byte("this time for sure")}}}, -} - -func TestSize(t *testing.T) { - for _, tc := range SizeTests { - size := Size(tc.pb) - b, err := Marshal(tc.pb) - if err != nil { - t.Errorf("%v: Marshal failed: %v", tc.desc, err) - continue - } - if size != len(b) { - t.Errorf("%v: Size(%v) = %d, want %d", tc.desc, tc.pb, size, len(b)) - t.Logf("%v: bytes: %#v", tc.desc, b) - } - } -} diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/Makefile b/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/Makefile deleted file mode 100644 index fc288628a..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -# 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. - - -include ../../Make.protobuf - -all: regenerate - -regenerate: - rm -f test.pb.go - make test.pb.go - -# The following rules are just aids to development. Not needed for typical testing. - -diff: regenerate - git diff test.pb.go - -restore: - cp test.pb.go.golden test.pb.go - -preserve: - cp test.pb.go test.pb.go.golden diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/golden_test.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/golden_test.go deleted file mode 100644 index 7172d0e96..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/golden_test.go +++ /dev/null @@ -1,86 +0,0 @@ -// Go support for Protocol Buffers - Google's data interchange format -// -// Copyright 2012 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. - -// Verify that the compiler output for test.proto is unchanged. - -package testdata - -import ( - "crypto/sha1" - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "testing" -) - -// sum returns in string form (for easy comparison) the SHA-1 hash of the named file. -func sum(t *testing.T, name string) string { - data, err := ioutil.ReadFile(name) - if err != nil { - t.Fatal(err) - } - t.Logf("sum(%q): length is %d", name, len(data)) - hash := sha1.New() - _, err = hash.Write(data) - if err != nil { - t.Fatal(err) - } - return fmt.Sprintf("% x", hash.Sum(nil)) -} - -func run(t *testing.T, name string, args ...string) { - cmd := exec.Command(name, args...) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err := cmd.Run() - if err != nil { - t.Fatal(err) - } -} - -func TestGolden(t *testing.T) { - // Compute the original checksum. - goldenSum := sum(t, "test.pb.go") - // Run the proto compiler. - run(t, "protoc", "--go_out="+os.TempDir(), "test.proto") - newFile := filepath.Join(os.TempDir(), "test.pb.go") - defer os.Remove(newFile) - // Compute the new checksum. - newSum := sum(t, newFile) - // Verify - if newSum != goldenSum { - run(t, "diff", "-u", "test.pb.go", newFile) - t.Fatal("Code generated by protoc-gen-go has changed; update test.pb.go") - } -} diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.pb.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.pb.go deleted file mode 100644 index f47d9e0e3..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.pb.go +++ /dev/null @@ -1,2389 +0,0 @@ -// Code generated by protoc-gen-go. -// source: test.proto -// DO NOT EDIT! - -/* -Package testdata is a generated protocol buffer package. - -It is generated from these files: - test.proto - -It has these top-level messages: - GoEnum - GoTestField - GoTest - GoSkipTest - NonPackedTest - PackedTest - MaxTag - OldMessage - NewMessage - InnerMessage - OtherMessage - MyMessage - Ext - MyMessageSet - Empty - MessageList - Strings - Defaults - SubDefaults - RepeatedEnum - MoreRepeated - GroupOld - GroupNew - FloatingPoint - MessageWithMap -*/ -package testdata - -import proto "github.com/golang/protobuf/proto" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = math.Inf - -type FOO int32 - -const ( - FOO_FOO1 FOO = 1 -) - -var FOO_name = map[int32]string{ - 1: "FOO1", -} -var FOO_value = map[string]int32{ - "FOO1": 1, -} - -func (x FOO) Enum() *FOO { - p := new(FOO) - *p = x - return p -} -func (x FOO) String() string { - return proto.EnumName(FOO_name, int32(x)) -} -func (x *FOO) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(FOO_value, data, "FOO") - if err != nil { - return err - } - *x = FOO(value) - return nil -} - -// An enum, for completeness. -type GoTest_KIND int32 - -const ( - GoTest_VOID GoTest_KIND = 0 - // Basic types - GoTest_BOOL GoTest_KIND = 1 - GoTest_BYTES GoTest_KIND = 2 - GoTest_FINGERPRINT GoTest_KIND = 3 - GoTest_FLOAT GoTest_KIND = 4 - GoTest_INT GoTest_KIND = 5 - GoTest_STRING GoTest_KIND = 6 - GoTest_TIME GoTest_KIND = 7 - // Groupings - GoTest_TUPLE GoTest_KIND = 8 - GoTest_ARRAY GoTest_KIND = 9 - GoTest_MAP GoTest_KIND = 10 - // Table types - GoTest_TABLE GoTest_KIND = 11 - // Functions - GoTest_FUNCTION GoTest_KIND = 12 -) - -var GoTest_KIND_name = map[int32]string{ - 0: "VOID", - 1: "BOOL", - 2: "BYTES", - 3: "FINGERPRINT", - 4: "FLOAT", - 5: "INT", - 6: "STRING", - 7: "TIME", - 8: "TUPLE", - 9: "ARRAY", - 10: "MAP", - 11: "TABLE", - 12: "FUNCTION", -} -var GoTest_KIND_value = map[string]int32{ - "VOID": 0, - "BOOL": 1, - "BYTES": 2, - "FINGERPRINT": 3, - "FLOAT": 4, - "INT": 5, - "STRING": 6, - "TIME": 7, - "TUPLE": 8, - "ARRAY": 9, - "MAP": 10, - "TABLE": 11, - "FUNCTION": 12, -} - -func (x GoTest_KIND) Enum() *GoTest_KIND { - p := new(GoTest_KIND) - *p = x - return p -} -func (x GoTest_KIND) String() string { - return proto.EnumName(GoTest_KIND_name, int32(x)) -} -func (x *GoTest_KIND) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(GoTest_KIND_value, data, "GoTest_KIND") - if err != nil { - return err - } - *x = GoTest_KIND(value) - return nil -} - -type MyMessage_Color int32 - -const ( - MyMessage_RED MyMessage_Color = 0 - MyMessage_GREEN MyMessage_Color = 1 - MyMessage_BLUE MyMessage_Color = 2 -) - -var MyMessage_Color_name = map[int32]string{ - 0: "RED", - 1: "GREEN", - 2: "BLUE", -} -var MyMessage_Color_value = map[string]int32{ - "RED": 0, - "GREEN": 1, - "BLUE": 2, -} - -func (x MyMessage_Color) Enum() *MyMessage_Color { - p := new(MyMessage_Color) - *p = x - return p -} -func (x MyMessage_Color) String() string { - return proto.EnumName(MyMessage_Color_name, int32(x)) -} -func (x *MyMessage_Color) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(MyMessage_Color_value, data, "MyMessage_Color") - if err != nil { - return err - } - *x = MyMessage_Color(value) - return nil -} - -type Defaults_Color int32 - -const ( - Defaults_RED Defaults_Color = 0 - Defaults_GREEN Defaults_Color = 1 - Defaults_BLUE Defaults_Color = 2 -) - -var Defaults_Color_name = map[int32]string{ - 0: "RED", - 1: "GREEN", - 2: "BLUE", -} -var Defaults_Color_value = map[string]int32{ - "RED": 0, - "GREEN": 1, - "BLUE": 2, -} - -func (x Defaults_Color) Enum() *Defaults_Color { - p := new(Defaults_Color) - *p = x - return p -} -func (x Defaults_Color) String() string { - return proto.EnumName(Defaults_Color_name, int32(x)) -} -func (x *Defaults_Color) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(Defaults_Color_value, data, "Defaults_Color") - if err != nil { - return err - } - *x = Defaults_Color(value) - return nil -} - -type RepeatedEnum_Color int32 - -const ( - RepeatedEnum_RED RepeatedEnum_Color = 1 -) - -var RepeatedEnum_Color_name = map[int32]string{ - 1: "RED", -} -var RepeatedEnum_Color_value = map[string]int32{ - "RED": 1, -} - -func (x RepeatedEnum_Color) Enum() *RepeatedEnum_Color { - p := new(RepeatedEnum_Color) - *p = x - return p -} -func (x RepeatedEnum_Color) String() string { - return proto.EnumName(RepeatedEnum_Color_name, int32(x)) -} -func (x *RepeatedEnum_Color) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(RepeatedEnum_Color_value, data, "RepeatedEnum_Color") - if err != nil { - return err - } - *x = RepeatedEnum_Color(value) - return nil -} - -type GoEnum struct { - Foo *FOO `protobuf:"varint,1,req,name=foo,enum=testdata.FOO" json:"foo,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GoEnum) Reset() { *m = GoEnum{} } -func (m *GoEnum) String() string { return proto.CompactTextString(m) } -func (*GoEnum) ProtoMessage() {} - -func (m *GoEnum) GetFoo() FOO { - if m != nil && m.Foo != nil { - return *m.Foo - } - return FOO_FOO1 -} - -type GoTestField struct { - Label *string `protobuf:"bytes,1,req" json:"Label,omitempty"` - Type *string `protobuf:"bytes,2,req" json:"Type,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GoTestField) Reset() { *m = GoTestField{} } -func (m *GoTestField) String() string { return proto.CompactTextString(m) } -func (*GoTestField) ProtoMessage() {} - -func (m *GoTestField) GetLabel() string { - if m != nil && m.Label != nil { - return *m.Label - } - return "" -} - -func (m *GoTestField) GetType() string { - if m != nil && m.Type != nil { - return *m.Type - } - return "" -} - -type GoTest struct { - // Some typical parameters - Kind *GoTest_KIND `protobuf:"varint,1,req,enum=testdata.GoTest_KIND" json:"Kind,omitempty"` - Table *string `protobuf:"bytes,2,opt" json:"Table,omitempty"` - Param *int32 `protobuf:"varint,3,opt" json:"Param,omitempty"` - // Required, repeated and optional foreign fields. - RequiredField *GoTestField `protobuf:"bytes,4,req" json:"RequiredField,omitempty"` - RepeatedField []*GoTestField `protobuf:"bytes,5,rep" json:"RepeatedField,omitempty"` - OptionalField *GoTestField `protobuf:"bytes,6,opt" json:"OptionalField,omitempty"` - // Required fields of all basic types - F_BoolRequired *bool `protobuf:"varint,10,req,name=F_Bool_required" json:"F_Bool_required,omitempty"` - F_Int32Required *int32 `protobuf:"varint,11,req,name=F_Int32_required" json:"F_Int32_required,omitempty"` - F_Int64Required *int64 `protobuf:"varint,12,req,name=F_Int64_required" json:"F_Int64_required,omitempty"` - F_Fixed32Required *uint32 `protobuf:"fixed32,13,req,name=F_Fixed32_required" json:"F_Fixed32_required,omitempty"` - F_Fixed64Required *uint64 `protobuf:"fixed64,14,req,name=F_Fixed64_required" json:"F_Fixed64_required,omitempty"` - F_Uint32Required *uint32 `protobuf:"varint,15,req,name=F_Uint32_required" json:"F_Uint32_required,omitempty"` - F_Uint64Required *uint64 `protobuf:"varint,16,req,name=F_Uint64_required" json:"F_Uint64_required,omitempty"` - F_FloatRequired *float32 `protobuf:"fixed32,17,req,name=F_Float_required" json:"F_Float_required,omitempty"` - F_DoubleRequired *float64 `protobuf:"fixed64,18,req,name=F_Double_required" json:"F_Double_required,omitempty"` - F_StringRequired *string `protobuf:"bytes,19,req,name=F_String_required" json:"F_String_required,omitempty"` - F_BytesRequired []byte `protobuf:"bytes,101,req,name=F_Bytes_required" json:"F_Bytes_required,omitempty"` - F_Sint32Required *int32 `protobuf:"zigzag32,102,req,name=F_Sint32_required" json:"F_Sint32_required,omitempty"` - F_Sint64Required *int64 `protobuf:"zigzag64,103,req,name=F_Sint64_required" json:"F_Sint64_required,omitempty"` - // Repeated fields of all basic types - F_BoolRepeated []bool `protobuf:"varint,20,rep,name=F_Bool_repeated" json:"F_Bool_repeated,omitempty"` - F_Int32Repeated []int32 `protobuf:"varint,21,rep,name=F_Int32_repeated" json:"F_Int32_repeated,omitempty"` - F_Int64Repeated []int64 `protobuf:"varint,22,rep,name=F_Int64_repeated" json:"F_Int64_repeated,omitempty"` - F_Fixed32Repeated []uint32 `protobuf:"fixed32,23,rep,name=F_Fixed32_repeated" json:"F_Fixed32_repeated,omitempty"` - F_Fixed64Repeated []uint64 `protobuf:"fixed64,24,rep,name=F_Fixed64_repeated" json:"F_Fixed64_repeated,omitempty"` - F_Uint32Repeated []uint32 `protobuf:"varint,25,rep,name=F_Uint32_repeated" json:"F_Uint32_repeated,omitempty"` - F_Uint64Repeated []uint64 `protobuf:"varint,26,rep,name=F_Uint64_repeated" json:"F_Uint64_repeated,omitempty"` - F_FloatRepeated []float32 `protobuf:"fixed32,27,rep,name=F_Float_repeated" json:"F_Float_repeated,omitempty"` - F_DoubleRepeated []float64 `protobuf:"fixed64,28,rep,name=F_Double_repeated" json:"F_Double_repeated,omitempty"` - F_StringRepeated []string `protobuf:"bytes,29,rep,name=F_String_repeated" json:"F_String_repeated,omitempty"` - F_BytesRepeated [][]byte `protobuf:"bytes,201,rep,name=F_Bytes_repeated" json:"F_Bytes_repeated,omitempty"` - F_Sint32Repeated []int32 `protobuf:"zigzag32,202,rep,name=F_Sint32_repeated" json:"F_Sint32_repeated,omitempty"` - F_Sint64Repeated []int64 `protobuf:"zigzag64,203,rep,name=F_Sint64_repeated" json:"F_Sint64_repeated,omitempty"` - // Optional fields of all basic types - F_BoolOptional *bool `protobuf:"varint,30,opt,name=F_Bool_optional" json:"F_Bool_optional,omitempty"` - F_Int32Optional *int32 `protobuf:"varint,31,opt,name=F_Int32_optional" json:"F_Int32_optional,omitempty"` - F_Int64Optional *int64 `protobuf:"varint,32,opt,name=F_Int64_optional" json:"F_Int64_optional,omitempty"` - F_Fixed32Optional *uint32 `protobuf:"fixed32,33,opt,name=F_Fixed32_optional" json:"F_Fixed32_optional,omitempty"` - F_Fixed64Optional *uint64 `protobuf:"fixed64,34,opt,name=F_Fixed64_optional" json:"F_Fixed64_optional,omitempty"` - F_Uint32Optional *uint32 `protobuf:"varint,35,opt,name=F_Uint32_optional" json:"F_Uint32_optional,omitempty"` - F_Uint64Optional *uint64 `protobuf:"varint,36,opt,name=F_Uint64_optional" json:"F_Uint64_optional,omitempty"` - F_FloatOptional *float32 `protobuf:"fixed32,37,opt,name=F_Float_optional" json:"F_Float_optional,omitempty"` - F_DoubleOptional *float64 `protobuf:"fixed64,38,opt,name=F_Double_optional" json:"F_Double_optional,omitempty"` - F_StringOptional *string `protobuf:"bytes,39,opt,name=F_String_optional" json:"F_String_optional,omitempty"` - F_BytesOptional []byte `protobuf:"bytes,301,opt,name=F_Bytes_optional" json:"F_Bytes_optional,omitempty"` - F_Sint32Optional *int32 `protobuf:"zigzag32,302,opt,name=F_Sint32_optional" json:"F_Sint32_optional,omitempty"` - F_Sint64Optional *int64 `protobuf:"zigzag64,303,opt,name=F_Sint64_optional" json:"F_Sint64_optional,omitempty"` - // Default-valued fields of all basic types - F_BoolDefaulted *bool `protobuf:"varint,40,opt,name=F_Bool_defaulted,def=1" json:"F_Bool_defaulted,omitempty"` - F_Int32Defaulted *int32 `protobuf:"varint,41,opt,name=F_Int32_defaulted,def=32" json:"F_Int32_defaulted,omitempty"` - F_Int64Defaulted *int64 `protobuf:"varint,42,opt,name=F_Int64_defaulted,def=64" json:"F_Int64_defaulted,omitempty"` - F_Fixed32Defaulted *uint32 `protobuf:"fixed32,43,opt,name=F_Fixed32_defaulted,def=320" json:"F_Fixed32_defaulted,omitempty"` - F_Fixed64Defaulted *uint64 `protobuf:"fixed64,44,opt,name=F_Fixed64_defaulted,def=640" json:"F_Fixed64_defaulted,omitempty"` - F_Uint32Defaulted *uint32 `protobuf:"varint,45,opt,name=F_Uint32_defaulted,def=3200" json:"F_Uint32_defaulted,omitempty"` - F_Uint64Defaulted *uint64 `protobuf:"varint,46,opt,name=F_Uint64_defaulted,def=6400" json:"F_Uint64_defaulted,omitempty"` - F_FloatDefaulted *float32 `protobuf:"fixed32,47,opt,name=F_Float_defaulted,def=314159" json:"F_Float_defaulted,omitempty"` - F_DoubleDefaulted *float64 `protobuf:"fixed64,48,opt,name=F_Double_defaulted,def=271828" json:"F_Double_defaulted,omitempty"` - F_StringDefaulted *string `protobuf:"bytes,49,opt,name=F_String_defaulted,def=hello, \"world!\"\n" json:"F_String_defaulted,omitempty"` - F_BytesDefaulted []byte `protobuf:"bytes,401,opt,name=F_Bytes_defaulted,def=Bignose" json:"F_Bytes_defaulted,omitempty"` - F_Sint32Defaulted *int32 `protobuf:"zigzag32,402,opt,name=F_Sint32_defaulted,def=-32" json:"F_Sint32_defaulted,omitempty"` - F_Sint64Defaulted *int64 `protobuf:"zigzag64,403,opt,name=F_Sint64_defaulted,def=-64" json:"F_Sint64_defaulted,omitempty"` - // Packed repeated fields (no string or bytes). - F_BoolRepeatedPacked []bool `protobuf:"varint,50,rep,packed,name=F_Bool_repeated_packed" json:"F_Bool_repeated_packed,omitempty"` - F_Int32RepeatedPacked []int32 `protobuf:"varint,51,rep,packed,name=F_Int32_repeated_packed" json:"F_Int32_repeated_packed,omitempty"` - F_Int64RepeatedPacked []int64 `protobuf:"varint,52,rep,packed,name=F_Int64_repeated_packed" json:"F_Int64_repeated_packed,omitempty"` - F_Fixed32RepeatedPacked []uint32 `protobuf:"fixed32,53,rep,packed,name=F_Fixed32_repeated_packed" json:"F_Fixed32_repeated_packed,omitempty"` - F_Fixed64RepeatedPacked []uint64 `protobuf:"fixed64,54,rep,packed,name=F_Fixed64_repeated_packed" json:"F_Fixed64_repeated_packed,omitempty"` - F_Uint32RepeatedPacked []uint32 `protobuf:"varint,55,rep,packed,name=F_Uint32_repeated_packed" json:"F_Uint32_repeated_packed,omitempty"` - F_Uint64RepeatedPacked []uint64 `protobuf:"varint,56,rep,packed,name=F_Uint64_repeated_packed" json:"F_Uint64_repeated_packed,omitempty"` - F_FloatRepeatedPacked []float32 `protobuf:"fixed32,57,rep,packed,name=F_Float_repeated_packed" json:"F_Float_repeated_packed,omitempty"` - F_DoubleRepeatedPacked []float64 `protobuf:"fixed64,58,rep,packed,name=F_Double_repeated_packed" json:"F_Double_repeated_packed,omitempty"` - F_Sint32RepeatedPacked []int32 `protobuf:"zigzag32,502,rep,packed,name=F_Sint32_repeated_packed" json:"F_Sint32_repeated_packed,omitempty"` - F_Sint64RepeatedPacked []int64 `protobuf:"zigzag64,503,rep,packed,name=F_Sint64_repeated_packed" json:"F_Sint64_repeated_packed,omitempty"` - Requiredgroup *GoTest_RequiredGroup `protobuf:"group,70,req,name=RequiredGroup" json:"requiredgroup,omitempty"` - Repeatedgroup []*GoTest_RepeatedGroup `protobuf:"group,80,rep,name=RepeatedGroup" json:"repeatedgroup,omitempty"` - Optionalgroup *GoTest_OptionalGroup `protobuf:"group,90,opt,name=OptionalGroup" json:"optionalgroup,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GoTest) Reset() { *m = GoTest{} } -func (m *GoTest) String() string { return proto.CompactTextString(m) } -func (*GoTest) ProtoMessage() {} - -const Default_GoTest_F_BoolDefaulted bool = true -const Default_GoTest_F_Int32Defaulted int32 = 32 -const Default_GoTest_F_Int64Defaulted int64 = 64 -const Default_GoTest_F_Fixed32Defaulted uint32 = 320 -const Default_GoTest_F_Fixed64Defaulted uint64 = 640 -const Default_GoTest_F_Uint32Defaulted uint32 = 3200 -const Default_GoTest_F_Uint64Defaulted uint64 = 6400 -const Default_GoTest_F_FloatDefaulted float32 = 314159 -const Default_GoTest_F_DoubleDefaulted float64 = 271828 -const Default_GoTest_F_StringDefaulted string = "hello, \"world!\"\n" - -var Default_GoTest_F_BytesDefaulted []byte = []byte("Bignose") - -const Default_GoTest_F_Sint32Defaulted int32 = -32 -const Default_GoTest_F_Sint64Defaulted int64 = -64 - -func (m *GoTest) GetKind() GoTest_KIND { - if m != nil && m.Kind != nil { - return *m.Kind - } - return GoTest_VOID -} - -func (m *GoTest) GetTable() string { - if m != nil && m.Table != nil { - return *m.Table - } - return "" -} - -func (m *GoTest) GetParam() int32 { - if m != nil && m.Param != nil { - return *m.Param - } - return 0 -} - -func (m *GoTest) GetRequiredField() *GoTestField { - if m != nil { - return m.RequiredField - } - return nil -} - -func (m *GoTest) GetRepeatedField() []*GoTestField { - if m != nil { - return m.RepeatedField - } - return nil -} - -func (m *GoTest) GetOptionalField() *GoTestField { - if m != nil { - return m.OptionalField - } - return nil -} - -func (m *GoTest) GetF_BoolRequired() bool { - if m != nil && m.F_BoolRequired != nil { - return *m.F_BoolRequired - } - return false -} - -func (m *GoTest) GetF_Int32Required() int32 { - if m != nil && m.F_Int32Required != nil { - return *m.F_Int32Required - } - return 0 -} - -func (m *GoTest) GetF_Int64Required() int64 { - if m != nil && m.F_Int64Required != nil { - return *m.F_Int64Required - } - return 0 -} - -func (m *GoTest) GetF_Fixed32Required() uint32 { - if m != nil && m.F_Fixed32Required != nil { - return *m.F_Fixed32Required - } - return 0 -} - -func (m *GoTest) GetF_Fixed64Required() uint64 { - if m != nil && m.F_Fixed64Required != nil { - return *m.F_Fixed64Required - } - return 0 -} - -func (m *GoTest) GetF_Uint32Required() uint32 { - if m != nil && m.F_Uint32Required != nil { - return *m.F_Uint32Required - } - return 0 -} - -func (m *GoTest) GetF_Uint64Required() uint64 { - if m != nil && m.F_Uint64Required != nil { - return *m.F_Uint64Required - } - return 0 -} - -func (m *GoTest) GetF_FloatRequired() float32 { - if m != nil && m.F_FloatRequired != nil { - return *m.F_FloatRequired - } - return 0 -} - -func (m *GoTest) GetF_DoubleRequired() float64 { - if m != nil && m.F_DoubleRequired != nil { - return *m.F_DoubleRequired - } - return 0 -} - -func (m *GoTest) GetF_StringRequired() string { - if m != nil && m.F_StringRequired != nil { - return *m.F_StringRequired - } - return "" -} - -func (m *GoTest) GetF_BytesRequired() []byte { - if m != nil { - return m.F_BytesRequired - } - return nil -} - -func (m *GoTest) GetF_Sint32Required() int32 { - if m != nil && m.F_Sint32Required != nil { - return *m.F_Sint32Required - } - return 0 -} - -func (m *GoTest) GetF_Sint64Required() int64 { - if m != nil && m.F_Sint64Required != nil { - return *m.F_Sint64Required - } - return 0 -} - -func (m *GoTest) GetF_BoolRepeated() []bool { - if m != nil { - return m.F_BoolRepeated - } - return nil -} - -func (m *GoTest) GetF_Int32Repeated() []int32 { - if m != nil { - return m.F_Int32Repeated - } - return nil -} - -func (m *GoTest) GetF_Int64Repeated() []int64 { - if m != nil { - return m.F_Int64Repeated - } - return nil -} - -func (m *GoTest) GetF_Fixed32Repeated() []uint32 { - if m != nil { - return m.F_Fixed32Repeated - } - return nil -} - -func (m *GoTest) GetF_Fixed64Repeated() []uint64 { - if m != nil { - return m.F_Fixed64Repeated - } - return nil -} - -func (m *GoTest) GetF_Uint32Repeated() []uint32 { - if m != nil { - return m.F_Uint32Repeated - } - return nil -} - -func (m *GoTest) GetF_Uint64Repeated() []uint64 { - if m != nil { - return m.F_Uint64Repeated - } - return nil -} - -func (m *GoTest) GetF_FloatRepeated() []float32 { - if m != nil { - return m.F_FloatRepeated - } - return nil -} - -func (m *GoTest) GetF_DoubleRepeated() []float64 { - if m != nil { - return m.F_DoubleRepeated - } - return nil -} - -func (m *GoTest) GetF_StringRepeated() []string { - if m != nil { - return m.F_StringRepeated - } - return nil -} - -func (m *GoTest) GetF_BytesRepeated() [][]byte { - if m != nil { - return m.F_BytesRepeated - } - return nil -} - -func (m *GoTest) GetF_Sint32Repeated() []int32 { - if m != nil { - return m.F_Sint32Repeated - } - return nil -} - -func (m *GoTest) GetF_Sint64Repeated() []int64 { - if m != nil { - return m.F_Sint64Repeated - } - return nil -} - -func (m *GoTest) GetF_BoolOptional() bool { - if m != nil && m.F_BoolOptional != nil { - return *m.F_BoolOptional - } - return false -} - -func (m *GoTest) GetF_Int32Optional() int32 { - if m != nil && m.F_Int32Optional != nil { - return *m.F_Int32Optional - } - return 0 -} - -func (m *GoTest) GetF_Int64Optional() int64 { - if m != nil && m.F_Int64Optional != nil { - return *m.F_Int64Optional - } - return 0 -} - -func (m *GoTest) GetF_Fixed32Optional() uint32 { - if m != nil && m.F_Fixed32Optional != nil { - return *m.F_Fixed32Optional - } - return 0 -} - -func (m *GoTest) GetF_Fixed64Optional() uint64 { - if m != nil && m.F_Fixed64Optional != nil { - return *m.F_Fixed64Optional - } - return 0 -} - -func (m *GoTest) GetF_Uint32Optional() uint32 { - if m != nil && m.F_Uint32Optional != nil { - return *m.F_Uint32Optional - } - return 0 -} - -func (m *GoTest) GetF_Uint64Optional() uint64 { - if m != nil && m.F_Uint64Optional != nil { - return *m.F_Uint64Optional - } - return 0 -} - -func (m *GoTest) GetF_FloatOptional() float32 { - if m != nil && m.F_FloatOptional != nil { - return *m.F_FloatOptional - } - return 0 -} - -func (m *GoTest) GetF_DoubleOptional() float64 { - if m != nil && m.F_DoubleOptional != nil { - return *m.F_DoubleOptional - } - return 0 -} - -func (m *GoTest) GetF_StringOptional() string { - if m != nil && m.F_StringOptional != nil { - return *m.F_StringOptional - } - return "" -} - -func (m *GoTest) GetF_BytesOptional() []byte { - if m != nil { - return m.F_BytesOptional - } - return nil -} - -func (m *GoTest) GetF_Sint32Optional() int32 { - if m != nil && m.F_Sint32Optional != nil { - return *m.F_Sint32Optional - } - return 0 -} - -func (m *GoTest) GetF_Sint64Optional() int64 { - if m != nil && m.F_Sint64Optional != nil { - return *m.F_Sint64Optional - } - return 0 -} - -func (m *GoTest) GetF_BoolDefaulted() bool { - if m != nil && m.F_BoolDefaulted != nil { - return *m.F_BoolDefaulted - } - return Default_GoTest_F_BoolDefaulted -} - -func (m *GoTest) GetF_Int32Defaulted() int32 { - if m != nil && m.F_Int32Defaulted != nil { - return *m.F_Int32Defaulted - } - return Default_GoTest_F_Int32Defaulted -} - -func (m *GoTest) GetF_Int64Defaulted() int64 { - if m != nil && m.F_Int64Defaulted != nil { - return *m.F_Int64Defaulted - } - return Default_GoTest_F_Int64Defaulted -} - -func (m *GoTest) GetF_Fixed32Defaulted() uint32 { - if m != nil && m.F_Fixed32Defaulted != nil { - return *m.F_Fixed32Defaulted - } - return Default_GoTest_F_Fixed32Defaulted -} - -func (m *GoTest) GetF_Fixed64Defaulted() uint64 { - if m != nil && m.F_Fixed64Defaulted != nil { - return *m.F_Fixed64Defaulted - } - return Default_GoTest_F_Fixed64Defaulted -} - -func (m *GoTest) GetF_Uint32Defaulted() uint32 { - if m != nil && m.F_Uint32Defaulted != nil { - return *m.F_Uint32Defaulted - } - return Default_GoTest_F_Uint32Defaulted -} - -func (m *GoTest) GetF_Uint64Defaulted() uint64 { - if m != nil && m.F_Uint64Defaulted != nil { - return *m.F_Uint64Defaulted - } - return Default_GoTest_F_Uint64Defaulted -} - -func (m *GoTest) GetF_FloatDefaulted() float32 { - if m != nil && m.F_FloatDefaulted != nil { - return *m.F_FloatDefaulted - } - return Default_GoTest_F_FloatDefaulted -} - -func (m *GoTest) GetF_DoubleDefaulted() float64 { - if m != nil && m.F_DoubleDefaulted != nil { - return *m.F_DoubleDefaulted - } - return Default_GoTest_F_DoubleDefaulted -} - -func (m *GoTest) GetF_StringDefaulted() string { - if m != nil && m.F_StringDefaulted != nil { - return *m.F_StringDefaulted - } - return Default_GoTest_F_StringDefaulted -} - -func (m *GoTest) GetF_BytesDefaulted() []byte { - if m != nil && m.F_BytesDefaulted != nil { - return m.F_BytesDefaulted - } - return append([]byte(nil), Default_GoTest_F_BytesDefaulted...) -} - -func (m *GoTest) GetF_Sint32Defaulted() int32 { - if m != nil && m.F_Sint32Defaulted != nil { - return *m.F_Sint32Defaulted - } - return Default_GoTest_F_Sint32Defaulted -} - -func (m *GoTest) GetF_Sint64Defaulted() int64 { - if m != nil && m.F_Sint64Defaulted != nil { - return *m.F_Sint64Defaulted - } - return Default_GoTest_F_Sint64Defaulted -} - -func (m *GoTest) GetF_BoolRepeatedPacked() []bool { - if m != nil { - return m.F_BoolRepeatedPacked - } - return nil -} - -func (m *GoTest) GetF_Int32RepeatedPacked() []int32 { - if m != nil { - return m.F_Int32RepeatedPacked - } - return nil -} - -func (m *GoTest) GetF_Int64RepeatedPacked() []int64 { - if m != nil { - return m.F_Int64RepeatedPacked - } - return nil -} - -func (m *GoTest) GetF_Fixed32RepeatedPacked() []uint32 { - if m != nil { - return m.F_Fixed32RepeatedPacked - } - return nil -} - -func (m *GoTest) GetF_Fixed64RepeatedPacked() []uint64 { - if m != nil { - return m.F_Fixed64RepeatedPacked - } - return nil -} - -func (m *GoTest) GetF_Uint32RepeatedPacked() []uint32 { - if m != nil { - return m.F_Uint32RepeatedPacked - } - return nil -} - -func (m *GoTest) GetF_Uint64RepeatedPacked() []uint64 { - if m != nil { - return m.F_Uint64RepeatedPacked - } - return nil -} - -func (m *GoTest) GetF_FloatRepeatedPacked() []float32 { - if m != nil { - return m.F_FloatRepeatedPacked - } - return nil -} - -func (m *GoTest) GetF_DoubleRepeatedPacked() []float64 { - if m != nil { - return m.F_DoubleRepeatedPacked - } - return nil -} - -func (m *GoTest) GetF_Sint32RepeatedPacked() []int32 { - if m != nil { - return m.F_Sint32RepeatedPacked - } - return nil -} - -func (m *GoTest) GetF_Sint64RepeatedPacked() []int64 { - if m != nil { - return m.F_Sint64RepeatedPacked - } - return nil -} - -func (m *GoTest) GetRequiredgroup() *GoTest_RequiredGroup { - if m != nil { - return m.Requiredgroup - } - return nil -} - -func (m *GoTest) GetRepeatedgroup() []*GoTest_RepeatedGroup { - if m != nil { - return m.Repeatedgroup - } - return nil -} - -func (m *GoTest) GetOptionalgroup() *GoTest_OptionalGroup { - if m != nil { - return m.Optionalgroup - } - return nil -} - -// Required, repeated, and optional groups. -type GoTest_RequiredGroup struct { - RequiredField *string `protobuf:"bytes,71,req" json:"RequiredField,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GoTest_RequiredGroup) Reset() { *m = GoTest_RequiredGroup{} } -func (m *GoTest_RequiredGroup) String() string { return proto.CompactTextString(m) } -func (*GoTest_RequiredGroup) ProtoMessage() {} - -func (m *GoTest_RequiredGroup) GetRequiredField() string { - if m != nil && m.RequiredField != nil { - return *m.RequiredField - } - return "" -} - -type GoTest_RepeatedGroup struct { - RequiredField *string `protobuf:"bytes,81,req" json:"RequiredField,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GoTest_RepeatedGroup) Reset() { *m = GoTest_RepeatedGroup{} } -func (m *GoTest_RepeatedGroup) String() string { return proto.CompactTextString(m) } -func (*GoTest_RepeatedGroup) ProtoMessage() {} - -func (m *GoTest_RepeatedGroup) GetRequiredField() string { - if m != nil && m.RequiredField != nil { - return *m.RequiredField - } - return "" -} - -type GoTest_OptionalGroup struct { - RequiredField *string `protobuf:"bytes,91,req" json:"RequiredField,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GoTest_OptionalGroup) Reset() { *m = GoTest_OptionalGroup{} } -func (m *GoTest_OptionalGroup) String() string { return proto.CompactTextString(m) } -func (*GoTest_OptionalGroup) ProtoMessage() {} - -func (m *GoTest_OptionalGroup) GetRequiredField() string { - if m != nil && m.RequiredField != nil { - return *m.RequiredField - } - return "" -} - -// For testing skipping of unrecognized fields. -// Numbers are all big, larger than tag numbers in GoTestField, -// the message used in the corresponding test. -type GoSkipTest struct { - SkipInt32 *int32 `protobuf:"varint,11,req,name=skip_int32" json:"skip_int32,omitempty"` - SkipFixed32 *uint32 `protobuf:"fixed32,12,req,name=skip_fixed32" json:"skip_fixed32,omitempty"` - SkipFixed64 *uint64 `protobuf:"fixed64,13,req,name=skip_fixed64" json:"skip_fixed64,omitempty"` - SkipString *string `protobuf:"bytes,14,req,name=skip_string" json:"skip_string,omitempty"` - Skipgroup *GoSkipTest_SkipGroup `protobuf:"group,15,req,name=SkipGroup" json:"skipgroup,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GoSkipTest) Reset() { *m = GoSkipTest{} } -func (m *GoSkipTest) String() string { return proto.CompactTextString(m) } -func (*GoSkipTest) ProtoMessage() {} - -func (m *GoSkipTest) GetSkipInt32() int32 { - if m != nil && m.SkipInt32 != nil { - return *m.SkipInt32 - } - return 0 -} - -func (m *GoSkipTest) GetSkipFixed32() uint32 { - if m != nil && m.SkipFixed32 != nil { - return *m.SkipFixed32 - } - return 0 -} - -func (m *GoSkipTest) GetSkipFixed64() uint64 { - if m != nil && m.SkipFixed64 != nil { - return *m.SkipFixed64 - } - return 0 -} - -func (m *GoSkipTest) GetSkipString() string { - if m != nil && m.SkipString != nil { - return *m.SkipString - } - return "" -} - -func (m *GoSkipTest) GetSkipgroup() *GoSkipTest_SkipGroup { - if m != nil { - return m.Skipgroup - } - return nil -} - -type GoSkipTest_SkipGroup struct { - GroupInt32 *int32 `protobuf:"varint,16,req,name=group_int32" json:"group_int32,omitempty"` - GroupString *string `protobuf:"bytes,17,req,name=group_string" json:"group_string,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GoSkipTest_SkipGroup) Reset() { *m = GoSkipTest_SkipGroup{} } -func (m *GoSkipTest_SkipGroup) String() string { return proto.CompactTextString(m) } -func (*GoSkipTest_SkipGroup) ProtoMessage() {} - -func (m *GoSkipTest_SkipGroup) GetGroupInt32() int32 { - if m != nil && m.GroupInt32 != nil { - return *m.GroupInt32 - } - return 0 -} - -func (m *GoSkipTest_SkipGroup) GetGroupString() string { - if m != nil && m.GroupString != nil { - return *m.GroupString - } - return "" -} - -// For testing packed/non-packed decoder switching. -// A serialized instance of one should be deserializable as the other. -type NonPackedTest struct { - A []int32 `protobuf:"varint,1,rep,name=a" json:"a,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *NonPackedTest) Reset() { *m = NonPackedTest{} } -func (m *NonPackedTest) String() string { return proto.CompactTextString(m) } -func (*NonPackedTest) ProtoMessage() {} - -func (m *NonPackedTest) GetA() []int32 { - if m != nil { - return m.A - } - return nil -} - -type PackedTest struct { - B []int32 `protobuf:"varint,1,rep,packed,name=b" json:"b,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *PackedTest) Reset() { *m = PackedTest{} } -func (m *PackedTest) String() string { return proto.CompactTextString(m) } -func (*PackedTest) ProtoMessage() {} - -func (m *PackedTest) GetB() []int32 { - if m != nil { - return m.B - } - return nil -} - -type MaxTag struct { - // Maximum possible tag number. - LastField *string `protobuf:"bytes,536870911,opt,name=last_field" json:"last_field,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *MaxTag) Reset() { *m = MaxTag{} } -func (m *MaxTag) String() string { return proto.CompactTextString(m) } -func (*MaxTag) ProtoMessage() {} - -func (m *MaxTag) GetLastField() string { - if m != nil && m.LastField != nil { - return *m.LastField - } - return "" -} - -type OldMessage struct { - Nested *OldMessage_Nested `protobuf:"bytes,1,opt,name=nested" json:"nested,omitempty"` - Num *int32 `protobuf:"varint,2,opt,name=num" json:"num,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *OldMessage) Reset() { *m = OldMessage{} } -func (m *OldMessage) String() string { return proto.CompactTextString(m) } -func (*OldMessage) ProtoMessage() {} - -func (m *OldMessage) GetNested() *OldMessage_Nested { - if m != nil { - return m.Nested - } - return nil -} - -func (m *OldMessage) GetNum() int32 { - if m != nil && m.Num != nil { - return *m.Num - } - return 0 -} - -type OldMessage_Nested struct { - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *OldMessage_Nested) Reset() { *m = OldMessage_Nested{} } -func (m *OldMessage_Nested) String() string { return proto.CompactTextString(m) } -func (*OldMessage_Nested) ProtoMessage() {} - -func (m *OldMessage_Nested) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -// NewMessage is wire compatible with OldMessage; -// imagine it as a future version. -type NewMessage struct { - Nested *NewMessage_Nested `protobuf:"bytes,1,opt,name=nested" json:"nested,omitempty"` - // This is an int32 in OldMessage. - Num *int64 `protobuf:"varint,2,opt,name=num" json:"num,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *NewMessage) Reset() { *m = NewMessage{} } -func (m *NewMessage) String() string { return proto.CompactTextString(m) } -func (*NewMessage) ProtoMessage() {} - -func (m *NewMessage) GetNested() *NewMessage_Nested { - if m != nil { - return m.Nested - } - return nil -} - -func (m *NewMessage) GetNum() int64 { - if m != nil && m.Num != nil { - return *m.Num - } - return 0 -} - -type NewMessage_Nested struct { - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - FoodGroup *string `protobuf:"bytes,2,opt,name=food_group" json:"food_group,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *NewMessage_Nested) Reset() { *m = NewMessage_Nested{} } -func (m *NewMessage_Nested) String() string { return proto.CompactTextString(m) } -func (*NewMessage_Nested) ProtoMessage() {} - -func (m *NewMessage_Nested) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -func (m *NewMessage_Nested) GetFoodGroup() string { - if m != nil && m.FoodGroup != nil { - return *m.FoodGroup - } - return "" -} - -type InnerMessage struct { - Host *string `protobuf:"bytes,1,req,name=host" json:"host,omitempty"` - Port *int32 `protobuf:"varint,2,opt,name=port,def=4000" json:"port,omitempty"` - Connected *bool `protobuf:"varint,3,opt,name=connected" json:"connected,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *InnerMessage) Reset() { *m = InnerMessage{} } -func (m *InnerMessage) String() string { return proto.CompactTextString(m) } -func (*InnerMessage) ProtoMessage() {} - -const Default_InnerMessage_Port int32 = 4000 - -func (m *InnerMessage) GetHost() string { - if m != nil && m.Host != nil { - return *m.Host - } - return "" -} - -func (m *InnerMessage) GetPort() int32 { - if m != nil && m.Port != nil { - return *m.Port - } - return Default_InnerMessage_Port -} - -func (m *InnerMessage) GetConnected() bool { - if m != nil && m.Connected != nil { - return *m.Connected - } - return false -} - -type OtherMessage struct { - Key *int64 `protobuf:"varint,1,opt,name=key" json:"key,omitempty"` - Value []byte `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` - Weight *float32 `protobuf:"fixed32,3,opt,name=weight" json:"weight,omitempty"` - Inner *InnerMessage `protobuf:"bytes,4,opt,name=inner" json:"inner,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *OtherMessage) Reset() { *m = OtherMessage{} } -func (m *OtherMessage) String() string { return proto.CompactTextString(m) } -func (*OtherMessage) ProtoMessage() {} - -func (m *OtherMessage) GetKey() int64 { - if m != nil && m.Key != nil { - return *m.Key - } - return 0 -} - -func (m *OtherMessage) GetValue() []byte { - if m != nil { - return m.Value - } - return nil -} - -func (m *OtherMessage) GetWeight() float32 { - if m != nil && m.Weight != nil { - return *m.Weight - } - return 0 -} - -func (m *OtherMessage) GetInner() *InnerMessage { - if m != nil { - return m.Inner - } - return nil -} - -type MyMessage struct { - Count *int32 `protobuf:"varint,1,req,name=count" json:"count,omitempty"` - Name *string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` - Quote *string `protobuf:"bytes,3,opt,name=quote" json:"quote,omitempty"` - Pet []string `protobuf:"bytes,4,rep,name=pet" json:"pet,omitempty"` - Inner *InnerMessage `protobuf:"bytes,5,opt,name=inner" json:"inner,omitempty"` - Others []*OtherMessage `protobuf:"bytes,6,rep,name=others" json:"others,omitempty"` - RepInner []*InnerMessage `protobuf:"bytes,12,rep,name=rep_inner" json:"rep_inner,omitempty"` - Bikeshed *MyMessage_Color `protobuf:"varint,7,opt,name=bikeshed,enum=testdata.MyMessage_Color" json:"bikeshed,omitempty"` - Somegroup *MyMessage_SomeGroup `protobuf:"group,8,opt,name=SomeGroup" json:"somegroup,omitempty"` - // This field becomes [][]byte in the generated code. - RepBytes [][]byte `protobuf:"bytes,10,rep,name=rep_bytes" json:"rep_bytes,omitempty"` - Bigfloat *float64 `protobuf:"fixed64,11,opt,name=bigfloat" json:"bigfloat,omitempty"` - XXX_extensions map[int32]proto.Extension `json:"-"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *MyMessage) Reset() { *m = MyMessage{} } -func (m *MyMessage) String() string { return proto.CompactTextString(m) } -func (*MyMessage) ProtoMessage() {} - -var extRange_MyMessage = []proto.ExtensionRange{ - {100, 536870911}, -} - -func (*MyMessage) ExtensionRangeArray() []proto.ExtensionRange { - return extRange_MyMessage -} -func (m *MyMessage) ExtensionMap() map[int32]proto.Extension { - if m.XXX_extensions == nil { - m.XXX_extensions = make(map[int32]proto.Extension) - } - return m.XXX_extensions -} - -func (m *MyMessage) GetCount() int32 { - if m != nil && m.Count != nil { - return *m.Count - } - return 0 -} - -func (m *MyMessage) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -func (m *MyMessage) GetQuote() string { - if m != nil && m.Quote != nil { - return *m.Quote - } - return "" -} - -func (m *MyMessage) GetPet() []string { - if m != nil { - return m.Pet - } - return nil -} - -func (m *MyMessage) GetInner() *InnerMessage { - if m != nil { - return m.Inner - } - return nil -} - -func (m *MyMessage) GetOthers() []*OtherMessage { - if m != nil { - return m.Others - } - return nil -} - -func (m *MyMessage) GetRepInner() []*InnerMessage { - if m != nil { - return m.RepInner - } - return nil -} - -func (m *MyMessage) GetBikeshed() MyMessage_Color { - if m != nil && m.Bikeshed != nil { - return *m.Bikeshed - } - return MyMessage_RED -} - -func (m *MyMessage) GetSomegroup() *MyMessage_SomeGroup { - if m != nil { - return m.Somegroup - } - return nil -} - -func (m *MyMessage) GetRepBytes() [][]byte { - if m != nil { - return m.RepBytes - } - return nil -} - -func (m *MyMessage) GetBigfloat() float64 { - if m != nil && m.Bigfloat != nil { - return *m.Bigfloat - } - return 0 -} - -type MyMessage_SomeGroup struct { - GroupField *int32 `protobuf:"varint,9,opt,name=group_field" json:"group_field,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *MyMessage_SomeGroup) Reset() { *m = MyMessage_SomeGroup{} } -func (m *MyMessage_SomeGroup) String() string { return proto.CompactTextString(m) } -func (*MyMessage_SomeGroup) ProtoMessage() {} - -func (m *MyMessage_SomeGroup) GetGroupField() int32 { - if m != nil && m.GroupField != nil { - return *m.GroupField - } - return 0 -} - -type Ext struct { - Data *string `protobuf:"bytes,1,opt,name=data" json:"data,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Ext) Reset() { *m = Ext{} } -func (m *Ext) String() string { return proto.CompactTextString(m) } -func (*Ext) ProtoMessage() {} - -func (m *Ext) GetData() string { - if m != nil && m.Data != nil { - return *m.Data - } - return "" -} - -var E_Ext_More = &proto.ExtensionDesc{ - ExtendedType: (*MyMessage)(nil), - ExtensionType: (*Ext)(nil), - Field: 103, - Name: "testdata.Ext.more", - Tag: "bytes,103,opt,name=more", -} - -var E_Ext_Text = &proto.ExtensionDesc{ - ExtendedType: (*MyMessage)(nil), - ExtensionType: (*string)(nil), - Field: 104, - Name: "testdata.Ext.text", - Tag: "bytes,104,opt,name=text", -} - -var E_Ext_Number = &proto.ExtensionDesc{ - ExtendedType: (*MyMessage)(nil), - ExtensionType: (*int32)(nil), - Field: 105, - Name: "testdata.Ext.number", - Tag: "varint,105,opt,name=number", -} - -type MyMessageSet struct { - XXX_extensions map[int32]proto.Extension `json:"-"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *MyMessageSet) Reset() { *m = MyMessageSet{} } -func (m *MyMessageSet) String() string { return proto.CompactTextString(m) } -func (*MyMessageSet) ProtoMessage() {} - -func (m *MyMessageSet) Marshal() ([]byte, error) { - return proto.MarshalMessageSet(m.ExtensionMap()) -} -func (m *MyMessageSet) Unmarshal(buf []byte) error { - return proto.UnmarshalMessageSet(buf, m.ExtensionMap()) -} -func (m *MyMessageSet) MarshalJSON() ([]byte, error) { - return proto.MarshalMessageSetJSON(m.XXX_extensions) -} -func (m *MyMessageSet) UnmarshalJSON(buf []byte) error { - return proto.UnmarshalMessageSetJSON(buf, m.XXX_extensions) -} - -// ensure MyMessageSet satisfies proto.Marshaler and proto.Unmarshaler -var _ proto.Marshaler = (*MyMessageSet)(nil) -var _ proto.Unmarshaler = (*MyMessageSet)(nil) - -var extRange_MyMessageSet = []proto.ExtensionRange{ - {100, 2147483646}, -} - -func (*MyMessageSet) ExtensionRangeArray() []proto.ExtensionRange { - return extRange_MyMessageSet -} -func (m *MyMessageSet) ExtensionMap() map[int32]proto.Extension { - if m.XXX_extensions == nil { - m.XXX_extensions = make(map[int32]proto.Extension) - } - return m.XXX_extensions -} - -type Empty struct { - XXX_unrecognized []byte `json:"-"` -} - -func (m *Empty) Reset() { *m = Empty{} } -func (m *Empty) String() string { return proto.CompactTextString(m) } -func (*Empty) ProtoMessage() {} - -type MessageList struct { - Message []*MessageList_Message `protobuf:"group,1,rep" json:"message,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *MessageList) Reset() { *m = MessageList{} } -func (m *MessageList) String() string { return proto.CompactTextString(m) } -func (*MessageList) ProtoMessage() {} - -func (m *MessageList) GetMessage() []*MessageList_Message { - if m != nil { - return m.Message - } - return nil -} - -type MessageList_Message struct { - Name *string `protobuf:"bytes,2,req,name=name" json:"name,omitempty"` - Count *int32 `protobuf:"varint,3,req,name=count" json:"count,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *MessageList_Message) Reset() { *m = MessageList_Message{} } -func (m *MessageList_Message) String() string { return proto.CompactTextString(m) } -func (*MessageList_Message) ProtoMessage() {} - -func (m *MessageList_Message) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -func (m *MessageList_Message) GetCount() int32 { - if m != nil && m.Count != nil { - return *m.Count - } - return 0 -} - -type Strings struct { - StringField *string `protobuf:"bytes,1,opt,name=string_field" json:"string_field,omitempty"` - BytesField []byte `protobuf:"bytes,2,opt,name=bytes_field" json:"bytes_field,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Strings) Reset() { *m = Strings{} } -func (m *Strings) String() string { return proto.CompactTextString(m) } -func (*Strings) ProtoMessage() {} - -func (m *Strings) GetStringField() string { - if m != nil && m.StringField != nil { - return *m.StringField - } - return "" -} - -func (m *Strings) GetBytesField() []byte { - if m != nil { - return m.BytesField - } - return nil -} - -type Defaults struct { - // Default-valued fields of all basic types. - // Same as GoTest, but copied here to make testing easier. - F_Bool *bool `protobuf:"varint,1,opt,def=1" json:"F_Bool,omitempty"` - F_Int32 *int32 `protobuf:"varint,2,opt,def=32" json:"F_Int32,omitempty"` - F_Int64 *int64 `protobuf:"varint,3,opt,def=64" json:"F_Int64,omitempty"` - F_Fixed32 *uint32 `protobuf:"fixed32,4,opt,def=320" json:"F_Fixed32,omitempty"` - F_Fixed64 *uint64 `protobuf:"fixed64,5,opt,def=640" json:"F_Fixed64,omitempty"` - F_Uint32 *uint32 `protobuf:"varint,6,opt,def=3200" json:"F_Uint32,omitempty"` - F_Uint64 *uint64 `protobuf:"varint,7,opt,def=6400" json:"F_Uint64,omitempty"` - F_Float *float32 `protobuf:"fixed32,8,opt,def=314159" json:"F_Float,omitempty"` - F_Double *float64 `protobuf:"fixed64,9,opt,def=271828" json:"F_Double,omitempty"` - F_String *string `protobuf:"bytes,10,opt,def=hello, \"world!\"\n" json:"F_String,omitempty"` - F_Bytes []byte `protobuf:"bytes,11,opt,def=Bignose" json:"F_Bytes,omitempty"` - F_Sint32 *int32 `protobuf:"zigzag32,12,opt,def=-32" json:"F_Sint32,omitempty"` - F_Sint64 *int64 `protobuf:"zigzag64,13,opt,def=-64" json:"F_Sint64,omitempty"` - F_Enum *Defaults_Color `protobuf:"varint,14,opt,enum=testdata.Defaults_Color,def=1" json:"F_Enum,omitempty"` - // More fields with crazy defaults. - F_Pinf *float32 `protobuf:"fixed32,15,opt,def=inf" json:"F_Pinf,omitempty"` - F_Ninf *float32 `protobuf:"fixed32,16,opt,def=-inf" json:"F_Ninf,omitempty"` - F_Nan *float32 `protobuf:"fixed32,17,opt,def=nan" json:"F_Nan,omitempty"` - // Sub-message. - Sub *SubDefaults `protobuf:"bytes,18,opt,name=sub" json:"sub,omitempty"` - // Redundant but explicit defaults. - StrZero *string `protobuf:"bytes,19,opt,name=str_zero,def=" json:"str_zero,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Defaults) Reset() { *m = Defaults{} } -func (m *Defaults) String() string { return proto.CompactTextString(m) } -func (*Defaults) ProtoMessage() {} - -const Default_Defaults_F_Bool bool = true -const Default_Defaults_F_Int32 int32 = 32 -const Default_Defaults_F_Int64 int64 = 64 -const Default_Defaults_F_Fixed32 uint32 = 320 -const Default_Defaults_F_Fixed64 uint64 = 640 -const Default_Defaults_F_Uint32 uint32 = 3200 -const Default_Defaults_F_Uint64 uint64 = 6400 -const Default_Defaults_F_Float float32 = 314159 -const Default_Defaults_F_Double float64 = 271828 -const Default_Defaults_F_String string = "hello, \"world!\"\n" - -var Default_Defaults_F_Bytes []byte = []byte("Bignose") - -const Default_Defaults_F_Sint32 int32 = -32 -const Default_Defaults_F_Sint64 int64 = -64 -const Default_Defaults_F_Enum Defaults_Color = Defaults_GREEN - -var Default_Defaults_F_Pinf float32 = float32(math.Inf(1)) -var Default_Defaults_F_Ninf float32 = float32(math.Inf(-1)) -var Default_Defaults_F_Nan float32 = float32(math.NaN()) - -func (m *Defaults) GetF_Bool() bool { - if m != nil && m.F_Bool != nil { - return *m.F_Bool - } - return Default_Defaults_F_Bool -} - -func (m *Defaults) GetF_Int32() int32 { - if m != nil && m.F_Int32 != nil { - return *m.F_Int32 - } - return Default_Defaults_F_Int32 -} - -func (m *Defaults) GetF_Int64() int64 { - if m != nil && m.F_Int64 != nil { - return *m.F_Int64 - } - return Default_Defaults_F_Int64 -} - -func (m *Defaults) GetF_Fixed32() uint32 { - if m != nil && m.F_Fixed32 != nil { - return *m.F_Fixed32 - } - return Default_Defaults_F_Fixed32 -} - -func (m *Defaults) GetF_Fixed64() uint64 { - if m != nil && m.F_Fixed64 != nil { - return *m.F_Fixed64 - } - return Default_Defaults_F_Fixed64 -} - -func (m *Defaults) GetF_Uint32() uint32 { - if m != nil && m.F_Uint32 != nil { - return *m.F_Uint32 - } - return Default_Defaults_F_Uint32 -} - -func (m *Defaults) GetF_Uint64() uint64 { - if m != nil && m.F_Uint64 != nil { - return *m.F_Uint64 - } - return Default_Defaults_F_Uint64 -} - -func (m *Defaults) GetF_Float() float32 { - if m != nil && m.F_Float != nil { - return *m.F_Float - } - return Default_Defaults_F_Float -} - -func (m *Defaults) GetF_Double() float64 { - if m != nil && m.F_Double != nil { - return *m.F_Double - } - return Default_Defaults_F_Double -} - -func (m *Defaults) GetF_String() string { - if m != nil && m.F_String != nil { - return *m.F_String - } - return Default_Defaults_F_String -} - -func (m *Defaults) GetF_Bytes() []byte { - if m != nil && m.F_Bytes != nil { - return m.F_Bytes - } - return append([]byte(nil), Default_Defaults_F_Bytes...) -} - -func (m *Defaults) GetF_Sint32() int32 { - if m != nil && m.F_Sint32 != nil { - return *m.F_Sint32 - } - return Default_Defaults_F_Sint32 -} - -func (m *Defaults) GetF_Sint64() int64 { - if m != nil && m.F_Sint64 != nil { - return *m.F_Sint64 - } - return Default_Defaults_F_Sint64 -} - -func (m *Defaults) GetF_Enum() Defaults_Color { - if m != nil && m.F_Enum != nil { - return *m.F_Enum - } - return Default_Defaults_F_Enum -} - -func (m *Defaults) GetF_Pinf() float32 { - if m != nil && m.F_Pinf != nil { - return *m.F_Pinf - } - return Default_Defaults_F_Pinf -} - -func (m *Defaults) GetF_Ninf() float32 { - if m != nil && m.F_Ninf != nil { - return *m.F_Ninf - } - return Default_Defaults_F_Ninf -} - -func (m *Defaults) GetF_Nan() float32 { - if m != nil && m.F_Nan != nil { - return *m.F_Nan - } - return Default_Defaults_F_Nan -} - -func (m *Defaults) GetSub() *SubDefaults { - if m != nil { - return m.Sub - } - return nil -} - -func (m *Defaults) GetStrZero() string { - if m != nil && m.StrZero != nil { - return *m.StrZero - } - return "" -} - -type SubDefaults struct { - N *int64 `protobuf:"varint,1,opt,name=n,def=7" json:"n,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *SubDefaults) Reset() { *m = SubDefaults{} } -func (m *SubDefaults) String() string { return proto.CompactTextString(m) } -func (*SubDefaults) ProtoMessage() {} - -const Default_SubDefaults_N int64 = 7 - -func (m *SubDefaults) GetN() int64 { - if m != nil && m.N != nil { - return *m.N - } - return Default_SubDefaults_N -} - -type RepeatedEnum struct { - Color []RepeatedEnum_Color `protobuf:"varint,1,rep,name=color,enum=testdata.RepeatedEnum_Color" json:"color,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *RepeatedEnum) Reset() { *m = RepeatedEnum{} } -func (m *RepeatedEnum) String() string { return proto.CompactTextString(m) } -func (*RepeatedEnum) ProtoMessage() {} - -func (m *RepeatedEnum) GetColor() []RepeatedEnum_Color { - if m != nil { - return m.Color - } - return nil -} - -type MoreRepeated struct { - Bools []bool `protobuf:"varint,1,rep,name=bools" json:"bools,omitempty"` - BoolsPacked []bool `protobuf:"varint,2,rep,packed,name=bools_packed" json:"bools_packed,omitempty"` - Ints []int32 `protobuf:"varint,3,rep,name=ints" json:"ints,omitempty"` - IntsPacked []int32 `protobuf:"varint,4,rep,packed,name=ints_packed" json:"ints_packed,omitempty"` - Int64SPacked []int64 `protobuf:"varint,7,rep,packed,name=int64s_packed" json:"int64s_packed,omitempty"` - Strings []string `protobuf:"bytes,5,rep,name=strings" json:"strings,omitempty"` - Fixeds []uint32 `protobuf:"fixed32,6,rep,name=fixeds" json:"fixeds,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *MoreRepeated) Reset() { *m = MoreRepeated{} } -func (m *MoreRepeated) String() string { return proto.CompactTextString(m) } -func (*MoreRepeated) ProtoMessage() {} - -func (m *MoreRepeated) GetBools() []bool { - if m != nil { - return m.Bools - } - return nil -} - -func (m *MoreRepeated) GetBoolsPacked() []bool { - if m != nil { - return m.BoolsPacked - } - return nil -} - -func (m *MoreRepeated) GetInts() []int32 { - if m != nil { - return m.Ints - } - return nil -} - -func (m *MoreRepeated) GetIntsPacked() []int32 { - if m != nil { - return m.IntsPacked - } - return nil -} - -func (m *MoreRepeated) GetInt64SPacked() []int64 { - if m != nil { - return m.Int64SPacked - } - return nil -} - -func (m *MoreRepeated) GetStrings() []string { - if m != nil { - return m.Strings - } - return nil -} - -func (m *MoreRepeated) GetFixeds() []uint32 { - if m != nil { - return m.Fixeds - } - return nil -} - -type GroupOld struct { - G *GroupOld_G `protobuf:"group,101,opt" json:"g,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GroupOld) Reset() { *m = GroupOld{} } -func (m *GroupOld) String() string { return proto.CompactTextString(m) } -func (*GroupOld) ProtoMessage() {} - -func (m *GroupOld) GetG() *GroupOld_G { - if m != nil { - return m.G - } - return nil -} - -type GroupOld_G struct { - X *int32 `protobuf:"varint,2,opt,name=x" json:"x,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GroupOld_G) Reset() { *m = GroupOld_G{} } -func (m *GroupOld_G) String() string { return proto.CompactTextString(m) } -func (*GroupOld_G) ProtoMessage() {} - -func (m *GroupOld_G) GetX() int32 { - if m != nil && m.X != nil { - return *m.X - } - return 0 -} - -type GroupNew struct { - G *GroupNew_G `protobuf:"group,101,opt" json:"g,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GroupNew) Reset() { *m = GroupNew{} } -func (m *GroupNew) String() string { return proto.CompactTextString(m) } -func (*GroupNew) ProtoMessage() {} - -func (m *GroupNew) GetG() *GroupNew_G { - if m != nil { - return m.G - } - return nil -} - -type GroupNew_G struct { - X *int32 `protobuf:"varint,2,opt,name=x" json:"x,omitempty"` - Y *int32 `protobuf:"varint,3,opt,name=y" json:"y,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GroupNew_G) Reset() { *m = GroupNew_G{} } -func (m *GroupNew_G) String() string { return proto.CompactTextString(m) } -func (*GroupNew_G) ProtoMessage() {} - -func (m *GroupNew_G) GetX() int32 { - if m != nil && m.X != nil { - return *m.X - } - return 0 -} - -func (m *GroupNew_G) GetY() int32 { - if m != nil && m.Y != nil { - return *m.Y - } - return 0 -} - -type FloatingPoint struct { - F *float64 `protobuf:"fixed64,1,req,name=f" json:"f,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *FloatingPoint) Reset() { *m = FloatingPoint{} } -func (m *FloatingPoint) String() string { return proto.CompactTextString(m) } -func (*FloatingPoint) ProtoMessage() {} - -func (m *FloatingPoint) GetF() float64 { - if m != nil && m.F != nil { - return *m.F - } - return 0 -} - -type MessageWithMap struct { - NameMapping map[int32]string `protobuf:"bytes,1,rep,name=name_mapping" json:"name_mapping,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - MsgMapping map[int64]*FloatingPoint `protobuf:"bytes,2,rep,name=msg_mapping" json:"msg_mapping,omitempty" protobuf_key:"zigzag64,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - ByteMapping map[bool][]byte `protobuf:"bytes,3,rep,name=byte_mapping" json:"byte_mapping,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *MessageWithMap) Reset() { *m = MessageWithMap{} } -func (m *MessageWithMap) String() string { return proto.CompactTextString(m) } -func (*MessageWithMap) ProtoMessage() {} - -func (m *MessageWithMap) GetNameMapping() map[int32]string { - if m != nil { - return m.NameMapping - } - return nil -} - -func (m *MessageWithMap) GetMsgMapping() map[int64]*FloatingPoint { - if m != nil { - return m.MsgMapping - } - return nil -} - -func (m *MessageWithMap) GetByteMapping() map[bool][]byte { - if m != nil { - return m.ByteMapping - } - return nil -} - -var E_Greeting = &proto.ExtensionDesc{ - ExtendedType: (*MyMessage)(nil), - ExtensionType: ([]string)(nil), - Field: 106, - Name: "testdata.greeting", - Tag: "bytes,106,rep,name=greeting", -} - -var E_X201 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 201, - Name: "testdata.x201", - Tag: "bytes,201,opt,name=x201", -} - -var E_X202 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 202, - Name: "testdata.x202", - Tag: "bytes,202,opt,name=x202", -} - -var E_X203 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 203, - Name: "testdata.x203", - Tag: "bytes,203,opt,name=x203", -} - -var E_X204 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 204, - Name: "testdata.x204", - Tag: "bytes,204,opt,name=x204", -} - -var E_X205 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 205, - Name: "testdata.x205", - Tag: "bytes,205,opt,name=x205", -} - -var E_X206 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 206, - Name: "testdata.x206", - Tag: "bytes,206,opt,name=x206", -} - -var E_X207 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 207, - Name: "testdata.x207", - Tag: "bytes,207,opt,name=x207", -} - -var E_X208 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 208, - Name: "testdata.x208", - Tag: "bytes,208,opt,name=x208", -} - -var E_X209 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 209, - Name: "testdata.x209", - Tag: "bytes,209,opt,name=x209", -} - -var E_X210 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 210, - Name: "testdata.x210", - Tag: "bytes,210,opt,name=x210", -} - -var E_X211 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 211, - Name: "testdata.x211", - Tag: "bytes,211,opt,name=x211", -} - -var E_X212 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 212, - Name: "testdata.x212", - Tag: "bytes,212,opt,name=x212", -} - -var E_X213 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 213, - Name: "testdata.x213", - Tag: "bytes,213,opt,name=x213", -} - -var E_X214 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 214, - Name: "testdata.x214", - Tag: "bytes,214,opt,name=x214", -} - -var E_X215 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 215, - Name: "testdata.x215", - Tag: "bytes,215,opt,name=x215", -} - -var E_X216 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 216, - Name: "testdata.x216", - Tag: "bytes,216,opt,name=x216", -} - -var E_X217 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 217, - Name: "testdata.x217", - Tag: "bytes,217,opt,name=x217", -} - -var E_X218 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 218, - Name: "testdata.x218", - Tag: "bytes,218,opt,name=x218", -} - -var E_X219 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 219, - Name: "testdata.x219", - Tag: "bytes,219,opt,name=x219", -} - -var E_X220 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 220, - Name: "testdata.x220", - Tag: "bytes,220,opt,name=x220", -} - -var E_X221 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 221, - Name: "testdata.x221", - Tag: "bytes,221,opt,name=x221", -} - -var E_X222 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 222, - Name: "testdata.x222", - Tag: "bytes,222,opt,name=x222", -} - -var E_X223 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 223, - Name: "testdata.x223", - Tag: "bytes,223,opt,name=x223", -} - -var E_X224 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 224, - Name: "testdata.x224", - Tag: "bytes,224,opt,name=x224", -} - -var E_X225 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 225, - Name: "testdata.x225", - Tag: "bytes,225,opt,name=x225", -} - -var E_X226 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 226, - Name: "testdata.x226", - Tag: "bytes,226,opt,name=x226", -} - -var E_X227 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 227, - Name: "testdata.x227", - Tag: "bytes,227,opt,name=x227", -} - -var E_X228 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 228, - Name: "testdata.x228", - Tag: "bytes,228,opt,name=x228", -} - -var E_X229 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 229, - Name: "testdata.x229", - Tag: "bytes,229,opt,name=x229", -} - -var E_X230 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 230, - Name: "testdata.x230", - Tag: "bytes,230,opt,name=x230", -} - -var E_X231 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 231, - Name: "testdata.x231", - Tag: "bytes,231,opt,name=x231", -} - -var E_X232 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 232, - Name: "testdata.x232", - Tag: "bytes,232,opt,name=x232", -} - -var E_X233 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 233, - Name: "testdata.x233", - Tag: "bytes,233,opt,name=x233", -} - -var E_X234 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 234, - Name: "testdata.x234", - Tag: "bytes,234,opt,name=x234", -} - -var E_X235 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 235, - Name: "testdata.x235", - Tag: "bytes,235,opt,name=x235", -} - -var E_X236 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 236, - Name: "testdata.x236", - Tag: "bytes,236,opt,name=x236", -} - -var E_X237 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 237, - Name: "testdata.x237", - Tag: "bytes,237,opt,name=x237", -} - -var E_X238 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 238, - Name: "testdata.x238", - Tag: "bytes,238,opt,name=x238", -} - -var E_X239 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 239, - Name: "testdata.x239", - Tag: "bytes,239,opt,name=x239", -} - -var E_X240 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 240, - Name: "testdata.x240", - Tag: "bytes,240,opt,name=x240", -} - -var E_X241 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 241, - Name: "testdata.x241", - Tag: "bytes,241,opt,name=x241", -} - -var E_X242 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 242, - Name: "testdata.x242", - Tag: "bytes,242,opt,name=x242", -} - -var E_X243 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 243, - Name: "testdata.x243", - Tag: "bytes,243,opt,name=x243", -} - -var E_X244 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 244, - Name: "testdata.x244", - Tag: "bytes,244,opt,name=x244", -} - -var E_X245 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 245, - Name: "testdata.x245", - Tag: "bytes,245,opt,name=x245", -} - -var E_X246 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 246, - Name: "testdata.x246", - Tag: "bytes,246,opt,name=x246", -} - -var E_X247 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 247, - Name: "testdata.x247", - Tag: "bytes,247,opt,name=x247", -} - -var E_X248 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 248, - Name: "testdata.x248", - Tag: "bytes,248,opt,name=x248", -} - -var E_X249 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 249, - Name: "testdata.x249", - Tag: "bytes,249,opt,name=x249", -} - -var E_X250 = &proto.ExtensionDesc{ - ExtendedType: (*MyMessageSet)(nil), - ExtensionType: (*Empty)(nil), - Field: 250, - Name: "testdata.x250", - Tag: "bytes,250,opt,name=x250", -} - -func init() { - proto.RegisterEnum("testdata.FOO", FOO_name, FOO_value) - proto.RegisterEnum("testdata.GoTest_KIND", GoTest_KIND_name, GoTest_KIND_value) - proto.RegisterEnum("testdata.MyMessage_Color", MyMessage_Color_name, MyMessage_Color_value) - proto.RegisterEnum("testdata.Defaults_Color", Defaults_Color_name, Defaults_Color_value) - proto.RegisterEnum("testdata.RepeatedEnum_Color", RepeatedEnum_Color_name, RepeatedEnum_Color_value) - proto.RegisterExtension(E_Ext_More) - proto.RegisterExtension(E_Ext_Text) - proto.RegisterExtension(E_Ext_Number) - proto.RegisterExtension(E_Greeting) - proto.RegisterExtension(E_X201) - proto.RegisterExtension(E_X202) - proto.RegisterExtension(E_X203) - proto.RegisterExtension(E_X204) - proto.RegisterExtension(E_X205) - proto.RegisterExtension(E_X206) - proto.RegisterExtension(E_X207) - proto.RegisterExtension(E_X208) - proto.RegisterExtension(E_X209) - proto.RegisterExtension(E_X210) - proto.RegisterExtension(E_X211) - proto.RegisterExtension(E_X212) - proto.RegisterExtension(E_X213) - proto.RegisterExtension(E_X214) - proto.RegisterExtension(E_X215) - proto.RegisterExtension(E_X216) - proto.RegisterExtension(E_X217) - proto.RegisterExtension(E_X218) - proto.RegisterExtension(E_X219) - proto.RegisterExtension(E_X220) - proto.RegisterExtension(E_X221) - proto.RegisterExtension(E_X222) - proto.RegisterExtension(E_X223) - proto.RegisterExtension(E_X224) - proto.RegisterExtension(E_X225) - proto.RegisterExtension(E_X226) - proto.RegisterExtension(E_X227) - proto.RegisterExtension(E_X228) - proto.RegisterExtension(E_X229) - proto.RegisterExtension(E_X230) - proto.RegisterExtension(E_X231) - proto.RegisterExtension(E_X232) - proto.RegisterExtension(E_X233) - proto.RegisterExtension(E_X234) - proto.RegisterExtension(E_X235) - proto.RegisterExtension(E_X236) - proto.RegisterExtension(E_X237) - proto.RegisterExtension(E_X238) - proto.RegisterExtension(E_X239) - proto.RegisterExtension(E_X240) - proto.RegisterExtension(E_X241) - proto.RegisterExtension(E_X242) - proto.RegisterExtension(E_X243) - proto.RegisterExtension(E_X244) - proto.RegisterExtension(E_X245) - proto.RegisterExtension(E_X246) - proto.RegisterExtension(E_X247) - proto.RegisterExtension(E_X248) - proto.RegisterExtension(E_X249) - proto.RegisterExtension(E_X250) -} diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.proto b/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.proto deleted file mode 100644 index 6cc755bae..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.proto +++ /dev/null @@ -1,434 +0,0 @@ -// 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. - -// A feature-rich test file for the protocol compiler and libraries. - -syntax = "proto2"; - -package testdata; - -enum FOO { FOO1 = 1; }; - -message GoEnum { - required FOO foo = 1; -} - -message GoTestField { - required string Label = 1; - required string Type = 2; -} - -message GoTest { - // An enum, for completeness. - enum KIND { - VOID = 0; - - // Basic types - BOOL = 1; - BYTES = 2; - FINGERPRINT = 3; - FLOAT = 4; - INT = 5; - STRING = 6; - TIME = 7; - - // Groupings - TUPLE = 8; - ARRAY = 9; - MAP = 10; - - // Table types - TABLE = 11; - - // Functions - FUNCTION = 12; // last tag - }; - - // Some typical parameters - required KIND Kind = 1; - optional string Table = 2; - optional int32 Param = 3; - - // Required, repeated and optional foreign fields. - required GoTestField RequiredField = 4; - repeated GoTestField RepeatedField = 5; - optional GoTestField OptionalField = 6; - - // Required fields of all basic types - required bool F_Bool_required = 10; - required int32 F_Int32_required = 11; - required int64 F_Int64_required = 12; - required fixed32 F_Fixed32_required = 13; - required fixed64 F_Fixed64_required = 14; - required uint32 F_Uint32_required = 15; - required uint64 F_Uint64_required = 16; - required float F_Float_required = 17; - required double F_Double_required = 18; - required string F_String_required = 19; - required bytes F_Bytes_required = 101; - required sint32 F_Sint32_required = 102; - required sint64 F_Sint64_required = 103; - - // Repeated fields of all basic types - repeated bool F_Bool_repeated = 20; - repeated int32 F_Int32_repeated = 21; - repeated int64 F_Int64_repeated = 22; - repeated fixed32 F_Fixed32_repeated = 23; - repeated fixed64 F_Fixed64_repeated = 24; - repeated uint32 F_Uint32_repeated = 25; - repeated uint64 F_Uint64_repeated = 26; - repeated float F_Float_repeated = 27; - repeated double F_Double_repeated = 28; - repeated string F_String_repeated = 29; - repeated bytes F_Bytes_repeated = 201; - repeated sint32 F_Sint32_repeated = 202; - repeated sint64 F_Sint64_repeated = 203; - - // Optional fields of all basic types - optional bool F_Bool_optional = 30; - optional int32 F_Int32_optional = 31; - optional int64 F_Int64_optional = 32; - optional fixed32 F_Fixed32_optional = 33; - optional fixed64 F_Fixed64_optional = 34; - optional uint32 F_Uint32_optional = 35; - optional uint64 F_Uint64_optional = 36; - optional float F_Float_optional = 37; - optional double F_Double_optional = 38; - optional string F_String_optional = 39; - optional bytes F_Bytes_optional = 301; - optional sint32 F_Sint32_optional = 302; - optional sint64 F_Sint64_optional = 303; - - // Default-valued fields of all basic types - optional bool F_Bool_defaulted = 40 [default=true]; - optional int32 F_Int32_defaulted = 41 [default=32]; - optional int64 F_Int64_defaulted = 42 [default=64]; - optional fixed32 F_Fixed32_defaulted = 43 [default=320]; - optional fixed64 F_Fixed64_defaulted = 44 [default=640]; - optional uint32 F_Uint32_defaulted = 45 [default=3200]; - optional uint64 F_Uint64_defaulted = 46 [default=6400]; - optional float F_Float_defaulted = 47 [default=314159.]; - optional double F_Double_defaulted = 48 [default=271828.]; - optional string F_String_defaulted = 49 [default="hello, \"world!\"\n"]; - optional bytes F_Bytes_defaulted = 401 [default="Bignose"]; - optional sint32 F_Sint32_defaulted = 402 [default = -32]; - optional sint64 F_Sint64_defaulted = 403 [default = -64]; - - // Packed repeated fields (no string or bytes). - repeated bool F_Bool_repeated_packed = 50 [packed=true]; - repeated int32 F_Int32_repeated_packed = 51 [packed=true]; - repeated int64 F_Int64_repeated_packed = 52 [packed=true]; - repeated fixed32 F_Fixed32_repeated_packed = 53 [packed=true]; - repeated fixed64 F_Fixed64_repeated_packed = 54 [packed=true]; - repeated uint32 F_Uint32_repeated_packed = 55 [packed=true]; - repeated uint64 F_Uint64_repeated_packed = 56 [packed=true]; - repeated float F_Float_repeated_packed = 57 [packed=true]; - repeated double F_Double_repeated_packed = 58 [packed=true]; - repeated sint32 F_Sint32_repeated_packed = 502 [packed=true]; - repeated sint64 F_Sint64_repeated_packed = 503 [packed=true]; - - // Required, repeated, and optional groups. - required group RequiredGroup = 70 { - required string RequiredField = 71; - }; - - repeated group RepeatedGroup = 80 { - required string RequiredField = 81; - }; - - optional group OptionalGroup = 90 { - required string RequiredField = 91; - }; -} - -// For testing skipping of unrecognized fields. -// Numbers are all big, larger than tag numbers in GoTestField, -// the message used in the corresponding test. -message GoSkipTest { - required int32 skip_int32 = 11; - required fixed32 skip_fixed32 = 12; - required fixed64 skip_fixed64 = 13; - required string skip_string = 14; - required group SkipGroup = 15 { - required int32 group_int32 = 16; - required string group_string = 17; - } -} - -// For testing packed/non-packed decoder switching. -// A serialized instance of one should be deserializable as the other. -message NonPackedTest { - repeated int32 a = 1; -} - -message PackedTest { - repeated int32 b = 1 [packed=true]; -} - -message MaxTag { - // Maximum possible tag number. - optional string last_field = 536870911; -} - -message OldMessage { - message Nested { - optional string name = 1; - } - optional Nested nested = 1; - - optional int32 num = 2; -} - -// NewMessage is wire compatible with OldMessage; -// imagine it as a future version. -message NewMessage { - message Nested { - optional string name = 1; - optional string food_group = 2; - } - optional Nested nested = 1; - - // This is an int32 in OldMessage. - optional int64 num = 2; -} - -// Smaller tests for ASCII formatting. - -message InnerMessage { - required string host = 1; - optional int32 port = 2 [default=4000]; - optional bool connected = 3; -} - -message OtherMessage { - optional int64 key = 1; - optional bytes value = 2; - optional float weight = 3; - optional InnerMessage inner = 4; -} - -message MyMessage { - required int32 count = 1; - optional string name = 2; - optional string quote = 3; - repeated string pet = 4; - optional InnerMessage inner = 5; - repeated OtherMessage others = 6; - repeated InnerMessage rep_inner = 12; - - enum Color { - RED = 0; - GREEN = 1; - BLUE = 2; - }; - optional Color bikeshed = 7; - - optional group SomeGroup = 8 { - optional int32 group_field = 9; - } - - // This field becomes [][]byte in the generated code. - repeated bytes rep_bytes = 10; - - optional double bigfloat = 11; - - extensions 100 to max; -} - -message Ext { - extend MyMessage { - optional Ext more = 103; - optional string text = 104; - optional int32 number = 105; - } - - optional string data = 1; -} - -extend MyMessage { - repeated string greeting = 106; -} - -message MyMessageSet { - option message_set_wire_format = true; - extensions 100 to max; -} - -message Empty { -} - -extend MyMessageSet { - optional Empty x201 = 201; - optional Empty x202 = 202; - optional Empty x203 = 203; - optional Empty x204 = 204; - optional Empty x205 = 205; - optional Empty x206 = 206; - optional Empty x207 = 207; - optional Empty x208 = 208; - optional Empty x209 = 209; - optional Empty x210 = 210; - optional Empty x211 = 211; - optional Empty x212 = 212; - optional Empty x213 = 213; - optional Empty x214 = 214; - optional Empty x215 = 215; - optional Empty x216 = 216; - optional Empty x217 = 217; - optional Empty x218 = 218; - optional Empty x219 = 219; - optional Empty x220 = 220; - optional Empty x221 = 221; - optional Empty x222 = 222; - optional Empty x223 = 223; - optional Empty x224 = 224; - optional Empty x225 = 225; - optional Empty x226 = 226; - optional Empty x227 = 227; - optional Empty x228 = 228; - optional Empty x229 = 229; - optional Empty x230 = 230; - optional Empty x231 = 231; - optional Empty x232 = 232; - optional Empty x233 = 233; - optional Empty x234 = 234; - optional Empty x235 = 235; - optional Empty x236 = 236; - optional Empty x237 = 237; - optional Empty x238 = 238; - optional Empty x239 = 239; - optional Empty x240 = 240; - optional Empty x241 = 241; - optional Empty x242 = 242; - optional Empty x243 = 243; - optional Empty x244 = 244; - optional Empty x245 = 245; - optional Empty x246 = 246; - optional Empty x247 = 247; - optional Empty x248 = 248; - optional Empty x249 = 249; - optional Empty x250 = 250; -} - -message MessageList { - repeated group Message = 1 { - required string name = 2; - required int32 count = 3; - } -} - -message Strings { - optional string string_field = 1; - optional bytes bytes_field = 2; -} - -message Defaults { - enum Color { - RED = 0; - GREEN = 1; - BLUE = 2; - } - - // Default-valued fields of all basic types. - // Same as GoTest, but copied here to make testing easier. - optional bool F_Bool = 1 [default=true]; - optional int32 F_Int32 = 2 [default=32]; - optional int64 F_Int64 = 3 [default=64]; - optional fixed32 F_Fixed32 = 4 [default=320]; - optional fixed64 F_Fixed64 = 5 [default=640]; - optional uint32 F_Uint32 = 6 [default=3200]; - optional uint64 F_Uint64 = 7 [default=6400]; - optional float F_Float = 8 [default=314159.]; - optional double F_Double = 9 [default=271828.]; - optional string F_String = 10 [default="hello, \"world!\"\n"]; - optional bytes F_Bytes = 11 [default="Bignose"]; - optional sint32 F_Sint32 = 12 [default=-32]; - optional sint64 F_Sint64 = 13 [default=-64]; - optional Color F_Enum = 14 [default=GREEN]; - - // More fields with crazy defaults. - optional float F_Pinf = 15 [default=inf]; - optional float F_Ninf = 16 [default=-inf]; - optional float F_Nan = 17 [default=nan]; - - // Sub-message. - optional SubDefaults sub = 18; - - // Redundant but explicit defaults. - optional string str_zero = 19 [default=""]; -} - -message SubDefaults { - optional int64 n = 1 [default=7]; -} - -message RepeatedEnum { - enum Color { - RED = 1; - } - repeated Color color = 1; -} - -message MoreRepeated { - repeated bool bools = 1; - repeated bool bools_packed = 2 [packed=true]; - repeated int32 ints = 3; - repeated int32 ints_packed = 4 [packed=true]; - repeated int64 int64s_packed = 7 [packed=true]; - repeated string strings = 5; - repeated fixed32 fixeds = 6; -} - -// GroupOld and GroupNew have the same wire format. -// GroupNew has a new field inside a group. - -message GroupOld { - optional group G = 101 { - optional int32 x = 2; - } -} - -message GroupNew { - optional group G = 101 { - optional int32 x = 2; - optional int32 y = 3; - } -} - -message FloatingPoint { - required double f = 1; -} - -message MessageWithMap { - map name_mapping = 1; - map msg_mapping = 2; - map byte_mapping = 3; -} diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/text.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/text.go index 720eac470..2336b144c 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/text.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/text.go @@ -37,11 +37,11 @@ import ( "bufio" "bytes" "encoding" + "errors" "fmt" "io" "log" "math" - "os" "reflect" "sort" "strings" @@ -170,20 +170,12 @@ func writeName(w *textWriter, props *Properties) error { return nil } -var ( - messageSetType = reflect.TypeOf((*MessageSet)(nil)).Elem() -) - // raw is the interface satisfied by RawMessage. type raw interface { Bytes() []byte } func writeStruct(w *textWriter, sv reflect.Value) error { - if sv.Type() == messageSetType { - return writeMessageSet(w, sv.Addr().Interface().(*MessageSet)) - } - st := sv.Type() sprops := GetProperties(st) for i := 0; i < sv.NumField(); i++ { @@ -246,7 +238,7 @@ func writeStruct(w *textWriter, sv reflect.Value) error { } if fv.Kind() == reflect.Map { // Map fields are rendered as a repeated struct with key/value fields. - keys := fv.MapKeys() // TODO: should we sort these for deterministic output? + keys := fv.MapKeys() sort.Sort(mapKeys(keys)) for _, key := range keys { val := fv.MapIndex(key) @@ -283,20 +275,23 @@ func writeStruct(w *textWriter, sv reflect.Value) error { if err := w.WriteByte('\n'); err != nil { return err } - // value - if _, err := w.WriteString("value:"); err != nil { - return err - } - if !w.compact { - if err := w.WriteByte(' '); err != nil { + // nil values aren't legal, but we can avoid panicking because of them. + if val.Kind() != reflect.Ptr || !val.IsNil() { + // value + if _, err := w.WriteString("value:"); err != nil { + return err + } + if !w.compact { + if err := w.WriteByte(' '); err != nil { + return err + } + } + if err := writeAny(w, val, props.mvalprop); err != nil { + return err + } + if err := w.WriteByte('\n'); err != nil { return err } - } - if err := writeAny(w, val, props.mvalprop); err != nil { - return err - } - if err := w.WriteByte('\n'); err != nil { - return err } // close struct w.unindent() @@ -315,26 +310,34 @@ func writeStruct(w *textWriter, sv reflect.Value) error { } if fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice { // proto3 non-repeated scalar field; skip if zero value - switch fv.Kind() { - case reflect.Bool: - if !fv.Bool() { + if isProto3Zero(fv) { + continue + } + } + + if fv.Kind() == reflect.Interface { + // Check if it is a oneof. + if st.Field(i).Tag.Get("protobuf_oneof") != "" { + // fv is nil, or holds a pointer to generated struct. + // That generated struct has exactly one field, + // which has a protobuf struct tag. + if fv.IsNil() { continue } - case reflect.Int32, reflect.Int64: - if fv.Int() == 0 { - continue - } - case reflect.Uint32, reflect.Uint64: - if fv.Uint() == 0 { - continue - } - case reflect.Float32, reflect.Float64: - if fv.Float() == 0 { - continue - } - case reflect.String: - if fv.String() == "" { - continue + inner := fv.Elem().Elem() // interface -> *T -> T + tag := inner.Type().Field(0).Tag.Get("protobuf") + props = new(Properties) // Overwrite the outer props var, but not its pointee. + props.Parse(tag) + // Write the value in the oneof, not the oneof itself. + fv = inner.Field(0) + + // Special case to cope with malformed messages gracefully: + // If the value in the oneof is a nil pointer, don't panic + // in writeAny. + if fv.Kind() == reflect.Ptr && fv.IsNil() { + // Use errors.New so writeAny won't render quotes. + msg := errors.New("/* nil */") + fv = reflect.ValueOf(&msg).Elem() } } } @@ -514,44 +517,6 @@ func writeString(w *textWriter, s string) error { return w.WriteByte('"') } -func writeMessageSet(w *textWriter, ms *MessageSet) error { - for _, item := range ms.Item { - id := *item.TypeId - if msd, ok := messageSetMap[id]; ok { - // Known message set type. - if _, err := fmt.Fprintf(w, "[%s]: <\n", msd.name); err != nil { - return err - } - w.indent() - - pb := reflect.New(msd.t.Elem()) - if err := Unmarshal(item.Message, pb.Interface().(Message)); err != nil { - if _, err := fmt.Fprintf(w, "/* bad message: %v */\n", err); err != nil { - return err - } - } else { - if err := writeStruct(w, pb.Elem()); err != nil { - return err - } - } - } else { - // Unknown type. - if _, err := fmt.Fprintf(w, "[%d]: <\n", id); err != nil { - return err - } - w.indent() - if err := writeUnknownStruct(w, item.Message); err != nil { - return err - } - } - w.unindent() - if _, err := w.Write(gtNewline); err != nil { - return err - } - } - return nil -} - func writeUnknownStruct(w *textWriter, data []byte) (err error) { if !w.compact { if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil { @@ -666,10 +631,7 @@ func writeExtensions(w *textWriter, pv reflect.Value) error { pb, err := GetExtension(ep, desc) if err != nil { - if _, err := fmt.Fprintln(os.Stderr, "proto: failed getting extension: ", err); err != nil { - return err - } - continue + return fmt.Errorf("failed getting extension: %v", err) } // Repeated extensions will appear as a slice. diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser.go index ddd9579cd..451323262 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser.go @@ -119,6 +119,14 @@ func isWhitespace(c byte) bool { return false } +func isQuote(c byte) bool { + switch c { + case '"', '\'': + return true + } + return false +} + func (p *textParser) skipWhitespace() { i := 0 for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') { @@ -174,7 +182,7 @@ func (p *textParser) advance() { } unq, err := unquoteC(p.s[1:i], rune(p.s[0])) if err != nil { - p.errorf("invalid quoted string %v", p.s[0:i+1]) + p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err) return } p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)] @@ -333,13 +341,13 @@ func (p *textParser) next() *token { p.advance() if p.done { p.cur.value = "" - } else if len(p.cur.value) > 0 && p.cur.value[0] == '"' { + } else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) { // Look for multiple quoted strings separated by whitespace, // and concatenate them. cat := p.cur for { p.skipWhitespace() - if p.done || p.s[0] != '"' { + if p.done || !isQuote(p.s[0]) { break } p.advance() @@ -385,8 +393,7 @@ func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSet } // Returns the index in the struct for the named field, as well as the parsed tag properties. -func structFieldByName(st reflect.Type, name string) (int, *Properties, bool) { - sprops := GetProperties(st) +func structFieldByName(sprops *StructProperties, name string) (int, *Properties, bool) { i, ok := sprops.decoderOrigNames[name] if ok { return i, sprops.Prop[i], true @@ -438,7 +445,8 @@ func (p *textParser) checkForColon(props *Properties, typ reflect.Type) *ParseEr func (p *textParser) readStruct(sv reflect.Value, terminator string) error { st := sv.Type() - reqCount := GetProperties(st).reqCount + sprops := GetProperties(st) + reqCount := sprops.reqCount var reqFieldErr error fieldSet := make(map[string]bool) // A struct is a sequence of "name: value", terminated by one of @@ -520,99 +528,113 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error { sl = reflect.Append(sl, ext) SetExtension(ep, desc, sl.Interface()) } - } else { - // This is a normal, non-extension field. - name := tok.value - fi, props, ok := structFieldByName(st, name) - if !ok { - return p.errorf("unknown field name %q in %v", name, st) + if err := p.consumeOptionalSeparator(); err != nil { + return err } + continue + } - dst := sv.Field(fi) + // This is a normal, non-extension field. + name := tok.value + var dst reflect.Value + fi, props, ok := structFieldByName(sprops, name) + if ok { + dst = sv.Field(fi) + } else if oop, ok := sprops.OneofTypes[name]; ok { + // It is a oneof. + props = oop.Prop + nv := reflect.New(oop.Type.Elem()) + dst = nv.Elem().Field(0) + sv.Field(oop.Field).Set(nv) + } + if !dst.IsValid() { + return p.errorf("unknown field name %q in %v", name, st) + } - if dst.Kind() == reflect.Map { - // Consume any colon. - if err := p.checkForColon(props, dst.Type()); err != nil { - return err - } - - // Construct the map if it doesn't already exist. - if dst.IsNil() { - dst.Set(reflect.MakeMap(dst.Type())) - } - key := reflect.New(dst.Type().Key()).Elem() - val := reflect.New(dst.Type().Elem()).Elem() - - // The map entry should be this sequence of tokens: - // < key : KEY value : VALUE > - // Technically the "key" and "value" could come in any order, - // but in practice they won't. - - tok := p.next() - var terminator string - switch tok.value { - case "<": - terminator = ">" - case "{": - terminator = "}" - default: - return p.errorf("expected '{' or '<', found %q", tok.value) - } - if err := p.consumeToken("key"); err != nil { - return err - } - if err := p.consumeToken(":"); err != nil { - return err - } - if err := p.readAny(key, props.mkeyprop); err != nil { - return err - } - if err := p.consumeToken("value"); err != nil { - return err - } - if err := p.consumeToken(":"); err != nil { - return err - } - if err := p.readAny(val, props.mvalprop); err != nil { - return err - } - if err := p.consumeToken(terminator); err != nil { - return err - } - - dst.SetMapIndex(key, val) - continue - } - - // Check that it's not already set if it's not a repeated field. - if !props.Repeated && fieldSet[name] { - return p.errorf("non-repeated field %q was repeated", name) - } - - if err := p.checkForColon(props, st.Field(fi).Type); err != nil { + if dst.Kind() == reflect.Map { + // Consume any colon. + if err := p.checkForColon(props, dst.Type()); err != nil { return err } - // Parse into the field. - fieldSet[name] = true - if err := p.readAny(dst, props); err != nil { - if _, ok := err.(*RequiredNotSetError); !ok { - return err - } - reqFieldErr = err - } else if props.Required { - reqCount-- + // Construct the map if it doesn't already exist. + if dst.IsNil() { + dst.Set(reflect.MakeMap(dst.Type())) } + key := reflect.New(dst.Type().Key()).Elem() + val := reflect.New(dst.Type().Elem()).Elem() + + // The map entry should be this sequence of tokens: + // < key : KEY value : VALUE > + // Technically the "key" and "value" could come in any order, + // but in practice they won't. + + tok := p.next() + var terminator string + switch tok.value { + case "<": + terminator = ">" + case "{": + terminator = "}" + default: + return p.errorf("expected '{' or '<', found %q", tok.value) + } + if err := p.consumeToken("key"); err != nil { + return err + } + if err := p.consumeToken(":"); err != nil { + return err + } + if err := p.readAny(key, props.mkeyprop); err != nil { + return err + } + if err := p.consumeOptionalSeparator(); err != nil { + return err + } + if err := p.consumeToken("value"); err != nil { + return err + } + if err := p.checkForColon(props.mvalprop, dst.Type().Elem()); err != nil { + return err + } + if err := p.readAny(val, props.mvalprop); err != nil { + return err + } + if err := p.consumeOptionalSeparator(); err != nil { + return err + } + if err := p.consumeToken(terminator); err != nil { + return err + } + + dst.SetMapIndex(key, val) + continue } - // For backward compatibility, permit a semicolon or comma after a field. - tok = p.next() - if tok.err != nil { - return tok.err + // Check that it's not already set if it's not a repeated field. + if !props.Repeated && fieldSet[name] { + return p.errorf("non-repeated field %q was repeated", name) } - if tok.value != ";" && tok.value != "," { - p.back() + + if err := p.checkForColon(props, dst.Type()); err != nil { + return err } + + // Parse into the field. + fieldSet[name] = true + if err := p.readAny(dst, props); err != nil { + if _, ok := err.(*RequiredNotSetError); !ok { + return err + } + reqFieldErr = err + } else if props.Required { + reqCount-- + } + + if err := p.consumeOptionalSeparator(); err != nil { + return err + } + } if reqCount > 0 { @@ -621,6 +643,19 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error { return reqFieldErr } +// consumeOptionalSeparator consumes an optional semicolon or comma. +// It is used in readStruct to provide backward compatibility. +func (p *textParser) consumeOptionalSeparator() error { + tok := p.next() + if tok.err != nil { + return tok.err + } + if tok.value != ";" && tok.value != "," { + p.back() + } + return nil +} + func (p *textParser) readAny(v reflect.Value, props *Properties) error { tok := p.next() if tok.err != nil { @@ -645,18 +680,32 @@ func (p *textParser) readAny(v reflect.Value, props *Properties) error { fv.Set(reflect.ValueOf(bytes)) return nil } - // Repeated field. May already exist. - flen := fv.Len() - if flen == fv.Cap() { - nav := reflect.MakeSlice(at, flen, 2*flen+1) - reflect.Copy(nav, fv) - fv.Set(nav) + // Repeated field. + if tok.value == "[" { + // Repeated field with list notation, like [1,2,3]. + for { + fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem())) + err := p.readAny(fv.Index(fv.Len()-1), props) + if err != nil { + return err + } + tok := p.next() + if tok.err != nil { + return tok.err + } + if tok.value == "]" { + break + } + if tok.value != "," { + return p.errorf("Expected ']' or ',' found %q", tok.value) + } + } + return nil } - fv.SetLen(flen + 1) - - // Read one. + // One value of the repeated field. p.back() - return p.readAny(fv.Index(flen), props) + fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem())) + return p.readAny(fv.Index(fv.Len()-1), props) case reflect.Bool: // Either "true", "false", 1 or 0. switch tok.value { diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser_test.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser_test.go deleted file mode 100644 index e5ee8b922..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser_test.go +++ /dev/null @@ -1,509 +0,0 @@ -// 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. - -package proto_test - -import ( - "math" - "reflect" - "testing" - - proto3pb "./proto3_proto" - . "./testdata" - . "github.com/golang/protobuf/proto" -) - -type UnmarshalTextTest struct { - in string - err string // if "", no error expected - out *MyMessage -} - -func buildExtStructTest(text string) UnmarshalTextTest { - msg := &MyMessage{ - Count: Int32(42), - } - SetExtension(msg, E_Ext_More, &Ext{ - Data: String("Hello, world!"), - }) - return UnmarshalTextTest{in: text, out: msg} -} - -func buildExtDataTest(text string) UnmarshalTextTest { - msg := &MyMessage{ - Count: Int32(42), - } - SetExtension(msg, E_Ext_Text, String("Hello, world!")) - SetExtension(msg, E_Ext_Number, Int32(1729)) - return UnmarshalTextTest{in: text, out: msg} -} - -func buildExtRepStringTest(text string) UnmarshalTextTest { - msg := &MyMessage{ - Count: Int32(42), - } - if err := SetExtension(msg, E_Greeting, []string{"bula", "hola"}); err != nil { - panic(err) - } - return UnmarshalTextTest{in: text, out: msg} -} - -var unMarshalTextTests = []UnmarshalTextTest{ - // Basic - { - in: " count:42\n name:\"Dave\" ", - out: &MyMessage{ - Count: Int32(42), - Name: String("Dave"), - }, - }, - - // Empty quoted string - { - in: `count:42 name:""`, - out: &MyMessage{ - Count: Int32(42), - Name: String(""), - }, - }, - - // Quoted string concatenation - { - in: `count:42 name: "My name is "` + "\n" + `"elsewhere"`, - out: &MyMessage{ - Count: Int32(42), - Name: String("My name is elsewhere"), - }, - }, - - // Quoted string with escaped apostrophe - { - in: `count:42 name: "HOLIDAY - New Year\'s Day"`, - out: &MyMessage{ - Count: Int32(42), - Name: String("HOLIDAY - New Year's Day"), - }, - }, - - // Quoted string with single quote - { - in: `count:42 name: 'Roger "The Ramster" Ramjet'`, - out: &MyMessage{ - Count: Int32(42), - Name: String(`Roger "The Ramster" Ramjet`), - }, - }, - - // Quoted string with all the accepted special characters from the C++ test - { - in: `count:42 name: ` + "\"\\\"A string with \\' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"", - out: &MyMessage{ - Count: Int32(42), - Name: String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces"), - }, - }, - - // Quoted string with quoted backslash - { - in: `count:42 name: "\\'xyz"`, - out: &MyMessage{ - Count: Int32(42), - Name: String(`\'xyz`), - }, - }, - - // Quoted string with UTF-8 bytes. - { - in: "count:42 name: '\303\277\302\201\xAB'", - out: &MyMessage{ - Count: Int32(42), - Name: String("\303\277\302\201\xAB"), - }, - }, - - // Bad quoted string - { - in: `inner: < host: "\0" >` + "\n", - err: `line 1.15: invalid quoted string "\0"`, - }, - - // Number too large for int64 - { - in: "count: 1 others { key: 123456789012345678901 }", - err: "line 1.23: invalid int64: 123456789012345678901", - }, - - // Number too large for int32 - { - in: "count: 1234567890123", - err: "line 1.7: invalid int32: 1234567890123", - }, - - // Number in hexadecimal - { - in: "count: 0x2beef", - out: &MyMessage{ - Count: Int32(0x2beef), - }, - }, - - // Number in octal - { - in: "count: 024601", - out: &MyMessage{ - Count: Int32(024601), - }, - }, - - // Floating point number with "f" suffix - { - in: "count: 4 others:< weight: 17.0f >", - out: &MyMessage{ - Count: Int32(4), - Others: []*OtherMessage{ - { - Weight: Float32(17), - }, - }, - }, - }, - - // Floating point positive infinity - { - in: "count: 4 bigfloat: inf", - out: &MyMessage{ - Count: Int32(4), - Bigfloat: Float64(math.Inf(1)), - }, - }, - - // Floating point negative infinity - { - in: "count: 4 bigfloat: -inf", - out: &MyMessage{ - Count: Int32(4), - Bigfloat: Float64(math.Inf(-1)), - }, - }, - - // Number too large for float32 - { - in: "others:< weight: 12345678901234567890123456789012345678901234567890 >", - err: "line 1.17: invalid float32: 12345678901234567890123456789012345678901234567890", - }, - - // Number posing as a quoted string - { - in: `inner: < host: 12 >` + "\n", - err: `line 1.15: invalid string: 12`, - }, - - // Quoted string posing as int32 - { - in: `count: "12"`, - err: `line 1.7: invalid int32: "12"`, - }, - - // Quoted string posing a float32 - { - in: `others:< weight: "17.4" >`, - err: `line 1.17: invalid float32: "17.4"`, - }, - - // Enum - { - in: `count:42 bikeshed: BLUE`, - out: &MyMessage{ - Count: Int32(42), - Bikeshed: MyMessage_BLUE.Enum(), - }, - }, - - // Repeated field - { - in: `count:42 pet: "horsey" pet:"bunny"`, - out: &MyMessage{ - Count: Int32(42), - Pet: []string{"horsey", "bunny"}, - }, - }, - - // Repeated message with/without colon and <>/{} - { - in: `count:42 others:{} others{} others:<> others:{}`, - out: &MyMessage{ - Count: Int32(42), - Others: []*OtherMessage{ - {}, - {}, - {}, - {}, - }, - }, - }, - - // Missing colon for inner message - { - in: `count:42 inner < host: "cauchy.syd" >`, - out: &MyMessage{ - Count: Int32(42), - Inner: &InnerMessage{ - Host: String("cauchy.syd"), - }, - }, - }, - - // Missing colon for string field - { - in: `name "Dave"`, - err: `line 1.5: expected ':', found "\"Dave\""`, - }, - - // Missing colon for int32 field - { - in: `count 42`, - err: `line 1.6: expected ':', found "42"`, - }, - - // Missing required field - { - in: `name: "Pawel"`, - err: `proto: required field "testdata.MyMessage.count" not set`, - out: &MyMessage{ - Name: String("Pawel"), - }, - }, - - // Repeated non-repeated field - { - in: `name: "Rob" name: "Russ"`, - err: `line 1.12: non-repeated field "name" was repeated`, - }, - - // Group - { - in: `count: 17 SomeGroup { group_field: 12 }`, - out: &MyMessage{ - Count: Int32(17), - Somegroup: &MyMessage_SomeGroup{ - GroupField: Int32(12), - }, - }, - }, - - // Semicolon between fields - { - in: `count:3;name:"Calvin"`, - out: &MyMessage{ - Count: Int32(3), - Name: String("Calvin"), - }, - }, - // Comma between fields - { - in: `count:4,name:"Ezekiel"`, - out: &MyMessage{ - Count: Int32(4), - Name: String("Ezekiel"), - }, - }, - - // Extension - buildExtStructTest(`count: 42 [testdata.Ext.more]:`), - buildExtStructTest(`count: 42 [testdata.Ext.more] {data:"Hello, world!"}`), - buildExtDataTest(`count: 42 [testdata.Ext.text]:"Hello, world!" [testdata.Ext.number]:1729`), - buildExtRepStringTest(`count: 42 [testdata.greeting]:"bula" [testdata.greeting]:"hola"`), - - // Big all-in-one - { - in: "count:42 # Meaning\n" + - `name:"Dave" ` + - `quote:"\"I didn't want to go.\"" ` + - `pet:"bunny" ` + - `pet:"kitty" ` + - `pet:"horsey" ` + - `inner:<` + - ` host:"footrest.syd" ` + - ` port:7001 ` + - ` connected:true ` + - `> ` + - `others:<` + - ` key:3735928559 ` + - ` value:"\x01A\a\f" ` + - `> ` + - `others:<` + - " weight:58.9 # Atomic weight of Co\n" + - ` inner:<` + - ` host:"lesha.mtv" ` + - ` port:8002 ` + - ` >` + - `>`, - out: &MyMessage{ - Count: Int32(42), - Name: String("Dave"), - Quote: String(`"I didn't want to go."`), - Pet: []string{"bunny", "kitty", "horsey"}, - Inner: &InnerMessage{ - Host: String("footrest.syd"), - Port: Int32(7001), - Connected: Bool(true), - }, - Others: []*OtherMessage{ - { - Key: Int64(3735928559), - Value: []byte{0x1, 'A', '\a', '\f'}, - }, - { - Weight: Float32(58.9), - Inner: &InnerMessage{ - Host: String("lesha.mtv"), - Port: Int32(8002), - }, - }, - }, - }, - }, -} - -func TestUnmarshalText(t *testing.T) { - for i, test := range unMarshalTextTests { - pb := new(MyMessage) - err := UnmarshalText(test.in, pb) - if test.err == "" { - // We don't expect failure. - if err != nil { - t.Errorf("Test %d: Unexpected error: %v", i, err) - } else if !reflect.DeepEqual(pb, test.out) { - t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v", - i, pb, test.out) - } - } else { - // We do expect failure. - if err == nil { - t.Errorf("Test %d: Didn't get expected error: %v", i, test.err) - } else if err.Error() != test.err { - t.Errorf("Test %d: Incorrect error.\nHave: %v\nWant: %v", - i, err.Error(), test.err) - } else if _, ok := err.(*RequiredNotSetError); ok && test.out != nil && !reflect.DeepEqual(pb, test.out) { - t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v", - i, pb, test.out) - } - } - } -} - -func TestUnmarshalTextCustomMessage(t *testing.T) { - msg := &textMessage{} - if err := UnmarshalText("custom", msg); err != nil { - t.Errorf("Unexpected error from custom unmarshal: %v", err) - } - if UnmarshalText("not custom", msg) == nil { - t.Errorf("Didn't get expected error from custom unmarshal") - } -} - -// Regression test; this caused a panic. -func TestRepeatedEnum(t *testing.T) { - pb := new(RepeatedEnum) - if err := UnmarshalText("color: RED", pb); err != nil { - t.Fatal(err) - } - exp := &RepeatedEnum{ - Color: []RepeatedEnum_Color{RepeatedEnum_RED}, - } - if !Equal(pb, exp) { - t.Errorf("Incorrect populated \nHave: %v\nWant: %v", pb, exp) - } -} - -func TestProto3TextParsing(t *testing.T) { - m := new(proto3pb.Message) - const in = `name: "Wallace" true_scotsman: true` - want := &proto3pb.Message{ - Name: "Wallace", - TrueScotsman: true, - } - if err := UnmarshalText(in, m); err != nil { - t.Fatal(err) - } - if !Equal(m, want) { - t.Errorf("\n got %v\nwant %v", m, want) - } -} - -func TestMapParsing(t *testing.T) { - m := new(MessageWithMap) - const in = `name_mapping: name_mapping:` + - `msg_mapping:>` + - `byte_mapping:` - want := &MessageWithMap{ - NameMapping: map[int32]string{ - 1: "Beatles", - 1234: "Feist", - }, - MsgMapping: map[int64]*FloatingPoint{ - -4: {F: Float64(2.0)}, - }, - ByteMapping: map[bool][]byte{ - true: []byte("so be it"), - }, - } - if err := UnmarshalText(in, m); err != nil { - t.Fatal(err) - } - if !Equal(m, want) { - t.Errorf("\n got %v\nwant %v", m, want) - } -} - -var benchInput string - -func init() { - benchInput = "count: 4\n" - for i := 0; i < 1000; i++ { - benchInput += "pet: \"fido\"\n" - } - - // Check it is valid input. - pb := new(MyMessage) - err := UnmarshalText(benchInput, pb) - if err != nil { - panic("Bad benchmark input: " + err.Error()) - } -} - -func BenchmarkUnmarshalText(b *testing.B) { - pb := new(MyMessage) - for i := 0; i < b.N; i++ { - UnmarshalText(benchInput, pb) - } - b.SetBytes(int64(len(benchInput))) -} diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_test.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_test.go deleted file mode 100644 index 707bedd00..000000000 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_test.go +++ /dev/null @@ -1,436 +0,0 @@ -// 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. - -package proto_test - -import ( - "bytes" - "errors" - "io/ioutil" - "math" - "strings" - "testing" - - "github.com/golang/protobuf/proto" - - proto3pb "./proto3_proto" - pb "./testdata" -) - -// textMessage implements the methods that allow it to marshal and unmarshal -// itself as text. -type textMessage struct { -} - -func (*textMessage) MarshalText() ([]byte, error) { - return []byte("custom"), nil -} - -func (*textMessage) UnmarshalText(bytes []byte) error { - if string(bytes) != "custom" { - return errors.New("expected 'custom'") - } - return nil -} - -func (*textMessage) Reset() {} -func (*textMessage) String() string { return "" } -func (*textMessage) ProtoMessage() {} - -func newTestMessage() *pb.MyMessage { - msg := &pb.MyMessage{ - Count: proto.Int32(42), - Name: proto.String("Dave"), - Quote: proto.String(`"I didn't want to go."`), - Pet: []string{"bunny", "kitty", "horsey"}, - Inner: &pb.InnerMessage{ - Host: proto.String("footrest.syd"), - Port: proto.Int32(7001), - Connected: proto.Bool(true), - }, - Others: []*pb.OtherMessage{ - { - Key: proto.Int64(0xdeadbeef), - Value: []byte{1, 65, 7, 12}, - }, - { - Weight: proto.Float32(6.022), - Inner: &pb.InnerMessage{ - Host: proto.String("lesha.mtv"), - Port: proto.Int32(8002), - }, - }, - }, - Bikeshed: pb.MyMessage_BLUE.Enum(), - Somegroup: &pb.MyMessage_SomeGroup{ - GroupField: proto.Int32(8), - }, - // One normally wouldn't do this. - // This is an undeclared tag 13, as a varint (wire type 0) with value 4. - XXX_unrecognized: []byte{13<<3 | 0, 4}, - } - ext := &pb.Ext{ - Data: proto.String("Big gobs for big rats"), - } - if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil { - panic(err) - } - greetings := []string{"adg", "easy", "cow"} - if err := proto.SetExtension(msg, pb.E_Greeting, greetings); err != nil { - panic(err) - } - - // Add an unknown extension. We marshal a pb.Ext, and fake the ID. - b, err := proto.Marshal(&pb.Ext{Data: proto.String("3G skiing")}) - if err != nil { - panic(err) - } - b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...) - proto.SetRawExtension(msg, 201, b) - - // Extensions can be plain fields, too, so let's test that. - b = append(proto.EncodeVarint(202<<3|proto.WireVarint), 19) - proto.SetRawExtension(msg, 202, b) - - return msg -} - -const text = `count: 42 -name: "Dave" -quote: "\"I didn't want to go.\"" -pet: "bunny" -pet: "kitty" -pet: "horsey" -inner: < - host: "footrest.syd" - port: 7001 - connected: true -> -others: < - key: 3735928559 - value: "\001A\007\014" -> -others: < - weight: 6.022 - inner: < - host: "lesha.mtv" - port: 8002 - > -> -bikeshed: BLUE -SomeGroup { - group_field: 8 -} -/* 2 unknown bytes */ -13: 4 -[testdata.Ext.more]: < - data: "Big gobs for big rats" -> -[testdata.greeting]: "adg" -[testdata.greeting]: "easy" -[testdata.greeting]: "cow" -/* 13 unknown bytes */ -201: "\t3G skiing" -/* 3 unknown bytes */ -202: 19 -` - -func TestMarshalText(t *testing.T) { - buf := new(bytes.Buffer) - if err := proto.MarshalText(buf, newTestMessage()); err != nil { - t.Fatalf("proto.MarshalText: %v", err) - } - s := buf.String() - if s != text { - t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, text) - } -} - -func TestMarshalTextCustomMessage(t *testing.T) { - buf := new(bytes.Buffer) - if err := proto.MarshalText(buf, &textMessage{}); err != nil { - t.Fatalf("proto.MarshalText: %v", err) - } - s := buf.String() - if s != "custom" { - t.Errorf("Got %q, expected %q", s, "custom") - } -} -func TestMarshalTextNil(t *testing.T) { - want := "" - tests := []proto.Message{nil, (*pb.MyMessage)(nil)} - for i, test := range tests { - buf := new(bytes.Buffer) - if err := proto.MarshalText(buf, test); err != nil { - t.Fatal(err) - } - if got := buf.String(); got != want { - t.Errorf("%d: got %q want %q", i, got, want) - } - } -} - -func TestMarshalTextUnknownEnum(t *testing.T) { - // The Color enum only specifies values 0-2. - m := &pb.MyMessage{Bikeshed: pb.MyMessage_Color(3).Enum()} - got := m.String() - const want = `bikeshed:3 ` - if got != want { - t.Errorf("\n got %q\nwant %q", got, want) - } -} - -func BenchmarkMarshalTextBuffered(b *testing.B) { - buf := new(bytes.Buffer) - m := newTestMessage() - for i := 0; i < b.N; i++ { - buf.Reset() - proto.MarshalText(buf, m) - } -} - -func BenchmarkMarshalTextUnbuffered(b *testing.B) { - w := ioutil.Discard - m := newTestMessage() - for i := 0; i < b.N; i++ { - proto.MarshalText(w, m) - } -} - -func compact(src string) string { - // s/[ \n]+/ /g; s/ $//; - dst := make([]byte, len(src)) - space, comment := false, false - j := 0 - for i := 0; i < len(src); i++ { - if strings.HasPrefix(src[i:], "/*") { - comment = true - i++ - continue - } - if comment && strings.HasPrefix(src[i:], "*/") { - comment = false - i++ - continue - } - if comment { - continue - } - c := src[i] - if c == ' ' || c == '\n' { - space = true - continue - } - if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') { - space = false - } - if c == '{' { - space = false - } - if space { - dst[j] = ' ' - j++ - space = false - } - dst[j] = c - j++ - } - if space { - dst[j] = ' ' - j++ - } - return string(dst[0:j]) -} - -var compactText = compact(text) - -func TestCompactText(t *testing.T) { - s := proto.CompactTextString(newTestMessage()) - if s != compactText { - t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v\n===\n", s, compactText) - } -} - -func TestStringEscaping(t *testing.T) { - testCases := []struct { - in *pb.Strings - out string - }{ - { - // Test data from C++ test (TextFormatTest.StringEscape). - // Single divergence: we don't escape apostrophes. - &pb.Strings{StringField: proto.String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces")}, - "string_field: \"\\\"A string with ' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"\n", - }, - { - // Test data from the same C++ test. - &pb.Strings{StringField: proto.String("\350\260\267\346\255\214")}, - "string_field: \"\\350\\260\\267\\346\\255\\214\"\n", - }, - { - // Some UTF-8. - &pb.Strings{StringField: proto.String("\x00\x01\xff\x81")}, - `string_field: "\000\001\377\201"` + "\n", - }, - } - - for i, tc := range testCases { - var buf bytes.Buffer - if err := proto.MarshalText(&buf, tc.in); err != nil { - t.Errorf("proto.MarsalText: %v", err) - continue - } - s := buf.String() - if s != tc.out { - t.Errorf("#%d: Got:\n%s\nExpected:\n%s\n", i, s, tc.out) - continue - } - - // Check round-trip. - pb := new(pb.Strings) - if err := proto.UnmarshalText(s, pb); err != nil { - t.Errorf("#%d: UnmarshalText: %v", i, err) - continue - } - if !proto.Equal(pb, tc.in) { - t.Errorf("#%d: Round-trip failed:\nstart: %v\n end: %v", i, tc.in, pb) - } - } -} - -// A limitedWriter accepts some output before it fails. -// This is a proxy for something like a nearly-full or imminently-failing disk, -// or a network connection that is about to die. -type limitedWriter struct { - b bytes.Buffer - limit int -} - -var outOfSpace = errors.New("proto: insufficient space") - -func (w *limitedWriter) Write(p []byte) (n int, err error) { - var avail = w.limit - w.b.Len() - if avail <= 0 { - return 0, outOfSpace - } - if len(p) <= avail { - return w.b.Write(p) - } - n, _ = w.b.Write(p[:avail]) - return n, outOfSpace -} - -func TestMarshalTextFailing(t *testing.T) { - // Try lots of different sizes to exercise more error code-paths. - for lim := 0; lim < len(text); lim++ { - buf := new(limitedWriter) - buf.limit = lim - err := proto.MarshalText(buf, newTestMessage()) - // We expect a certain error, but also some partial results in the buffer. - if err != outOfSpace { - t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", err, outOfSpace) - } - s := buf.b.String() - x := text[:buf.limit] - if s != x { - t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, x) - } - } -} - -func TestFloats(t *testing.T) { - tests := []struct { - f float64 - want string - }{ - {0, "0"}, - {4.7, "4.7"}, - {math.Inf(1), "inf"}, - {math.Inf(-1), "-inf"}, - {math.NaN(), "nan"}, - } - for _, test := range tests { - msg := &pb.FloatingPoint{F: &test.f} - got := strings.TrimSpace(msg.String()) - want := `f:` + test.want - if got != want { - t.Errorf("f=%f: got %q, want %q", test.f, got, want) - } - } -} - -func TestRepeatedNilText(t *testing.T) { - m := &pb.MessageList{ - Message: []*pb.MessageList_Message{ - nil, - &pb.MessageList_Message{ - Name: proto.String("Horse"), - }, - nil, - }, - } - want := `Message -Message { - name: "Horse" -} -Message -` - if s := proto.MarshalTextString(m); s != want { - t.Errorf(" got: %s\nwant: %s", s, want) - } -} - -func TestProto3Text(t *testing.T) { - tests := []struct { - m proto.Message - want string - }{ - // zero message - {&proto3pb.Message{}, ``}, - // zero message except for an empty byte slice - {&proto3pb.Message{Data: []byte{}}, ``}, - // trivial case - {&proto3pb.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`}, - // empty map - {&pb.MessageWithMap{}, ``}, - // non-empty map; current map format is the same as a repeated struct - { - &pb.MessageWithMap{NameMapping: map[int32]string{1234: "Feist"}}, - `name_mapping:`, - }, - } - for _, test := range tests { - got := strings.TrimSpace(test.m.String()) - if got != test.want { - t.Errorf("\n got %s\nwant %s", got, test.want) - } - } -} diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/LICENSE b/Godeps/_workspace/src/github.com/google/cadvisor/LICENSE new file mode 100644 index 000000000..97cec18e8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/google/cadvisor/LICENSE @@ -0,0 +1,190 @@ + Copyright 2014 The cAdvisor Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + 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/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container.go b/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container.go index bb4fc322e..6e7e65897 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container.go @@ -23,6 +23,8 @@ type CpuSpec struct { Limit uint64 `json:"limit"` MaxLimit uint64 `json:"max_limit"` Mask string `json:"mask,omitempty"` + Quota uint64 `json:"quota,omitempty"` + Period uint64 `json:"period,omitempty"` } type MemorySpec struct { @@ -397,6 +399,9 @@ type FsStats struct { // The block device name associated with the filesystem. Device string `json:"device,omitempty"` + // Type of the filesytem. + Type string `json:"type"` + // Number of bytes that can be consumed by the container on this filesystem. Limit uint64 `json:"capacity"` @@ -410,6 +415,9 @@ type FsStats struct { // Number of bytes available for non-root user. Available uint64 `json:"available"` + // Number of available Inodes + InodesFree uint64 `json:"inodes_free"` + // Number of reads completed // This is the total number of reads completed successfully. ReadsCompleted uint64 `json:"reads_completed"` diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container_test.go b/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container_test.go deleted file mode 100644 index 58dc79e39..000000000 --- a/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/container_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2014 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 v1 - -import ( - "testing" - "time" -) - -func TestStatsStartTime(t *testing.T) { - N := 10 - stats := make([]*ContainerStats, 0, N) - ct := time.Now() - for i := 0; i < N; i++ { - s := &ContainerStats{ - Timestamp: ct.Add(time.Duration(i) * time.Second), - } - stats = append(stats, s) - } - cinfo := &ContainerInfo{ - ContainerReference: ContainerReference{ - Name: "/some/container", - }, - Stats: stats, - } - ref := ct.Add(time.Duration(N-1) * time.Second) - end := cinfo.StatsEndTime() - - if !ref.Equal(end) { - t.Errorf("end time is %v; should be %v", end, ref) - } -} - -func TestStatsEndTime(t *testing.T) { - N := 10 - stats := make([]*ContainerStats, 0, N) - ct := time.Now() - for i := 0; i < N; i++ { - s := &ContainerStats{ - Timestamp: ct.Add(time.Duration(i) * time.Second), - } - stats = append(stats, s) - } - cinfo := &ContainerInfo{ - ContainerReference: ContainerReference{ - Name: "/some/container", - }, - Stats: stats, - } - ref := ct - start := cinfo.StatsStartTime() - - if !ref.Equal(start) { - t.Errorf("start time is %v; should be %v", start, ref) - } -} - -func createStats(cpuUsage, memUsage uint64, timestamp time.Time) *ContainerStats { - stats := &ContainerStats{} - stats.Cpu.Usage.PerCpu = []uint64{cpuUsage} - stats.Cpu.Usage.Total = cpuUsage - stats.Cpu.Usage.System = 0 - stats.Cpu.Usage.User = cpuUsage - stats.Memory.Usage = memUsage - stats.Timestamp = timestamp - return stats -} diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/machine.go b/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/machine.go index f26291c11..74a5df49c 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/machine.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/machine.go @@ -20,6 +20,12 @@ type FsInfo struct { // Total number of bytes available on the filesystem. Capacity uint64 `json:"capacity"` + + // Type of device. + Type string `json:"type"` + + // Total number of inodes available on the filesystem. + Inodes uint64 `json:"inodes"` } type Node struct { @@ -115,10 +121,11 @@ type NetInfo struct { type CloudProvider string const ( - GCE CloudProvider = "GCE" - AWS = "AWS" - Baremetal = "Baremetal" - UnkownProvider = "Unknown" + GCE CloudProvider = "GCE" + AWS = "AWS" + Azure = "Azure" + Baremetal = "Baremetal" + UnknownProvider = "Unknown" ) type InstanceType string @@ -128,6 +135,12 @@ const ( UnknownInstance = "Unknown" ) +type InstanceID string + +const ( + UnNamedInstance InstanceID = "None" +) + type MachineInfo struct { // The number of cores in this machine. NumCores int `json:"num_cores"` @@ -165,6 +178,9 @@ type MachineInfo struct { // Type of cloud instance (e.g. GCE standard) the machine is. InstanceType InstanceType `json:"instance_type"` + + // ID of cloud instance (e.g. instance-1) given to it by the cloud provider. + InstanceID InstanceID `json:"instance_id"` } type VersionInfo struct { diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/test/datagen.go b/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/test/datagen.go deleted file mode 100644 index 42a0526d4..000000000 --- a/Godeps/_workspace/src/github.com/google/cadvisor/info/v1/test/datagen.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2014 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 test - -import ( - "fmt" - "math/rand" - "time" - - info "github.com/google/cadvisor/info/v1" -) - -func GenerateRandomStats(numStats, numCores int, duration time.Duration) []*info.ContainerStats { - ret := make([]*info.ContainerStats, numStats) - perCoreUsages := make([]uint64, numCores) - currentTime := time.Now() - for i := range perCoreUsages { - perCoreUsages[i] = uint64(rand.Int63n(1000)) - } - for i := 0; i < numStats; i++ { - stats := new(info.ContainerStats) - stats.Timestamp = currentTime - currentTime = currentTime.Add(duration) - - percore := make([]uint64, numCores) - for i := range perCoreUsages { - perCoreUsages[i] += uint64(rand.Int63n(1000)) - percore[i] = perCoreUsages[i] - stats.Cpu.Usage.Total += percore[i] - } - stats.Cpu.Usage.PerCpu = percore - stats.Cpu.Usage.User = stats.Cpu.Usage.Total - stats.Cpu.Usage.System = 0 - stats.Memory.Usage = uint64(rand.Int63n(4096)) - stats.Memory.Cache = uint64(rand.Int63n(4096)) - stats.Memory.RSS = uint64(rand.Int63n(4096)) - ret[i] = stats - } - return ret -} - -func GenerateRandomContainerSpec(numCores int) info.ContainerSpec { - ret := info.ContainerSpec{ - CreationTime: time.Now(), - HasCpu: true, - Cpu: info.CpuSpec{}, - HasMemory: true, - Memory: info.MemorySpec{}, - } - ret.Cpu.Limit = uint64(1000 + rand.Int63n(2000)) - ret.Cpu.MaxLimit = uint64(1000 + rand.Int63n(2000)) - ret.Cpu.Mask = fmt.Sprintf("0-%d", numCores-1) - ret.Memory.Limit = uint64(4096 + rand.Int63n(4096)) - return ret -} - -func GenerateRandomContainerInfo(containerName string, numCores int, query *info.ContainerInfoRequest, duration time.Duration) *info.ContainerInfo { - stats := GenerateRandomStats(query.NumStats, numCores, duration) - spec := GenerateRandomContainerSpec(numCores) - - ret := &info.ContainerInfo{ - ContainerReference: info.ContainerReference{ - Name: containerName, - }, - Spec: spec, - Stats: stats, - } - return ret -} diff --git a/Godeps/_workspace/src/github.com/google/gofuzz/example_test.go b/Godeps/_workspace/src/github.com/google/gofuzz/example_test.go deleted file mode 100644 index 792707a3a..000000000 --- a/Godeps/_workspace/src/github.com/google/gofuzz/example_test.go +++ /dev/null @@ -1,225 +0,0 @@ -/* -Copyright 2014 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 fuzz_test - -import ( - "encoding/json" - "fmt" - "math/rand" - - "github.com/google/gofuzz" -) - -func ExampleSimple() { - type MyType struct { - A string - B string - C int - D struct { - E float64 - } - } - - f := fuzz.New() - object := MyType{} - - uniqueObjects := map[MyType]int{} - - for i := 0; i < 1000; i++ { - f.Fuzz(&object) - uniqueObjects[object]++ - } - fmt.Printf("Got %v unique objects.\n", len(uniqueObjects)) - // Output: - // Got 1000 unique objects. -} - -func ExampleCustom() { - type MyType struct { - A int - B string - } - - counter := 0 - f := fuzz.New().Funcs( - func(i *int, c fuzz.Continue) { - *i = counter - counter++ - }, - ) - object := MyType{} - - uniqueObjects := map[MyType]int{} - - for i := 0; i < 100; i++ { - f.Fuzz(&object) - if object.A != i { - fmt.Printf("Unexpected value: %#v\n", object) - } - uniqueObjects[object]++ - } - fmt.Printf("Got %v unique objects.\n", len(uniqueObjects)) - // Output: - // Got 100 unique objects. -} - -func ExampleComplex() { - type OtherType struct { - A string - B string - } - type MyType struct { - Pointer *OtherType - Map map[string]OtherType - PointerMap *map[string]OtherType - Slice []OtherType - SlicePointer []*OtherType - PointerSlicePointer *[]*OtherType - } - - f := fuzz.New().RandSource(rand.NewSource(0)).NilChance(0).NumElements(1, 1).Funcs( - func(o *OtherType, c fuzz.Continue) { - o.A = "Foo" - o.B = "Bar" - }, - func(op **OtherType, c fuzz.Continue) { - *op = &OtherType{"A", "B"} - }, - func(m map[string]OtherType, c fuzz.Continue) { - m["Works Because"] = OtherType{ - "Fuzzer", - "Preallocated", - } - }, - ) - object := MyType{} - f.Fuzz(&object) - bytes, err := json.MarshalIndent(&object, "", " ") - if err != nil { - fmt.Printf("error: %v\n", err) - } - fmt.Printf("%s\n", string(bytes)) - // Output: - // { - // "Pointer": { - // "A": "A", - // "B": "B" - // }, - // "Map": { - // "Works Because": { - // "A": "Fuzzer", - // "B": "Preallocated" - // } - // }, - // "PointerMap": { - // "Works Because": { - // "A": "Fuzzer", - // "B": "Preallocated" - // } - // }, - // "Slice": [ - // { - // "A": "Foo", - // "B": "Bar" - // } - // ], - // "SlicePointer": [ - // { - // "A": "A", - // "B": "B" - // } - // ], - // "PointerSlicePointer": [ - // { - // "A": "A", - // "B": "B" - // } - // ] - // } -} - -func ExampleMap() { - f := fuzz.New().NilChance(0).NumElements(1, 1) - var myMap map[struct{ A, B, C int }]string - f.Fuzz(&myMap) - fmt.Printf("myMap has %v element(s).\n", len(myMap)) - // Output: - // myMap has 1 element(s). -} - -func ExampleSingle() { - f := fuzz.New() - var i int - f.Fuzz(&i) - - // Technically, we'd expect this to fail one out of 2 billion attempts... - fmt.Printf("(i == 0) == %v", i == 0) - // Output: - // (i == 0) == false -} - -func ExampleEnum() { - type MyEnum string - const ( - A MyEnum = "A" - B MyEnum = "B" - ) - type MyInfo struct { - Type MyEnum - AInfo *string - BInfo *string - } - - f := fuzz.New().NilChance(0).Funcs( - func(e *MyInfo, c fuzz.Continue) { - // Note c's embedded Rand allows for direct use. - // We could also use c.RandBool() here. - switch c.Intn(2) { - case 0: - e.Type = A - c.Fuzz(&e.AInfo) - case 1: - e.Type = B - c.Fuzz(&e.BInfo) - } - }, - ) - - for i := 0; i < 100; i++ { - var myObject MyInfo - f.Fuzz(&myObject) - switch myObject.Type { - case A: - if myObject.AInfo == nil { - fmt.Println("AInfo should have been set!") - } - if myObject.BInfo != nil { - fmt.Println("BInfo should NOT have been set!") - } - case B: - if myObject.BInfo == nil { - fmt.Println("BInfo should have been set!") - } - if myObject.AInfo != nil { - fmt.Println("AInfo should NOT have been set!") - } - default: - fmt.Println("Invalid enum value!") - } - } - // Output: -} diff --git a/Godeps/_workspace/src/github.com/google/gofuzz/fuzz_test.go b/Godeps/_workspace/src/github.com/google/gofuzz/fuzz_test.go deleted file mode 100644 index 12abc8f65..000000000 --- a/Godeps/_workspace/src/github.com/google/gofuzz/fuzz_test.go +++ /dev/null @@ -1,384 +0,0 @@ -/* -Copyright 2014 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 fuzz - -import ( - "reflect" - "testing" - "time" -) - -func TestFuzz_basic(t *testing.T) { - obj := &struct { - I int - I8 int8 - I16 int16 - I32 int32 - I64 int64 - U uint - U8 uint8 - U16 uint16 - U32 uint32 - U64 uint64 - Uptr uintptr - S string - B bool - T time.Time - }{} - - failed := map[string]int{} - for i := 0; i < 10; i++ { - New().Fuzz(obj) - - if n, v := "i", obj.I; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "i8", obj.I8; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "i16", obj.I16; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "i32", obj.I32; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "i64", obj.I64; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "u", obj.U; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "u8", obj.U8; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "u16", obj.U16; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "u32", obj.U32; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "u64", obj.U64; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "uptr", obj.Uptr; v == 0 { - failed[n] = failed[n] + 1 - } - if n, v := "s", obj.S; v == "" { - failed[n] = failed[n] + 1 - } - if n, v := "b", obj.B; v == false { - failed[n] = failed[n] + 1 - } - if n, v := "t", obj.T; v.IsZero() { - failed[n] = failed[n] + 1 - } - } - checkFailed(t, failed) -} - -func checkFailed(t *testing.T, failed map[string]int) { - for k, v := range failed { - if v > 8 { - t.Errorf("%v seems to not be getting set, was zero value %v times", k, v) - } - } -} - -func TestFuzz_structptr(t *testing.T) { - obj := &struct { - A *struct { - S string - } - }{} - - f := New().NilChance(.5) - failed := map[string]int{} - for i := 0; i < 10; i++ { - f.Fuzz(obj) - - if n, v := "a not nil", obj.A; v == nil { - failed[n] = failed[n] + 1 - } - if n, v := "a nil", obj.A; v != nil { - failed[n] = failed[n] + 1 - } - if n, v := "as", obj.A; v == nil || v.S == "" { - failed[n] = failed[n] + 1 - } - } - checkFailed(t, failed) -} - -// tryFuzz tries fuzzing up to 20 times. Fail if check() never passes, report the highest -// stage it ever got to. -func tryFuzz(t *testing.T, f *Fuzzer, obj interface{}, check func() (stage int, passed bool)) { - maxStage := 0 - for i := 0; i < 20; i++ { - f.Fuzz(obj) - stage, passed := check() - if stage > maxStage { - maxStage = stage - } - if passed { - return - } - } - t.Errorf("Only ever got to stage %v", maxStage) -} - -func TestFuzz_structmap(t *testing.T) { - obj := &struct { - A map[struct { - S string - }]struct { - S2 string - } - B map[string]string - }{} - - tryFuzz(t, New(), obj, func() (int, bool) { - if obj.A == nil { - return 1, false - } - if len(obj.A) == 0 { - return 2, false - } - for k, v := range obj.A { - if k.S == "" { - return 3, false - } - if v.S2 == "" { - return 4, false - } - } - - if obj.B == nil { - return 5, false - } - if len(obj.B) == 0 { - return 6, false - } - for k, v := range obj.B { - if k == "" { - return 7, false - } - if v == "" { - return 8, false - } - } - return 9, true - }) -} - -func TestFuzz_structslice(t *testing.T) { - obj := &struct { - A []struct { - S string - } - B []string - }{} - - tryFuzz(t, New(), obj, func() (int, bool) { - if obj.A == nil { - return 1, false - } - if len(obj.A) == 0 { - return 2, false - } - for _, v := range obj.A { - if v.S == "" { - return 3, false - } - } - - if obj.B == nil { - return 4, false - } - if len(obj.B) == 0 { - return 5, false - } - for _, v := range obj.B { - if v == "" { - return 6, false - } - } - return 7, true - }) -} - -func TestFuzz_custom(t *testing.T) { - obj := &struct { - A string - B *string - C map[string]string - D *map[string]string - }{} - - testPhrase := "gotcalled" - testMap := map[string]string{"C": "D"} - f := New().Funcs( - func(s *string, c Continue) { - *s = testPhrase - }, - func(m map[string]string, c Continue) { - m["C"] = "D" - }, - ) - - tryFuzz(t, f, obj, func() (int, bool) { - if obj.A != testPhrase { - return 1, false - } - if obj.B == nil { - return 2, false - } - if *obj.B != testPhrase { - return 3, false - } - if e, a := testMap, obj.C; !reflect.DeepEqual(e, a) { - return 4, false - } - if obj.D == nil { - return 5, false - } - if e, a := testMap, *obj.D; !reflect.DeepEqual(e, a) { - return 6, false - } - return 7, true - }) -} - -type SelfFuzzer string - -// Implement fuzz.Interface. -func (sf *SelfFuzzer) Fuzz(c Continue) { - *sf = selfFuzzerTestPhrase -} - -const selfFuzzerTestPhrase = "was fuzzed" - -func TestFuzz_interface(t *testing.T) { - f := New() - - var obj1 SelfFuzzer - tryFuzz(t, f, &obj1, func() (int, bool) { - if obj1 != selfFuzzerTestPhrase { - return 1, false - } - return 1, true - }) - - var obj2 map[int]SelfFuzzer - tryFuzz(t, f, &obj2, func() (int, bool) { - for _, v := range obj2 { - if v != selfFuzzerTestPhrase { - return 1, false - } - } - return 1, true - }) -} - -func TestFuzz_interfaceAndFunc(t *testing.T) { - const privateTestPhrase = "private phrase" - f := New().Funcs( - // This should take precedence over SelfFuzzer.Fuzz(). - func(s *SelfFuzzer, c Continue) { - *s = privateTestPhrase - }, - ) - - var obj1 SelfFuzzer - tryFuzz(t, f, &obj1, func() (int, bool) { - if obj1 != privateTestPhrase { - return 1, false - } - return 1, true - }) - - var obj2 map[int]SelfFuzzer - tryFuzz(t, f, &obj2, func() (int, bool) { - for _, v := range obj2 { - if v != privateTestPhrase { - return 1, false - } - } - return 1, true - }) -} - -func TestFuzz_noCustom(t *testing.T) { - type Inner struct { - Str string - } - type Outer struct { - Str string - In Inner - } - - testPhrase := "gotcalled" - f := New().Funcs( - func(outer *Outer, c Continue) { - outer.Str = testPhrase - c.Fuzz(&outer.In) - }, - func(inner *Inner, c Continue) { - inner.Str = testPhrase - }, - ) - c := Continue{f: f, Rand: f.r} - - // Fuzzer.Fuzz() - obj1 := Outer{} - f.Fuzz(&obj1) - if obj1.Str != testPhrase { - t.Errorf("expected Outer custom function to have been called") - } - if obj1.In.Str != testPhrase { - t.Errorf("expected Inner custom function to have been called") - } - - // Continue.Fuzz() - obj2 := Outer{} - c.Fuzz(&obj2) - if obj2.Str != testPhrase { - t.Errorf("expected Outer custom function to have been called") - } - if obj2.In.Str != testPhrase { - t.Errorf("expected Inner custom function to have been called") - } - - // Fuzzer.FuzzNoCustom() - obj3 := Outer{} - f.FuzzNoCustom(&obj3) - if obj3.Str == testPhrase { - t.Errorf("expected Outer custom function to not have been called") - } - if obj3.In.Str != testPhrase { - t.Errorf("expected Inner custom function to have been called") - } - - // Continue.FuzzNoCustom() - obj4 := Outer{} - c.FuzzNoCustom(&obj4) - if obj4.Str == testPhrase { - t.Errorf("expected Outer custom function to not have been called") - } - if obj4.In.Str != testPhrase { - t.Errorf("expected Inner custom function to have been called") - } -} diff --git a/Godeps/_workspace/src/github.com/imdario/mergo/mergo_test.go b/Godeps/_workspace/src/github.com/imdario/mergo/mergo_test.go deleted file mode 100644 index 072bddb79..000000000 --- a/Godeps/_workspace/src/github.com/imdario/mergo/mergo_test.go +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright 2013 Dario Castañé. All rights reserved. -// 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 mergo - -import ( - "gopkg.in/yaml.v1" - "io/ioutil" - "reflect" - "testing" -) - -type simpleTest struct { - Value int -} - -type complexTest struct { - St simpleTest - sz int - Id string -} - -type moreComplextText struct { - Ct complexTest - St simpleTest - Nt simpleTest -} - -type pointerTest struct { - C *simpleTest -} - -type sliceTest struct { - S []int -} - -func TestNil(t *testing.T) { - if err := Merge(nil, nil); err != ErrNilArguments { - t.Fail() - } -} - -func TestDifferentTypes(t *testing.T) { - a := simpleTest{42} - b := 42 - if err := Merge(&a, b); err != ErrDifferentArgumentsTypes { - t.Fail() - } -} - -func TestSimpleStruct(t *testing.T) { - a := simpleTest{} - b := simpleTest{42} - if err := Merge(&a, b); err != nil { - t.FailNow() - } - if a.Value != 42 { - t.Fatalf("b not merged in a properly: a.Value(%d) != b.Value(%d)", a.Value, b.Value) - } - if !reflect.DeepEqual(a, b) { - t.FailNow() - } -} - -func TestComplexStruct(t *testing.T) { - a := complexTest{} - a.Id = "athing" - b := complexTest{simpleTest{42}, 1, "bthing"} - if err := Merge(&a, b); err != nil { - t.FailNow() - } - if a.St.Value != 42 { - t.Fatalf("b not merged in a properly: a.St.Value(%d) != b.St.Value(%d)", a.St.Value, b.St.Value) - } - if a.sz == 1 { - t.Fatalf("a's private field sz not preserved from merge: a.sz(%d) == b.sz(%d)", a.sz, b.sz) - } - if a.Id != b.Id { - t.Fatalf("a's field Id not merged properly: a.Id(%s) != b.Id(%s)", a.Id, b.Id) - } -} - -func TestPointerStruct(t *testing.T) { - s1 := simpleTest{} - s2 := simpleTest{19} - a := pointerTest{&s1} - b := pointerTest{&s2} - if err := Merge(&a, b); err != nil { - t.FailNow() - } - if a.C.Value != b.C.Value { - //t.Fatalf("b not merged in a properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value) - } -} - -func TestPointerStructNil(t *testing.T) { - a := pointerTest{nil} - b := pointerTest{&simpleTest{19}} - if err := Merge(&a, b); err != nil { - t.FailNow() - } - if a.C.Value != b.C.Value { - t.Fatalf("b not merged in a properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value) - } -} - -func TestSliceStruct(t *testing.T) { - a := sliceTest{} - b := sliceTest{[]int{1, 2, 3}} - if err := Merge(&a, b); err != nil { - t.FailNow() - } - if len(b.S) != 3 { - t.FailNow() - } - if len(a.S) != len(b.S) { - t.Fatalf("b not merged in a properly %d != %d", len(a.S), len(b.S)) - } - - a = sliceTest{[]int{1}} - b = sliceTest{[]int{1, 2, 3}} - if err := Merge(&a, b); err != nil { - t.FailNow() - } - if len(b.S) != 3 { - t.FailNow() - } - if len(a.S) != len(b.S) { - t.Fatalf("b not merged in a properly %d != %d", len(a.S), len(b.S)) - } -} - -func TestMaps(t *testing.T) { - m := map[string]simpleTest{ - "a": simpleTest{}, - "b": simpleTest{42}, - } - n := map[string]simpleTest{ - "a": simpleTest{16}, - "b": simpleTest{}, - "c": simpleTest{12}, - } - if err := Merge(&m, n); err != nil { - t.Fatalf(err.Error()) - } - if len(m) != 3 { - t.Fatalf(`n not merged in m properly, m must have 3 elements instead of %d`, len(m)) - } - if m["a"].Value != 0 { - t.Fatalf(`n merged in m because I solved non-addressable map values TODO: m["a"].Value(%d) != n["a"].Value(%d)`, m["a"].Value, n["a"].Value) - } - if m["b"].Value != 42 { - t.Fatalf(`n wrongly merged in m: m["b"].Value(%d) != n["b"].Value(%d)`, m["b"].Value, n["b"].Value) - } - if m["c"].Value != 12 { - t.Fatalf(`n not merged in m: m["c"].Value(%d) != n["c"].Value(%d)`, m["c"].Value, n["c"].Value) - } -} - -func TestYAMLMaps(t *testing.T) { - thing := loadYAML("testdata/thing.yml") - license := loadYAML("testdata/license.yml") - ft := thing["fields"].(map[interface{}]interface{}) - fl := license["fields"].(map[interface{}]interface{}) - expectedLength := len(ft) + len(fl) - if err := Merge(&license, thing); err != nil { - t.Fatal(err.Error()) - } - currentLength := len(license["fields"].(map[interface{}]interface{})) - if currentLength != expectedLength { - t.Fatalf(`thing not merged in license properly, license must have %d elements instead of %d`, expectedLength, currentLength) - } - fields := license["fields"].(map[interface{}]interface{}) - if _, ok := fields["id"]; !ok { - t.Fatalf(`thing not merged in license properly, license must have a new id field from thing`) - } -} - -func TestTwoPointerValues(t *testing.T) { - a := &simpleTest{} - b := &simpleTest{42} - if err := Merge(a, b); err != nil { - t.Fatalf(`Boom. You crossed the streams: %s`, err) - } -} - -func TestMap(t *testing.T) { - a := complexTest{} - a.Id = "athing" - c := moreComplextText{a, simpleTest{}, simpleTest{}} - b := map[string]interface{}{ - "ct": map[string]interface{}{ - "st": map[string]interface{}{ - "value": 42, - }, - "sz": 1, - "id": "bthing", - }, - "st": &simpleTest{144}, // Mapping a reference - "zt": simpleTest{299}, // Mapping a missing field (zt doesn't exist) - "nt": simpleTest{3}, - } - if err := Map(&c, b); err != nil { - t.FailNow() - } - m := b["ct"].(map[string]interface{}) - n := m["st"].(map[string]interface{}) - o := b["st"].(*simpleTest) - p := b["nt"].(simpleTest) - if c.Ct.St.Value != 42 { - t.Fatalf("b not merged in a properly: c.Ct.St.Value(%d) != b.Ct.St.Value(%d)", c.Ct.St.Value, n["value"]) - } - if c.St.Value != 144 { - t.Fatalf("b not merged in a properly: c.St.Value(%d) != b.St.Value(%d)", c.St.Value, o.Value) - } - if c.Nt.Value != 3 { - t.Fatalf("b not merged in a properly: c.Nt.Value(%d) != b.Nt.Value(%d)", c.St.Value, p.Value) - } - if c.Ct.sz == 1 { - t.Fatalf("a's private field sz not preserved from merge: c.Ct.sz(%d) == b.Ct.sz(%d)", c.Ct.sz, m["sz"]) - } - if c.Ct.Id != m["id"] { - t.Fatalf("a's field Id not merged properly: c.Ct.Id(%s) != b.Ct.Id(%s)", c.Ct.Id, m["id"]) - } -} - -func TestSimpleMap(t *testing.T) { - a := simpleTest{} - b := map[string]interface{}{ - "value": 42, - } - if err := Map(&a, b); err != nil { - t.FailNow() - } - if a.Value != 42 { - t.Fatalf("b not merged in a properly: a.Value(%d) != b.Value(%v)", a.Value, b["value"]) - } -} - -type pointerMapTest struct { - A int - hidden int - B *simpleTest -} - -func TestBackAndForth(t *testing.T) { - pt := pointerMapTest{42, 1, &simpleTest{66}} - m := make(map[string]interface{}) - if err := Map(&m, pt); err != nil { - t.FailNow() - } - var ( - v interface{} - ok bool - ) - if v, ok = m["a"]; v.(int) != pt.A || !ok { - t.Fatalf("pt not merged properly: m[`a`](%d) != pt.A(%d)", v, pt.A) - } - if v, ok = m["b"]; !ok { - t.Fatalf("pt not merged properly: B is missing in m") - } - var st *simpleTest - if st = v.(*simpleTest); st.Value != 66 { - t.Fatalf("something went wrong while mapping pt on m, B wasn't copied") - } - bpt := pointerMapTest{} - if err := Map(&bpt, m); err != nil { - t.Fatal(err) - } - if bpt.A != pt.A { - t.Fatalf("pt not merged properly: bpt.A(%d) != pt.A(%d)", bpt.A, pt.A) - } - if bpt.hidden == pt.hidden { - t.Fatalf("pt unexpectedly merged: bpt.hidden(%d) == pt.hidden(%d)", bpt.hidden, pt.hidden) - } - if bpt.B.Value != pt.B.Value { - t.Fatalf("pt not merged properly: bpt.B.Value(%d) != pt.B.Value(%d)", bpt.B.Value, pt.B.Value) - } -} - -func loadYAML(path string) (m map[string]interface{}) { - m = make(map[string]interface{}) - raw, _ := ioutil.ReadFile(path) - _ = yaml.Unmarshal(raw, &m) - return -} diff --git a/Godeps/_workspace/src/github.com/imdario/mergo/testdata/license.yml b/Godeps/_workspace/src/github.com/imdario/mergo/testdata/license.yml deleted file mode 100644 index 62fdb61ec..000000000 --- a/Godeps/_workspace/src/github.com/imdario/mergo/testdata/license.yml +++ /dev/null @@ -1,3 +0,0 @@ -import: ../../../../fossene/db/schema/thing.yml -fields: - site: string diff --git a/Godeps/_workspace/src/github.com/imdario/mergo/testdata/thing.yml b/Godeps/_workspace/src/github.com/imdario/mergo/testdata/thing.yml deleted file mode 100644 index c28eab0d0..000000000 --- a/Godeps/_workspace/src/github.com/imdario/mergo/testdata/thing.yml +++ /dev/null @@ -1,5 +0,0 @@ -fields: - id: int - name: string - parent: ref "datu:thing" - status: enum(draft, public, private) diff --git a/Godeps/_workspace/src/github.com/juju/ratelimit/ratelimit_test.go b/Godeps/_workspace/src/github.com/juju/ratelimit/ratelimit_test.go deleted file mode 100644 index 62d88ded0..000000000 --- a/Godeps/_workspace/src/github.com/juju/ratelimit/ratelimit_test.go +++ /dev/null @@ -1,389 +0,0 @@ -// Copyright 2014 Canonical Ltd. -// Licensed under the LGPLv3 with static-linking exception. -// See LICENCE file for details. - -package ratelimit - -import ( - "math" - "testing" - "time" - - gc "gopkg.in/check.v1" -) - -func TestPackage(t *testing.T) { - gc.TestingT(t) -} - -type rateLimitSuite struct{} - -var _ = gc.Suite(rateLimitSuite{}) - -type takeReq struct { - time time.Duration - count int64 - expectWait time.Duration -} - -var takeTests = []struct { - about string - fillInterval time.Duration - capacity int64 - reqs []takeReq -}{{ - about: "serial requests", - fillInterval: 250 * time.Millisecond, - capacity: 10, - reqs: []takeReq{{ - time: 0, - count: 0, - expectWait: 0, - }, { - time: 0, - count: 10, - expectWait: 0, - }, { - time: 0, - count: 1, - expectWait: 250 * time.Millisecond, - }, { - time: 250 * time.Millisecond, - count: 1, - expectWait: 250 * time.Millisecond, - }}, -}, { - about: "concurrent requests", - fillInterval: 250 * time.Millisecond, - capacity: 10, - reqs: []takeReq{{ - time: 0, - count: 10, - expectWait: 0, - }, { - time: 0, - count: 2, - expectWait: 500 * time.Millisecond, - }, { - time: 0, - count: 2, - expectWait: 1000 * time.Millisecond, - }, { - time: 0, - count: 1, - expectWait: 1250 * time.Millisecond, - }}, -}, { - about: "more than capacity", - fillInterval: 1 * time.Millisecond, - capacity: 10, - reqs: []takeReq{{ - time: 0, - count: 10, - expectWait: 0, - }, { - time: 20 * time.Millisecond, - count: 15, - expectWait: 5 * time.Millisecond, - }}, -}, { - about: "sub-quantum time", - fillInterval: 10 * time.Millisecond, - capacity: 10, - reqs: []takeReq{{ - time: 0, - count: 10, - expectWait: 0, - }, { - time: 7 * time.Millisecond, - count: 1, - expectWait: 3 * time.Millisecond, - }, { - time: 8 * time.Millisecond, - count: 1, - expectWait: 12 * time.Millisecond, - }}, -}, { - about: "within capacity", - fillInterval: 10 * time.Millisecond, - capacity: 5, - reqs: []takeReq{{ - time: 0, - count: 5, - expectWait: 0, - }, { - time: 60 * time.Millisecond, - count: 5, - expectWait: 0, - }, { - time: 60 * time.Millisecond, - count: 1, - expectWait: 10 * time.Millisecond, - }, { - time: 80 * time.Millisecond, - count: 2, - expectWait: 10 * time.Millisecond, - }}, -}} - -var availTests = []struct { - about string - capacity int64 - fillInterval time.Duration - take int64 - sleep time.Duration - - expectCountAfterTake int64 - expectCountAfterSleep int64 -}{{ - about: "should fill tokens after interval", - capacity: 5, - fillInterval: time.Second, - take: 5, - sleep: time.Second, - expectCountAfterTake: 0, - expectCountAfterSleep: 1, -}, { - about: "should fill tokens plus existing count", - capacity: 2, - fillInterval: time.Second, - take: 1, - sleep: time.Second, - expectCountAfterTake: 1, - expectCountAfterSleep: 2, -}, { - about: "shouldn't fill before interval", - capacity: 2, - fillInterval: 2 * time.Second, - take: 1, - sleep: time.Second, - expectCountAfterTake: 1, - expectCountAfterSleep: 1, -}, { - about: "should fill only once after 1*interval before 2*interval", - capacity: 2, - fillInterval: 2 * time.Second, - take: 1, - sleep: 3 * time.Second, - expectCountAfterTake: 1, - expectCountAfterSleep: 2, -}} - -func (rateLimitSuite) TestTake(c *gc.C) { - for i, test := range takeTests { - tb := NewBucket(test.fillInterval, test.capacity) - for j, req := range test.reqs { - d, ok := tb.take(tb.startTime.Add(req.time), req.count, infinityDuration) - c.Assert(ok, gc.Equals, true) - if d != req.expectWait { - c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expectWait) - } - } - } -} - -func (rateLimitSuite) TestTakeMaxDuration(c *gc.C) { - for i, test := range takeTests { - tb := NewBucket(test.fillInterval, test.capacity) - for j, req := range test.reqs { - if req.expectWait > 0 { - d, ok := tb.take(tb.startTime.Add(req.time), req.count, req.expectWait-1) - c.Assert(ok, gc.Equals, false) - c.Assert(d, gc.Equals, time.Duration(0)) - } - d, ok := tb.take(tb.startTime.Add(req.time), req.count, req.expectWait) - c.Assert(ok, gc.Equals, true) - if d != req.expectWait { - c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expectWait) - } - } - } -} - -type takeAvailableReq struct { - time time.Duration - count int64 - expect int64 -} - -var takeAvailableTests = []struct { - about string - fillInterval time.Duration - capacity int64 - reqs []takeAvailableReq -}{{ - about: "serial requests", - fillInterval: 250 * time.Millisecond, - capacity: 10, - reqs: []takeAvailableReq{{ - time: 0, - count: 0, - expect: 0, - }, { - time: 0, - count: 10, - expect: 10, - }, { - time: 0, - count: 1, - expect: 0, - }, { - time: 250 * time.Millisecond, - count: 1, - expect: 1, - }}, -}, { - about: "concurrent requests", - fillInterval: 250 * time.Millisecond, - capacity: 10, - reqs: []takeAvailableReq{{ - time: 0, - count: 5, - expect: 5, - }, { - time: 0, - count: 2, - expect: 2, - }, { - time: 0, - count: 5, - expect: 3, - }, { - time: 0, - count: 1, - expect: 0, - }}, -}, { - about: "more than capacity", - fillInterval: 1 * time.Millisecond, - capacity: 10, - reqs: []takeAvailableReq{{ - time: 0, - count: 10, - expect: 10, - }, { - time: 20 * time.Millisecond, - count: 15, - expect: 10, - }}, -}, { - about: "within capacity", - fillInterval: 10 * time.Millisecond, - capacity: 5, - reqs: []takeAvailableReq{{ - time: 0, - count: 5, - expect: 5, - }, { - time: 60 * time.Millisecond, - count: 5, - expect: 5, - }, { - time: 70 * time.Millisecond, - count: 1, - expect: 1, - }}, -}} - -func (rateLimitSuite) TestTakeAvailable(c *gc.C) { - for i, test := range takeAvailableTests { - tb := NewBucket(test.fillInterval, test.capacity) - for j, req := range test.reqs { - d := tb.takeAvailable(tb.startTime.Add(req.time), req.count) - if d != req.expect { - c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expect) - } - } - } -} - -func (rateLimitSuite) TestPanics(c *gc.C) { - c.Assert(func() { NewBucket(0, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0") - c.Assert(func() { NewBucket(-2, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0") - c.Assert(func() { NewBucket(1, 0) }, gc.PanicMatches, "token bucket capacity is not > 0") - c.Assert(func() { NewBucket(1, -2) }, gc.PanicMatches, "token bucket capacity is not > 0") -} - -func isCloseTo(x, y, tolerance float64) bool { - return math.Abs(x-y)/y < tolerance -} - -func (rateLimitSuite) TestRate(c *gc.C) { - tb := NewBucket(1, 1) - if !isCloseTo(tb.Rate(), 1e9, 0.00001) { - c.Fatalf("got %v want 1e9", tb.Rate()) - } - tb = NewBucket(2*time.Second, 1) - if !isCloseTo(tb.Rate(), 0.5, 0.00001) { - c.Fatalf("got %v want 0.5", tb.Rate()) - } - tb = NewBucketWithQuantum(100*time.Millisecond, 1, 5) - if !isCloseTo(tb.Rate(), 50, 0.00001) { - c.Fatalf("got %v want 50", tb.Rate()) - } -} - -func checkRate(c *gc.C, rate float64) { - tb := NewBucketWithRate(rate, 1<<62) - if !isCloseTo(tb.Rate(), rate, rateMargin) { - c.Fatalf("got %g want %v", tb.Rate(), rate) - } - d, ok := tb.take(tb.startTime, 1<<62, infinityDuration) - c.Assert(ok, gc.Equals, true) - c.Assert(d, gc.Equals, time.Duration(0)) - - // Check that the actual rate is as expected by - // asking for a not-quite multiple of the bucket's - // quantum and checking that the wait time - // correct. - d, ok = tb.take(tb.startTime, tb.quantum*2-tb.quantum/2, infinityDuration) - c.Assert(ok, gc.Equals, true) - expectTime := 1e9 * float64(tb.quantum) * 2 / rate - if !isCloseTo(float64(d), expectTime, rateMargin) { - c.Fatalf("rate %g: got %g want %v", rate, float64(d), expectTime) - } -} - -func (rateLimitSuite) TestNewWithRate(c *gc.C) { - for rate := float64(1); rate < 1e6; rate += 7 { - checkRate(c, rate) - } - for _, rate := range []float64{ - 1024 * 1024 * 1024, - 1e-5, - 0.9e-5, - 0.5, - 0.9, - 0.9e8, - 3e12, - 4e18, - } { - checkRate(c, rate) - checkRate(c, rate/3) - checkRate(c, rate*1.3) - } -} - -func TestAvailable(t *testing.T) { - for i, tt := range availTests { - tb := NewBucket(tt.fillInterval, tt.capacity) - if c := tb.takeAvailable(tb.startTime, tt.take); c != tt.take { - t.Fatalf("#%d: %s, take = %d, want = %d", i, tt.about, c, tt.take) - } - if c := tb.available(tb.startTime); c != tt.expectCountAfterTake { - t.Fatalf("#%d: %s, after take, available = %d, want = %d", i, tt.about, c, tt.expectCountAfterTake) - } - if c := tb.available(tb.startTime.Add(tt.sleep)); c != tt.expectCountAfterSleep { - t.Fatalf("#%d: %s, after some time it should fill in new tokens, available = %d, want = %d", - i, tt.about, c, tt.expectCountAfterSleep) - } - } - -} - -func BenchmarkWait(b *testing.B) { - tb := NewBucket(1, 16*1024) - for i := b.N - 1; i >= 0; i-- { - tb.Wait(1) - } -} diff --git a/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/LICENSE b/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/LICENSE new file mode 100644 index 000000000..13f15dfce --- /dev/null +++ b/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/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 2013 Matt T. Proud + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT 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/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/all_test.go b/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/all_test.go deleted file mode 100644 index 094156e66..000000000 --- a/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/all_test.go +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2013 Matt T. Proud -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pbutil - -import ( - "bytes" - "math/rand" - "reflect" - "testing" - "testing/quick" - - "github.com/matttproud/golang_protobuf_extensions/pbtest" - - . "github.com/golang/protobuf/proto" - . "github.com/golang/protobuf/proto/testdata" -) - -func TestWriteDelimited(t *testing.T) { - for _, test := range []struct { - msg Message - buf []byte - n int - err error - }{ - { - msg: &Empty{}, - n: 1, - buf: []byte{0}, - }, - { - msg: &GoEnum{Foo: FOO_FOO1.Enum()}, - n: 3, - buf: []byte{2, 8, 1}, - }, - { - msg: &Strings{ - StringField: String(`This is my gigantic, unhappy string. It exceeds -the encoding size of a single byte varint. We are using it to fuzz test the -correctness of the header decoding mechanisms, which may prove problematic. -I expect it may. Let's hope you enjoy testing as much as we do.`), - }, - n: 271, - buf: []byte{141, 2, 10, 138, 2, 84, 104, 105, 115, 32, 105, 115, 32, 109, - 121, 32, 103, 105, 103, 97, 110, 116, 105, 99, 44, 32, 117, 110, 104, - 97, 112, 112, 121, 32, 115, 116, 114, 105, 110, 103, 46, 32, 32, 73, - 116, 32, 101, 120, 99, 101, 101, 100, 115, 10, 116, 104, 101, 32, 101, - 110, 99, 111, 100, 105, 110, 103, 32, 115, 105, 122, 101, 32, 111, 102, - 32, 97, 32, 115, 105, 110, 103, 108, 101, 32, 98, 121, 116, 101, 32, - 118, 97, 114, 105, 110, 116, 46, 32, 32, 87, 101, 32, 97, 114, 101, 32, - 117, 115, 105, 110, 103, 32, 105, 116, 32, 116, 111, 32, 102, 117, 122, - 122, 32, 116, 101, 115, 116, 32, 116, 104, 101, 10, 99, 111, 114, 114, - 101, 99, 116, 110, 101, 115, 115, 32, 111, 102, 32, 116, 104, 101, 32, - 104, 101, 97, 100, 101, 114, 32, 100, 101, 99, 111, 100, 105, 110, 103, - 32, 109, 101, 99, 104, 97, 110, 105, 115, 109, 115, 44, 32, 119, 104, - 105, 99, 104, 32, 109, 97, 121, 32, 112, 114, 111, 118, 101, 32, 112, - 114, 111, 98, 108, 101, 109, 97, 116, 105, 99, 46, 10, 73, 32, 101, 120, - 112, 101, 99, 116, 32, 105, 116, 32, 109, 97, 121, 46, 32, 32, 76, 101, - 116, 39, 115, 32, 104, 111, 112, 101, 32, 121, 111, 117, 32, 101, 110, - 106, 111, 121, 32, 116, 101, 115, 116, 105, 110, 103, 32, 97, 115, 32, - 109, 117, 99, 104, 32, 97, 115, 32, 119, 101, 32, 100, 111, 46}, - }, - } { - var buf bytes.Buffer - if n, err := WriteDelimited(&buf, test.msg); n != test.n || err != test.err { - t.Fatalf("WriteDelimited(buf, %#v) = %v, %v; want %v, %v", test.msg, n, err, test.n, test.err) - } - if out := buf.Bytes(); !bytes.Equal(out, test.buf) { - t.Fatalf("WriteDelimited(buf, %#v); buf = %v; want %v", test.msg, out, test.buf) - } - } -} - -func TestReadDelimited(t *testing.T) { - for _, test := range []struct { - buf []byte - msg Message - n int - err error - }{ - { - buf: []byte{0}, - msg: &Empty{}, - n: 1, - }, - { - n: 3, - buf: []byte{2, 8, 1}, - msg: &GoEnum{Foo: FOO_FOO1.Enum()}, - }, - { - buf: []byte{141, 2, 10, 138, 2, 84, 104, 105, 115, 32, 105, 115, 32, 109, - 121, 32, 103, 105, 103, 97, 110, 116, 105, 99, 44, 32, 117, 110, 104, - 97, 112, 112, 121, 32, 115, 116, 114, 105, 110, 103, 46, 32, 32, 73, - 116, 32, 101, 120, 99, 101, 101, 100, 115, 10, 116, 104, 101, 32, 101, - 110, 99, 111, 100, 105, 110, 103, 32, 115, 105, 122, 101, 32, 111, 102, - 32, 97, 32, 115, 105, 110, 103, 108, 101, 32, 98, 121, 116, 101, 32, - 118, 97, 114, 105, 110, 116, 46, 32, 32, 87, 101, 32, 97, 114, 101, 32, - 117, 115, 105, 110, 103, 32, 105, 116, 32, 116, 111, 32, 102, 117, 122, - 122, 32, 116, 101, 115, 116, 32, 116, 104, 101, 10, 99, 111, 114, 114, - 101, 99, 116, 110, 101, 115, 115, 32, 111, 102, 32, 116, 104, 101, 32, - 104, 101, 97, 100, 101, 114, 32, 100, 101, 99, 111, 100, 105, 110, 103, - 32, 109, 101, 99, 104, 97, 110, 105, 115, 109, 115, 44, 32, 119, 104, - 105, 99, 104, 32, 109, 97, 121, 32, 112, 114, 111, 118, 101, 32, 112, - 114, 111, 98, 108, 101, 109, 97, 116, 105, 99, 46, 10, 73, 32, 101, 120, - 112, 101, 99, 116, 32, 105, 116, 32, 109, 97, 121, 46, 32, 32, 76, 101, - 116, 39, 115, 32, 104, 111, 112, 101, 32, 121, 111, 117, 32, 101, 110, - 106, 111, 121, 32, 116, 101, 115, 116, 105, 110, 103, 32, 97, 115, 32, - 109, 117, 99, 104, 32, 97, 115, 32, 119, 101, 32, 100, 111, 46}, - msg: &Strings{ - StringField: String(`This is my gigantic, unhappy string. It exceeds -the encoding size of a single byte varint. We are using it to fuzz test the -correctness of the header decoding mechanisms, which may prove problematic. -I expect it may. Let's hope you enjoy testing as much as we do.`), - }, - n: 271, - }, - } { - msg := Clone(test.msg) - msg.Reset() - if n, err := ReadDelimited(bytes.NewBuffer(test.buf), msg); n != test.n || err != test.err { - t.Fatalf("ReadDelimited(%v, msg) = %v, %v; want %v, %v", test.buf, n, err, test.n, test.err) - } - if !Equal(msg, test.msg) { - t.Fatalf("ReadDelimited(%v, msg); msg = %v; want %v", test.buf, msg, test.msg) - } - } -} - -func TestEndToEndValid(t *testing.T) { - for _, test := range [][]Message{ - {&Empty{}}, - {&GoEnum{Foo: FOO_FOO1.Enum()}, &Empty{}, &GoEnum{Foo: FOO_FOO1.Enum()}}, - {&GoEnum{Foo: FOO_FOO1.Enum()}}, - {&Strings{ - StringField: String(`This is my gigantic, unhappy string. It exceeds -the encoding size of a single byte varint. We are using it to fuzz test the -correctness of the header decoding mechanisms, which may prove problematic. -I expect it may. Let's hope you enjoy testing as much as we do.`), - }}, - } { - var buf bytes.Buffer - var written int - for i, msg := range test { - n, err := WriteDelimited(&buf, msg) - if err != nil { - // Assumption: TestReadDelimited and TestWriteDelimited are sufficient - // and inputs for this test are explicitly exercised there. - t.Fatalf("WriteDelimited(buf, %v[%d]) = ?, %v; wanted ?, nil", test, i, err) - } - written += n - } - var read int - for i, msg := range test { - out := Clone(msg) - out.Reset() - n, _ := ReadDelimited(&buf, out) - // Decide to do EOF checking? - read += n - if !Equal(out, msg) { - t.Fatalf("out = %v; want %v[%d] = %#v", out, test, i, msg) - } - } - if read != written { - t.Fatalf("%v read = %d; want %d", test, read, written) - } - } -} - -// rndMessage generates a random valid Protocol Buffer message. -func rndMessage(r *rand.Rand) Message { - var t reflect.Type - switch v := rand.Intn(23); v { - // TODO(br): Uncomment the elements below once fix is incorporated, except - // for the elements marked as patently incompatible. - // case 0: - // t = reflect.TypeOf(&GoEnum{}) - // break - // case 1: - // t = reflect.TypeOf(&GoTestField{}) - // break - case 2: - t = reflect.TypeOf(&GoTest{}) - break - // case 3: - // t = reflect.TypeOf(&GoSkipTest{}) - // break - // case 4: - // t = reflect.TypeOf(&NonPackedTest{}) - // break - // case 5: - // t = reflect.TypeOf(&PackedTest{}) - // break - case 6: - t = reflect.TypeOf(&MaxTag{}) - break - case 7: - t = reflect.TypeOf(&OldMessage{}) - break - case 8: - t = reflect.TypeOf(&NewMessage{}) - break - case 9: - t = reflect.TypeOf(&InnerMessage{}) - break - case 10: - t = reflect.TypeOf(&OtherMessage{}) - break - case 11: - // PATENTLY INVALID FOR FUZZ GENERATION - // t = reflect.TypeOf(&MyMessage{}) - break - // case 12: - // t = reflect.TypeOf(&Ext{}) - // break - case 13: - // PATENTLY INVALID FOR FUZZ GENERATION - // t = reflect.TypeOf(&MyMessageSet{}) - break - // case 14: - // t = reflect.TypeOf(&Empty{}) - // break - // case 15: - // t = reflect.TypeOf(&MessageList{}) - // break - // case 16: - // t = reflect.TypeOf(&Strings{}) - // break - // case 17: - // t = reflect.TypeOf(&Defaults{}) - // break - // case 17: - // t = reflect.TypeOf(&SubDefaults{}) - // break - // case 18: - // t = reflect.TypeOf(&RepeatedEnum{}) - // break - case 19: - t = reflect.TypeOf(&MoreRepeated{}) - break - // case 20: - // t = reflect.TypeOf(&GroupOld{}) - // break - // case 21: - // t = reflect.TypeOf(&GroupNew{}) - // break - case 22: - t = reflect.TypeOf(&FloatingPoint{}) - break - default: - // TODO(br): Replace with an unreachable once fixed. - t = reflect.TypeOf(&GoTest{}) - break - } - if t == nil { - t = reflect.TypeOf(&GoTest{}) - } - v, ok := quick.Value(t, r) - if !ok { - panic("attempt to generate illegal item; consult item 11") - } - if err := pbtest.SanitizeGenerated(v.Interface().(Message)); err != nil { - panic(err) - } - return v.Interface().(Message) -} - -// rndMessages generates several random Protocol Buffer messages. -func rndMessages(r *rand.Rand) []Message { - n := r.Intn(128) - out := make([]Message, 0, n) - for i := 0; i < n; i++ { - out = append(out, rndMessage(r)) - } - return out -} - -func TestFuzz(t *testing.T) { - rnd := rand.New(rand.NewSource(42)) - check := func() bool { - messages := rndMessages(rnd) - var buf bytes.Buffer - var written int - for i, msg := range messages { - n, err := WriteDelimited(&buf, msg) - if err != nil { - t.Fatalf("WriteDelimited(buf, %v[%d]) = ?, %v; wanted ?, nil", messages, i, err) - } - written += n - } - var read int - for i, msg := range messages { - out := Clone(msg) - out.Reset() - n, _ := ReadDelimited(&buf, out) - read += n - if !Equal(out, msg) { - t.Fatalf("out = %v; want %v[%d] = %#v", out, messages, i, msg) - } - } - if read != written { - t.Fatalf("%v read = %d; want %d", messages, read, written) - } - return true - } - if err := quick.Check(check, nil); err != nil { - t.Fatal(err) - } -} diff --git a/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/fixtures_test.go b/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/fixtures_test.go deleted file mode 100644 index d6d9b2559..000000000 --- a/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/fixtures_test.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// http://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 pbutil - -import ( - . "github.com/golang/protobuf/proto" - . "github.com/golang/protobuf/proto/testdata" -) - -// FROM https://github.com/golang/protobuf/blob/master/proto/all_test.go. - -func initGoTestField() *GoTestField { - f := new(GoTestField) - f.Label = String("label") - f.Type = String("type") - return f -} - -// These are all structurally equivalent but the tag numbers differ. -// (It's remarkable that required, optional, and repeated all have -// 8 letters.) -func initGoTest_RequiredGroup() *GoTest_RequiredGroup { - return &GoTest_RequiredGroup{ - RequiredField: String("required"), - } -} - -func initGoTest_OptionalGroup() *GoTest_OptionalGroup { - return &GoTest_OptionalGroup{ - RequiredField: String("optional"), - } -} - -func initGoTest_RepeatedGroup() *GoTest_RepeatedGroup { - return &GoTest_RepeatedGroup{ - RequiredField: String("repeated"), - } -} - -func initGoTest(setdefaults bool) *GoTest { - pb := new(GoTest) - if setdefaults { - pb.F_BoolDefaulted = Bool(Default_GoTest_F_BoolDefaulted) - pb.F_Int32Defaulted = Int32(Default_GoTest_F_Int32Defaulted) - pb.F_Int64Defaulted = Int64(Default_GoTest_F_Int64Defaulted) - pb.F_Fixed32Defaulted = Uint32(Default_GoTest_F_Fixed32Defaulted) - pb.F_Fixed64Defaulted = Uint64(Default_GoTest_F_Fixed64Defaulted) - pb.F_Uint32Defaulted = Uint32(Default_GoTest_F_Uint32Defaulted) - pb.F_Uint64Defaulted = Uint64(Default_GoTest_F_Uint64Defaulted) - pb.F_FloatDefaulted = Float32(Default_GoTest_F_FloatDefaulted) - pb.F_DoubleDefaulted = Float64(Default_GoTest_F_DoubleDefaulted) - pb.F_StringDefaulted = String(Default_GoTest_F_StringDefaulted) - pb.F_BytesDefaulted = Default_GoTest_F_BytesDefaulted - pb.F_Sint32Defaulted = Int32(Default_GoTest_F_Sint32Defaulted) - pb.F_Sint64Defaulted = Int64(Default_GoTest_F_Sint64Defaulted) - } - - pb.Kind = GoTest_TIME.Enum() - pb.RequiredField = initGoTestField() - pb.F_BoolRequired = Bool(true) - pb.F_Int32Required = Int32(3) - pb.F_Int64Required = Int64(6) - pb.F_Fixed32Required = Uint32(32) - pb.F_Fixed64Required = Uint64(64) - pb.F_Uint32Required = Uint32(3232) - pb.F_Uint64Required = Uint64(6464) - pb.F_FloatRequired = Float32(3232) - pb.F_DoubleRequired = Float64(6464) - pb.F_StringRequired = String("string") - pb.F_BytesRequired = []byte("bytes") - pb.F_Sint32Required = Int32(-32) - pb.F_Sint64Required = Int64(-64) - pb.Requiredgroup = initGoTest_RequiredGroup() - - return pb -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/LICENSE b/Godeps/_workspace/src/github.com/opencontainers/runc/LICENSE new file mode 100644 index 000000000..27448585a --- /dev/null +++ b/Godeps/_workspace/src/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/Godeps/_workspace/src/github.com/opencontainers/runc/NOTICE b/Godeps/_workspace/src/github.com/opencontainers/runc/NOTICE new file mode 100644 index 000000000..5c97abce4 --- /dev/null +++ b/Godeps/_workspace/src/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/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/cgroups_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/cgroups_test.go deleted file mode 100644 index 2f702bc2e..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/cgroups_test.go +++ /dev/null @@ -1,18 +0,0 @@ -// +build linux - -package cgroups - -import ( - "testing" -) - -func TestParseCgroups(t *testing.T) { - cgroups, err := ParseCgroupFile("/proc/self/cgroup") - if err != nil { - t.Fatal(err) - } - - if _, ok := cgroups["cpu"]; !ok { - t.Fail() - } -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio_test.go deleted file mode 100644 index 695739204..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio_test.go +++ /dev/null @@ -1,636 +0,0 @@ -// +build linux - -package fs - -import ( - "strconv" - "testing" - - "github.com/opencontainers/runc/libcontainer/cgroups" - "github.com/opencontainers/runc/libcontainer/configs" -) - -const ( - sectorsRecursiveContents = `8:0 1024` - serviceBytesRecursiveContents = `8:0 Read 100 -8:0 Write 200 -8:0 Sync 300 -8:0 Async 500 -8:0 Total 500 -Total 500` - servicedRecursiveContents = `8:0 Read 10 -8:0 Write 40 -8:0 Sync 20 -8:0 Async 30 -8:0 Total 50 -Total 50` - queuedRecursiveContents = `8:0 Read 1 -8:0 Write 4 -8:0 Sync 2 -8:0 Async 3 -8:0 Total 5 -Total 5` - serviceTimeRecursiveContents = `8:0 Read 173959 -8:0 Write 0 -8:0 Sync 0 -8:0 Async 173959 -8:0 Total 17395 -Total 17395` - waitTimeRecursiveContents = `8:0 Read 15571 -8:0 Write 0 -8:0 Sync 0 -8:0 Async 15571 -8:0 Total 15571` - mergedRecursiveContents = `8:0 Read 5 -8:0 Write 10 -8:0 Sync 0 -8:0 Async 0 -8:0 Total 15 -Total 15` - timeRecursiveContents = `8:0 8` - throttleServiceBytes = `8:0 Read 11030528 -8:0 Write 23 -8:0 Sync 42 -8:0 Async 11030528 -8:0 Total 11030528 -252:0 Read 11030528 -252:0 Write 23 -252:0 Sync 42 -252:0 Async 11030528 -252:0 Total 11030528 -Total 22061056` - throttleServiced = `8:0 Read 164 -8:0 Write 23 -8:0 Sync 42 -8:0 Async 164 -8:0 Total 164 -252:0 Read 164 -252:0 Write 23 -252:0 Sync 42 -252:0 Async 164 -252:0 Total 164 -Total 328` -) - -func appendBlkioStatEntry(blkioStatEntries *[]cgroups.BlkioStatEntry, major, minor, value uint64, op string) { - *blkioStatEntries = append(*blkioStatEntries, cgroups.BlkioStatEntry{Major: major, Minor: minor, Value: value, Op: op}) -} - -func TestBlkioSetWeight(t *testing.T) { - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - - const ( - weightBefore = 100 - weightAfter = 200 - ) - - helper.writeFileContents(map[string]string{ - "blkio.weight": strconv.Itoa(weightBefore), - }) - - helper.CgroupData.config.Resources.BlkioWeight = weightAfter - blkio := &BlkioGroup{} - if err := blkio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamUint(helper.CgroupPath, "blkio.weight") - if err != nil { - t.Fatalf("Failed to parse blkio.weight - %s", err) - } - - if value != weightAfter { - t.Fatal("Got the wrong value, set blkio.weight failed.") - } -} - -func TestBlkioSetWeightDevice(t *testing.T) { - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - - const ( - weightDeviceBefore = "8:0 400" - ) - - wd := configs.NewWeightDevice(8, 0, 500, 0) - weightDeviceAfter := wd.WeightString() - - helper.writeFileContents(map[string]string{ - "blkio.weight_device": weightDeviceBefore, - }) - - helper.CgroupData.config.Resources.BlkioWeightDevice = []*configs.WeightDevice{wd} - blkio := &BlkioGroup{} - if err := blkio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamString(helper.CgroupPath, "blkio.weight_device") - if err != nil { - t.Fatalf("Failed to parse blkio.weight_device - %s", err) - } - - if value != weightDeviceAfter { - t.Fatal("Got the wrong value, set blkio.weight_device failed.") - } -} - -// regression #274 -func TestBlkioSetMultipleWeightDevice(t *testing.T) { - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - - const ( - weightDeviceBefore = "8:0 400" - ) - - wd1 := configs.NewWeightDevice(8, 0, 500, 0) - wd2 := configs.NewWeightDevice(8, 16, 500, 0) - // we cannot actually set and check both because normal ioutil.WriteFile - // when writing to cgroup file will overwrite the whole file content instead - // of updating it as the kernel is doing. Just check the second device - // is present will suffice for the test to ensure multiple writes are done. - weightDeviceAfter := wd2.WeightString() - - helper.writeFileContents(map[string]string{ - "blkio.weight_device": weightDeviceBefore, - }) - - helper.CgroupData.config.Resources.BlkioWeightDevice = []*configs.WeightDevice{wd1, wd2} - blkio := &BlkioGroup{} - if err := blkio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamString(helper.CgroupPath, "blkio.weight_device") - if err != nil { - t.Fatalf("Failed to parse blkio.weight_device - %s", err) - } - - if value != weightDeviceAfter { - t.Fatal("Got the wrong value, set blkio.weight_device failed.") - } -} - -func TestBlkioStats(t *testing.T) { - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents, - "blkio.io_serviced_recursive": servicedRecursiveContents, - "blkio.io_queued_recursive": queuedRecursiveContents, - "blkio.io_service_time_recursive": serviceTimeRecursiveContents, - "blkio.io_wait_time_recursive": waitTimeRecursiveContents, - "blkio.io_merged_recursive": mergedRecursiveContents, - "blkio.time_recursive": timeRecursiveContents, - "blkio.sectors_recursive": sectorsRecursiveContents, - }) - - blkio := &BlkioGroup{} - actualStats := *cgroups.NewStats() - err := blkio.GetStats(helper.CgroupPath, &actualStats) - if err != nil { - t.Fatal(err) - } - - // Verify expected stats. - expectedStats := cgroups.BlkioStats{} - appendBlkioStatEntry(&expectedStats.SectorsRecursive, 8, 0, 1024, "") - - appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 100, "Read") - appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 200, "Write") - appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 300, "Sync") - appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 500, "Async") - appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 500, "Total") - - appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 10, "Read") - appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 40, "Write") - appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 20, "Sync") - appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 30, "Async") - appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 50, "Total") - - appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 1, "Read") - appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 4, "Write") - appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 2, "Sync") - appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 3, "Async") - appendBlkioStatEntry(&expectedStats.IoQueuedRecursive, 8, 0, 5, "Total") - - appendBlkioStatEntry(&expectedStats.IoServiceTimeRecursive, 8, 0, 173959, "Read") - appendBlkioStatEntry(&expectedStats.IoServiceTimeRecursive, 8, 0, 0, "Write") - appendBlkioStatEntry(&expectedStats.IoServiceTimeRecursive, 8, 0, 0, "Sync") - appendBlkioStatEntry(&expectedStats.IoServiceTimeRecursive, 8, 0, 173959, "Async") - appendBlkioStatEntry(&expectedStats.IoServiceTimeRecursive, 8, 0, 17395, "Total") - - appendBlkioStatEntry(&expectedStats.IoWaitTimeRecursive, 8, 0, 15571, "Read") - appendBlkioStatEntry(&expectedStats.IoWaitTimeRecursive, 8, 0, 0, "Write") - appendBlkioStatEntry(&expectedStats.IoWaitTimeRecursive, 8, 0, 0, "Sync") - appendBlkioStatEntry(&expectedStats.IoWaitTimeRecursive, 8, 0, 15571, "Async") - appendBlkioStatEntry(&expectedStats.IoWaitTimeRecursive, 8, 0, 15571, "Total") - - appendBlkioStatEntry(&expectedStats.IoMergedRecursive, 8, 0, 5, "Read") - appendBlkioStatEntry(&expectedStats.IoMergedRecursive, 8, 0, 10, "Write") - appendBlkioStatEntry(&expectedStats.IoMergedRecursive, 8, 0, 0, "Sync") - appendBlkioStatEntry(&expectedStats.IoMergedRecursive, 8, 0, 0, "Async") - appendBlkioStatEntry(&expectedStats.IoMergedRecursive, 8, 0, 15, "Total") - - appendBlkioStatEntry(&expectedStats.IoTimeRecursive, 8, 0, 8, "") - - expectBlkioStatsEquals(t, expectedStats, actualStats.BlkioStats) -} - -func TestBlkioStatsNoSectorsFile(t *testing.T) { - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents, - "blkio.io_serviced_recursive": servicedRecursiveContents, - "blkio.io_queued_recursive": queuedRecursiveContents, - "blkio.io_service_time_recursive": serviceTimeRecursiveContents, - "blkio.io_wait_time_recursive": waitTimeRecursiveContents, - "blkio.io_merged_recursive": mergedRecursiveContents, - "blkio.time_recursive": timeRecursiveContents, - }) - - blkio := &BlkioGroup{} - actualStats := *cgroups.NewStats() - err := blkio.GetStats(helper.CgroupPath, &actualStats) - if err != nil { - t.Fatalf("Failed unexpectedly: %s", err) - } -} - -func TestBlkioStatsNoServiceBytesFile(t *testing.T) { - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "blkio.io_serviced_recursive": servicedRecursiveContents, - "blkio.io_queued_recursive": queuedRecursiveContents, - "blkio.sectors_recursive": sectorsRecursiveContents, - "blkio.io_service_time_recursive": serviceTimeRecursiveContents, - "blkio.io_wait_time_recursive": waitTimeRecursiveContents, - "blkio.io_merged_recursive": mergedRecursiveContents, - "blkio.time_recursive": timeRecursiveContents, - }) - - blkio := &BlkioGroup{} - actualStats := *cgroups.NewStats() - err := blkio.GetStats(helper.CgroupPath, &actualStats) - if err != nil { - t.Fatalf("Failed unexpectedly: %s", err) - } -} - -func TestBlkioStatsNoServicedFile(t *testing.T) { - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents, - "blkio.io_queued_recursive": queuedRecursiveContents, - "blkio.sectors_recursive": sectorsRecursiveContents, - "blkio.io_service_time_recursive": serviceTimeRecursiveContents, - "blkio.io_wait_time_recursive": waitTimeRecursiveContents, - "blkio.io_merged_recursive": mergedRecursiveContents, - "blkio.time_recursive": timeRecursiveContents, - }) - - blkio := &BlkioGroup{} - actualStats := *cgroups.NewStats() - err := blkio.GetStats(helper.CgroupPath, &actualStats) - if err != nil { - t.Fatalf("Failed unexpectedly: %s", err) - } -} - -func TestBlkioStatsNoQueuedFile(t *testing.T) { - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents, - "blkio.io_serviced_recursive": servicedRecursiveContents, - "blkio.sectors_recursive": sectorsRecursiveContents, - "blkio.io_service_time_recursive": serviceTimeRecursiveContents, - "blkio.io_wait_time_recursive": waitTimeRecursiveContents, - "blkio.io_merged_recursive": mergedRecursiveContents, - "blkio.time_recursive": timeRecursiveContents, - }) - - blkio := &BlkioGroup{} - actualStats := *cgroups.NewStats() - err := blkio.GetStats(helper.CgroupPath, &actualStats) - if err != nil { - t.Fatalf("Failed unexpectedly: %s", err) - } -} - -func TestBlkioStatsNoServiceTimeFile(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode.") - } - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents, - "blkio.io_serviced_recursive": servicedRecursiveContents, - "blkio.io_queued_recursive": queuedRecursiveContents, - "blkio.io_wait_time_recursive": waitTimeRecursiveContents, - "blkio.io_merged_recursive": mergedRecursiveContents, - "blkio.time_recursive": timeRecursiveContents, - "blkio.sectors_recursive": sectorsRecursiveContents, - }) - - blkio := &BlkioGroup{} - actualStats := *cgroups.NewStats() - err := blkio.GetStats(helper.CgroupPath, &actualStats) - if err != nil { - t.Fatalf("Failed unexpectedly: %s", err) - } -} - -func TestBlkioStatsNoWaitTimeFile(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode.") - } - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents, - "blkio.io_serviced_recursive": servicedRecursiveContents, - "blkio.io_queued_recursive": queuedRecursiveContents, - "blkio.io_service_time_recursive": serviceTimeRecursiveContents, - "blkio.io_merged_recursive": mergedRecursiveContents, - "blkio.time_recursive": timeRecursiveContents, - "blkio.sectors_recursive": sectorsRecursiveContents, - }) - - blkio := &BlkioGroup{} - actualStats := *cgroups.NewStats() - err := blkio.GetStats(helper.CgroupPath, &actualStats) - if err != nil { - t.Fatalf("Failed unexpectedly: %s", err) - } -} - -func TestBlkioStatsNoMergedFile(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode.") - } - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents, - "blkio.io_serviced_recursive": servicedRecursiveContents, - "blkio.io_queued_recursive": queuedRecursiveContents, - "blkio.io_service_time_recursive": serviceTimeRecursiveContents, - "blkio.io_wait_time_recursive": waitTimeRecursiveContents, - "blkio.time_recursive": timeRecursiveContents, - "blkio.sectors_recursive": sectorsRecursiveContents, - }) - - blkio := &BlkioGroup{} - actualStats := *cgroups.NewStats() - err := blkio.GetStats(helper.CgroupPath, &actualStats) - if err != nil { - t.Fatalf("Failed unexpectedly: %s", err) - } -} - -func TestBlkioStatsNoTimeFile(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode.") - } - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "blkio.io_service_bytes_recursive": serviceBytesRecursiveContents, - "blkio.io_serviced_recursive": servicedRecursiveContents, - "blkio.io_queued_recursive": queuedRecursiveContents, - "blkio.io_service_time_recursive": serviceTimeRecursiveContents, - "blkio.io_wait_time_recursive": waitTimeRecursiveContents, - "blkio.io_merged_recursive": mergedRecursiveContents, - "blkio.sectors_recursive": sectorsRecursiveContents, - }) - - blkio := &BlkioGroup{} - actualStats := *cgroups.NewStats() - err := blkio.GetStats(helper.CgroupPath, &actualStats) - if err != nil { - t.Fatalf("Failed unexpectedly: %s", err) - } -} - -func TestBlkioStatsUnexpectedNumberOfFields(t *testing.T) { - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "blkio.io_service_bytes_recursive": "8:0 Read 100 100", - "blkio.io_serviced_recursive": servicedRecursiveContents, - "blkio.io_queued_recursive": queuedRecursiveContents, - "blkio.sectors_recursive": sectorsRecursiveContents, - "blkio.io_service_time_recursive": serviceTimeRecursiveContents, - "blkio.io_wait_time_recursive": waitTimeRecursiveContents, - "blkio.io_merged_recursive": mergedRecursiveContents, - "blkio.time_recursive": timeRecursiveContents, - }) - - blkio := &BlkioGroup{} - actualStats := *cgroups.NewStats() - err := blkio.GetStats(helper.CgroupPath, &actualStats) - if err == nil { - t.Fatal("Expected to fail, but did not") - } -} - -func TestBlkioStatsUnexpectedFieldType(t *testing.T) { - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "blkio.io_service_bytes_recursive": "8:0 Read Write", - "blkio.io_serviced_recursive": servicedRecursiveContents, - "blkio.io_queued_recursive": queuedRecursiveContents, - "blkio.sectors_recursive": sectorsRecursiveContents, - "blkio.io_service_time_recursive": serviceTimeRecursiveContents, - "blkio.io_wait_time_recursive": waitTimeRecursiveContents, - "blkio.io_merged_recursive": mergedRecursiveContents, - "blkio.time_recursive": timeRecursiveContents, - }) - - blkio := &BlkioGroup{} - actualStats := *cgroups.NewStats() - err := blkio.GetStats(helper.CgroupPath, &actualStats) - if err == nil { - t.Fatal("Expected to fail, but did not") - } -} - -func TestNonCFQBlkioStats(t *testing.T) { - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "blkio.io_service_bytes_recursive": "", - "blkio.io_serviced_recursive": "", - "blkio.io_queued_recursive": "", - "blkio.sectors_recursive": "", - "blkio.io_service_time_recursive": "", - "blkio.io_wait_time_recursive": "", - "blkio.io_merged_recursive": "", - "blkio.time_recursive": "", - "blkio.throttle.io_service_bytes": throttleServiceBytes, - "blkio.throttle.io_serviced": throttleServiced, - }) - - blkio := &BlkioGroup{} - actualStats := *cgroups.NewStats() - err := blkio.GetStats(helper.CgroupPath, &actualStats) - if err != nil { - t.Fatal(err) - } - - // Verify expected stats. - expectedStats := cgroups.BlkioStats{} - - appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 11030528, "Read") - appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 23, "Write") - appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 42, "Sync") - appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 11030528, "Async") - appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 11030528, "Total") - appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 252, 0, 11030528, "Read") - appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 252, 0, 23, "Write") - appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 252, 0, 42, "Sync") - appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 252, 0, 11030528, "Async") - appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 252, 0, 11030528, "Total") - - appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 164, "Read") - appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 23, "Write") - appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 42, "Sync") - appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 164, "Async") - appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 164, "Total") - appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 252, 0, 164, "Read") - appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 252, 0, 23, "Write") - appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 252, 0, 42, "Sync") - appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 252, 0, 164, "Async") - appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 252, 0, 164, "Total") - - expectBlkioStatsEquals(t, expectedStats, actualStats.BlkioStats) -} - -func TestBlkioSetThrottleReadBpsDevice(t *testing.T) { - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - - const ( - throttleBefore = `8:0 1024` - ) - - td := configs.NewThrottleDevice(8, 0, 2048) - throttleAfter := td.String() - - helper.writeFileContents(map[string]string{ - "blkio.throttle.read_bps_device": throttleBefore, - }) - - helper.CgroupData.config.Resources.BlkioThrottleReadBpsDevice = []*configs.ThrottleDevice{td} - blkio := &BlkioGroup{} - if err := blkio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.read_bps_device") - if err != nil { - t.Fatalf("Failed to parse blkio.throttle.read_bps_device - %s", err) - } - - if value != throttleAfter { - t.Fatal("Got the wrong value, set blkio.throttle.read_bps_device failed.") - } -} -func TestBlkioSetThrottleWriteBpsDevice(t *testing.T) { - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - - const ( - throttleBefore = `8:0 1024` - ) - - td := configs.NewThrottleDevice(8, 0, 2048) - throttleAfter := td.String() - - helper.writeFileContents(map[string]string{ - "blkio.throttle.write_bps_device": throttleBefore, - }) - - helper.CgroupData.config.Resources.BlkioThrottleWriteBpsDevice = []*configs.ThrottleDevice{td} - blkio := &BlkioGroup{} - if err := blkio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.write_bps_device") - if err != nil { - t.Fatalf("Failed to parse blkio.throttle.write_bps_device - %s", err) - } - - if value != throttleAfter { - t.Fatal("Got the wrong value, set blkio.throttle.write_bps_device failed.") - } -} -func TestBlkioSetThrottleReadIOpsDevice(t *testing.T) { - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - - const ( - throttleBefore = `8:0 1024` - ) - - td := configs.NewThrottleDevice(8, 0, 2048) - throttleAfter := td.String() - - helper.writeFileContents(map[string]string{ - "blkio.throttle.read_iops_device": throttleBefore, - }) - - helper.CgroupData.config.Resources.BlkioThrottleReadIOPSDevice = []*configs.ThrottleDevice{td} - blkio := &BlkioGroup{} - if err := blkio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.read_iops_device") - if err != nil { - t.Fatalf("Failed to parse blkio.throttle.read_iops_device - %s", err) - } - - if value != throttleAfter { - t.Fatal("Got the wrong value, set blkio.throttle.read_iops_device failed.") - } -} -func TestBlkioSetThrottleWriteIOpsDevice(t *testing.T) { - helper := NewCgroupTestUtil("blkio", t) - defer helper.cleanup() - - const ( - throttleBefore = `8:0 1024` - ) - - td := configs.NewThrottleDevice(8, 0, 2048) - throttleAfter := td.String() - - helper.writeFileContents(map[string]string{ - "blkio.throttle.write_iops_device": throttleBefore, - }) - - helper.CgroupData.config.Resources.BlkioThrottleWriteIOPSDevice = []*configs.ThrottleDevice{td} - blkio := &BlkioGroup{} - if err := blkio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.write_iops_device") - if err != nil { - t.Fatalf("Failed to parse blkio.throttle.write_iops_device - %s", err) - } - - if value != throttleAfter { - t.Fatal("Got the wrong value, set blkio.throttle.write_iops_device failed.") - } -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu_test.go deleted file mode 100644 index 554fd5e85..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu_test.go +++ /dev/null @@ -1,163 +0,0 @@ -// +build linux - -package fs - -import ( - "fmt" - "strconv" - "testing" - - "github.com/opencontainers/runc/libcontainer/cgroups" -) - -func TestCpuSetShares(t *testing.T) { - helper := NewCgroupTestUtil("cpu", t) - defer helper.cleanup() - - const ( - sharesBefore = 1024 - sharesAfter = 512 - ) - - helper.writeFileContents(map[string]string{ - "cpu.shares": strconv.Itoa(sharesBefore), - }) - - helper.CgroupData.config.Resources.CpuShares = sharesAfter - cpu := &CpuGroup{} - if err := cpu.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamUint(helper.CgroupPath, "cpu.shares") - if err != nil { - t.Fatalf("Failed to parse cpu.shares - %s", err) - } - - if value != sharesAfter { - t.Fatal("Got the wrong value, set cpu.shares failed.") - } -} - -func TestCpuSetBandWidth(t *testing.T) { - helper := NewCgroupTestUtil("cpu", t) - defer helper.cleanup() - - const ( - quotaBefore = 8000 - quotaAfter = 5000 - periodBefore = 10000 - periodAfter = 7000 - rtRuntimeBefore = 8000 - rtRuntimeAfter = 5000 - rtPeriodBefore = 10000 - rtPeriodAfter = 7000 - ) - - helper.writeFileContents(map[string]string{ - "cpu.cfs_quota_us": strconv.Itoa(quotaBefore), - "cpu.cfs_period_us": strconv.Itoa(periodBefore), - "cpu.rt_runtime_us": strconv.Itoa(rtRuntimeBefore), - "cpu.rt_period_us": strconv.Itoa(rtPeriodBefore), - }) - - helper.CgroupData.config.Resources.CpuQuota = quotaAfter - helper.CgroupData.config.Resources.CpuPeriod = periodAfter - helper.CgroupData.config.Resources.CpuRtRuntime = rtRuntimeAfter - helper.CgroupData.config.Resources.CpuRtPeriod = rtPeriodAfter - cpu := &CpuGroup{} - if err := cpu.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - quota, err := getCgroupParamUint(helper.CgroupPath, "cpu.cfs_quota_us") - if err != nil { - t.Fatalf("Failed to parse cpu.cfs_quota_us - %s", err) - } - if quota != quotaAfter { - t.Fatal("Got the wrong value, set cpu.cfs_quota_us failed.") - } - - period, err := getCgroupParamUint(helper.CgroupPath, "cpu.cfs_period_us") - if err != nil { - t.Fatalf("Failed to parse cpu.cfs_period_us - %s", err) - } - if period != periodAfter { - t.Fatal("Got the wrong value, set cpu.cfs_period_us failed.") - } - rtRuntime, err := getCgroupParamUint(helper.CgroupPath, "cpu.rt_runtime_us") - if err != nil { - t.Fatalf("Failed to parse cpu.rt_runtime_us - %s", err) - } - if rtRuntime != rtRuntimeAfter { - t.Fatal("Got the wrong value, set cpu.rt_runtime_us failed.") - } - rtPeriod, err := getCgroupParamUint(helper.CgroupPath, "cpu.rt_period_us") - if err != nil { - t.Fatalf("Failed to parse cpu.rt_period_us - %s", err) - } - if rtPeriod != rtPeriodAfter { - t.Fatal("Got the wrong value, set cpu.rt_period_us failed.") - } -} - -func TestCpuStats(t *testing.T) { - helper := NewCgroupTestUtil("cpu", t) - defer helper.cleanup() - - const ( - kNrPeriods = 2000 - kNrThrottled = 200 - kThrottledTime = uint64(18446744073709551615) - ) - - cpuStatContent := fmt.Sprintf("nr_periods %d\n nr_throttled %d\n throttled_time %d\n", - kNrPeriods, kNrThrottled, kThrottledTime) - helper.writeFileContents(map[string]string{ - "cpu.stat": cpuStatContent, - }) - - cpu := &CpuGroup{} - actualStats := *cgroups.NewStats() - err := cpu.GetStats(helper.CgroupPath, &actualStats) - if err != nil { - t.Fatal(err) - } - - expectedStats := cgroups.ThrottlingData{ - Periods: kNrPeriods, - ThrottledPeriods: kNrThrottled, - ThrottledTime: kThrottledTime} - - expectThrottlingDataEquals(t, expectedStats, actualStats.CpuStats.ThrottlingData) -} - -func TestNoCpuStatFile(t *testing.T) { - helper := NewCgroupTestUtil("cpu", t) - defer helper.cleanup() - - cpu := &CpuGroup{} - actualStats := *cgroups.NewStats() - err := cpu.GetStats(helper.CgroupPath, &actualStats) - if err != nil { - t.Fatal("Expected not to fail, but did") - } -} - -func TestInvalidCpuStat(t *testing.T) { - helper := NewCgroupTestUtil("cpu", t) - defer helper.cleanup() - cpuStatContent := `nr_periods 2000 - nr_throttled 200 - throttled_time fortytwo` - helper.writeFileContents(map[string]string{ - "cpu.stat": cpuStatContent, - }) - - cpu := &CpuGroup{} - actualStats := *cgroups.NewStats() - err := cpu.GetStats(helper.CgroupPath, &actualStats) - if err == nil { - t.Fatal("Expected failed stat parsing.") - } -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset_test.go deleted file mode 100644 index 0f929151f..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset_test.go +++ /dev/null @@ -1,65 +0,0 @@ -// +build linux - -package fs - -import ( - "testing" -) - -func TestCpusetSetCpus(t *testing.T) { - helper := NewCgroupTestUtil("cpuset", t) - defer helper.cleanup() - - const ( - cpusBefore = "0" - cpusAfter = "1-3" - ) - - helper.writeFileContents(map[string]string{ - "cpuset.cpus": cpusBefore, - }) - - helper.CgroupData.config.Resources.CpusetCpus = cpusAfter - cpuset := &CpusetGroup{} - if err := cpuset.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamString(helper.CgroupPath, "cpuset.cpus") - if err != nil { - t.Fatalf("Failed to parse cpuset.cpus - %s", err) - } - - if value != cpusAfter { - t.Fatal("Got the wrong value, set cpuset.cpus failed.") - } -} - -func TestCpusetSetMems(t *testing.T) { - helper := NewCgroupTestUtil("cpuset", t) - defer helper.cleanup() - - const ( - memsBefore = "0" - memsAfter = "1" - ) - - helper.writeFileContents(map[string]string{ - "cpuset.mems": memsBefore, - }) - - helper.CgroupData.config.Resources.CpusetMems = memsAfter - cpuset := &CpusetGroup{} - if err := cpuset.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamString(helper.CgroupPath, "cpuset.mems") - if err != nil { - t.Fatalf("Failed to parse cpuset.mems - %s", err) - } - - if value != memsAfter { - t.Fatal("Got the wrong value, set cpuset.mems failed.") - } -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices_test.go deleted file mode 100644 index ee44084ee..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// +build linux - -package fs - -import ( - "testing" - - "github.com/opencontainers/runc/libcontainer/configs" -) - -var ( - allowedDevices = []*configs.Device{ - { - Path: "/dev/zero", - Type: 'c', - Major: 1, - Minor: 5, - Permissions: "rwm", - FileMode: 0666, - }, - } - allowedList = "c 1:5 rwm" - deniedDevices = []*configs.Device{ - { - Path: "/dev/null", - Type: 'c', - Major: 1, - Minor: 3, - Permissions: "rwm", - FileMode: 0666, - }, - } - deniedList = "c 1:3 rwm" -) - -func TestDevicesSetAllow(t *testing.T) { - helper := NewCgroupTestUtil("devices", t) - defer helper.cleanup() - - helper.writeFileContents(map[string]string{ - "devices.deny": "a", - }) - - helper.CgroupData.config.Resources.AllowAllDevices = false - helper.CgroupData.config.Resources.AllowedDevices = allowedDevices - devices := &DevicesGroup{} - if err := devices.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamString(helper.CgroupPath, "devices.allow") - if err != nil { - t.Fatalf("Failed to parse devices.allow - %s", err) - } - - if value != allowedList { - t.Fatal("Got the wrong value, set devices.allow failed.") - } -} - -func TestDevicesSetDeny(t *testing.T) { - helper := NewCgroupTestUtil("devices", t) - defer helper.cleanup() - - helper.writeFileContents(map[string]string{ - "devices.allow": "a", - }) - - helper.CgroupData.config.Resources.AllowAllDevices = true - helper.CgroupData.config.Resources.DeniedDevices = deniedDevices - devices := &DevicesGroup{} - if err := devices.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamString(helper.CgroupPath, "devices.deny") - if err != nil { - t.Fatalf("Failed to parse devices.deny - %s", err) - } - - if value != deniedList { - t.Fatal("Got the wrong value, set devices.deny failed.") - } -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer_test.go deleted file mode 100644 index 77708db9a..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// +build linux - -package fs - -import ( - "testing" - - "github.com/opencontainers/runc/libcontainer/configs" -) - -func TestFreezerSetState(t *testing.T) { - helper := NewCgroupTestUtil("freezer", t) - defer helper.cleanup() - - helper.writeFileContents(map[string]string{ - "freezer.state": string(configs.Frozen), - }) - - helper.CgroupData.config.Resources.Freezer = configs.Thawed - freezer := &FreezerGroup{} - if err := freezer.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamString(helper.CgroupPath, "freezer.state") - if err != nil { - t.Fatalf("Failed to parse freezer.state - %s", err) - } - if value != string(configs.Thawed) { - t.Fatal("Got the wrong value, set freezer.state failed.") - } -} - -func TestFreezerSetInvalidState(t *testing.T) { - helper := NewCgroupTestUtil("freezer", t) - defer helper.cleanup() - - const ( - invalidArg configs.FreezerState = "Invalid" - ) - - helper.CgroupData.config.Resources.Freezer = invalidArg - freezer := &FreezerGroup{} - if err := freezer.Set(helper.CgroupPath, helper.CgroupData.config); err == nil { - t.Fatal("Failed to return invalid argument error") - } -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb_test.go deleted file mode 100644 index 2d41c4eb2..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb_test.go +++ /dev/null @@ -1,154 +0,0 @@ -// +build linux - -package fs - -import ( - "fmt" - "strconv" - "testing" - - "github.com/opencontainers/runc/libcontainer/cgroups" - "github.com/opencontainers/runc/libcontainer/configs" -) - -const ( - hugetlbUsageContents = "128\n" - hugetlbMaxUsageContents = "256\n" - hugetlbFailcnt = "100\n" -) - -var ( - usage = "hugetlb.%s.usage_in_bytes" - limit = "hugetlb.%s.limit_in_bytes" - maxUsage = "hugetlb.%s.max_usage_in_bytes" - failcnt = "hugetlb.%s.failcnt" -) - -func TestHugetlbSetHugetlb(t *testing.T) { - helper := NewCgroupTestUtil("hugetlb", t) - defer helper.cleanup() - - const ( - hugetlbBefore = 256 - hugetlbAfter = 512 - ) - - for _, pageSize := range HugePageSizes { - helper.writeFileContents(map[string]string{ - fmt.Sprintf(limit, pageSize): strconv.Itoa(hugetlbBefore), - }) - } - - for _, pageSize := range HugePageSizes { - helper.CgroupData.config.Resources.HugetlbLimit = []*configs.HugepageLimit{ - { - Pagesize: pageSize, - Limit: hugetlbAfter, - }, - } - hugetlb := &HugetlbGroup{} - if err := hugetlb.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - } - - for _, pageSize := range HugePageSizes { - limit := fmt.Sprintf(limit, pageSize) - value, err := getCgroupParamUint(helper.CgroupPath, limit) - if err != nil { - t.Fatalf("Failed to parse %s - %s", limit, err) - } - if value != hugetlbAfter { - t.Fatalf("Set hugetlb.limit_in_bytes failed. Expected: %v, Got: %v", hugetlbAfter, value) - } - } -} - -func TestHugetlbStats(t *testing.T) { - helper := NewCgroupTestUtil("hugetlb", t) - defer helper.cleanup() - for _, pageSize := range HugePageSizes { - helper.writeFileContents(map[string]string{ - fmt.Sprintf(usage, pageSize): hugetlbUsageContents, - fmt.Sprintf(maxUsage, pageSize): hugetlbMaxUsageContents, - fmt.Sprintf(failcnt, pageSize): hugetlbFailcnt, - }) - } - - hugetlb := &HugetlbGroup{} - actualStats := *cgroups.NewStats() - err := hugetlb.GetStats(helper.CgroupPath, &actualStats) - if err != nil { - t.Fatal(err) - } - expectedStats := cgroups.HugetlbStats{Usage: 128, MaxUsage: 256, Failcnt: 100} - for _, pageSize := range HugePageSizes { - expectHugetlbStatEquals(t, expectedStats, actualStats.HugetlbStats[pageSize]) - } -} - -func TestHugetlbStatsNoUsageFile(t *testing.T) { - helper := NewCgroupTestUtil("hugetlb", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - maxUsage: hugetlbMaxUsageContents, - }) - - hugetlb := &HugetlbGroup{} - actualStats := *cgroups.NewStats() - err := hugetlb.GetStats(helper.CgroupPath, &actualStats) - if err == nil { - t.Fatal("Expected failure") - } -} - -func TestHugetlbStatsNoMaxUsageFile(t *testing.T) { - helper := NewCgroupTestUtil("hugetlb", t) - defer helper.cleanup() - for _, pageSize := range HugePageSizes { - helper.writeFileContents(map[string]string{ - fmt.Sprintf(usage, pageSize): hugetlbUsageContents, - }) - } - - hugetlb := &HugetlbGroup{} - actualStats := *cgroups.NewStats() - err := hugetlb.GetStats(helper.CgroupPath, &actualStats) - if err == nil { - t.Fatal("Expected failure") - } -} - -func TestHugetlbStatsBadUsageFile(t *testing.T) { - helper := NewCgroupTestUtil("hugetlb", t) - defer helper.cleanup() - for _, pageSize := range HugePageSizes { - helper.writeFileContents(map[string]string{ - fmt.Sprintf(usage, pageSize): "bad", - maxUsage: hugetlbMaxUsageContents, - }) - } - - hugetlb := &HugetlbGroup{} - actualStats := *cgroups.NewStats() - err := hugetlb.GetStats(helper.CgroupPath, &actualStats) - if err == nil { - t.Fatal("Expected failure") - } -} - -func TestHugetlbStatsBadMaxUsageFile(t *testing.T) { - helper := NewCgroupTestUtil("hugetlb", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - usage: hugetlbUsageContents, - maxUsage: "bad", - }) - - hugetlb := &HugetlbGroup{} - actualStats := *cgroups.NewStats() - err := hugetlb.GetStats(helper.CgroupPath, &actualStats) - if err == nil { - t.Fatal("Expected failure") - } -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory_test.go deleted file mode 100644 index 9464599b0..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory_test.go +++ /dev/null @@ -1,294 +0,0 @@ -// +build linux - -package fs - -import ( - "strconv" - "testing" - - "github.com/opencontainers/runc/libcontainer/cgroups" -) - -const ( - memoryStatContents = `cache 512 -rss 1024` - memoryUsageContents = "2048\n" - memoryMaxUsageContents = "4096\n" - memoryFailcnt = "100\n" -) - -func TestMemorySetMemory(t *testing.T) { - helper := NewCgroupTestUtil("memory", t) - defer helper.cleanup() - - const ( - memoryBefore = 314572800 // 300M - memoryAfter = 524288000 // 500M - reservationBefore = 209715200 // 200M - reservationAfter = 314572800 // 300M - ) - - helper.writeFileContents(map[string]string{ - "memory.limit_in_bytes": strconv.Itoa(memoryBefore), - "memory.soft_limit_in_bytes": strconv.Itoa(reservationBefore), - }) - - helper.CgroupData.config.Resources.Memory = memoryAfter - helper.CgroupData.config.Resources.MemoryReservation = reservationAfter - memory := &MemoryGroup{} - if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamUint(helper.CgroupPath, "memory.limit_in_bytes") - if err != nil { - t.Fatalf("Failed to parse memory.limit_in_bytes - %s", err) - } - if value != memoryAfter { - t.Fatal("Got the wrong value, set memory.limit_in_bytes failed.") - } - - value, err = getCgroupParamUint(helper.CgroupPath, "memory.soft_limit_in_bytes") - if err != nil { - t.Fatalf("Failed to parse memory.soft_limit_in_bytes - %s", err) - } - if value != reservationAfter { - t.Fatal("Got the wrong value, set memory.soft_limit_in_bytes failed.") - } -} - -func TestMemorySetMemoryswap(t *testing.T) { - helper := NewCgroupTestUtil("memory", t) - defer helper.cleanup() - - const ( - memoryswapBefore = 314572800 // 300M - memoryswapAfter = 524288000 // 500M - ) - - helper.writeFileContents(map[string]string{ - "memory.memsw.limit_in_bytes": strconv.Itoa(memoryswapBefore), - }) - - helper.CgroupData.config.Resources.MemorySwap = memoryswapAfter - memory := &MemoryGroup{} - if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamUint(helper.CgroupPath, "memory.memsw.limit_in_bytes") - if err != nil { - t.Fatalf("Failed to parse memory.memsw.limit_in_bytes - %s", err) - } - if value != memoryswapAfter { - t.Fatal("Got the wrong value, set memory.memsw.limit_in_bytes failed.") - } -} - -func TestMemorySetKernelMemory(t *testing.T) { - helper := NewCgroupTestUtil("memory", t) - defer helper.cleanup() - - const ( - kernelMemoryBefore = 314572800 // 300M - kernelMemoryAfter = 524288000 // 500M - ) - - helper.writeFileContents(map[string]string{ - "memory.kmem.limit_in_bytes": strconv.Itoa(kernelMemoryBefore), - }) - - helper.CgroupData.config.Resources.KernelMemory = kernelMemoryAfter - memory := &MemoryGroup{} - if err := memory.SetKernelMemory(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamUint(helper.CgroupPath, "memory.kmem.limit_in_bytes") - if err != nil { - t.Fatalf("Failed to parse memory.kmem.limit_in_bytes - %s", err) - } - if value != kernelMemoryAfter { - t.Fatal("Got the wrong value, set memory.kmem.limit_in_bytes failed.") - } -} - -func TestMemorySetMemorySwappinessDefault(t *testing.T) { - helper := NewCgroupTestUtil("memory", t) - defer helper.cleanup() - - const ( - swappinessBefore = 60 //deafult is 60 - swappinessAfter = 0 - ) - - helper.writeFileContents(map[string]string{ - "memory.swappiness": strconv.Itoa(swappinessBefore), - }) - - helper.CgroupData.config.Resources.Memory = swappinessAfter - memory := &MemoryGroup{} - if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamUint(helper.CgroupPath, "memory.swappiness") - if err != nil { - t.Fatalf("Failed to parse memory.swappiness - %s", err) - } - if value != swappinessAfter { - t.Fatal("Got the wrong value, set memory.swappiness failed.") - } -} - -func TestMemoryStats(t *testing.T) { - helper := NewCgroupTestUtil("memory", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "memory.stat": memoryStatContents, - "memory.usage_in_bytes": memoryUsageContents, - "memory.max_usage_in_bytes": memoryMaxUsageContents, - "memory.failcnt": memoryFailcnt, - "memory.memsw.usage_in_bytes": memoryUsageContents, - "memory.memsw.max_usage_in_bytes": memoryMaxUsageContents, - "memory.memsw.failcnt": memoryFailcnt, - "memory.kmem.usage_in_bytes": memoryUsageContents, - "memory.kmem.max_usage_in_bytes": memoryMaxUsageContents, - "memory.kmem.failcnt": memoryFailcnt, - }) - - memory := &MemoryGroup{} - actualStats := *cgroups.NewStats() - err := memory.GetStats(helper.CgroupPath, &actualStats) - if err != nil { - t.Fatal(err) - } - expectedStats := cgroups.MemoryStats{Cache: 512, Usage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100}, SwapUsage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100}, KernelUsage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100}, Stats: map[string]uint64{"cache": 512, "rss": 1024}} - expectMemoryStatEquals(t, expectedStats, actualStats.MemoryStats) -} - -func TestMemoryStatsNoStatFile(t *testing.T) { - helper := NewCgroupTestUtil("memory", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "memory.usage_in_bytes": memoryUsageContents, - "memory.max_usage_in_bytes": memoryMaxUsageContents, - }) - - memory := &MemoryGroup{} - actualStats := *cgroups.NewStats() - err := memory.GetStats(helper.CgroupPath, &actualStats) - if err != nil { - t.Fatal(err) - } -} - -func TestMemoryStatsNoUsageFile(t *testing.T) { - helper := NewCgroupTestUtil("memory", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "memory.stat": memoryStatContents, - "memory.max_usage_in_bytes": memoryMaxUsageContents, - }) - - memory := &MemoryGroup{} - actualStats := *cgroups.NewStats() - err := memory.GetStats(helper.CgroupPath, &actualStats) - if err == nil { - t.Fatal("Expected failure") - } -} - -func TestMemoryStatsNoMaxUsageFile(t *testing.T) { - helper := NewCgroupTestUtil("memory", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "memory.stat": memoryStatContents, - "memory.usage_in_bytes": memoryUsageContents, - }) - - memory := &MemoryGroup{} - actualStats := *cgroups.NewStats() - err := memory.GetStats(helper.CgroupPath, &actualStats) - if err == nil { - t.Fatal("Expected failure") - } -} - -func TestMemoryStatsBadStatFile(t *testing.T) { - helper := NewCgroupTestUtil("memory", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "memory.stat": "rss rss", - "memory.usage_in_bytes": memoryUsageContents, - "memory.max_usage_in_bytes": memoryMaxUsageContents, - }) - - memory := &MemoryGroup{} - actualStats := *cgroups.NewStats() - err := memory.GetStats(helper.CgroupPath, &actualStats) - if err == nil { - t.Fatal("Expected failure") - } -} - -func TestMemoryStatsBadUsageFile(t *testing.T) { - helper := NewCgroupTestUtil("memory", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "memory.stat": memoryStatContents, - "memory.usage_in_bytes": "bad", - "memory.max_usage_in_bytes": memoryMaxUsageContents, - }) - - memory := &MemoryGroup{} - actualStats := *cgroups.NewStats() - err := memory.GetStats(helper.CgroupPath, &actualStats) - if err == nil { - t.Fatal("Expected failure") - } -} - -func TestMemoryStatsBadMaxUsageFile(t *testing.T) { - helper := NewCgroupTestUtil("memory", t) - defer helper.cleanup() - helper.writeFileContents(map[string]string{ - "memory.stat": memoryStatContents, - "memory.usage_in_bytes": memoryUsageContents, - "memory.max_usage_in_bytes": "bad", - }) - - memory := &MemoryGroup{} - actualStats := *cgroups.NewStats() - err := memory.GetStats(helper.CgroupPath, &actualStats) - if err == nil { - t.Fatal("Expected failure") - } -} - -func TestMemorySetOomControl(t *testing.T) { - helper := NewCgroupTestUtil("memory", t) - defer helper.cleanup() - - const ( - oom_kill_disable = 1 // disable oom killer, default is 0 - ) - - helper.writeFileContents(map[string]string{ - "memory.oom_control": strconv.Itoa(oom_kill_disable), - }) - - memory := &MemoryGroup{} - if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamUint(helper.CgroupPath, "memory.oom_control") - if err != nil { - t.Fatalf("Failed to parse memory.oom_control - %s", err) - } - - if value != oom_kill_disable { - t.Fatalf("Got the wrong value, set memory.oom_control failed.") - } -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls_test.go deleted file mode 100644 index 974bd9d88..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls_test.go +++ /dev/null @@ -1,38 +0,0 @@ -// +build linux - -package fs - -import ( - "testing" -) - -const ( - classidBefore = "0x100002" - classidAfter = "0x100001" -) - -func TestNetClsSetClassid(t *testing.T) { - helper := NewCgroupTestUtil("net_cls", t) - defer helper.cleanup() - - helper.writeFileContents(map[string]string{ - "net_cls.classid": classidBefore, - }) - - helper.CgroupData.config.Resources.NetClsClassid = classidAfter - netcls := &NetClsGroup{} - if err := netcls.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - // As we are in mock environment, we can't get correct value of classid from - // net_cls.classid. - // So. we just judge if we successfully write classid into file - value, err := getCgroupParamString(helper.CgroupPath, "net_cls.classid") - if err != nil { - t.Fatalf("Failed to parse net_cls.classid - %s", err) - } - if value != classidAfter { - t.Fatal("Got the wrong value, set net_cls.classid failed.") - } -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio_test.go deleted file mode 100644 index efbf0639a..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio_test.go +++ /dev/null @@ -1,38 +0,0 @@ -// +build linux - -package fs - -import ( - "strings" - "testing" - - "github.com/opencontainers/runc/libcontainer/configs" -) - -var ( - prioMap = []*configs.IfPrioMap{ - { - Interface: "test", - Priority: 5, - }, - } -) - -func TestNetPrioSetIfPrio(t *testing.T) { - helper := NewCgroupTestUtil("net_prio", t) - defer helper.cleanup() - - helper.CgroupData.config.Resources.NetPrioIfpriomap = prioMap - netPrio := &NetPrioGroup{} - if err := netPrio.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamString(helper.CgroupPath, "net_prio.ifpriomap") - if err != nil { - t.Fatalf("Failed to parse net_prio.ifpriomap - %s", err) - } - if !strings.Contains(value, "test 5") { - t.Fatal("Got the wrong value, set net_prio.ifpriomap failed.") - } -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids_test.go deleted file mode 100644 index 06b11927a..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids_test.go +++ /dev/null @@ -1,83 +0,0 @@ -// +build linux - -package fs - -import ( - "strconv" - "testing" - - "github.com/opencontainers/runc/libcontainer/cgroups" -) - -const ( - maxUnlimited = -1 - maxLimited = 1024 -) - -func TestPidsSetMax(t *testing.T) { - helper := NewCgroupTestUtil("pids", t) - defer helper.cleanup() - - helper.writeFileContents(map[string]string{ - "pids.max": "max", - }) - - helper.CgroupData.config.Resources.PidsLimit = maxLimited - pids := &PidsGroup{} - if err := pids.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamUint(helper.CgroupPath, "pids.max") - if err != nil { - t.Fatalf("Failed to parse pids.max - %s", err) - } - - if value != maxLimited { - t.Fatalf("Expected %d, got %d for setting pids.max - limited", maxLimited, value) - } -} - -func TestPidsSetUnlimited(t *testing.T) { - helper := NewCgroupTestUtil("pids", t) - defer helper.cleanup() - - helper.writeFileContents(map[string]string{ - "pids.max": strconv.Itoa(maxLimited), - }) - - helper.CgroupData.config.Resources.PidsLimit = maxUnlimited - pids := &PidsGroup{} - if err := pids.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { - t.Fatal(err) - } - - value, err := getCgroupParamString(helper.CgroupPath, "pids.max") - if err != nil { - t.Fatalf("Failed to parse pids.max - %s", err) - } - - if value != "max" { - t.Fatalf("Expected %s, got %s for setting pids.max - unlimited", "max", value) - } -} - -func TestPidsStats(t *testing.T) { - helper := NewCgroupTestUtil("pids", t) - defer helper.cleanup() - - helper.writeFileContents(map[string]string{ - "pids.current": strconv.Itoa(1337), - "pids.max": strconv.Itoa(maxLimited), - }) - - pids := &PidsGroup{} - stats := *cgroups.NewStats() - if err := pids.GetStats(helper.CgroupPath, &stats); err != nil { - t.Fatal(err) - } - - if stats.PidsStats.Current != 1337 { - t.Fatalf("Expected %d, got %d for pids.current", 1337, stats.PidsStats.Current) - } -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/stats_util_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/stats_util_test.go deleted file mode 100644 index b1777cc0a..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/stats_util_test.go +++ /dev/null @@ -1,113 +0,0 @@ -// +build linux - -package fs - -import ( - "fmt" - "testing" - - "github.com/Sirupsen/logrus" - "github.com/opencontainers/runc/libcontainer/cgroups" -) - -func blkioStatEntryEquals(expected, actual []cgroups.BlkioStatEntry) error { - if len(expected) != len(actual) { - return fmt.Errorf("blkioStatEntries length do not match") - } - for i, expValue := range expected { - actValue := actual[i] - if expValue != actValue { - return fmt.Errorf("Expected blkio stat entry %v but found %v", expValue, actValue) - } - } - return nil -} - -func expectBlkioStatsEquals(t *testing.T, expected, actual cgroups.BlkioStats) { - if err := blkioStatEntryEquals(expected.IoServiceBytesRecursive, actual.IoServiceBytesRecursive); err != nil { - logrus.Printf("blkio IoServiceBytesRecursive do not match - %s\n", err) - t.Fail() - } - - if err := blkioStatEntryEquals(expected.IoServicedRecursive, actual.IoServicedRecursive); err != nil { - logrus.Printf("blkio IoServicedRecursive do not match - %s\n", err) - t.Fail() - } - - if err := blkioStatEntryEquals(expected.IoQueuedRecursive, actual.IoQueuedRecursive); err != nil { - logrus.Printf("blkio IoQueuedRecursive do not match - %s\n", err) - t.Fail() - } - - if err := blkioStatEntryEquals(expected.SectorsRecursive, actual.SectorsRecursive); err != nil { - logrus.Printf("blkio SectorsRecursive do not match - %s\n", err) - t.Fail() - } - - if err := blkioStatEntryEquals(expected.IoServiceTimeRecursive, actual.IoServiceTimeRecursive); err != nil { - logrus.Printf("blkio IoServiceTimeRecursive do not match - %s\n", err) - t.Fail() - } - - if err := blkioStatEntryEquals(expected.IoWaitTimeRecursive, actual.IoWaitTimeRecursive); err != nil { - logrus.Printf("blkio IoWaitTimeRecursive do not match - %s\n", err) - t.Fail() - } - - if err := blkioStatEntryEquals(expected.IoMergedRecursive, actual.IoMergedRecursive); err != nil { - logrus.Printf("blkio IoMergedRecursive do not match - %v vs %v\n", expected.IoMergedRecursive, actual.IoMergedRecursive) - t.Fail() - } - - if err := blkioStatEntryEquals(expected.IoTimeRecursive, actual.IoTimeRecursive); err != nil { - logrus.Printf("blkio IoTimeRecursive do not match - %s\n", err) - t.Fail() - } -} - -func expectThrottlingDataEquals(t *testing.T, expected, actual cgroups.ThrottlingData) { - if expected != actual { - logrus.Printf("Expected throttling data %v but found %v\n", expected, actual) - t.Fail() - } -} - -func expectHugetlbStatEquals(t *testing.T, expected, actual cgroups.HugetlbStats) { - if expected != actual { - logrus.Printf("Expected hugetlb stats %v but found %v\n", expected, actual) - t.Fail() - } -} - -func expectMemoryStatEquals(t *testing.T, expected, actual cgroups.MemoryStats) { - expectMemoryDataEquals(t, expected.Usage, actual.Usage) - expectMemoryDataEquals(t, expected.SwapUsage, actual.SwapUsage) - expectMemoryDataEquals(t, expected.KernelUsage, actual.KernelUsage) - - for key, expValue := range expected.Stats { - actValue, ok := actual.Stats[key] - if !ok { - logrus.Printf("Expected memory stat key %s not found\n", key) - t.Fail() - } - if expValue != actValue { - logrus.Printf("Expected memory stat value %d but found %d\n", expValue, actValue) - t.Fail() - } - } -} - -func expectMemoryDataEquals(t *testing.T, expected, actual cgroups.MemoryData) { - if expected.Usage != actual.Usage { - logrus.Printf("Expected memory usage %d but found %d\n", expected.Usage, actual.Usage) - t.Fail() - } - if expected.MaxUsage != actual.MaxUsage { - logrus.Printf("Expected memory max usage %d but found %d\n", expected.MaxUsage, actual.MaxUsage) - t.Fail() - } - if expected.Failcnt != actual.Failcnt { - logrus.Printf("Expected memory failcnt %d but found %d\n", expected.Failcnt, actual.Failcnt) - t.Fail() - } -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/util_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/util_test.go deleted file mode 100644 index 7067e799f..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/util_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// +build linux - -/* -Utility for testing cgroup operations. - -Creates a mock of the cgroup filesystem for the duration of the test. -*/ -package fs - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/opencontainers/runc/libcontainer/configs" -) - -type cgroupTestUtil struct { - // cgroup data to use in tests. - CgroupData *cgroupData - - // Path to the mock cgroup directory. - CgroupPath string - - // Temporary directory to store mock cgroup filesystem. - tempDir string - t *testing.T -} - -// Creates a new test util for the specified subsystem -func NewCgroupTestUtil(subsystem string, t *testing.T) *cgroupTestUtil { - d := &cgroupData{ - config: &configs.Cgroup{}, - } - d.config.Resources = &configs.Resources{} - tempDir, err := ioutil.TempDir("", "cgroup_test") - if err != nil { - t.Fatal(err) - } - d.root = tempDir - testCgroupPath := filepath.Join(d.root, subsystem) - if err != nil { - t.Fatal(err) - } - - // Ensure the full mock cgroup path exists. - err = os.MkdirAll(testCgroupPath, 0755) - if err != nil { - t.Fatal(err) - } - return &cgroupTestUtil{CgroupData: d, CgroupPath: testCgroupPath, tempDir: tempDir, t: t} -} - -func (c *cgroupTestUtil) cleanup() { - os.RemoveAll(c.tempDir) -} - -// Write the specified contents on the mock of the specified cgroup files. -func (c *cgroupTestUtil) writeFileContents(fileContents map[string]string) { - for file, contents := range fileContents { - err := writeFile(c.CgroupPath, file, contents) - if err != nil { - c.t.Fatal(err) - } - } -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/utils_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/utils_test.go deleted file mode 100644 index 99cdc18e0..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/fs/utils_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// +build linux - -package fs - -import ( - "io/ioutil" - "math" - "os" - "path/filepath" - "strconv" - "testing" -) - -const ( - cgroupFile = "cgroup.file" - floatValue = 2048.0 - floatString = "2048" -) - -func TestGetCgroupParamsInt(t *testing.T) { - // Setup tempdir. - tempDir, err := ioutil.TempDir("", "cgroup_utils_test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tempDir) - tempFile := filepath.Join(tempDir, cgroupFile) - - // Success. - err = ioutil.WriteFile(tempFile, []byte(floatString), 0755) - if err != nil { - t.Fatal(err) - } - value, err := getCgroupParamUint(tempDir, cgroupFile) - if err != nil { - t.Fatal(err) - } else if value != floatValue { - t.Fatalf("Expected %d to equal %f", value, floatValue) - } - - // Success with new line. - err = ioutil.WriteFile(tempFile, []byte(floatString+"\n"), 0755) - if err != nil { - t.Fatal(err) - } - value, err = getCgroupParamUint(tempDir, cgroupFile) - if err != nil { - t.Fatal(err) - } else if value != floatValue { - t.Fatalf("Expected %d to equal %f", value, floatValue) - } - - // Success with negative values - err = ioutil.WriteFile(tempFile, []byte("-12345"), 0755) - if err != nil { - t.Fatal(err) - } - value, err = getCgroupParamUint(tempDir, cgroupFile) - if err != nil { - t.Fatal(err) - } else if value != 0 { - t.Fatalf("Expected %d to equal %d", value, 0) - } - - // Success with negative values lesser than min int64 - s := strconv.FormatFloat(math.MinInt64, 'f', -1, 64) - err = ioutil.WriteFile(tempFile, []byte(s), 0755) - if err != nil { - t.Fatal(err) - } - value, err = getCgroupParamUint(tempDir, cgroupFile) - if err != nil { - t.Fatal(err) - } else if value != 0 { - t.Fatalf("Expected %d to equal %d", value, 0) - } - - // Not a float. - err = ioutil.WriteFile(tempFile, []byte("not-a-float"), 0755) - if err != nil { - t.Fatal(err) - } - _, err = getCgroupParamUint(tempDir, cgroupFile) - if err == nil { - t.Fatal("Expecting error, got none") - } - - // Unknown file. - err = os.Remove(tempFile) - if err != nil { - t.Fatal(err) - } - _, err = getCgroupParamUint(tempDir, cgroupFile) - if err == nil { - t.Fatal("Expecting error, got none") - } -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_nosystemd.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_nosystemd.go deleted file mode 100644 index 7de9ae605..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_nosystemd.go +++ /dev/null @@ -1,55 +0,0 @@ -// +build !linux - -package systemd - -import ( - "fmt" - - "github.com/opencontainers/runc/libcontainer/cgroups" - "github.com/opencontainers/runc/libcontainer/configs" -) - -type Manager struct { - Cgroups *configs.Cgroup - Paths map[string]string -} - -func UseSystemd() bool { - return false -} - -func (m *Manager) Apply(pid int) error { - return fmt.Errorf("Systemd not supported") -} - -func (m *Manager) GetPids() ([]int, error) { - return nil, fmt.Errorf("Systemd not supported") -} - -func (m *Manager) GetAllPids() ([]int, error) { - return nil, fmt.Errorf("Systemd not supported") -} - -func (m *Manager) Destroy() error { - return fmt.Errorf("Systemd not supported") -} - -func (m *Manager) GetPaths() map[string]string { - return nil -} - -func (m *Manager) GetStats() (*cgroups.Stats, error) { - return nil, fmt.Errorf("Systemd not supported") -} - -func (m *Manager) Set(container *configs.Config) error { - return nil, fmt.Errorf("Systemd not supported") -} - -func (m *Manager) Freeze(state configs.FreezerState) error { - return fmt.Errorf("Systemd not supported") -} - -func Freeze(c *configs.Cgroup, state configs.FreezerState) error { - return fmt.Errorf("Systemd not supported") -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go deleted file mode 100644 index db020a971..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go +++ /dev/null @@ -1,596 +0,0 @@ -// +build linux - -package systemd - -import ( - "errors" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strconv" - "strings" - "sync" - "time" - - systemdDbus "github.com/coreos/go-systemd/dbus" - systemdUtil "github.com/coreos/go-systemd/util" - "github.com/godbus/dbus" - "github.com/opencontainers/runc/libcontainer/cgroups" - "github.com/opencontainers/runc/libcontainer/cgroups/fs" - "github.com/opencontainers/runc/libcontainer/configs" -) - -type Manager struct { - mu sync.Mutex - Cgroups *configs.Cgroup - Paths map[string]string -} - -type subsystem interface { - // Name returns the name of the subsystem. - Name() string - // Returns the stats, as 'stats', corresponding to the cgroup under 'path'. - GetStats(path string, stats *cgroups.Stats) error - // Set the cgroup represented by cgroup. - Set(path string, cgroup *configs.Cgroup) error -} - -var errSubsystemDoesNotExist = errors.New("cgroup: subsystem does not exist") - -type subsystemSet []subsystem - -func (s subsystemSet) Get(name string) (subsystem, error) { - for _, ss := range s { - if ss.Name() == name { - return ss, nil - } - } - return nil, errSubsystemDoesNotExist -} - -var subsystems = subsystemSet{ - &fs.CpusetGroup{}, - &fs.DevicesGroup{}, - &fs.MemoryGroup{}, - &fs.CpuGroup{}, - &fs.CpuacctGroup{}, - &fs.PidsGroup{}, - &fs.BlkioGroup{}, - &fs.HugetlbGroup{}, - &fs.PerfEventGroup{}, - &fs.FreezerGroup{}, - &fs.NetPrioGroup{}, - &fs.NetClsGroup{}, - &fs.NameGroup{GroupName: "name=systemd"}, -} - -const ( - testScopeWait = 4 -) - -var ( - connLock sync.Mutex - theConn *systemdDbus.Conn - hasStartTransientUnit bool - hasTransientDefaultDependencies bool -) - -func newProp(name string, units interface{}) systemdDbus.Property { - return systemdDbus.Property{ - Name: name, - Value: dbus.MakeVariant(units), - } -} - -func UseSystemd() bool { - if !systemdUtil.IsRunningSystemd() { - return false - } - - connLock.Lock() - defer connLock.Unlock() - - if theConn == nil { - var err error - theConn, err = systemdDbus.New() - if err != nil { - return false - } - - // Assume we have StartTransientUnit - hasStartTransientUnit = true - - // But if we get UnknownMethod error we don't - if _, err := theConn.StartTransientUnit("test.scope", "invalid", nil, nil); err != nil { - if dbusError, ok := err.(dbus.Error); ok { - if dbusError.Name == "org.freedesktop.DBus.Error.UnknownMethod" { - hasStartTransientUnit = false - return hasStartTransientUnit - } - } - } - - // Ensure the scope name we use doesn't exist. Use the Pid to - // avoid collisions between multiple libcontainer users on a - // single host. - scope := fmt.Sprintf("libcontainer-%d-systemd-test-default-dependencies.scope", os.Getpid()) - testScopeExists := true - for i := 0; i <= testScopeWait; i++ { - if _, err := theConn.StopUnit(scope, "replace", nil); err != nil { - if dbusError, ok := err.(dbus.Error); ok { - if strings.Contains(dbusError.Name, "org.freedesktop.systemd1.NoSuchUnit") { - testScopeExists = false - break - } - } - } - time.Sleep(time.Millisecond) - } - - // Bail out if we can't kill this scope without testing for DefaultDependencies - if testScopeExists { - return hasStartTransientUnit - } - - // Assume StartTransientUnit on a scope allows DefaultDependencies - hasTransientDefaultDependencies = true - ddf := newProp("DefaultDependencies", false) - if _, err := theConn.StartTransientUnit(scope, "replace", []systemdDbus.Property{ddf}, nil); err != nil { - if dbusError, ok := err.(dbus.Error); ok { - if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") { - hasTransientDefaultDependencies = false - } - } - } - - // Not critical because of the stop unit logic above. - theConn.StopUnit(scope, "replace", nil) - } - return hasStartTransientUnit -} - -func getIfaceForUnit(unitName string) string { - if strings.HasSuffix(unitName, ".scope") { - return "Scope" - } - if strings.HasSuffix(unitName, ".service") { - return "Service" - } - return "Unit" -} - -func (m *Manager) Apply(pid int) error { - var ( - c = m.Cgroups - unitName = getUnitName(c) - slice = "system.slice" - properties []systemdDbus.Property - ) - - if c.Paths != nil { - paths := make(map[string]string) - for name, path := range c.Paths { - _, err := getSubsystemPath(m.Cgroups, name) - if err != nil { - // Don't fail if a cgroup hierarchy was not found, just skip this subsystem - if cgroups.IsNotFound(err) { - continue - } - return err - } - paths[name] = path - } - m.Paths = paths - return cgroups.EnterPid(m.Paths, pid) - } - - if c.Parent != "" { - slice = c.Parent - } - - properties = append(properties, - systemdDbus.PropSlice(slice), - systemdDbus.PropDescription("docker container "+c.Name), - newProp("PIDs", []uint32{uint32(pid)}), - ) - - // Always enable accounting, this gets us the same behaviour as the fs implementation, - // plus the kernel has some problems with joining the memory cgroup at a later time. - properties = append(properties, - newProp("MemoryAccounting", true), - newProp("CPUAccounting", true), - newProp("BlockIOAccounting", true)) - - if hasTransientDefaultDependencies { - properties = append(properties, - newProp("DefaultDependencies", false)) - } - - if c.Resources.Memory != 0 { - properties = append(properties, - newProp("MemoryLimit", uint64(c.Resources.Memory))) - } - - if c.Resources.CpuShares != 0 { - properties = append(properties, - newProp("CPUShares", uint64(c.Resources.CpuShares))) - } - - if c.Resources.BlkioWeight != 0 { - properties = append(properties, - newProp("BlockIOWeight", uint64(c.Resources.BlkioWeight))) - } - - // We need to set kernel memory before processes join cgroup because - // kmem.limit_in_bytes can only be set when the cgroup is empty. - // And swap memory limit needs to be set after memory limit, only - // memory limit is handled by systemd, so it's kind of ugly here. - if c.Resources.KernelMemory > 0 { - if err := setKernelMemory(c); err != nil { - return err - } - } - - if _, err := theConn.StartTransientUnit(unitName, "replace", properties, nil); err != nil { - return err - } - - if err := joinDevices(c, pid); err != nil { - return err - } - - // TODO: CpuQuota and CpuPeriod not available in systemd - // we need to manually join the cpu.cfs_quota_us and cpu.cfs_period_us - if err := joinCpu(c, pid); err != nil { - return err - } - - // TODO: MemoryReservation and MemorySwap not available in systemd - if err := joinMemory(c, pid); err != nil { - return err - } - - // we need to manually join the freezer, net_cls, net_prio, pids and cpuset cgroup in systemd - // because it does not currently support it via the dbus api. - if err := joinFreezer(c, pid); err != nil { - return err - } - - if err := joinNetPrio(c, pid); err != nil { - return err - } - if err := joinNetCls(c, pid); err != nil { - return err - } - - if err := joinPids(c, pid); err != nil { - return err - } - - if err := joinCpuset(c, pid); err != nil { - return err - } - - if err := joinHugetlb(c, pid); err != nil { - return err - } - - if err := joinPerfEvent(c, pid); err != nil { - return err - } - // FIXME: Systemd does have `BlockIODeviceWeight` property, but we got problem - // using that (at least on systemd 208, see https://github.com/opencontainers/runc/libcontainer/pull/354), - // so use fs work around for now. - if err := joinBlkio(c, pid); err != nil { - return err - } - - paths := make(map[string]string) - for _, s := range subsystems { - subsystemPath, err := getSubsystemPath(m.Cgroups, s.Name()) - if err != nil { - // Don't fail if a cgroup hierarchy was not found, just skip this subsystem - if cgroups.IsNotFound(err) { - continue - } - return err - } - paths[s.Name()] = subsystemPath - } - m.Paths = paths - return nil -} - -func (m *Manager) Destroy() error { - if m.Cgroups.Paths != nil { - return nil - } - m.mu.Lock() - defer m.mu.Unlock() - theConn.StopUnit(getUnitName(m.Cgroups), "replace", nil) - if err := cgroups.RemovePaths(m.Paths); err != nil { - return err - } - m.Paths = make(map[string]string) - return nil -} - -func (m *Manager) GetPaths() map[string]string { - m.mu.Lock() - paths := m.Paths - m.mu.Unlock() - return paths -} - -func writeFile(dir, file, data string) error { - // Normally dir should not be empty, one case is that cgroup subsystem - // is not mounted, we will get empty dir, and we want it fail here. - if dir == "" { - return fmt.Errorf("no such directory for %s.", file) - } - return ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700) -} - -func join(c *configs.Cgroup, subsystem string, pid int) (string, error) { - path, err := getSubsystemPath(c, subsystem) - if err != nil { - return "", err - } - if err := os.MkdirAll(path, 0755); err != nil { - return "", err - } - if err := writeFile(path, "cgroup.procs", strconv.Itoa(pid)); err != nil { - return "", err - } - - return path, nil -} - -func joinCpu(c *configs.Cgroup, pid int) error { - _, err := join(c, "cpu", pid) - if err != nil && !cgroups.IsNotFound(err) { - return err - } - return nil -} - -func joinFreezer(c *configs.Cgroup, pid int) error { - _, err := join(c, "freezer", pid) - if err != nil && !cgroups.IsNotFound(err) { - return err - } - return nil -} - -func joinNetPrio(c *configs.Cgroup, pid int) error { - _, err := join(c, "net_prio", pid) - if err != nil && !cgroups.IsNotFound(err) { - return err - } - return nil -} - -func joinNetCls(c *configs.Cgroup, pid int) error { - _, err := join(c, "net_cls", pid) - if err != nil && !cgroups.IsNotFound(err) { - return err - } - return nil -} - -func joinPids(c *configs.Cgroup, pid int) error { - _, err := join(c, "pids", pid) - if err != nil && !cgroups.IsNotFound(err) { - return err - } - return nil -} - -// systemd represents slice heirarchy using `-`, so we need to follow suit when -// generating the path of slice. Essentially, test-a-b.slice becomes -// test.slice/test-a.slice/test-a-b.slice. -func expandSlice(slice string) (string, error) { - suffix := ".slice" - sliceName := strings.TrimSuffix(slice, suffix) - - var path, prefix string - for _, component := range strings.Split(sliceName, "-") { - // test--a.slice isn't permitted, nor is -test.slice. - if component == "" { - return "", fmt.Errorf("invalid slice name: %s", slice) - } - - // Append the component to the path and to the prefix. - path += prefix + component + suffix + "/" - prefix += component + "-" - } - - return path, nil -} - -func getSubsystemPath(c *configs.Cgroup, subsystem string) (string, error) { - mountpoint, err := cgroups.FindCgroupMountpoint(subsystem) - if err != nil { - return "", err - } - - initPath, err := cgroups.GetInitCgroupDir(subsystem) - if err != nil { - return "", err - } - - slice := "system.slice" - if c.Parent != "" { - slice = c.Parent - } - - slice, err = expandSlice(slice) - if err != nil { - return "", err - } - - return filepath.Join(mountpoint, initPath, slice, getUnitName(c)), nil -} - -func (m *Manager) Freeze(state configs.FreezerState) error { - path, err := getSubsystemPath(m.Cgroups, "freezer") - if err != nil { - return err - } - prevState := m.Cgroups.Resources.Freezer - m.Cgroups.Resources.Freezer = state - freezer, err := subsystems.Get("freezer") - if err != nil { - return err - } - err = freezer.Set(path, m.Cgroups) - if err != nil { - m.Cgroups.Resources.Freezer = prevState - return err - } - return nil -} - -func (m *Manager) GetPids() ([]int, error) { - path, err := getSubsystemPath(m.Cgroups, "devices") - if err != nil { - return nil, err - } - return cgroups.GetPids(path) -} - -func (m *Manager) GetAllPids() ([]int, error) { - path, err := getSubsystemPath(m.Cgroups, "devices") - if err != nil { - return nil, err - } - return cgroups.GetAllPids(path) -} - -func (m *Manager) GetStats() (*cgroups.Stats, error) { - m.mu.Lock() - defer m.mu.Unlock() - stats := cgroups.NewStats() - for name, path := range m.Paths { - sys, err := subsystems.Get(name) - if err == errSubsystemDoesNotExist || !cgroups.PathExists(path) { - continue - } - if err := sys.GetStats(path, stats); err != nil { - return nil, err - } - } - - return stats, nil -} - -func (m *Manager) Set(container *configs.Config) error { - for _, sys := range subsystems { - // Get the subsystem path, but don't error out for not found cgroups. - path, err := getSubsystemPath(container.Cgroups, sys.Name()) - if err != nil && !cgroups.IsNotFound(err) { - return err - } - - if err := sys.Set(path, container.Cgroups); err != nil { - return err - } - } - - if m.Paths["cpu"] != "" { - if err := fs.CheckCpushares(m.Paths["cpu"], container.Cgroups.Resources.CpuShares); err != nil { - return err - } - } - return nil -} - -func getUnitName(c *configs.Cgroup) string { - return fmt.Sprintf("%s-%s.scope", c.ScopePrefix, c.Name) -} - -// Atm we can't use the systemd device support because of two missing things: -// * Support for wildcards to allow mknod on any device -// * Support for wildcards to allow /dev/pts support -// -// The second is available in more recent systemd as "char-pts", but not in e.g. v208 which is -// in wide use. When both these are available we will be able to switch, but need to keep the old -// implementation for backwards compat. -// -// Note: we can't use systemd to set up the initial limits, and then change the cgroup -// because systemd will re-write the device settings if it needs to re-apply the cgroup context. -// This happens at least for v208 when any sibling unit is started. -func joinDevices(c *configs.Cgroup, pid int) error { - _, err := join(c, "devices", pid) - // Even if it's `not found` error, we'll return err because devices cgroup - // is hard requirement for container security. - if err != nil { - return err - } - return nil -} - -func setKernelMemory(c *configs.Cgroup) error { - path, err := getSubsystemPath(c, "memory") - if err != nil && !cgroups.IsNotFound(err) { - return err - } - - if err := os.MkdirAll(path, 0755); err != nil { - return err - } - - // This doesn't get called by manager.Set, so we need to do it here. - s := &fs.MemoryGroup{} - return s.SetKernelMemory(path, c) -} - -func joinMemory(c *configs.Cgroup, pid int) error { - _, err := join(c, "memory", pid) - if err != nil && !cgroups.IsNotFound(err) { - return err - } - return nil -} - -// systemd does not atm set up the cpuset controller, so we must manually -// join it. Additionally that is a very finicky controller where each -// level must have a full setup as the default for a new directory is "no cpus" -func joinCpuset(c *configs.Cgroup, pid int) error { - path, err := getSubsystemPath(c, "cpuset") - if err != nil && !cgroups.IsNotFound(err) { - return err - } - - s := &fs.CpusetGroup{} - - return s.ApplyDir(path, c, pid) -} - -// `BlockIODeviceWeight` property of systemd does not work properly, and systemd -// expects device path instead of major minor numbers, which is also confusing -// for users. So we use fs work around for now. -func joinBlkio(c *configs.Cgroup, pid int) error { - _, err := join(c, "blkio", pid) - if err != nil { - return err - } - return nil -} - -func joinHugetlb(c *configs.Cgroup, pid int) error { - _, err := join(c, "hugetlb", pid) - if err != nil && !cgroups.IsNotFound(err) { - return err - } - return nil -} - -func joinPerfEvent(c *configs.Cgroup, pid int) error { - _, err := join(c, "perf_event", pid) - if err != nil && !cgroups.IsNotFound(err) { - return err - } - return nil -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/configs/config_unix_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/configs/config_unix_test.go deleted file mode 100644 index 27d07d4e8..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/configs/config_unix_test.go +++ /dev/null @@ -1,156 +0,0 @@ -// +build linux freebsd - -package configs - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "testing" -) - -// Checks whether the expected capability is specified in the capabilities. -func contains(expected string, values []string) bool { - for _, v := range values { - if v == expected { - return true - } - } - return false -} - -func containsDevice(expected *Device, values []*Device) bool { - for _, d := range values { - if d.Path == expected.Path && - d.Permissions == expected.Permissions && - d.FileMode == expected.FileMode && - d.Major == expected.Major && - d.Minor == expected.Minor && - d.Type == expected.Type { - return true - } - } - return false -} - -func loadConfig(name string) (*Config, error) { - f, err := os.Open(filepath.Join("../sample_configs", name)) - if err != nil { - return nil, err - } - defer f.Close() - - var container *Config - if err := json.NewDecoder(f).Decode(&container); err != nil { - return nil, err - } - - // Check that a config doesn't contain extra fields - var configMap, abstractMap map[string]interface{} - - if _, err := f.Seek(0, 0); err != nil { - return nil, err - } - - if err := json.NewDecoder(f).Decode(&abstractMap); err != nil { - return nil, err - } - - configData, err := json.Marshal(&container) - if err != nil { - return nil, err - } - - if err := json.Unmarshal(configData, &configMap); err != nil { - return nil, err - } - - for k := range configMap { - delete(abstractMap, k) - } - - if len(abstractMap) != 0 { - return nil, fmt.Errorf("unknown fields: %s", abstractMap) - } - - return container, nil -} - -func TestRemoveNamespace(t *testing.T) { - ns := Namespaces{ - {Type: NEWNET}, - } - if !ns.Remove(NEWNET) { - t.Fatal("NEWNET was not removed") - } - if len(ns) != 0 { - t.Fatalf("namespaces should have 0 items but reports %d", len(ns)) - } -} - -func TestHostUIDNoUSERNS(t *testing.T) { - config := &Config{ - Namespaces: Namespaces{}, - } - uid, err := config.HostUID() - if err != nil { - t.Fatal(err) - } - if uid != 0 { - t.Fatalf("expected uid 0 with no USERNS but received %d", uid) - } -} - -func TestHostUIDWithUSERNS(t *testing.T) { - config := &Config{ - Namespaces: Namespaces{{Type: NEWUSER}}, - UidMappings: []IDMap{ - { - ContainerID: 0, - HostID: 1000, - Size: 1, - }, - }, - } - uid, err := config.HostUID() - if err != nil { - t.Fatal(err) - } - if uid != 1000 { - t.Fatalf("expected uid 1000 with no USERNS but received %d", uid) - } -} - -func TestHostGIDNoUSERNS(t *testing.T) { - config := &Config{ - Namespaces: Namespaces{}, - } - uid, err := config.HostGID() - if err != nil { - t.Fatal(err) - } - if uid != 0 { - t.Fatalf("expected gid 0 with no USERNS but received %d", uid) - } -} - -func TestHostGIDWithUSERNS(t *testing.T) { - config := &Config{ - Namespaces: Namespaces{{Type: NEWUSER}}, - GidMappings: []IDMap{ - { - ContainerID: 0, - HostID: 1000, - Size: 1, - }, - }, - } - uid, err := config.HostGID() - if err != nil { - t.Fatal(err) - } - if uid != 1000 { - t.Fatalf("expected gid 1000 with no USERNS but received %d", uid) - } -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/configs/config_windows_test.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/configs/config_windows_test.go deleted file mode 100644 index 1a0c8fa2d..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/configs/config_windows_test.go +++ /dev/null @@ -1,3 +0,0 @@ -package configs - -// All current tests are for Unix-specific functionality diff --git a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/configs/validate/config.go b/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/configs/validate/config.go deleted file mode 100644 index 848a67c34..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/configs/validate/config.go +++ /dev/null @@ -1,93 +0,0 @@ -package validate - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/opencontainers/runc/libcontainer/configs" -) - -type Validator interface { - Validate(*configs.Config) error -} - -func New() Validator { - return &ConfigValidator{} -} - -type ConfigValidator struct { -} - -func (v *ConfigValidator) Validate(config *configs.Config) error { - if err := v.rootfs(config); err != nil { - return err - } - if err := v.network(config); err != nil { - return err - } - if err := v.hostname(config); err != nil { - return err - } - if err := v.security(config); err != nil { - return err - } - if err := v.usernamespace(config); err != nil { - return err - } - return nil -} - -// rootfs validates the the rootfs is an absolute path and is not a symlink -// to the container's root filesystem. -func (v *ConfigValidator) rootfs(config *configs.Config) error { - cleaned, err := filepath.Abs(config.Rootfs) - if err != nil { - return err - } - if cleaned, err = filepath.EvalSymlinks(cleaned); err != nil { - return err - } - if config.Rootfs != cleaned { - return fmt.Errorf("%s is not an absolute path or is a symlink", config.Rootfs) - } - return nil -} - -func (v *ConfigValidator) network(config *configs.Config) error { - if !config.Namespaces.Contains(configs.NEWNET) { - if len(config.Networks) > 0 || len(config.Routes) > 0 { - return fmt.Errorf("unable to apply network settings without a private NET namespace") - } - } - return nil -} - -func (v *ConfigValidator) hostname(config *configs.Config) error { - if config.Hostname != "" && !config.Namespaces.Contains(configs.NEWUTS) { - return fmt.Errorf("unable to set hostname without a private UTS namespace") - } - return nil -} - -func (v *ConfigValidator) security(config *configs.Config) error { - // restrict sys without mount namespace - if (len(config.MaskPaths) > 0 || len(config.ReadonlyPaths) > 0) && - !config.Namespaces.Contains(configs.NEWNS) { - return fmt.Errorf("unable to restrict sys entries without a private MNT namespace") - } - return nil -} - -func (v *ConfigValidator) usernamespace(config *configs.Config) error { - if config.Namespaces.Contains(configs.NEWUSER) { - if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) { - return fmt.Errorf("USER namespaces aren't enabled in the kernel") - } - } else { - if config.UidMappings != nil || config.GidMappings != nil { - return fmt.Errorf("User namespace mappings specified, but USER namespace isn't enabled in the config") - } - } - return nil -} diff --git a/Godeps/_workspace/src/github.com/pborman/uuid/json_test.go b/Godeps/_workspace/src/github.com/pborman/uuid/json_test.go deleted file mode 100644 index b5eae0924..000000000 --- a/Godeps/_workspace/src/github.com/pborman/uuid/json_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2014 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "encoding/json" - "reflect" - "testing" -) - -var testUUID = Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") - -func TestJSON(t *testing.T) { - type S struct { - ID1 UUID - ID2 UUID - } - s1 := S{ID1: testUUID} - data, err := json.Marshal(&s1) - if err != nil { - t.Fatal(err) - } - var s2 S - if err := json.Unmarshal(data, &s2); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(&s1, &s2) { - t.Errorf("got %#v, want %#v", s2, s1) - } -} diff --git a/Godeps/_workspace/src/github.com/pborman/uuid/seq_test.go b/Godeps/_workspace/src/github.com/pborman/uuid/seq_test.go deleted file mode 100644 index 3b3d1430d..000000000 --- a/Godeps/_workspace/src/github.com/pborman/uuid/seq_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2014 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "flag" - "runtime" - "testing" - "time" -) - -// This test is only run when --regressions is passed on the go test line. -var regressions = flag.Bool("regressions", false, "run uuid regression tests") - -// TestClockSeqRace tests for a particular race condition of returning two -// identical Version1 UUIDs. The duration of 1 minute was chosen as the race -// condition, before being fixed, nearly always occured in under 30 seconds. -func TestClockSeqRace(t *testing.T) { - if !*regressions { - t.Skip("skipping regression tests") - } - duration := time.Minute - - done := make(chan struct{}) - defer close(done) - - ch := make(chan UUID, 10000) - ncpu := runtime.NumCPU() - switch ncpu { - case 0, 1: - // We can't run the test effectively. - t.Skip("skipping race test, only one CPU detected") - return - default: - runtime.GOMAXPROCS(ncpu) - } - for i := 0; i < ncpu; i++ { - go func() { - for { - select { - case <-done: - return - case ch <- NewUUID(): - } - } - }() - } - - uuids := make(map[string]bool) - cnt := 0 - start := time.Now() - for u := range ch { - s := u.String() - if uuids[s] { - t.Errorf("duplicate uuid after %d in %v: %s", cnt, time.Since(start), s) - return - } - uuids[s] = true - if time.Since(start) > duration { - return - } - cnt++ - } -} diff --git a/Godeps/_workspace/src/github.com/pborman/uuid/uuid_test.go b/Godeps/_workspace/src/github.com/pborman/uuid/uuid_test.go deleted file mode 100644 index 417ebeb26..000000000 --- a/Godeps/_workspace/src/github.com/pborman/uuid/uuid_test.go +++ /dev/null @@ -1,390 +0,0 @@ -// Copyright 2011 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "bytes" - "fmt" - "os" - "strings" - "testing" - "time" -) - -type test struct { - in string - version Version - variant Variant - isuuid bool -} - -var tests = []test{ - {"f47ac10b-58cc-0372-8567-0e02b2c3d479", 0, RFC4122, true}, - {"f47ac10b-58cc-1372-8567-0e02b2c3d479", 1, RFC4122, true}, - {"f47ac10b-58cc-2372-8567-0e02b2c3d479", 2, RFC4122, true}, - {"f47ac10b-58cc-3372-8567-0e02b2c3d479", 3, RFC4122, true}, - {"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true}, - {"f47ac10b-58cc-5372-8567-0e02b2c3d479", 5, RFC4122, true}, - {"f47ac10b-58cc-6372-8567-0e02b2c3d479", 6, RFC4122, true}, - {"f47ac10b-58cc-7372-8567-0e02b2c3d479", 7, RFC4122, true}, - {"f47ac10b-58cc-8372-8567-0e02b2c3d479", 8, RFC4122, true}, - {"f47ac10b-58cc-9372-8567-0e02b2c3d479", 9, RFC4122, true}, - {"f47ac10b-58cc-a372-8567-0e02b2c3d479", 10, RFC4122, true}, - {"f47ac10b-58cc-b372-8567-0e02b2c3d479", 11, RFC4122, true}, - {"f47ac10b-58cc-c372-8567-0e02b2c3d479", 12, RFC4122, true}, - {"f47ac10b-58cc-d372-8567-0e02b2c3d479", 13, RFC4122, true}, - {"f47ac10b-58cc-e372-8567-0e02b2c3d479", 14, RFC4122, true}, - {"f47ac10b-58cc-f372-8567-0e02b2c3d479", 15, RFC4122, true}, - - {"urn:uuid:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true}, - {"URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true}, - {"f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true}, - {"f47ac10b-58cc-4372-1567-0e02b2c3d479", 4, Reserved, true}, - {"f47ac10b-58cc-4372-2567-0e02b2c3d479", 4, Reserved, true}, - {"f47ac10b-58cc-4372-3567-0e02b2c3d479", 4, Reserved, true}, - {"f47ac10b-58cc-4372-4567-0e02b2c3d479", 4, Reserved, true}, - {"f47ac10b-58cc-4372-5567-0e02b2c3d479", 4, Reserved, true}, - {"f47ac10b-58cc-4372-6567-0e02b2c3d479", 4, Reserved, true}, - {"f47ac10b-58cc-4372-7567-0e02b2c3d479", 4, Reserved, true}, - {"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true}, - {"f47ac10b-58cc-4372-9567-0e02b2c3d479", 4, RFC4122, true}, - {"f47ac10b-58cc-4372-a567-0e02b2c3d479", 4, RFC4122, true}, - {"f47ac10b-58cc-4372-b567-0e02b2c3d479", 4, RFC4122, true}, - {"f47ac10b-58cc-4372-c567-0e02b2c3d479", 4, Microsoft, true}, - {"f47ac10b-58cc-4372-d567-0e02b2c3d479", 4, Microsoft, true}, - {"f47ac10b-58cc-4372-e567-0e02b2c3d479", 4, Future, true}, - {"f47ac10b-58cc-4372-f567-0e02b2c3d479", 4, Future, true}, - - {"f47ac10b158cc-5372-a567-0e02b2c3d479", 0, Invalid, false}, - {"f47ac10b-58cc25372-a567-0e02b2c3d479", 0, Invalid, false}, - {"f47ac10b-58cc-53723a567-0e02b2c3d479", 0, Invalid, false}, - {"f47ac10b-58cc-5372-a56740e02b2c3d479", 0, Invalid, false}, - {"f47ac10b-58cc-5372-a567-0e02-2c3d479", 0, Invalid, false}, - {"g47ac10b-58cc-4372-a567-0e02b2c3d479", 0, Invalid, false}, -} - -var constants = []struct { - c interface{} - name string -}{ - {Person, "Person"}, - {Group, "Group"}, - {Org, "Org"}, - {Invalid, "Invalid"}, - {RFC4122, "RFC4122"}, - {Reserved, "Reserved"}, - {Microsoft, "Microsoft"}, - {Future, "Future"}, - {Domain(17), "Domain17"}, - {Variant(42), "BadVariant42"}, -} - -func testTest(t *testing.T, in string, tt test) { - uuid := Parse(in) - if ok := (uuid != nil); ok != tt.isuuid { - t.Errorf("Parse(%s) got %v expected %v\b", in, ok, tt.isuuid) - } - if uuid == nil { - return - } - - if v := uuid.Variant(); v != tt.variant { - t.Errorf("Variant(%s) got %d expected %d\b", in, v, tt.variant) - } - if v, _ := uuid.Version(); v != tt.version { - t.Errorf("Version(%s) got %d expected %d\b", in, v, tt.version) - } -} - -func TestUUID(t *testing.T) { - for _, tt := range tests { - testTest(t, tt.in, tt) - testTest(t, strings.ToUpper(tt.in), tt) - } -} - -func TestConstants(t *testing.T) { - for x, tt := range constants { - v, ok := tt.c.(fmt.Stringer) - if !ok { - t.Errorf("%x: %v: not a stringer", x, v) - } else if s := v.String(); s != tt.name { - v, _ := tt.c.(int) - t.Errorf("%x: Constant %T:%d gives %q, expected %q\n", x, tt.c, v, s, tt.name) - } - } -} - -func TestRandomUUID(t *testing.T) { - m := make(map[string]bool) - for x := 1; x < 32; x++ { - uuid := NewRandom() - s := uuid.String() - if m[s] { - t.Errorf("NewRandom returned duplicated UUID %s\n", s) - } - m[s] = true - if v, _ := uuid.Version(); v != 4 { - t.Errorf("Random UUID of version %s\n", v) - } - if uuid.Variant() != RFC4122 { - t.Errorf("Random UUID is variant %d\n", uuid.Variant()) - } - } -} - -func TestNew(t *testing.T) { - m := make(map[string]bool) - for x := 1; x < 32; x++ { - s := New() - if m[s] { - t.Errorf("New returned duplicated UUID %s\n", s) - } - m[s] = true - uuid := Parse(s) - if uuid == nil { - t.Errorf("New returned %q which does not decode\n", s) - continue - } - if v, _ := uuid.Version(); v != 4 { - t.Errorf("Random UUID of version %s\n", v) - } - if uuid.Variant() != RFC4122 { - t.Errorf("Random UUID is variant %d\n", uuid.Variant()) - } - } -} - -func clockSeq(t *testing.T, uuid UUID) int { - seq, ok := uuid.ClockSequence() - if !ok { - t.Fatalf("%s: invalid clock sequence\n", uuid) - } - return seq -} - -func TestClockSeq(t *testing.T) { - // Fake time.Now for this test to return a monotonically advancing time; restore it at end. - defer func(orig func() time.Time) { timeNow = orig }(timeNow) - monTime := time.Now() - timeNow = func() time.Time { - monTime = monTime.Add(1 * time.Second) - return monTime - } - - SetClockSequence(-1) - uuid1 := NewUUID() - uuid2 := NewUUID() - - if clockSeq(t, uuid1) != clockSeq(t, uuid2) { - t.Errorf("clock sequence %d != %d\n", clockSeq(t, uuid1), clockSeq(t, uuid2)) - } - - SetClockSequence(-1) - uuid2 = NewUUID() - - // Just on the very off chance we generated the same sequence - // two times we try again. - if clockSeq(t, uuid1) == clockSeq(t, uuid2) { - SetClockSequence(-1) - uuid2 = NewUUID() - } - if clockSeq(t, uuid1) == clockSeq(t, uuid2) { - t.Errorf("Duplicate clock sequence %d\n", clockSeq(t, uuid1)) - } - - SetClockSequence(0x1234) - uuid1 = NewUUID() - if seq := clockSeq(t, uuid1); seq != 0x1234 { - t.Errorf("%s: expected seq 0x1234 got 0x%04x\n", uuid1, seq) - } -} - -func TestCoding(t *testing.T) { - text := "7d444840-9dc0-11d1-b245-5ffdce74fad2" - urn := "urn:uuid:7d444840-9dc0-11d1-b245-5ffdce74fad2" - data := UUID{ - 0x7d, 0x44, 0x48, 0x40, - 0x9d, 0xc0, - 0x11, 0xd1, - 0xb2, 0x45, - 0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2, - } - if v := data.String(); v != text { - t.Errorf("%x: encoded to %s, expected %s\n", data, v, text) - } - if v := data.URN(); v != urn { - t.Errorf("%x: urn is %s, expected %s\n", data, v, urn) - } - - uuid := Parse(text) - if !Equal(uuid, data) { - t.Errorf("%s: decoded to %s, expected %s\n", text, uuid, data) - } -} - -func TestVersion1(t *testing.T) { - uuid1 := NewUUID() - uuid2 := NewUUID() - - if Equal(uuid1, uuid2) { - t.Errorf("%s:duplicate uuid\n", uuid1) - } - if v, _ := uuid1.Version(); v != 1 { - t.Errorf("%s: version %s expected 1\n", uuid1, v) - } - if v, _ := uuid2.Version(); v != 1 { - t.Errorf("%s: version %s expected 1\n", uuid2, v) - } - n1 := uuid1.NodeID() - n2 := uuid2.NodeID() - if !bytes.Equal(n1, n2) { - t.Errorf("Different nodes %x != %x\n", n1, n2) - } - t1, ok := uuid1.Time() - if !ok { - t.Errorf("%s: invalid time\n", uuid1) - } - t2, ok := uuid2.Time() - if !ok { - t.Errorf("%s: invalid time\n", uuid2) - } - q1, ok := uuid1.ClockSequence() - if !ok { - t.Errorf("%s: invalid clock sequence\n", uuid1) - } - q2, ok := uuid2.ClockSequence() - if !ok { - t.Errorf("%s: invalid clock sequence", uuid2) - } - - switch { - case t1 == t2 && q1 == q2: - t.Errorf("time stopped\n") - case t1 > t2 && q1 == q2: - t.Errorf("time reversed\n") - case t1 < t2 && q1 != q2: - t.Errorf("clock sequence chaned unexpectedly\n") - } -} - -func TestNodeAndTime(t *testing.T) { - // Time is February 5, 1998 12:30:23.136364800 AM GMT - - uuid := Parse("7d444840-9dc0-11d1-b245-5ffdce74fad2") - node := []byte{0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2} - - ts, ok := uuid.Time() - if ok { - c := time.Unix(ts.UnixTime()) - want := time.Date(1998, 2, 5, 0, 30, 23, 136364800, time.UTC) - if !c.Equal(want) { - t.Errorf("Got time %v, want %v", c, want) - } - } else { - t.Errorf("%s: bad time\n", uuid) - } - if !bytes.Equal(node, uuid.NodeID()) { - t.Errorf("Expected node %v got %v\n", node, uuid.NodeID()) - } -} - -func TestMD5(t *testing.T) { - uuid := NewMD5(NameSpace_DNS, []byte("python.org")).String() - want := "6fa459ea-ee8a-3ca4-894e-db77e160355e" - if uuid != want { - t.Errorf("MD5: got %q expected %q\n", uuid, want) - } -} - -func TestSHA1(t *testing.T) { - uuid := NewSHA1(NameSpace_DNS, []byte("python.org")).String() - want := "886313e1-3b8a-5372-9b90-0c9aee199e5d" - if uuid != want { - t.Errorf("SHA1: got %q expected %q\n", uuid, want) - } -} - -func TestNodeID(t *testing.T) { - nid := []byte{1, 2, 3, 4, 5, 6} - SetNodeInterface("") - s := NodeInterface() - if s == "" || s == "user" { - t.Errorf("NodeInterface %q after SetInteface\n", s) - } - node1 := NodeID() - if node1 == nil { - t.Errorf("NodeID nil after SetNodeInterface\n", s) - } - SetNodeID(nid) - s = NodeInterface() - if s != "user" { - t.Errorf("Expected NodeInterface %q got %q\n", "user", s) - } - node2 := NodeID() - if node2 == nil { - t.Errorf("NodeID nil after SetNodeID\n", s) - } - if bytes.Equal(node1, node2) { - t.Errorf("NodeID not changed after SetNodeID\n", s) - } else if !bytes.Equal(nid, node2) { - t.Errorf("NodeID is %x, expected %x\n", node2, nid) - } -} - -func testDCE(t *testing.T, name string, uuid UUID, domain Domain, id uint32) { - if uuid == nil { - t.Errorf("%s failed\n", name) - return - } - if v, _ := uuid.Version(); v != 2 { - t.Errorf("%s: %s: expected version 2, got %s\n", name, uuid, v) - return - } - if v, ok := uuid.Domain(); !ok || v != domain { - if !ok { - t.Errorf("%s: %d: Domain failed\n", name, uuid) - } else { - t.Errorf("%s: %s: expected domain %d, got %d\n", name, uuid, domain, v) - } - } - if v, ok := uuid.Id(); !ok || v != id { - if !ok { - t.Errorf("%s: %d: Id failed\n", name, uuid) - } else { - t.Errorf("%s: %s: expected id %d, got %d\n", name, uuid, id, v) - } - } -} - -func TestDCE(t *testing.T) { - testDCE(t, "NewDCESecurity", NewDCESecurity(42, 12345678), 42, 12345678) - testDCE(t, "NewDCEPerson", NewDCEPerson(), Person, uint32(os.Getuid())) - testDCE(t, "NewDCEGroup", NewDCEGroup(), Group, uint32(os.Getgid())) -} - -type badRand struct{} - -func (r badRand) Read(buf []byte) (int, error) { - for i, _ := range buf { - buf[i] = byte(i) - } - return len(buf), nil -} - -func TestBadRand(t *testing.T) { - SetRand(badRand{}) - uuid1 := New() - uuid2 := New() - if uuid1 != uuid2 { - t.Errorf("execpted duplicates, got %q and %q\n", uuid1, uuid2) - } - SetRand(nil) - uuid1 = New() - uuid2 = New() - if uuid1 == uuid2 { - t.Errorf("unexecpted duplicates, got %q\n", uuid1) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/LICENSE b/Godeps/_workspace/src/github.com/prometheus/client_golang/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/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/Godeps/_workspace/src/github.com/prometheus/client_golang/NOTICE b/Godeps/_workspace/src/github.com/prometheus/client_golang/NOTICE new file mode 100644 index 000000000..37e4a7d41 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/NOTICE @@ -0,0 +1,28 @@ +Prometheus instrumentation library for Go applications +Copyright 2012-2015 The Prometheus Authors + +This product includes software developed at +SoundCloud Ltd. (http://soundcloud.com/). + + +The following components are included in this product: + +goautoneg +http://bitbucket.org/ww/goautoneg +Copyright 2011, Open Knowledge Foundation Ltd. +See README.txt for license details. + +perks - a fork of https://github.com/bmizerany/perks +https://github.com/beorn7/perks +Copyright 2013-2015 Blake Mizerany, Björn Rabenstein +See https://github.com/beorn7/perks/blob/master/README.md for license details. + +Go support for Protocol Buffers - Google's data interchange format +http://github.com/golang/protobuf/ +Copyright 2010 The Go Authors +See source code for license details. + +Support for streaming Protocol Buffer messages for the Go language (golang). +https://github.com/matttproud/golang_protobuf_extensions +Copyright 2013 Matt T. Proud +Licensed under the Apache License, Version 2.0 diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/benchmark_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/benchmark_test.go deleted file mode 100644 index 6ae7333fc..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/benchmark_test.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus - -import ( - "testing" -) - -func BenchmarkCounterWithLabelValues(b *testing.B) { - m := NewCounterVec( - CounterOpts{ - Name: "benchmark_counter", - Help: "A counter to benchmark it.", - }, - []string{"one", "two", "three"}, - ) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - m.WithLabelValues("eins", "zwei", "drei").Inc() - } -} - -func BenchmarkCounterWithMappedLabels(b *testing.B) { - m := NewCounterVec( - CounterOpts{ - Name: "benchmark_counter", - Help: "A counter to benchmark it.", - }, - []string{"one", "two", "three"}, - ) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - m.With(Labels{"two": "zwei", "one": "eins", "three": "drei"}).Inc() - } -} - -func BenchmarkCounterWithPreparedMappedLabels(b *testing.B) { - m := NewCounterVec( - CounterOpts{ - Name: "benchmark_counter", - Help: "A counter to benchmark it.", - }, - []string{"one", "two", "three"}, - ) - b.ReportAllocs() - b.ResetTimer() - labels := Labels{"two": "zwei", "one": "eins", "three": "drei"} - for i := 0; i < b.N; i++ { - m.With(labels).Inc() - } -} - -func BenchmarkCounterNoLabels(b *testing.B) { - m := NewCounter(CounterOpts{ - Name: "benchmark_counter", - Help: "A counter to benchmark it.", - }) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - m.Inc() - } -} - -func BenchmarkGaugeWithLabelValues(b *testing.B) { - m := NewGaugeVec( - GaugeOpts{ - Name: "benchmark_gauge", - Help: "A gauge to benchmark it.", - }, - []string{"one", "two", "three"}, - ) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - m.WithLabelValues("eins", "zwei", "drei").Set(3.1415) - } -} - -func BenchmarkGaugeNoLabels(b *testing.B) { - m := NewGauge(GaugeOpts{ - Name: "benchmark_gauge", - Help: "A gauge to benchmark it.", - }) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - m.Set(3.1415) - } -} - -func BenchmarkSummaryWithLabelValues(b *testing.B) { - m := NewSummaryVec( - SummaryOpts{ - Name: "benchmark_summary", - Help: "A summary to benchmark it.", - }, - []string{"one", "two", "three"}, - ) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - m.WithLabelValues("eins", "zwei", "drei").Observe(3.1415) - } -} - -func BenchmarkSummaryNoLabels(b *testing.B) { - m := NewSummary(SummaryOpts{ - Name: "benchmark_summary", - Help: "A summary to benchmark it.", - }, - ) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - m.Observe(3.1415) - } -} - -func BenchmarkHistogramWithLabelValues(b *testing.B) { - m := NewHistogramVec( - HistogramOpts{ - Name: "benchmark_histogram", - Help: "A histogram to benchmark it.", - }, - []string{"one", "two", "three"}, - ) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - m.WithLabelValues("eins", "zwei", "drei").Observe(3.1415) - } -} - -func BenchmarkHistogramNoLabels(b *testing.B) { - m := NewHistogram(HistogramOpts{ - Name: "benchmark_histogram", - Help: "A histogram to benchmark it.", - }, - ) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - m.Observe(3.1415) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter_test.go deleted file mode 100644 index 67391a23a..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter_test.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus - -import ( - "math" - "testing" - - dto "github.com/prometheus/client_model/go" -) - -func TestCounterAdd(t *testing.T) { - counter := NewCounter(CounterOpts{ - Name: "test", - Help: "test help", - ConstLabels: Labels{"a": "1", "b": "2"}, - }).(*counter) - counter.Inc() - if expected, got := 1., math.Float64frombits(counter.valBits); expected != got { - t.Errorf("Expected %f, got %f.", expected, got) - } - counter.Add(42) - if expected, got := 43., math.Float64frombits(counter.valBits); expected != got { - t.Errorf("Expected %f, got %f.", expected, got) - } - - if expected, got := "counter cannot decrease in value", decreaseCounter(counter).Error(); expected != got { - t.Errorf("Expected error %q, got %q.", expected, got) - } - - m := &dto.Metric{} - counter.Write(m) - - if expected, got := `label: label: counter: `, m.String(); expected != got { - t.Errorf("expected %q, got %q", expected, got) - } -} - -func decreaseCounter(c *counter) (err error) { - defer func() { - if e := recover(); e != nil { - err = e.(error) - } - }() - c.Add(-1) - return nil -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_clustermanager_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_clustermanager_test.go deleted file mode 100644 index 6f3e215d4..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_clustermanager_test.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus_test - -import ( - "sync" - - "github.com/prometheus/client_golang/prometheus" -) - -// ClusterManager is an example for a system that might have been built without -// Prometheus in mind. It models a central manager of jobs running in a -// cluster. To turn it into something that collects Prometheus metrics, we -// simply add the two methods required for the Collector interface. -// -// An additional challenge is that multiple instances of the ClusterManager are -// run within the same binary, each in charge of a different zone. We need to -// make use of ConstLabels to be able to register each ClusterManager instance -// with Prometheus. -type ClusterManager struct { - Zone string - OOMCount *prometheus.CounterVec - RAMUsage *prometheus.GaugeVec - mtx sync.Mutex // Protects OOMCount and RAMUsage. - // ... many more fields -} - -// ReallyExpensiveAssessmentOfTheSystemState is a mock for the data gathering a -// real cluster manager would have to do. Since it may actually be really -// expensive, it must only be called once per collection. This implementation, -// obviously, only returns some made-up data. -func (c *ClusterManager) ReallyExpensiveAssessmentOfTheSystemState() ( - oomCountByHost map[string]int, ramUsageByHost map[string]float64, -) { - // Just example fake data. - oomCountByHost = map[string]int{ - "foo.example.org": 42, - "bar.example.org": 2001, - } - ramUsageByHost = map[string]float64{ - "foo.example.org": 6.023e23, - "bar.example.org": 3.14, - } - return -} - -// Describe faces the interesting challenge that the two metric vectors that are -// used in this example are already Collectors themselves. However, thanks to -// the use of channels, it is really easy to "chain" Collectors. Here we simply -// call the Describe methods of the two metric vectors. -func (c *ClusterManager) Describe(ch chan<- *prometheus.Desc) { - c.OOMCount.Describe(ch) - c.RAMUsage.Describe(ch) -} - -// Collect first triggers the ReallyExpensiveAssessmentOfTheSystemState. Then it -// sets the retrieved values in the two metric vectors and then sends all their -// metrics to the channel (again using a chaining technique as in the Describe -// method). Since Collect could be called multiple times concurrently, that part -// is protected by a mutex. -func (c *ClusterManager) Collect(ch chan<- prometheus.Metric) { - oomCountByHost, ramUsageByHost := c.ReallyExpensiveAssessmentOfTheSystemState() - c.mtx.Lock() - defer c.mtx.Unlock() - for host, oomCount := range oomCountByHost { - c.OOMCount.WithLabelValues(host).Set(float64(oomCount)) - } - for host, ramUsage := range ramUsageByHost { - c.RAMUsage.WithLabelValues(host).Set(ramUsage) - } - c.OOMCount.Collect(ch) - c.RAMUsage.Collect(ch) - // All metrics in OOMCount and RAMUsage are sent to the channel now. We - // can safely reset the two metric vectors now, so that we can start - // fresh in the next Collect cycle. (Imagine a host disappears from the - // cluster. If we did not reset here, its Metric would stay in the - // metric vectors forever.) - c.OOMCount.Reset() - c.RAMUsage.Reset() -} - -// NewClusterManager creates the two metric vectors OOMCount and RAMUsage. Note -// that the zone is set as a ConstLabel. (It's different in each instance of the -// ClusterManager, but constant over the lifetime of an instance.) The reported -// values are partitioned by host, which is therefore a variable label. -func NewClusterManager(zone string) *ClusterManager { - return &ClusterManager{ - Zone: zone, - OOMCount: prometheus.NewCounterVec( - prometheus.CounterOpts{ - Subsystem: "clustermanager", - Name: "oom_count", - Help: "number of OOM crashes", - ConstLabels: prometheus.Labels{"zone": zone}, - }, - []string{"host"}, - ), - RAMUsage: prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Subsystem: "clustermanager", - Name: "ram_usage_bytes", - Help: "RAM usage as reported to the cluster manager", - ConstLabels: prometheus.Labels{"zone": zone}, - }, - []string{"host"}, - ), - } -} - -func ExampleCollector_clustermanager() { - workerDB := NewClusterManager("db") - workerCA := NewClusterManager("ca") - prometheus.MustRegister(workerDB) - prometheus.MustRegister(workerCA) - - // Since we are dealing with custom Collector implementations, it might - // be a good idea to enable the collect checks in the registry. - prometheus.EnableCollectChecks(true) -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_memstats_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_memstats_test.go deleted file mode 100644 index a84d07250..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_memstats_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus_test - -import ( - "runtime" - - "github.com/prometheus/client_golang/prometheus" -) - -var ( - allocDesc = prometheus.NewDesc( - prometheus.BuildFQName("", "memstats", "alloc_bytes"), - "bytes allocated and still in use", - nil, nil, - ) - totalAllocDesc = prometheus.NewDesc( - prometheus.BuildFQName("", "memstats", "total_alloc_bytes"), - "bytes allocated (even if freed)", - nil, nil, - ) - numGCDesc = prometheus.NewDesc( - prometheus.BuildFQName("", "memstats", "num_gc_total"), - "number of GCs run", - nil, nil, - ) -) - -// MemStatsCollector is an example for a custom Collector that solves the -// problem of feeding into multiple metrics at the same time. The -// runtime.ReadMemStats should happen only once, and then the results need to be -// fed into a number of separate Metrics. In this example, only a few of the -// values reported by ReadMemStats are used. For each, there is a Desc provided -// as a var, so the MemStatsCollector itself needs nothing else in the -// struct. Only the methods need to be implemented. -type MemStatsCollector struct{} - -// Describe just sends the three Desc objects for the Metrics we intend to -// collect. -func (_ MemStatsCollector) Describe(ch chan<- *prometheus.Desc) { - ch <- allocDesc - ch <- totalAllocDesc - ch <- numGCDesc -} - -// Collect does the trick by calling ReadMemStats once and then constructing -// three different Metrics on the fly. -func (_ MemStatsCollector) Collect(ch chan<- prometheus.Metric) { - var ms runtime.MemStats - runtime.ReadMemStats(&ms) - ch <- prometheus.MustNewConstMetric( - allocDesc, - prometheus.GaugeValue, - float64(ms.Alloc), - ) - ch <- prometheus.MustNewConstMetric( - totalAllocDesc, - prometheus.GaugeValue, - float64(ms.TotalAlloc), - ) - ch <- prometheus.MustNewConstMetric( - numGCDesc, - prometheus.CounterValue, - float64(ms.NumGC), - ) - // To avoid new allocations on each collection, you could also keep - // metric objects around and return the same objects each time, just - // with new values set. -} - -func ExampleCollector_memstats() { - prometheus.MustRegister(&MemStatsCollector{}) - // Since we are dealing with custom Collector implementations, it might - // be a good idea to enable the collect checks in the registry. - prometheus.EnableCollectChecks(true) -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_selfcollector_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_selfcollector_test.go deleted file mode 100644 index 608deeb02..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_selfcollector_test.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus_test - -import ( - "runtime" - - "github.com/golang/protobuf/proto" - - dto "github.com/prometheus/client_model/go" - - "github.com/prometheus/client_golang/prometheus" -) - -func NewCallbackMetric(desc *prometheus.Desc, callback func() float64) *CallbackMetric { - result := &CallbackMetric{desc: desc, callback: callback} - result.Init(result) // Initialize the SelfCollector. - return result -} - -// TODO: Come up with a better example. - -// CallbackMetric is an example for a user-defined Metric that exports the -// result of a function call as a metric of type "untyped" without any -// labels. It uses SelfCollector to turn the Metric into a Collector so that it -// can be registered with Prometheus. -// -// Note that this example is pretty much academic as the prometheus package -// already provides an UntypedFunc type. -type CallbackMetric struct { - prometheus.SelfCollector - - desc *prometheus.Desc - callback func() float64 -} - -func (cm *CallbackMetric) Desc() *prometheus.Desc { - return cm.desc -} - -func (cm *CallbackMetric) Write(m *dto.Metric) error { - m.Untyped = &dto.Untyped{Value: proto.Float64(cm.callback())} - return nil -} - -func ExampleSelfCollector() { - m := NewCallbackMetric( - prometheus.NewDesc( - "runtime_goroutines_count", - "Total number of goroutines that currently exist.", - nil, nil, // No labels, these must be nil. - ), - func() float64 { - return float64(runtime.NumGoroutine()) - }, - ) - prometheus.MustRegister(m) -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/examples_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/examples_test.go deleted file mode 100644 index 0344e465b..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/examples_test.go +++ /dev/null @@ -1,649 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus_test - -import ( - "flag" - "fmt" - "math" - "net/http" - "os" - "runtime" - "sort" - "time" - - dto "github.com/prometheus/client_model/go" - - "github.com/golang/protobuf/proto" - - "github.com/prometheus/client_golang/prometheus" -) - -func ExampleGauge() { - opsQueued := prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: "our_company", - Subsystem: "blob_storage", - Name: "ops_queued", - Help: "Number of blob storage operations waiting to be processed.", - }) - prometheus.MustRegister(opsQueued) - - // 10 operations queued by the goroutine managing incoming requests. - opsQueued.Add(10) - // A worker goroutine has picked up a waiting operation. - opsQueued.Dec() - // And once more... - opsQueued.Dec() -} - -func ExampleGaugeVec() { - binaryVersion := flag.String("binary_version", "debug", "Version of the binary: debug, canary, production.") - flag.Parse() - - opsQueued := prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: "our_company", - Subsystem: "blob_storage", - Name: "ops_queued", - Help: "Number of blob storage operations waiting to be processed, partitioned by user and type.", - ConstLabels: prometheus.Labels{"binary_version": *binaryVersion}, - }, - []string{ - // Which user has requested the operation? - "user", - // Of what type is the operation? - "type", - }, - ) - prometheus.MustRegister(opsQueued) - - // Increase a value using compact (but order-sensitive!) WithLabelValues(). - opsQueued.WithLabelValues("bob", "put").Add(4) - // Increase a value with a map using WithLabels. More verbose, but order - // doesn't matter anymore. - opsQueued.With(prometheus.Labels{"type": "delete", "user": "alice"}).Inc() -} - -func ExampleGaugeFunc() { - if err := prometheus.Register(prometheus.NewGaugeFunc( - prometheus.GaugeOpts{ - Subsystem: "runtime", - Name: "goroutines_count", - Help: "Number of goroutines that currently exist.", - }, - func() float64 { return float64(runtime.NumGoroutine()) }, - )); err == nil { - fmt.Println("GaugeFunc 'goroutines_count' registered.") - } - // Note that the count of goroutines is a gauge (and not a counter) as - // it can go up and down. - - // Output: - // GaugeFunc 'goroutines_count' registered. -} - -func ExampleCounter() { - pushCounter := prometheus.NewCounter(prometheus.CounterOpts{ - Name: "repository_pushes", // Note: No help string... - }) - err := prometheus.Register(pushCounter) // ... so this will return an error. - if err != nil { - fmt.Println("Push counter couldn't be registered, no counting will happen:", err) - return - } - - // Try it once more, this time with a help string. - pushCounter = prometheus.NewCounter(prometheus.CounterOpts{ - Name: "repository_pushes", - Help: "Number of pushes to external repository.", - }) - err = prometheus.Register(pushCounter) - if err != nil { - fmt.Println("Push counter couldn't be registered AGAIN, no counting will happen:", err) - return - } - - pushComplete := make(chan struct{}) - // TODO: Start a goroutine that performs repository pushes and reports - // each completion via the channel. - for _ = range pushComplete { - pushCounter.Inc() - } - // Output: - // Push counter couldn't be registered, no counting will happen: descriptor Desc{fqName: "repository_pushes", help: "", constLabels: {}, variableLabels: []} is invalid: empty help string -} - -func ExampleCounterVec() { - binaryVersion := flag.String("environment", "test", "Execution environment: test, staging, production.") - flag.Parse() - - httpReqs := prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "http_requests_total", - Help: "How many HTTP requests processed, partitioned by status code and HTTP method.", - ConstLabels: prometheus.Labels{"env": *binaryVersion}, - }, - []string{"code", "method"}, - ) - prometheus.MustRegister(httpReqs) - - httpReqs.WithLabelValues("404", "POST").Add(42) - - // If you have to access the same set of labels very frequently, it - // might be good to retrieve the metric only once and keep a handle to - // it. But beware of deletion of that metric, see below! - m := httpReqs.WithLabelValues("200", "GET") - for i := 0; i < 1000000; i++ { - m.Inc() - } - // Delete a metric from the vector. If you have previously kept a handle - // to that metric (as above), future updates via that handle will go - // unseen (even if you re-create a metric with the same label set - // later). - httpReqs.DeleteLabelValues("200", "GET") - // Same thing with the more verbose Labels syntax. - httpReqs.Delete(prometheus.Labels{"method": "GET", "code": "200"}) -} - -func ExampleInstrumentHandler() { - // Handle the "/doc" endpoint with the standard http.FileServer handler. - // By wrapping the handler with InstrumentHandler, request count, - // request and response sizes, and request latency are automatically - // exported to Prometheus, partitioned by HTTP status code and method - // and by the handler name (here "fileserver"). - http.Handle("/doc", prometheus.InstrumentHandler( - "fileserver", http.FileServer(http.Dir("/usr/share/doc")), - )) - // The Prometheus handler still has to be registered to handle the - // "/metrics" endpoint. The handler returned by prometheus.Handler() is - // already instrumented - with "prometheus" as the handler name. In this - // example, we want the handler name to be "metrics", so we instrument - // the uninstrumented Prometheus handler ourselves. - http.Handle("/metrics", prometheus.InstrumentHandler( - "metrics", prometheus.UninstrumentedHandler(), - )) -} - -func ExampleLabelPairSorter() { - labelPairs := []*dto.LabelPair{ - &dto.LabelPair{Name: proto.String("status"), Value: proto.String("404")}, - &dto.LabelPair{Name: proto.String("method"), Value: proto.String("get")}, - } - - sort.Sort(prometheus.LabelPairSorter(labelPairs)) - - fmt.Println(labelPairs) - // Output: - // [name:"method" value:"get" name:"status" value:"404" ] -} - -func ExampleRegister() { - // Imagine you have a worker pool and want to count the tasks completed. - taskCounter := prometheus.NewCounter(prometheus.CounterOpts{ - Subsystem: "worker_pool", - Name: "completed_tasks_total", - Help: "Total number of tasks completed.", - }) - // This will register fine. - if err := prometheus.Register(taskCounter); err != nil { - fmt.Println(err) - } else { - fmt.Println("taskCounter registered.") - } - // Don't forget to tell the HTTP server about the Prometheus handler. - // (In a real program, you still need to start the HTTP server...) - http.Handle("/metrics", prometheus.Handler()) - - // Now you can start workers and give every one of them a pointer to - // taskCounter and let it increment it whenever it completes a task. - taskCounter.Inc() // This has to happen somewhere in the worker code. - - // But wait, you want to see how individual workers perform. So you need - // a vector of counters, with one element for each worker. - taskCounterVec := prometheus.NewCounterVec( - prometheus.CounterOpts{ - Subsystem: "worker_pool", - Name: "completed_tasks_total", - Help: "Total number of tasks completed.", - }, - []string{"worker_id"}, - ) - - // Registering will fail because we already have a metric of that name. - if err := prometheus.Register(taskCounterVec); err != nil { - fmt.Println("taskCounterVec not registered:", err) - } else { - fmt.Println("taskCounterVec registered.") - } - - // To fix, first unregister the old taskCounter. - if prometheus.Unregister(taskCounter) { - fmt.Println("taskCounter unregistered.") - } - - // Try registering taskCounterVec again. - if err := prometheus.Register(taskCounterVec); err != nil { - fmt.Println("taskCounterVec not registered:", err) - } else { - fmt.Println("taskCounterVec registered.") - } - // Bummer! Still doesn't work. - - // Prometheus will not allow you to ever export metrics with - // inconsistent help strings or label names. After unregistering, the - // unregistered metrics will cease to show up in the /metrics HTTP - // response, but the registry still remembers that those metrics had - // been exported before. For this example, we will now choose a - // different name. (In a real program, you would obviously not export - // the obsolete metric in the first place.) - taskCounterVec = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Subsystem: "worker_pool", - Name: "completed_tasks_by_id", - Help: "Total number of tasks completed.", - }, - []string{"worker_id"}, - ) - if err := prometheus.Register(taskCounterVec); err != nil { - fmt.Println("taskCounterVec not registered:", err) - } else { - fmt.Println("taskCounterVec registered.") - } - // Finally it worked! - - // The workers have to tell taskCounterVec their id to increment the - // right element in the metric vector. - taskCounterVec.WithLabelValues("42").Inc() // Code from worker 42. - - // Each worker could also keep a reference to their own counter element - // around. Pick the counter at initialization time of the worker. - myCounter := taskCounterVec.WithLabelValues("42") // From worker 42 initialization code. - myCounter.Inc() // Somewhere in the code of that worker. - - // Note that something like WithLabelValues("42", "spurious arg") would - // panic (because you have provided too many label values). If you want - // to get an error instead, use GetMetricWithLabelValues(...) instead. - notMyCounter, err := taskCounterVec.GetMetricWithLabelValues("42", "spurious arg") - if err != nil { - fmt.Println("Worker initialization failed:", err) - } - if notMyCounter == nil { - fmt.Println("notMyCounter is nil.") - } - - // A different (and somewhat tricky) approach is to use - // ConstLabels. ConstLabels are pairs of label names and label values - // that never change. You might ask what those labels are good for (and - // rightfully so - if they never change, they could as well be part of - // the metric name). There are essentially two use-cases: The first is - // if labels are constant throughout the lifetime of a binary execution, - // but they vary over time or between different instances of a running - // binary. The second is what we have here: Each worker creates and - // registers an own Counter instance where the only difference is in the - // value of the ConstLabels. Those Counters can all be registered - // because the different ConstLabel values guarantee that each worker - // will increment a different Counter metric. - counterOpts := prometheus.CounterOpts{ - Subsystem: "worker_pool", - Name: "completed_tasks", - Help: "Total number of tasks completed.", - ConstLabels: prometheus.Labels{"worker_id": "42"}, - } - taskCounterForWorker42 := prometheus.NewCounter(counterOpts) - if err := prometheus.Register(taskCounterForWorker42); err != nil { - fmt.Println("taskCounterVForWorker42 not registered:", err) - } else { - fmt.Println("taskCounterForWorker42 registered.") - } - // Obviously, in real code, taskCounterForWorker42 would be a member - // variable of a worker struct, and the "42" would be retrieved with a - // GetId() method or something. The Counter would be created and - // registered in the initialization code of the worker. - - // For the creation of the next Counter, we can recycle - // counterOpts. Just change the ConstLabels. - counterOpts.ConstLabels = prometheus.Labels{"worker_id": "2001"} - taskCounterForWorker2001 := prometheus.NewCounter(counterOpts) - if err := prometheus.Register(taskCounterForWorker2001); err != nil { - fmt.Println("taskCounterVForWorker2001 not registered:", err) - } else { - fmt.Println("taskCounterForWorker2001 registered.") - } - - taskCounterForWorker2001.Inc() - taskCounterForWorker42.Inc() - taskCounterForWorker2001.Inc() - - // Yet another approach would be to turn the workers themselves into - // Collectors and register them. See the Collector example for details. - - // Output: - // taskCounter registered. - // taskCounterVec not registered: a previously registered descriptor with the same fully-qualified name as Desc{fqName: "worker_pool_completed_tasks_total", help: "Total number of tasks completed.", constLabels: {}, variableLabels: [worker_id]} has different label names or a different help string - // taskCounter unregistered. - // taskCounterVec not registered: a previously registered descriptor with the same fully-qualified name as Desc{fqName: "worker_pool_completed_tasks_total", help: "Total number of tasks completed.", constLabels: {}, variableLabels: [worker_id]} has different label names or a different help string - // taskCounterVec registered. - // Worker initialization failed: inconsistent label cardinality - // notMyCounter is nil. - // taskCounterForWorker42 registered. - // taskCounterForWorker2001 registered. -} - -func ExampleSummary() { - temps := prometheus.NewSummary(prometheus.SummaryOpts{ - Name: "pond_temperature_celsius", - Help: "The temperature of the frog pond.", // Sorry, we can't measure how badly it smells. - }) - - // Simulate some observations. - for i := 0; i < 1000; i++ { - temps.Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10) - } - - // Just for demonstration, let's check the state of the summary by - // (ab)using its Write method (which is usually only used by Prometheus - // internally). - metric := &dto.Metric{} - temps.Write(metric) - fmt.Println(proto.MarshalTextString(metric)) - - // Output: - // summary: < - // sample_count: 1000 - // sample_sum: 29969.50000000001 - // quantile: < - // quantile: 0.5 - // value: 31.1 - // > - // quantile: < - // quantile: 0.9 - // value: 41.3 - // > - // quantile: < - // quantile: 0.99 - // value: 41.9 - // > - // > -} - -func ExampleSummaryVec() { - temps := prometheus.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "pond_temperature_celsius", - Help: "The temperature of the frog pond.", // Sorry, we can't measure how badly it smells. - }, - []string{"species"}, - ) - - // Simulate some observations. - for i := 0; i < 1000; i++ { - temps.WithLabelValues("litoria-caerulea").Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10) - temps.WithLabelValues("lithobates-catesbeianus").Observe(32 + math.Floor(100*math.Cos(float64(i)*0.11))/10) - } - - // Create a Summary without any observations. - temps.WithLabelValues("leiopelma-hochstetteri") - - // Just for demonstration, let's check the state of the summary vector - // by (ab)using its Collect method and the Write method of its elements - // (which is usually only used by Prometheus internally - code like the - // following will never appear in your own code). - metricChan := make(chan prometheus.Metric) - go func() { - defer close(metricChan) - temps.Collect(metricChan) - }() - - metricStrings := []string{} - for metric := range metricChan { - dtoMetric := &dto.Metric{} - metric.Write(dtoMetric) - metricStrings = append(metricStrings, proto.MarshalTextString(dtoMetric)) - } - sort.Strings(metricStrings) // For reproducible print order. - fmt.Println(metricStrings) - - // Output: - // [label: < - // name: "species" - // value: "leiopelma-hochstetteri" - // > - // summary: < - // sample_count: 0 - // sample_sum: 0 - // quantile: < - // quantile: 0.5 - // value: nan - // > - // quantile: < - // quantile: 0.9 - // value: nan - // > - // quantile: < - // quantile: 0.99 - // value: nan - // > - // > - // label: < - // name: "species" - // value: "lithobates-catesbeianus" - // > - // summary: < - // sample_count: 1000 - // sample_sum: 31956.100000000017 - // quantile: < - // quantile: 0.5 - // value: 32.4 - // > - // quantile: < - // quantile: 0.9 - // value: 41.4 - // > - // quantile: < - // quantile: 0.99 - // value: 41.9 - // > - // > - // label: < - // name: "species" - // value: "litoria-caerulea" - // > - // summary: < - // sample_count: 1000 - // sample_sum: 29969.50000000001 - // quantile: < - // quantile: 0.5 - // value: 31.1 - // > - // quantile: < - // quantile: 0.9 - // value: 41.3 - // > - // quantile: < - // quantile: 0.99 - // value: 41.9 - // > - // > - // ] -} - -func ExampleConstSummary() { - desc := prometheus.NewDesc( - "http_request_duration_seconds", - "A summary of the HTTP request durations.", - []string{"code", "method"}, - prometheus.Labels{"owner": "example"}, - ) - - // Create a constant summary from values we got from a 3rd party telemetry system. - s := prometheus.MustNewConstSummary( - desc, - 4711, 403.34, - map[float64]float64{0.5: 42.3, 0.9: 323.3}, - "200", "get", - ) - - // Just for demonstration, let's check the state of the summary by - // (ab)using its Write method (which is usually only used by Prometheus - // internally). - metric := &dto.Metric{} - s.Write(metric) - fmt.Println(proto.MarshalTextString(metric)) - - // Output: - // label: < - // name: "code" - // value: "200" - // > - // label: < - // name: "method" - // value: "get" - // > - // label: < - // name: "owner" - // value: "example" - // > - // summary: < - // sample_count: 4711 - // sample_sum: 403.34 - // quantile: < - // quantile: 0.5 - // value: 42.3 - // > - // quantile: < - // quantile: 0.9 - // value: 323.3 - // > - // > -} - -func ExampleHistogram() { - temps := prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "pond_temperature_celsius", - Help: "The temperature of the frog pond.", // Sorry, we can't measure how badly it smells. - Buckets: prometheus.LinearBuckets(20, 5, 5), // 5 buckets, each 5 centigrade wide. - }) - - // Simulate some observations. - for i := 0; i < 1000; i++ { - temps.Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10) - } - - // Just for demonstration, let's check the state of the histogram by - // (ab)using its Write method (which is usually only used by Prometheus - // internally). - metric := &dto.Metric{} - temps.Write(metric) - fmt.Println(proto.MarshalTextString(metric)) - - // Output: - // histogram: < - // sample_count: 1000 - // sample_sum: 29969.50000000001 - // bucket: < - // cumulative_count: 192 - // upper_bound: 20 - // > - // bucket: < - // cumulative_count: 366 - // upper_bound: 25 - // > - // bucket: < - // cumulative_count: 501 - // upper_bound: 30 - // > - // bucket: < - // cumulative_count: 638 - // upper_bound: 35 - // > - // bucket: < - // cumulative_count: 816 - // upper_bound: 40 - // > - // > -} - -func ExampleConstHistogram() { - desc := prometheus.NewDesc( - "http_request_duration_seconds", - "A histogram of the HTTP request durations.", - []string{"code", "method"}, - prometheus.Labels{"owner": "example"}, - ) - - // Create a constant histogram from values we got from a 3rd party telemetry system. - h := prometheus.MustNewConstHistogram( - desc, - 4711, 403.34, - map[float64]uint64{25: 121, 50: 2403, 100: 3221, 200: 4233}, - "200", "get", - ) - - // Just for demonstration, let's check the state of the histogram by - // (ab)using its Write method (which is usually only used by Prometheus - // internally). - metric := &dto.Metric{} - h.Write(metric) - fmt.Println(proto.MarshalTextString(metric)) - - // Output: - // label: < - // name: "code" - // value: "200" - // > - // label: < - // name: "method" - // value: "get" - // > - // label: < - // name: "owner" - // value: "example" - // > - // histogram: < - // sample_count: 4711 - // sample_sum: 403.34 - // bucket: < - // cumulative_count: 121 - // upper_bound: 25 - // > - // bucket: < - // cumulative_count: 2403 - // upper_bound: 50 - // > - // bucket: < - // cumulative_count: 3221 - // upper_bound: 100 - // > - // bucket: < - // cumulative_count: 4233 - // upper_bound: 200 - // > - // > -} - -func ExamplePushCollectors() { - hostname, _ := os.Hostname() - completionTime := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "db_backup_last_completion_time", - Help: "The timestamp of the last succesful completion of a DB backup.", - }) - completionTime.Set(float64(time.Now().Unix())) - if err := prometheus.PushCollectors( - "db_backup", hostname, - "http://pushgateway:9091", - completionTime, - ); err != nil { - fmt.Println("Could not push completion time to Pushgateway:", err) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/expvar_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/expvar_test.go deleted file mode 100644 index 5d3128fae..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/expvar_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus_test - -import ( - "expvar" - "fmt" - "sort" - "strings" - - dto "github.com/prometheus/client_model/go" - - "github.com/prometheus/client_golang/prometheus" -) - -func ExampleExpvarCollector() { - expvarCollector := prometheus.NewExpvarCollector(map[string]*prometheus.Desc{ - "memstats": prometheus.NewDesc( - "expvar_memstats", - "All numeric memstats as one metric family. Not a good role-model, actually... ;-)", - []string{"type"}, nil, - ), - "lone-int": prometheus.NewDesc( - "expvar_lone_int", - "Just an expvar int as an example.", - nil, nil, - ), - "http-request-map": prometheus.NewDesc( - "expvar_http_request_total", - "How many http requests processed, partitioned by status code and http method.", - []string{"code", "method"}, nil, - ), - }) - prometheus.MustRegister(expvarCollector) - - // The Prometheus part is done here. But to show that this example is - // doing anything, we have to manually export something via expvar. In - // real-life use-cases, some library would already have exported via - // expvar what we want to re-export as Prometheus metrics. - expvar.NewInt("lone-int").Set(42) - expvarMap := expvar.NewMap("http-request-map") - var ( - expvarMap1, expvarMap2 expvar.Map - expvarInt11, expvarInt12, expvarInt21, expvarInt22 expvar.Int - ) - expvarMap1.Init() - expvarMap2.Init() - expvarInt11.Set(3) - expvarInt12.Set(13) - expvarInt21.Set(11) - expvarInt22.Set(212) - expvarMap1.Set("POST", &expvarInt11) - expvarMap1.Set("GET", &expvarInt12) - expvarMap2.Set("POST", &expvarInt21) - expvarMap2.Set("GET", &expvarInt22) - expvarMap.Set("404", &expvarMap1) - expvarMap.Set("200", &expvarMap2) - // Results in the following expvar map: - // "http-request-count": {"200": {"POST": 11, "GET": 212}, "404": {"POST": 3, "GET": 13}} - - // Let's see what the scrape would yield, but exclude the memstats metrics. - metricStrings := []string{} - metric := dto.Metric{} - metricChan := make(chan prometheus.Metric) - go func() { - expvarCollector.Collect(metricChan) - close(metricChan) - }() - for m := range metricChan { - if strings.Index(m.Desc().String(), "expvar_memstats") == -1 { - metric.Reset() - m.Write(&metric) - metricStrings = append(metricStrings, metric.String()) - } - } - sort.Strings(metricStrings) - for _, s := range metricStrings { - fmt.Println(strings.TrimRight(s, " ")) - } - // Output: - // label: label: untyped: - // label: label: untyped: - // label: label: untyped: - // label: label: untyped: - // untyped: -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/gauge_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/gauge_test.go deleted file mode 100644 index 48cab4636..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/gauge_test.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus - -import ( - "math" - "math/rand" - "sync" - "testing" - "testing/quick" - - dto "github.com/prometheus/client_model/go" -) - -func listenGaugeStream(vals, result chan float64, done chan struct{}) { - var sum float64 -outer: - for { - select { - case <-done: - close(vals) - for v := range vals { - sum += v - } - break outer - case v := <-vals: - sum += v - } - } - result <- sum - close(result) -} - -func TestGaugeConcurrency(t *testing.T) { - it := func(n uint32) bool { - mutations := int(n % 10000) - concLevel := int(n%15 + 1) - - var start, end sync.WaitGroup - start.Add(1) - end.Add(concLevel) - - sStream := make(chan float64, mutations*concLevel) - result := make(chan float64) - done := make(chan struct{}) - - go listenGaugeStream(sStream, result, done) - go func() { - end.Wait() - close(done) - }() - - gge := NewGauge(GaugeOpts{ - Name: "test_gauge", - Help: "no help can be found here", - }) - for i := 0; i < concLevel; i++ { - vals := make([]float64, mutations) - for j := 0; j < mutations; j++ { - vals[j] = rand.Float64() - 0.5 - } - - go func(vals []float64) { - start.Wait() - for _, v := range vals { - sStream <- v - gge.Add(v) - } - end.Done() - }(vals) - } - start.Done() - - if expected, got := <-result, math.Float64frombits(gge.(*value).valBits); math.Abs(expected-got) > 0.000001 { - t.Fatalf("expected approx. %f, got %f", expected, got) - return false - } - return true - } - - if err := quick.Check(it, nil); err != nil { - t.Fatal(err) - } -} - -func TestGaugeVecConcurrency(t *testing.T) { - it := func(n uint32) bool { - mutations := int(n % 10000) - concLevel := int(n%15 + 1) - vecLength := int(n%5 + 1) - - var start, end sync.WaitGroup - start.Add(1) - end.Add(concLevel) - - sStreams := make([]chan float64, vecLength) - results := make([]chan float64, vecLength) - done := make(chan struct{}) - - for i := 0; i < vecLength; i++ { - sStreams[i] = make(chan float64, mutations*concLevel) - results[i] = make(chan float64) - go listenGaugeStream(sStreams[i], results[i], done) - } - - go func() { - end.Wait() - close(done) - }() - - gge := NewGaugeVec( - GaugeOpts{ - Name: "test_gauge", - Help: "no help can be found here", - }, - []string{"label"}, - ) - for i := 0; i < concLevel; i++ { - vals := make([]float64, mutations) - pick := make([]int, mutations) - for j := 0; j < mutations; j++ { - vals[j] = rand.Float64() - 0.5 - pick[j] = rand.Intn(vecLength) - } - - go func(vals []float64) { - start.Wait() - for i, v := range vals { - sStreams[pick[i]] <- v - gge.WithLabelValues(string('A' + pick[i])).Add(v) - } - end.Done() - }(vals) - } - start.Done() - - for i := range sStreams { - if expected, got := <-results[i], math.Float64frombits(gge.WithLabelValues(string('A'+i)).(*value).valBits); math.Abs(expected-got) > 0.000001 { - t.Fatalf("expected approx. %f, got %f", expected, got) - return false - } - } - return true - } - - if err := quick.Check(it, nil); err != nil { - t.Fatal(err) - } -} - -func TestGaugeFunc(t *testing.T) { - gf := NewGaugeFunc( - GaugeOpts{ - Name: "test_name", - Help: "test help", - ConstLabels: Labels{"a": "1", "b": "2"}, - }, - func() float64 { return 3.1415 }, - ) - - if expected, got := `Desc{fqName: "test_name", help: "test help", constLabels: {a="1",b="2"}, variableLabels: []}`, gf.Desc().String(); expected != got { - t.Errorf("expected %q, got %q", expected, got) - } - - m := &dto.Metric{} - gf.Write(m) - - if expected, got := `label: label: gauge: `, m.String(); expected != got { - t.Errorf("expected %q, got %q", expected, got) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector_test.go deleted file mode 100644 index 9a8858cbd..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package prometheus - -import ( - "runtime" - "testing" - "time" - - dto "github.com/prometheus/client_model/go" -) - -func TestGoCollector(t *testing.T) { - var ( - c = NewGoCollector() - ch = make(chan Metric) - waitc = make(chan struct{}) - closec = make(chan struct{}) - old = -1 - ) - defer close(closec) - - go func() { - c.Collect(ch) - go func(c <-chan struct{}) { - <-c - }(closec) - <-waitc - c.Collect(ch) - }() - - for { - select { - case metric := <-ch: - switch m := metric.(type) { - // Attention, this also catches Counter... - case Gauge: - pb := &dto.Metric{} - m.Write(pb) - if pb.GetGauge() == nil { - continue - } - - if old == -1 { - old = int(pb.GetGauge().GetValue()) - close(waitc) - continue - } - - if diff := int(pb.GetGauge().GetValue()) - old; diff != 1 { - // TODO: This is flaky in highly concurrent situations. - t.Errorf("want 1 new goroutine, got %d", diff) - } - - // GoCollector performs two sends per call. - // On line 27 we need to receive the second send - // to shut down cleanly. - <-ch - return - } - case <-time.After(1 * time.Second): - t.Fatalf("expected collect timed out") - } - } -} - -func TestGCCollector(t *testing.T) { - var ( - c = NewGoCollector() - ch = make(chan Metric) - waitc = make(chan struct{}) - closec = make(chan struct{}) - oldGC uint64 - oldPause float64 - ) - defer close(closec) - - go func() { - c.Collect(ch) - // force GC - runtime.GC() - <-waitc - c.Collect(ch) - }() - - first := true - for { - select { - case metric := <-ch: - switch m := metric.(type) { - case *constSummary, *value: - pb := &dto.Metric{} - m.Write(pb) - if pb.GetSummary() == nil { - continue - } - - if len(pb.GetSummary().Quantile) != 5 { - t.Errorf("expected 4 buckets, got %d", len(pb.GetSummary().Quantile)) - } - for idx, want := range []float64{0.0, 0.25, 0.5, 0.75, 1.0} { - if *pb.GetSummary().Quantile[idx].Quantile != want { - t.Errorf("bucket #%d is off, got %f, want %f", idx, *pb.GetSummary().Quantile[idx].Quantile, want) - } - } - if first { - first = false - oldGC = *pb.GetSummary().SampleCount - oldPause = *pb.GetSummary().SampleSum - close(waitc) - continue - } - if diff := *pb.GetSummary().SampleCount - oldGC; diff != 1 { - t.Errorf("want 1 new garbage collection run, got %d", diff) - } - if diff := *pb.GetSummary().SampleSum - oldPause; diff <= 0 { - t.Errorf("want moar pause, got %f", diff) - } - return - } - case <-time.After(1 * time.Second): - t.Fatalf("expected collect timed out") - } - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram_test.go deleted file mode 100644 index 11cf66b4f..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram_test.go +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright 2015 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus - -import ( - "math" - "math/rand" - "reflect" - "sort" - "sync" - "testing" - "testing/quick" - - dto "github.com/prometheus/client_model/go" -) - -func benchmarkHistogramObserve(w int, b *testing.B) { - b.StopTimer() - - wg := new(sync.WaitGroup) - wg.Add(w) - - g := new(sync.WaitGroup) - g.Add(1) - - s := NewHistogram(HistogramOpts{}) - - for i := 0; i < w; i++ { - go func() { - g.Wait() - - for i := 0; i < b.N; i++ { - s.Observe(float64(i)) - } - - wg.Done() - }() - } - - b.StartTimer() - g.Done() - wg.Wait() -} - -func BenchmarkHistogramObserve1(b *testing.B) { - benchmarkHistogramObserve(1, b) -} - -func BenchmarkHistogramObserve2(b *testing.B) { - benchmarkHistogramObserve(2, b) -} - -func BenchmarkHistogramObserve4(b *testing.B) { - benchmarkHistogramObserve(4, b) -} - -func BenchmarkHistogramObserve8(b *testing.B) { - benchmarkHistogramObserve(8, b) -} - -func benchmarkHistogramWrite(w int, b *testing.B) { - b.StopTimer() - - wg := new(sync.WaitGroup) - wg.Add(w) - - g := new(sync.WaitGroup) - g.Add(1) - - s := NewHistogram(HistogramOpts{}) - - for i := 0; i < 1000000; i++ { - s.Observe(float64(i)) - } - - for j := 0; j < w; j++ { - outs := make([]dto.Metric, b.N) - - go func(o []dto.Metric) { - g.Wait() - - for i := 0; i < b.N; i++ { - s.Write(&o[i]) - } - - wg.Done() - }(outs) - } - - b.StartTimer() - g.Done() - wg.Wait() -} - -func BenchmarkHistogramWrite1(b *testing.B) { - benchmarkHistogramWrite(1, b) -} - -func BenchmarkHistogramWrite2(b *testing.B) { - benchmarkHistogramWrite(2, b) -} - -func BenchmarkHistogramWrite4(b *testing.B) { - benchmarkHistogramWrite(4, b) -} - -func BenchmarkHistogramWrite8(b *testing.B) { - benchmarkHistogramWrite(8, b) -} - -// Intentionally adding +Inf here to test if that case is handled correctly. -// Also, getCumulativeCounts depends on it. -var testBuckets = []float64{-2, -1, -0.5, 0, 0.5, 1, 2, math.Inf(+1)} - -func TestHistogramConcurrency(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test in short mode.") - } - - rand.Seed(42) - - it := func(n uint32) bool { - mutations := int(n%1e4 + 1e4) - concLevel := int(n%5 + 1) - total := mutations * concLevel - - var start, end sync.WaitGroup - start.Add(1) - end.Add(concLevel) - - sum := NewHistogram(HistogramOpts{ - Name: "test_histogram", - Help: "helpless", - Buckets: testBuckets, - }) - - allVars := make([]float64, total) - var sampleSum float64 - for i := 0; i < concLevel; i++ { - vals := make([]float64, mutations) - for j := 0; j < mutations; j++ { - v := rand.NormFloat64() - vals[j] = v - allVars[i*mutations+j] = v - sampleSum += v - } - - go func(vals []float64) { - start.Wait() - for _, v := range vals { - sum.Observe(v) - } - end.Done() - }(vals) - } - sort.Float64s(allVars) - start.Done() - end.Wait() - - m := &dto.Metric{} - sum.Write(m) - if got, want := int(*m.Histogram.SampleCount), total; got != want { - t.Errorf("got sample count %d, want %d", got, want) - } - if got, want := *m.Histogram.SampleSum, sampleSum; math.Abs((got-want)/want) > 0.001 { - t.Errorf("got sample sum %f, want %f", got, want) - } - - wantCounts := getCumulativeCounts(allVars) - - if got, want := len(m.Histogram.Bucket), len(testBuckets)-1; got != want { - t.Errorf("got %d buckets in protobuf, want %d", got, want) - } - for i, wantBound := range testBuckets { - if i == len(testBuckets)-1 { - break // No +Inf bucket in protobuf. - } - if gotBound := *m.Histogram.Bucket[i].UpperBound; gotBound != wantBound { - t.Errorf("got bound %f, want %f", gotBound, wantBound) - } - if gotCount, wantCount := *m.Histogram.Bucket[i].CumulativeCount, wantCounts[i]; gotCount != wantCount { - t.Errorf("got count %d, want %d", gotCount, wantCount) - } - } - return true - } - - if err := quick.Check(it, nil); err != nil { - t.Error(err) - } -} - -func TestHistogramVecConcurrency(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test in short mode.") - } - - rand.Seed(42) - - objectives := make([]float64, 0, len(DefObjectives)) - for qu := range DefObjectives { - - objectives = append(objectives, qu) - } - sort.Float64s(objectives) - - it := func(n uint32) bool { - mutations := int(n%1e4 + 1e4) - concLevel := int(n%7 + 1) - vecLength := int(n%3 + 1) - - var start, end sync.WaitGroup - start.Add(1) - end.Add(concLevel) - - his := NewHistogramVec( - HistogramOpts{ - Name: "test_histogram", - Help: "helpless", - Buckets: []float64{-2, -1, -0.5, 0, 0.5, 1, 2, math.Inf(+1)}, - }, - []string{"label"}, - ) - - allVars := make([][]float64, vecLength) - sampleSums := make([]float64, vecLength) - for i := 0; i < concLevel; i++ { - vals := make([]float64, mutations) - picks := make([]int, mutations) - for j := 0; j < mutations; j++ { - v := rand.NormFloat64() - vals[j] = v - pick := rand.Intn(vecLength) - picks[j] = pick - allVars[pick] = append(allVars[pick], v) - sampleSums[pick] += v - } - - go func(vals []float64) { - start.Wait() - for i, v := range vals { - his.WithLabelValues(string('A' + picks[i])).Observe(v) - } - end.Done() - }(vals) - } - for _, vars := range allVars { - sort.Float64s(vars) - } - start.Done() - end.Wait() - - for i := 0; i < vecLength; i++ { - m := &dto.Metric{} - s := his.WithLabelValues(string('A' + i)) - s.Write(m) - - if got, want := len(m.Histogram.Bucket), len(testBuckets)-1; got != want { - t.Errorf("got %d buckets in protobuf, want %d", got, want) - } - if got, want := int(*m.Histogram.SampleCount), len(allVars[i]); got != want { - t.Errorf("got sample count %d, want %d", got, want) - } - if got, want := *m.Histogram.SampleSum, sampleSums[i]; math.Abs((got-want)/want) > 0.001 { - t.Errorf("got sample sum %f, want %f", got, want) - } - - wantCounts := getCumulativeCounts(allVars[i]) - - for j, wantBound := range testBuckets { - if j == len(testBuckets)-1 { - break // No +Inf bucket in protobuf. - } - if gotBound := *m.Histogram.Bucket[j].UpperBound; gotBound != wantBound { - t.Errorf("got bound %f, want %f", gotBound, wantBound) - } - if gotCount, wantCount := *m.Histogram.Bucket[j].CumulativeCount, wantCounts[j]; gotCount != wantCount { - t.Errorf("got count %d, want %d", gotCount, wantCount) - } - } - } - return true - } - - if err := quick.Check(it, nil); err != nil { - t.Error(err) - } -} - -func getCumulativeCounts(vars []float64) []uint64 { - counts := make([]uint64, len(testBuckets)) - for _, v := range vars { - for i := len(testBuckets) - 1; i >= 0; i-- { - if v > testBuckets[i] { - break - } - counts[i]++ - } - } - return counts -} - -func TestBuckets(t *testing.T) { - got := LinearBuckets(-15, 5, 6) - want := []float64{-15, -10, -5, 0, 5, 10} - if !reflect.DeepEqual(got, want) { - t.Errorf("linear buckets: got %v, want %v", got, want) - } - - got = ExponentialBuckets(100, 1.2, 3) - want = []float64{100, 120, 144} - if !reflect.DeepEqual(got, want) { - t.Errorf("linear buckets: got %v, want %v", got, want) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/http_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/http_test.go deleted file mode 100644 index ffe0418cf..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/http_test.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus - -import ( - "net/http" - "net/http/httptest" - "testing" - "time" - - dto "github.com/prometheus/client_model/go" -) - -type respBody string - -func (b respBody) ServeHTTP(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusTeapot) - w.Write([]byte(b)) -} - -func TestInstrumentHandler(t *testing.T) { - defer func(n nower) { - now = n.(nower) - }(now) - - instant := time.Now() - end := instant.Add(30 * time.Second) - now = nowSeries(instant, end) - respBody := respBody("Howdy there!") - - hndlr := InstrumentHandler("test-handler", respBody) - - opts := SummaryOpts{ - Subsystem: "http", - ConstLabels: Labels{"handler": "test-handler"}, - } - - reqCnt := MustRegisterOrGet(NewCounterVec( - CounterOpts{ - Namespace: opts.Namespace, - Subsystem: opts.Subsystem, - Name: "requests_total", - Help: "Total number of HTTP requests made.", - ConstLabels: opts.ConstLabels, - }, - instLabels, - )).(*CounterVec) - - opts.Name = "request_duration_microseconds" - opts.Help = "The HTTP request latencies in microseconds." - reqDur := MustRegisterOrGet(NewSummary(opts)).(Summary) - - opts.Name = "request_size_bytes" - opts.Help = "The HTTP request sizes in bytes." - MustRegisterOrGet(NewSummary(opts)) - - opts.Name = "response_size_bytes" - opts.Help = "The HTTP response sizes in bytes." - MustRegisterOrGet(NewSummary(opts)) - - reqCnt.Reset() - - resp := httptest.NewRecorder() - req := &http.Request{ - Method: "GET", - } - - hndlr.ServeHTTP(resp, req) - - if resp.Code != http.StatusTeapot { - t.Fatalf("expected status %d, got %d", http.StatusTeapot, resp.Code) - } - if string(resp.Body.Bytes()) != "Howdy there!" { - t.Fatalf("expected body %s, got %s", "Howdy there!", string(resp.Body.Bytes())) - } - - out := &dto.Metric{} - reqDur.Write(out) - if want, got := "test-handler", out.Label[0].GetValue(); want != got { - t.Errorf("want label value %q in reqDur, got %q", want, got) - } - if want, got := uint64(1), out.Summary.GetSampleCount(); want != got { - t.Errorf("want sample count %d in reqDur, got %d", want, got) - } - - out.Reset() - if want, got := 1, len(reqCnt.children); want != got { - t.Errorf("want %d children in reqCnt, got %d", want, got) - } - cnt, err := reqCnt.GetMetricWithLabelValues("get", "418") - if err != nil { - t.Fatal(err) - } - cnt.Write(out) - if want, got := "418", out.Label[0].GetValue(); want != got { - t.Errorf("want label value %q in reqCnt, got %q", want, got) - } - if want, got := "test-handler", out.Label[1].GetValue(); want != got { - t.Errorf("want label value %q in reqCnt, got %q", want, got) - } - if want, got := "get", out.Label[2].GetValue(); want != got { - t.Errorf("want label value %q in reqCnt, got %q", want, got) - } - if out.Counter == nil { - t.Fatal("expected non-nil counter in reqCnt") - } - if want, got := 1., out.Counter.GetValue(); want != got { - t.Errorf("want reqCnt of %f, got %f", want, got) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/metric_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/metric_test.go deleted file mode 100644 index 7145f5e53..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/metric_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus - -import "testing" - -func TestBuildFQName(t *testing.T) { - scenarios := []struct{ namespace, subsystem, name, result string }{ - {"a", "b", "c", "a_b_c"}, - {"", "b", "c", "b_c"}, - {"a", "", "c", "a_c"}, - {"", "", "c", "c"}, - {"a", "b", "", ""}, - {"a", "", "", ""}, - {"", "b", "", ""}, - {" ", "", "", ""}, - } - - for i, s := range scenarios { - if want, got := s.result, BuildFQName(s.namespace, s.subsystem, s.name); want != got { - t.Errorf("%d. want %s, got %s", i, want, got) - } - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/process_collector_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/process_collector_test.go deleted file mode 100644 index 829715acd..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/process_collector_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package prometheus - -import ( - "io/ioutil" - "net/http" - "net/http/httptest" - "os" - "regexp" - "testing" - - "github.com/prometheus/procfs" -) - -func TestProcessCollector(t *testing.T) { - if _, err := procfs.Self(); err != nil { - t.Skipf("skipping TestProcessCollector, procfs not available: %s", err) - } - - registry := newRegistry() - registry.Register(NewProcessCollector(os.Getpid(), "")) - registry.Register(NewProcessCollectorPIDFn( - func() (int, error) { return os.Getpid(), nil }, "foobar")) - - s := httptest.NewServer(InstrumentHandler("prometheus", registry)) - defer s.Close() - r, err := http.Get(s.URL) - if err != nil { - t.Fatal(err) - } - defer r.Body.Close() - body, err := ioutil.ReadAll(r.Body) - if err != nil { - t.Fatal(err) - } - - for _, re := range []*regexp.Regexp{ - regexp.MustCompile("process_cpu_seconds_total [0-9]"), - regexp.MustCompile("process_max_fds [0-9]{2,}"), - regexp.MustCompile("process_open_fds [1-9]"), - regexp.MustCompile("process_virtual_memory_bytes [1-9]"), - regexp.MustCompile("process_resident_memory_bytes [1-9]"), - regexp.MustCompile("process_start_time_seconds [0-9.]{10,}"), - regexp.MustCompile("foobar_process_cpu_seconds_total [0-9]"), - regexp.MustCompile("foobar_process_max_fds [0-9]{2,}"), - regexp.MustCompile("foobar_process_open_fds [1-9]"), - regexp.MustCompile("foobar_process_virtual_memory_bytes [1-9]"), - regexp.MustCompile("foobar_process_resident_memory_bytes [1-9]"), - regexp.MustCompile("foobar_process_start_time_seconds [0-9.]{10,}"), - } { - if !re.Match(body) { - t.Errorf("want body to match %s\n%s", re, body) - } - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry_test.go deleted file mode 100644 index f30c90c06..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry_test.go +++ /dev/null @@ -1,535 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT 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 (c) 2013, The Prometheus 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 prometheus - -import ( - "bytes" - "encoding/binary" - "net/http" - "testing" - - "github.com/golang/protobuf/proto" - dto "github.com/prometheus/client_model/go" -) - -type fakeResponseWriter struct { - header http.Header - body bytes.Buffer -} - -func (r *fakeResponseWriter) Header() http.Header { - return r.header -} - -func (r *fakeResponseWriter) Write(d []byte) (l int, err error) { - return r.body.Write(d) -} - -func (r *fakeResponseWriter) WriteHeader(c int) { -} - -func testHandler(t testing.TB) { - - metricVec := NewCounterVec( - CounterOpts{ - Name: "name", - Help: "docstring", - ConstLabels: Labels{"constname": "constvalue"}, - }, - []string{"labelname"}, - ) - - metricVec.WithLabelValues("val1").Inc() - metricVec.WithLabelValues("val2").Inc() - - varintBuf := make([]byte, binary.MaxVarintLen32) - - externalMetricFamily := &dto.MetricFamily{ - Name: proto.String("externalname"), - Help: proto.String("externaldocstring"), - Type: dto.MetricType_COUNTER.Enum(), - Metric: []*dto.Metric{ - { - Label: []*dto.LabelPair{ - { - Name: proto.String("externalconstname"), - Value: proto.String("externalconstvalue"), - }, - { - Name: proto.String("externallabelname"), - Value: proto.String("externalval1"), - }, - }, - Counter: &dto.Counter{ - Value: proto.Float64(1), - }, - }, - }, - } - marshaledExternalMetricFamily, err := proto.Marshal(externalMetricFamily) - if err != nil { - t.Fatal(err) - } - var externalBuf bytes.Buffer - l := binary.PutUvarint(varintBuf, uint64(len(marshaledExternalMetricFamily))) - _, err = externalBuf.Write(varintBuf[:l]) - if err != nil { - t.Fatal(err) - } - _, err = externalBuf.Write(marshaledExternalMetricFamily) - if err != nil { - t.Fatal(err) - } - externalMetricFamilyAsBytes := externalBuf.Bytes() - externalMetricFamilyAsText := []byte(`# HELP externalname externaldocstring -# TYPE externalname counter -externalname{externalconstname="externalconstvalue",externallabelname="externalval1"} 1 -`) - externalMetricFamilyAsProtoText := []byte(`name: "externalname" -help: "externaldocstring" -type: COUNTER -metric: < - label: < - name: "externalconstname" - value: "externalconstvalue" - > - label: < - name: "externallabelname" - value: "externalval1" - > - counter: < - value: 1 - > -> - -`) - externalMetricFamilyAsProtoCompactText := []byte(`name:"externalname" help:"externaldocstring" type:COUNTER metric: label: counter: > -`) - - expectedMetricFamily := &dto.MetricFamily{ - Name: proto.String("name"), - Help: proto.String("docstring"), - Type: dto.MetricType_COUNTER.Enum(), - Metric: []*dto.Metric{ - { - Label: []*dto.LabelPair{ - { - Name: proto.String("constname"), - Value: proto.String("constvalue"), - }, - { - Name: proto.String("labelname"), - Value: proto.String("val1"), - }, - }, - Counter: &dto.Counter{ - Value: proto.Float64(1), - }, - }, - { - Label: []*dto.LabelPair{ - { - Name: proto.String("constname"), - Value: proto.String("constvalue"), - }, - { - Name: proto.String("labelname"), - Value: proto.String("val2"), - }, - }, - Counter: &dto.Counter{ - Value: proto.Float64(1), - }, - }, - }, - } - marshaledExpectedMetricFamily, err := proto.Marshal(expectedMetricFamily) - if err != nil { - t.Fatal(err) - } - var buf bytes.Buffer - l = binary.PutUvarint(varintBuf, uint64(len(marshaledExpectedMetricFamily))) - _, err = buf.Write(varintBuf[:l]) - if err != nil { - t.Fatal(err) - } - _, err = buf.Write(marshaledExpectedMetricFamily) - if err != nil { - t.Fatal(err) - } - expectedMetricFamilyAsBytes := buf.Bytes() - expectedMetricFamilyAsText := []byte(`# HELP name docstring -# TYPE name counter -name{constname="constvalue",labelname="val1"} 1 -name{constname="constvalue",labelname="val2"} 1 -`) - expectedMetricFamilyAsProtoText := []byte(`name: "name" -help: "docstring" -type: COUNTER -metric: < - label: < - name: "constname" - value: "constvalue" - > - label: < - name: "labelname" - value: "val1" - > - counter: < - value: 1 - > -> -metric: < - label: < - name: "constname" - value: "constvalue" - > - label: < - name: "labelname" - value: "val2" - > - counter: < - value: 1 - > -> - -`) - expectedMetricFamilyAsProtoCompactText := []byte(`name:"name" help:"docstring" type:COUNTER metric: label: counter: > metric: label: counter: > -`) - - externalMetricFamilyWithSameName := &dto.MetricFamily{ - Name: proto.String("name"), - Help: proto.String("inconsistent help string does not matter here"), - Type: dto.MetricType_COUNTER.Enum(), - Metric: []*dto.Metric{ - { - Label: []*dto.LabelPair{ - { - Name: proto.String("constname"), - Value: proto.String("constvalue"), - }, - { - Name: proto.String("labelname"), - Value: proto.String("different_val"), - }, - }, - Counter: &dto.Counter{ - Value: proto.Float64(42), - }, - }, - }, - } - - expectedMetricFamilyMergedWithExternalAsProtoCompactText := []byte(`name:"name" help:"docstring" type:COUNTER metric: label: counter: > metric: label: counter: > metric: label: counter: > -`) - - type output struct { - headers map[string]string - body []byte - } - - var scenarios = []struct { - headers map[string]string - out output - collector Collector - externalMF []*dto.MetricFamily - }{ - { // 0 - headers: map[string]string{ - "Accept": "foo/bar;q=0.2, dings/bums;q=0.8", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `text/plain; version=0.0.4`, - }, - body: []byte{}, - }, - }, - { // 1 - headers: map[string]string{ - "Accept": "foo/bar;q=0.2, application/quark;q=0.8", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `text/plain; version=0.0.4`, - }, - body: []byte{}, - }, - }, - { // 2 - headers: map[string]string{ - "Accept": "foo/bar;q=0.2, application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=bla;q=0.8", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `text/plain; version=0.0.4`, - }, - body: []byte{}, - }, - }, - { // 3 - headers: map[string]string{ - "Accept": "text/plain;q=0.2, application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.8", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`, - }, - body: []byte{}, - }, - }, - { // 4 - headers: map[string]string{ - "Accept": "application/json", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `text/plain; version=0.0.4`, - }, - body: expectedMetricFamilyAsText, - }, - collector: metricVec, - }, - { // 5 - headers: map[string]string{ - "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`, - }, - body: expectedMetricFamilyAsBytes, - }, - collector: metricVec, - }, - { // 6 - headers: map[string]string{ - "Accept": "application/json", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `text/plain; version=0.0.4`, - }, - body: externalMetricFamilyAsText, - }, - externalMF: []*dto.MetricFamily{externalMetricFamily}, - }, - { // 7 - headers: map[string]string{ - "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`, - }, - body: externalMetricFamilyAsBytes, - }, - externalMF: []*dto.MetricFamily{externalMetricFamily}, - }, - { // 8 - headers: map[string]string{ - "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`, - }, - body: bytes.Join( - [][]byte{ - externalMetricFamilyAsBytes, - expectedMetricFamilyAsBytes, - }, - []byte{}, - ), - }, - collector: metricVec, - externalMF: []*dto.MetricFamily{externalMetricFamily}, - }, - { // 9 - headers: map[string]string{ - "Accept": "text/plain", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `text/plain; version=0.0.4`, - }, - body: []byte{}, - }, - }, - { // 10 - headers: map[string]string{ - "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=bla;q=0.2, text/plain;q=0.5", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `text/plain; version=0.0.4`, - }, - body: expectedMetricFamilyAsText, - }, - collector: metricVec, - }, - { // 11 - headers: map[string]string{ - "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=bla;q=0.2, text/plain;q=0.5;version=0.0.4", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `text/plain; version=0.0.4`, - }, - body: bytes.Join( - [][]byte{ - externalMetricFamilyAsText, - expectedMetricFamilyAsText, - }, - []byte{}, - ), - }, - collector: metricVec, - externalMF: []*dto.MetricFamily{externalMetricFamily}, - }, - { // 12 - headers: map[string]string{ - "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.2, text/plain;q=0.5;version=0.0.2", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`, - }, - body: bytes.Join( - [][]byte{ - externalMetricFamilyAsBytes, - expectedMetricFamilyAsBytes, - }, - []byte{}, - ), - }, - collector: metricVec, - externalMF: []*dto.MetricFamily{externalMetricFamily}, - }, - { // 13 - headers: map[string]string{ - "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=text;q=0.5, application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.4", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=text`, - }, - body: bytes.Join( - [][]byte{ - externalMetricFamilyAsProtoText, - expectedMetricFamilyAsProtoText, - }, - []byte{}, - ), - }, - collector: metricVec, - externalMF: []*dto.MetricFamily{externalMetricFamily}, - }, - { // 14 - headers: map[string]string{ - "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=compact-text", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=compact-text`, - }, - body: bytes.Join( - [][]byte{ - externalMetricFamilyAsProtoCompactText, - expectedMetricFamilyAsProtoCompactText, - }, - []byte{}, - ), - }, - collector: metricVec, - externalMF: []*dto.MetricFamily{externalMetricFamily}, - }, - { // 15 - headers: map[string]string{ - "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=compact-text", - }, - out: output{ - headers: map[string]string{ - "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=compact-text`, - }, - body: bytes.Join( - [][]byte{ - externalMetricFamilyAsProtoCompactText, - expectedMetricFamilyMergedWithExternalAsProtoCompactText, - }, - []byte{}, - ), - }, - collector: metricVec, - externalMF: []*dto.MetricFamily{ - externalMetricFamily, - externalMetricFamilyWithSameName, - }, - }, - } - for i, scenario := range scenarios { - registry := newRegistry() - registry.collectChecksEnabled = true - - if scenario.collector != nil { - registry.Register(scenario.collector) - } - if scenario.externalMF != nil { - registry.metricFamilyInjectionHook = func() []*dto.MetricFamily { - return scenario.externalMF - } - } - writer := &fakeResponseWriter{ - header: http.Header{}, - } - handler := InstrumentHandler("prometheus", registry) - request, _ := http.NewRequest("GET", "/", nil) - for key, value := range scenario.headers { - request.Header.Add(key, value) - } - handler(writer, request) - - for key, value := range scenario.out.headers { - if writer.Header().Get(key) != value { - t.Errorf( - "%d. expected %q for header %q, got %q", - i, value, key, writer.Header().Get(key), - ) - } - } - - if !bytes.Equal(scenario.out.body, writer.body.Bytes()) { - t.Errorf( - "%d. expected %q for body, got %q", - i, scenario.out.body, writer.body.Bytes(), - ) - } - } -} - -func TestHandler(t *testing.T) { - testHandler(t) -} - -func BenchmarkHandler(b *testing.B) { - for i := 0; i < b.N; i++ { - testHandler(b) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary_test.go deleted file mode 100644 index 0790cdfe7..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary_test.go +++ /dev/null @@ -1,347 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus - -import ( - "math" - "math/rand" - "sort" - "sync" - "testing" - "testing/quick" - "time" - - dto "github.com/prometheus/client_model/go" -) - -func benchmarkSummaryObserve(w int, b *testing.B) { - b.StopTimer() - - wg := new(sync.WaitGroup) - wg.Add(w) - - g := new(sync.WaitGroup) - g.Add(1) - - s := NewSummary(SummaryOpts{}) - - for i := 0; i < w; i++ { - go func() { - g.Wait() - - for i := 0; i < b.N; i++ { - s.Observe(float64(i)) - } - - wg.Done() - }() - } - - b.StartTimer() - g.Done() - wg.Wait() -} - -func BenchmarkSummaryObserve1(b *testing.B) { - benchmarkSummaryObserve(1, b) -} - -func BenchmarkSummaryObserve2(b *testing.B) { - benchmarkSummaryObserve(2, b) -} - -func BenchmarkSummaryObserve4(b *testing.B) { - benchmarkSummaryObserve(4, b) -} - -func BenchmarkSummaryObserve8(b *testing.B) { - benchmarkSummaryObserve(8, b) -} - -func benchmarkSummaryWrite(w int, b *testing.B) { - b.StopTimer() - - wg := new(sync.WaitGroup) - wg.Add(w) - - g := new(sync.WaitGroup) - g.Add(1) - - s := NewSummary(SummaryOpts{}) - - for i := 0; i < 1000000; i++ { - s.Observe(float64(i)) - } - - for j := 0; j < w; j++ { - outs := make([]dto.Metric, b.N) - - go func(o []dto.Metric) { - g.Wait() - - for i := 0; i < b.N; i++ { - s.Write(&o[i]) - } - - wg.Done() - }(outs) - } - - b.StartTimer() - g.Done() - wg.Wait() -} - -func BenchmarkSummaryWrite1(b *testing.B) { - benchmarkSummaryWrite(1, b) -} - -func BenchmarkSummaryWrite2(b *testing.B) { - benchmarkSummaryWrite(2, b) -} - -func BenchmarkSummaryWrite4(b *testing.B) { - benchmarkSummaryWrite(4, b) -} - -func BenchmarkSummaryWrite8(b *testing.B) { - benchmarkSummaryWrite(8, b) -} - -func TestSummaryConcurrency(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test in short mode.") - } - - rand.Seed(42) - - it := func(n uint32) bool { - mutations := int(n%1e4 + 1e4) - concLevel := int(n%5 + 1) - total := mutations * concLevel - - var start, end sync.WaitGroup - start.Add(1) - end.Add(concLevel) - - sum := NewSummary(SummaryOpts{ - Name: "test_summary", - Help: "helpless", - }) - - allVars := make([]float64, total) - var sampleSum float64 - for i := 0; i < concLevel; i++ { - vals := make([]float64, mutations) - for j := 0; j < mutations; j++ { - v := rand.NormFloat64() - vals[j] = v - allVars[i*mutations+j] = v - sampleSum += v - } - - go func(vals []float64) { - start.Wait() - for _, v := range vals { - sum.Observe(v) - } - end.Done() - }(vals) - } - sort.Float64s(allVars) - start.Done() - end.Wait() - - m := &dto.Metric{} - sum.Write(m) - if got, want := int(*m.Summary.SampleCount), total; got != want { - t.Errorf("got sample count %d, want %d", got, want) - } - if got, want := *m.Summary.SampleSum, sampleSum; math.Abs((got-want)/want) > 0.001 { - t.Errorf("got sample sum %f, want %f", got, want) - } - - objectives := make([]float64, 0, len(DefObjectives)) - for qu := range DefObjectives { - objectives = append(objectives, qu) - } - sort.Float64s(objectives) - - for i, wantQ := range objectives { - ε := DefObjectives[wantQ] - gotQ := *m.Summary.Quantile[i].Quantile - gotV := *m.Summary.Quantile[i].Value - min, max := getBounds(allVars, wantQ, ε) - if gotQ != wantQ { - t.Errorf("got quantile %f, want %f", gotQ, wantQ) - } - if gotV < min || gotV > max { - t.Errorf("got %f for quantile %f, want [%f,%f]", gotV, gotQ, min, max) - } - } - return true - } - - if err := quick.Check(it, nil); err != nil { - t.Error(err) - } -} - -func TestSummaryVecConcurrency(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test in short mode.") - } - - rand.Seed(42) - - objectives := make([]float64, 0, len(DefObjectives)) - for qu := range DefObjectives { - - objectives = append(objectives, qu) - } - sort.Float64s(objectives) - - it := func(n uint32) bool { - mutations := int(n%1e4 + 1e4) - concLevel := int(n%7 + 1) - vecLength := int(n%3 + 1) - - var start, end sync.WaitGroup - start.Add(1) - end.Add(concLevel) - - sum := NewSummaryVec( - SummaryOpts{ - Name: "test_summary", - Help: "helpless", - }, - []string{"label"}, - ) - - allVars := make([][]float64, vecLength) - sampleSums := make([]float64, vecLength) - for i := 0; i < concLevel; i++ { - vals := make([]float64, mutations) - picks := make([]int, mutations) - for j := 0; j < mutations; j++ { - v := rand.NormFloat64() - vals[j] = v - pick := rand.Intn(vecLength) - picks[j] = pick - allVars[pick] = append(allVars[pick], v) - sampleSums[pick] += v - } - - go func(vals []float64) { - start.Wait() - for i, v := range vals { - sum.WithLabelValues(string('A' + picks[i])).Observe(v) - } - end.Done() - }(vals) - } - for _, vars := range allVars { - sort.Float64s(vars) - } - start.Done() - end.Wait() - - for i := 0; i < vecLength; i++ { - m := &dto.Metric{} - s := sum.WithLabelValues(string('A' + i)) - s.Write(m) - if got, want := int(*m.Summary.SampleCount), len(allVars[i]); got != want { - t.Errorf("got sample count %d for label %c, want %d", got, 'A'+i, want) - } - if got, want := *m.Summary.SampleSum, sampleSums[i]; math.Abs((got-want)/want) > 0.001 { - t.Errorf("got sample sum %f for label %c, want %f", got, 'A'+i, want) - } - for j, wantQ := range objectives { - ε := DefObjectives[wantQ] - gotQ := *m.Summary.Quantile[j].Quantile - gotV := *m.Summary.Quantile[j].Value - min, max := getBounds(allVars[i], wantQ, ε) - if gotQ != wantQ { - t.Errorf("got quantile %f for label %c, want %f", gotQ, 'A'+i, wantQ) - } - if gotV < min || gotV > max { - t.Errorf("got %f for quantile %f for label %c, want [%f,%f]", gotV, gotQ, 'A'+i, min, max) - } - } - } - return true - } - - if err := quick.Check(it, nil); err != nil { - t.Error(err) - } -} - -func TestSummaryDecay(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test in short mode.") - // More because it depends on timing than because it is particularly long... - } - - sum := NewSummary(SummaryOpts{ - Name: "test_summary", - Help: "helpless", - MaxAge: 100 * time.Millisecond, - Objectives: map[float64]float64{0.1: 0.001}, - AgeBuckets: 10, - }) - - m := &dto.Metric{} - i := 0 - tick := time.NewTicker(time.Millisecond) - for _ = range tick.C { - i++ - sum.Observe(float64(i)) - if i%10 == 0 { - sum.Write(m) - if got, want := *m.Summary.Quantile[0].Value, math.Max(float64(i)/10, float64(i-90)); math.Abs(got-want) > 20 { - t.Errorf("%d. got %f, want %f", i, got, want) - } - m.Reset() - } - if i >= 1000 { - break - } - } - tick.Stop() - // Wait for MaxAge without observations and make sure quantiles are NaN. - time.Sleep(100 * time.Millisecond) - sum.Write(m) - if got := *m.Summary.Quantile[0].Value; !math.IsNaN(got) { - t.Errorf("got %f, want NaN after expiration", got) - } -} - -func getBounds(vars []float64, q, ε float64) (min, max float64) { - // TODO: This currently tolerates an error of up to 2*ε. The error must - // be at most ε, but for some reason, it's sometimes slightly - // higher. That's a bug. - n := float64(len(vars)) - lower := int((q - 2*ε) * n) - upper := int(math.Ceil((q + 2*ε) * n)) - min = vars[0] - if lower > 1 { - min = vars[lower-1] - } - max = vars[len(vars)-1] - if upper < len(vars) { - max = vars[upper-1] - } - return -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/vec_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/vec_test.go deleted file mode 100644 index 0e9431e65..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/vec_test.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package prometheus - -import ( - "hash/fnv" - "testing" -) - -func TestDelete(t *testing.T) { - desc := NewDesc("test", "helpless", []string{"l1", "l2"}, nil) - vec := MetricVec{ - children: map[uint64]Metric{}, - desc: desc, - hash: fnv.New64a(), - newMetric: func(lvs ...string) Metric { - return newValue(desc, UntypedValue, 0, lvs...) - }, - } - - if got, want := vec.Delete(Labels{"l1": "v1", "l2": "v2"}), false; got != want { - t.Errorf("got %v, want %v", got, want) - } - - vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42) - if got, want := vec.Delete(Labels{"l1": "v1", "l2": "v2"}), true; got != want { - t.Errorf("got %v, want %v", got, want) - } - if got, want := vec.Delete(Labels{"l1": "v1", "l2": "v2"}), false; got != want { - t.Errorf("got %v, want %v", got, want) - } - - vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42) - if got, want := vec.Delete(Labels{"l2": "v2", "l1": "v1"}), true; got != want { - t.Errorf("got %v, want %v", got, want) - } - if got, want := vec.Delete(Labels{"l2": "v2", "l1": "v1"}), false; got != want { - t.Errorf("got %v, want %v", got, want) - } - - vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42) - if got, want := vec.Delete(Labels{"l2": "v1", "l1": "v2"}), false; got != want { - t.Errorf("got %v, want %v", got, want) - } - if got, want := vec.Delete(Labels{"l1": "v1"}), false; got != want { - t.Errorf("got %v, want %v", got, want) - } -} - -func TestDeleteLabelValues(t *testing.T) { - desc := NewDesc("test", "helpless", []string{"l1", "l2"}, nil) - vec := MetricVec{ - children: map[uint64]Metric{}, - desc: desc, - hash: fnv.New64a(), - newMetric: func(lvs ...string) Metric { - return newValue(desc, UntypedValue, 0, lvs...) - }, - } - - if got, want := vec.DeleteLabelValues("v1", "v2"), false; got != want { - t.Errorf("got %v, want %v", got, want) - } - - vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42) - if got, want := vec.DeleteLabelValues("v1", "v2"), true; got != want { - t.Errorf("got %v, want %v", got, want) - } - if got, want := vec.DeleteLabelValues("v1", "v2"), false; got != want { - t.Errorf("got %v, want %v", got, want) - } - - vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42) - if got, want := vec.DeleteLabelValues("v2", "v1"), false; got != want { - t.Errorf("got %v, want %v", got, want) - } - if got, want := vec.DeleteLabelValues("v1"), false; got != want { - t.Errorf("got %v, want %v", got, want) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_model/LICENSE b/Godeps/_workspace/src/github.com/prometheus/client_model/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/client_model/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/Godeps/_workspace/src/github.com/prometheus/client_model/NOTICE b/Godeps/_workspace/src/github.com/prometheus/client_model/NOTICE new file mode 100644 index 000000000..20110e410 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/client_model/NOTICE @@ -0,0 +1,5 @@ +Data model artifacts for Prometheus. +Copyright 2012-2015 The Prometheus Authors + +This product includes software developed at +SoundCloud Ltd. (http://soundcloud.com/). diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/bench_test.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/bench_test.go deleted file mode 100644 index 92b16a028..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/bench_test.go +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2015 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package expfmt - -import ( - "bytes" - "compress/gzip" - "io" - "io/ioutil" - "testing" - - "github.com/matttproud/golang_protobuf_extensions/pbutil" - - dto "github.com/prometheus/client_model/go" -) - -var parser TextParser - -// Benchmarks to show how much penalty text format parsing actually inflicts. -// -// Example results on Linux 3.13.0, Intel(R) Core(TM) i7-4700MQ CPU @ 2.40GHz, go1.4. -// -// BenchmarkParseText 1000 1188535 ns/op 205085 B/op 6135 allocs/op -// BenchmarkParseTextGzip 1000 1376567 ns/op 246224 B/op 6151 allocs/op -// BenchmarkParseProto 10000 172790 ns/op 52258 B/op 1160 allocs/op -// BenchmarkParseProtoGzip 5000 324021 ns/op 94931 B/op 1211 allocs/op -// BenchmarkParseProtoMap 10000 187946 ns/op 58714 B/op 1203 allocs/op -// -// CONCLUSION: The overhead for the map is negligible. Text format needs ~5x more allocations. -// Without compression, it needs ~7x longer, but with compression (the more relevant scenario), -// the difference becomes less relevant, only ~4x. -// -// The test data contains 248 samples. -// -// BenchmarkProcessor002ParseOnly in the extraction package is not quite -// comparable to the benchmarks here, but it gives an idea: JSON parsing is even -// slower than text parsing and needs a comparable amount of allocs. - -// BenchmarkParseText benchmarks the parsing of a text-format scrape into metric -// family DTOs. -func BenchmarkParseText(b *testing.B) { - b.StopTimer() - data, err := ioutil.ReadFile("testdata/text") - if err != nil { - b.Fatal(err) - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - if _, err := parser.TextToMetricFamilies(bytes.NewReader(data)); err != nil { - b.Fatal(err) - } - } -} - -// BenchmarkParseTextGzip benchmarks the parsing of a gzipped text-format scrape -// into metric family DTOs. -func BenchmarkParseTextGzip(b *testing.B) { - b.StopTimer() - data, err := ioutil.ReadFile("testdata/text.gz") - if err != nil { - b.Fatal(err) - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - in, err := gzip.NewReader(bytes.NewReader(data)) - if err != nil { - b.Fatal(err) - } - if _, err := parser.TextToMetricFamilies(in); err != nil { - b.Fatal(err) - } - } -} - -// BenchmarkParseProto benchmarks the parsing of a protobuf-format scrape into -// metric family DTOs. Note that this does not build a map of metric families -// (as the text version does), because it is not required for Prometheus -// ingestion either. (However, it is required for the text-format parsing, as -// the metric family might be sprinkled all over the text, while the -// protobuf-format guarantees bundling at one place.) -func BenchmarkParseProto(b *testing.B) { - b.StopTimer() - data, err := ioutil.ReadFile("testdata/protobuf") - if err != nil { - b.Fatal(err) - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - family := &dto.MetricFamily{} - in := bytes.NewReader(data) - for { - family.Reset() - if _, err := pbutil.ReadDelimited(in, family); err != nil { - if err == io.EOF { - break - } - b.Fatal(err) - } - } - } -} - -// BenchmarkParseProtoGzip is like BenchmarkParseProto above, but parses gzipped -// protobuf format. -func BenchmarkParseProtoGzip(b *testing.B) { - b.StopTimer() - data, err := ioutil.ReadFile("testdata/protobuf.gz") - if err != nil { - b.Fatal(err) - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - family := &dto.MetricFamily{} - in, err := gzip.NewReader(bytes.NewReader(data)) - if err != nil { - b.Fatal(err) - } - for { - family.Reset() - if _, err := pbutil.ReadDelimited(in, family); err != nil { - if err == io.EOF { - break - } - b.Fatal(err) - } - } - } -} - -// BenchmarkParseProtoMap is like BenchmarkParseProto but DOES put the parsed -// metric family DTOs into a map. This is not happening during Prometheus -// ingestion. It is just here to measure the overhead of that map creation and -// separate it from the overhead of the text format parsing. -func BenchmarkParseProtoMap(b *testing.B) { - b.StopTimer() - data, err := ioutil.ReadFile("testdata/protobuf") - if err != nil { - b.Fatal(err) - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - families := map[string]*dto.MetricFamily{} - in := bytes.NewReader(data) - for { - family := &dto.MetricFamily{} - if _, err := pbutil.ReadDelimited(in, family); err != nil { - if err == io.EOF { - break - } - b.Fatal(err) - } - families[family.GetName()] = family - } - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode_test.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode_test.go deleted file mode 100644 index 307fcd21f..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode_test.go +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2015 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package expfmt - -import ( - "errors" - "io" - "net/http" - "reflect" - "sort" - "strings" - "testing" - - "github.com/prometheus/common/model" -) - -func TestTextDecoder(t *testing.T) { - var ( - ts = model.Now() - in = ` -# Only a quite simple scenario with two metric families. -# More complicated tests of the parser itself can be found in the text package. -# TYPE mf2 counter -mf2 3 -mf1{label="value1"} -3.14 123456 -mf1{label="value2"} 42 -mf2 4 -` - out = model.Vector{ - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "mf1", - "label": "value1", - }, - Value: -3.14, - Timestamp: 123456, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "mf1", - "label": "value2", - }, - Value: 42, - Timestamp: ts, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "mf2", - }, - Value: 3, - Timestamp: ts, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "mf2", - }, - Value: 4, - Timestamp: ts, - }, - } - ) - - dec := &SampleDecoder{ - Dec: &textDecoder{r: strings.NewReader(in)}, - Opts: &DecodeOptions{ - Timestamp: ts, - }, - } - var all model.Vector - for { - var smpls model.Vector - err := dec.Decode(&smpls) - if err == io.EOF { - break - } - if err != nil { - t.Fatal(err) - } - all = append(all, smpls...) - } - sort.Sort(all) - sort.Sort(out) - if !reflect.DeepEqual(all, out) { - t.Fatalf("output does not match") - } -} - -func TestProtoDecoder(t *testing.T) { - - var testTime = model.Now() - - scenarios := []struct { - in string - expected model.Vector - }{ - { - in: "", - }, - { - in: "\x8f\x01\n\rrequest_count\x12\x12Number of requests\x18\x00\"0\n#\n\x0fsome_label_name\x12\x10some_label_value\x1a\t\t\x00\x00\x00\x00\x00\x00E\xc0\"6\n)\n\x12another_label_name\x12\x13another_label_value\x1a\t\t\x00\x00\x00\x00\x00\x00U@", - expected: model.Vector{ - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_count", - "some_label_name": "some_label_value", - }, - Value: -42, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_count", - "another_label_name": "another_label_value", - }, - Value: 84, - Timestamp: testTime, - }, - }, - }, - { - in: "\xb9\x01\n\rrequest_count\x12\x12Number of requests\x18\x02\"O\n#\n\x0fsome_label_name\x12\x10some_label_value\"(\x1a\x12\t\xaeG\xe1z\x14\xae\xef?\x11\x00\x00\x00\x00\x00\x00E\xc0\x1a\x12\t+\x87\x16\xd9\xce\xf7\xef?\x11\x00\x00\x00\x00\x00\x00U\xc0\"A\n)\n\x12another_label_name\x12\x13another_label_value\"\x14\x1a\x12\t\x00\x00\x00\x00\x00\x00\xe0?\x11\x00\x00\x00\x00\x00\x00$@", - expected: model.Vector{ - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_count_count", - "some_label_name": "some_label_value", - }, - Value: 0, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_count_sum", - "some_label_name": "some_label_value", - }, - Value: 0, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_count", - "some_label_name": "some_label_value", - "quantile": "0.99", - }, - Value: -42, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_count", - "some_label_name": "some_label_value", - "quantile": "0.999", - }, - Value: -84, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_count_count", - "another_label_name": "another_label_value", - }, - Value: 0, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_count_sum", - "another_label_name": "another_label_value", - }, - Value: 0, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_count", - "another_label_name": "another_label_value", - "quantile": "0.5", - }, - Value: 10, - Timestamp: testTime, - }, - }, - }, - { - in: "\x8d\x01\n\x1drequest_duration_microseconds\x12\x15The response latency.\x18\x04\"S:Q\b\x85\x15\x11\xcd\xcc\xccL\x8f\xcb:A\x1a\v\b{\x11\x00\x00\x00\x00\x00\x00Y@\x1a\f\b\x9c\x03\x11\x00\x00\x00\x00\x00\x00^@\x1a\f\b\xd0\x04\x11\x00\x00\x00\x00\x00\x00b@\x1a\f\b\xf4\v\x11\x9a\x99\x99\x99\x99\x99e@\x1a\f\b\x85\x15\x11\x00\x00\x00\x00\x00\x00\xf0\u007f", - expected: model.Vector{ - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_duration_microseconds_bucket", - "le": "100", - }, - Value: 123, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_duration_microseconds_bucket", - "le": "120", - }, - Value: 412, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_duration_microseconds_bucket", - "le": "144", - }, - Value: 592, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_duration_microseconds_bucket", - "le": "172.8", - }, - Value: 1524, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_duration_microseconds_bucket", - "le": "+Inf", - }, - Value: 2693, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_duration_microseconds_sum", - }, - Value: 1756047.3, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_duration_microseconds_count", - }, - Value: 2693, - Timestamp: testTime, - }, - }, - }, - { - // The metric type is unset in this protobuf, which needs to be handled - // correctly by the decoder. - in: "\x1c\n\rrequest_count\"\v\x1a\t\t\x00\x00\x00\x00\x00\x00\xf0?", - expected: model.Vector{ - &model.Sample{ - Metric: model.Metric{ - model.MetricNameLabel: "request_count", - }, - Value: 1, - Timestamp: testTime, - }, - }, - }, - } - - for i, scenario := range scenarios { - dec := &SampleDecoder{ - Dec: &protoDecoder{r: strings.NewReader(scenario.in)}, - Opts: &DecodeOptions{ - Timestamp: testTime, - }, - } - - var all model.Vector - for { - var smpls model.Vector - err := dec.Decode(&smpls) - if err == io.EOF { - break - } - if err != nil { - t.Fatal(err) - } - all = append(all, smpls...) - } - sort.Sort(all) - sort.Sort(scenario.expected) - if !reflect.DeepEqual(all, scenario.expected) { - t.Fatalf("%d. output does not match, want: %#v, got %#v", i, scenario.expected, all) - } - } -} - -func testDiscriminatorHTTPHeader(t testing.TB) { - var scenarios = []struct { - input map[string]string - output Format - err error - }{ - { - input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="delimited"`}, - output: FmtProtoDelim, - err: nil, - }, - { - input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="illegal"; encoding="delimited"`}, - output: "", - err: errors.New("unrecognized protocol message illegal"), - }, - { - input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="illegal"`}, - output: "", - err: errors.New("unsupported encoding illegal"), - }, - { - input: map[string]string{"Content-Type": `text/plain; version=0.0.4`}, - output: FmtText, - err: nil, - }, - { - input: map[string]string{"Content-Type": `text/plain`}, - output: FmtText, - err: nil, - }, - { - input: map[string]string{"Content-Type": `text/plain; version=0.0.3`}, - output: "", - err: errors.New("unrecognized protocol version 0.0.3"), - }, - } - - for i, scenario := range scenarios { - var header http.Header - - if len(scenario.input) > 0 { - header = http.Header{} - } - - for key, value := range scenario.input { - header.Add(key, value) - } - - actual, err := ResponseFormat(header) - - if scenario.err != err { - if scenario.err != nil && err != nil { - if scenario.err.Error() != err.Error() { - t.Errorf("%d. expected %s, got %s", i, scenario.err, err) - } - } else if scenario.err != nil || err != nil { - t.Errorf("%d. expected %s, got %s", i, scenario.err, err) - } - } - - if !reflect.DeepEqual(scenario.output, actual) { - t.Errorf("%d. expected %s, got %s", i, scenario.output, actual) - } - } -} - -func TestDiscriminatorHTTPHeader(t *testing.T) { - testDiscriminatorHTTPHeader(t) -} - -func BenchmarkDiscriminatorHTTPHeader(b *testing.B) { - for i := 0; i < b.N; i++ { - testDiscriminatorHTTPHeader(b) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_0 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_0 deleted file mode 100644 index 139597f9c..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_0 +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_1 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_1 deleted file mode 100644 index 2ae870679..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_1 +++ /dev/null @@ -1,6 +0,0 @@ - -minimal_metric 1.234 -another_metric -3e3 103948 -# Even that: -no_labels{} 3 -# HELP line for non-existing metric will be ignored. diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_2 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_2 deleted file mode 100644 index 5c351db36..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_2 +++ /dev/null @@ -1,12 +0,0 @@ - -# A normal comment. -# -# TYPE name counter -name{labelname="val1",basename="basevalue"} NaN -name {labelname="val2",basename="base\"v\\al\nue"} 0.23 1234567890 -# HELP name two-line\n doc str\\ing - - # HELP name2 doc str"ing 2 - # TYPE name2 gauge -name2{labelname="val2" ,basename = "basevalue2" } +Inf 54321 -name2{ labelname = "val1" , }-Inf diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_3 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_3 deleted file mode 100644 index 0b3c345aa..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_3 +++ /dev/null @@ -1,22 +0,0 @@ - -# TYPE my_summary summary -my_summary{n1="val1",quantile="0.5"} 110 -decoy -1 -2 -my_summary{n1="val1",quantile="0.9"} 140 1 -my_summary_count{n1="val1"} 42 -# Latest timestamp wins in case of a summary. -my_summary_sum{n1="val1"} 4711 2 -fake_sum{n1="val1"} 2001 -# TYPE another_summary summary -another_summary_count{n2="val2",n1="val1"} 20 -my_summary_count{n2="val2",n1="val1"} 5 5 -another_summary{n1="val1",n2="val2",quantile=".3"} -1.2 -my_summary_sum{n1="val2"} 08 15 -my_summary{n1="val3", quantile="0.2"} 4711 - my_summary{n1="val1",n2="val2",quantile="-12.34",} NaN -# some -# funny comments -# HELP -# HELP -# HELP my_summary -# HELP my_summary diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_4 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_4 deleted file mode 100644 index bde0a387a..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_4 +++ /dev/null @@ -1,10 +0,0 @@ - -# HELP request_duration_microseconds The response latency. -# TYPE request_duration_microseconds histogram -request_duration_microseconds_bucket{le="100"} 123 -request_duration_microseconds_bucket{le="120"} 412 -request_duration_microseconds_bucket{le="144"} 592 -request_duration_microseconds_bucket{le="172.8"} 1524 -request_duration_microseconds_bucket{le="+Inf"} 2693 -request_duration_microseconds_sum 1.7560473e+06 -request_duration_microseconds_count 2693 diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_0 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_0 deleted file mode 100644 index 4c67f9a19..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_0 +++ /dev/null @@ -1 +0,0 @@ -bla 3.14 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_1 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_1 deleted file mode 100644 index b853478ee..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_1 +++ /dev/null @@ -1 +0,0 @@ -metric{label="\t"} 3.14 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_10 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_10 deleted file mode 100644 index b5fe5f5a6..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_10 +++ /dev/null @@ -1 +0,0 @@ -metric{label="bla"} 3.14 2 3 diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_11 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_11 deleted file mode 100644 index 57c7fbc0b..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_11 +++ /dev/null @@ -1 +0,0 @@ -metric{label="bla"} blubb diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_12 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_12 deleted file mode 100644 index 0a9df79a1..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_12 +++ /dev/null @@ -1,3 +0,0 @@ - -# HELP metric one -# HELP metric two diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_13 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_13 deleted file mode 100644 index 5bc742781..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_13 +++ /dev/null @@ -1,3 +0,0 @@ - -# TYPE metric counter -# TYPE metric untyped diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_14 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_14 deleted file mode 100644 index a9a24265b..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_14 +++ /dev/null @@ -1,3 +0,0 @@ - -metric 4.12 -# TYPE metric counter diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_15 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_15 deleted file mode 100644 index 7e95ca8f4..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_15 +++ /dev/null @@ -1,2 +0,0 @@ - -# TYPE metric bla diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_16 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_16 deleted file mode 100644 index 7825f8887..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_16 +++ /dev/null @@ -1,2 +0,0 @@ - -# TYPE met-ric diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_17 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_17 deleted file mode 100644 index 8f35cae0c..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_17 +++ /dev/null @@ -1 +0,0 @@ -@invalidmetric{label="bla"} 3.14 2 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_18 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_18 deleted file mode 100644 index 7ca2cc268..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_18 +++ /dev/null @@ -1 +0,0 @@ -{label="bla"} 3.14 2 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_19 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_19 deleted file mode 100644 index 7a6ccc0dd..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_19 +++ /dev/null @@ -1,3 +0,0 @@ - -# TYPE metric histogram -metric_bucket{le="bla"} 3.14 diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_2 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_2 deleted file mode 100644 index 726d0017c..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_2 +++ /dev/null @@ -1,3 +0,0 @@ - -metric{label="new -line"} 3.14 diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_3 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_3 deleted file mode 100644 index 6aa9e3081..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_3 +++ /dev/null @@ -1 +0,0 @@ -metric{@="bla"} 3.14 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_4 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_4 deleted file mode 100644 index d112cb902..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_4 +++ /dev/null @@ -1 +0,0 @@ -metric{__name__="bla"} 3.14 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_5 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_5 deleted file mode 100644 index b34554a8d..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_5 +++ /dev/null @@ -1 +0,0 @@ -metric{label+="bla"} 3.14 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_6 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_6 deleted file mode 100644 index c4d7df3d1..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_6 +++ /dev/null @@ -1 +0,0 @@ -metric{label=bla} 3.14 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_7 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_7 deleted file mode 100644 index 97eafc4a6..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_7 +++ /dev/null @@ -1,3 +0,0 @@ - -# TYPE metric summary -metric{quantile="bla"} 3.14 diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_8 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_8 deleted file mode 100644 index fc706496b..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_8 +++ /dev/null @@ -1 +0,0 @@ -metric{label="bla"+} 3.14 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_9 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_9 deleted file mode 100644 index 57b4879c0..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_9 +++ /dev/null @@ -1 +0,0 @@ -metric{label="bla"} 3.14 2.72 diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/minimal b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/minimal deleted file mode 100644 index be1e6a369..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/minimal +++ /dev/null @@ -1 +0,0 @@ -m{} 0 diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode_test.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode_test.go deleted file mode 100644 index c98ea29e1..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode_test.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2015 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package expfmt - -import ( - "os" - "reflect" - "testing" - - "github.com/golang/protobuf/proto" - dto "github.com/prometheus/client_model/go" -) - -func TestJSON2Decode(t *testing.T) { - f, err := os.Open("testdata/json2") - if err != nil { - t.Fatal(err) - } - defer f.Close() - - dec := newJSON2Decoder(f) - - var v1 dto.MetricFamily - if err := dec.Decode(&v1); err != nil { - t.Fatal(err) - } - - exp1 := dto.MetricFamily{ - Type: dto.MetricType_UNTYPED.Enum(), - Help: proto.String("RPC calls."), - Name: proto.String("rpc_calls_total"), - Metric: []*dto.Metric{ - { - Label: []*dto.LabelPair{ - { - Name: proto.String("job"), - Value: proto.String("batch_job"), - }, { - Name: proto.String("service"), - Value: proto.String("zed"), - }, - }, - Untyped: &dto.Untyped{ - Value: proto.Float64(25), - }, - }, - { - Label: []*dto.LabelPair{ - { - Name: proto.String("job"), - Value: proto.String("batch_job"), - }, { - Name: proto.String("service"), - Value: proto.String("bar"), - }, - }, - Untyped: &dto.Untyped{ - Value: proto.Float64(24), - }, - }, - }, - } - - if !reflect.DeepEqual(v1, exp1) { - t.Fatalf("Expected %v, got %v", exp1, v1) - } - - var v2 dto.MetricFamily - if err := dec.Decode(&v2); err != nil { - t.Fatal(err) - } - - exp2 := dto.MetricFamily{ - Type: dto.MetricType_UNTYPED.Enum(), - Help: proto.String("RPC latency."), - Name: proto.String("rpc_latency_microseconds"), - Metric: []*dto.Metric{ - { - Label: []*dto.LabelPair{ - { - Name: proto.String("percentile"), - Value: proto.String("0.010000"), - }, { - Name: proto.String("service"), - Value: proto.String("foo"), - }, - }, - Untyped: &dto.Untyped{ - Value: proto.Float64(15), - }, - }, - { - Label: []*dto.LabelPair{ - { - Name: proto.String("percentile"), - Value: proto.String("0.990000"), - }, { - Name: proto.String("service"), - Value: proto.String("foo"), - }, - }, - Untyped: &dto.Untyped{ - Value: proto.Float64(17), - }, - }, - }, - } - - if !reflect.DeepEqual(v2, exp2) { - t.Fatalf("Expected %v, got %v", exp2, v2) - } - -} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/json2 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/json2 deleted file mode 100644 index b914c9386..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/json2 +++ /dev/null @@ -1,46 +0,0 @@ -[ - { - "baseLabels": { - "__name__": "rpc_calls_total", - "job": "batch_job" - }, - "docstring": "RPC calls.", - "metric": { - "type": "counter", - "value": [ - { - "labels": { - "service": "zed" - }, - "value": 25 - }, - { - "labels": { - "service": "bar" - }, - "value": 24 - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_latency_microseconds" - }, - "docstring": "RPC latency.", - "metric": { - "type": "histogram", - "value": [ - { - "labels": { - "service": "foo" - }, - "value": { - "0.010000": 15, - "0.990000": 17 - } - } - ] - } - } -] diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf deleted file mode 100644 index d5aae5091..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf +++ /dev/null @@ -1,516 +0,0 @@ -fc08 0a22 6874 7470 5f72 6571 7565 7374 -5f64 7572 6174 696f 6e5f 6d69 6372 6f73 -6563 6f6e 6473 122b 5468 6520 4854 5450 -2072 6571 7565 7374 206c 6174 656e 6369 -6573 2069 6e20 6d69 6372 6f73 6563 6f6e -6473 2e18 0222 570a 0c0a 0768 616e 646c -6572 1201 2f22 4708 0011 0000 0000 0000 -0000 1a12 0900 0000 0000 00e0 3f11 0000 -0000 0000 0000 1a12 09cd cccc cccc ccec -3f11 0000 0000 0000 0000 1a12 09ae 47e1 -7a14 aeef 3f11 0000 0000 0000 0000 225d -0a12 0a07 6861 6e64 6c65 7212 072f 616c -6572 7473 2247 0800 1100 0000 0000 0000 -001a 1209 0000 0000 0000 e03f 1100 0000 -0000 0000 001a 1209 cdcc cccc cccc ec3f -1100 0000 0000 0000 001a 1209 ae47 e17a -14ae ef3f 1100 0000 0000 0000 0022 620a -170a 0768 616e 646c 6572 120c 2f61 7069 -2f6d 6574 7269 6373 2247 0800 1100 0000 -0000 0000 001a 1209 0000 0000 0000 e03f -1100 0000 0000 0000 001a 1209 cdcc cccc -cccc ec3f 1100 0000 0000 0000 001a 1209 -ae47 e17a 14ae ef3f 1100 0000 0000 0000 -0022 600a 150a 0768 616e 646c 6572 120a -2f61 7069 2f71 7565 7279 2247 0800 1100 -0000 0000 0000 001a 1209 0000 0000 0000 -e03f 1100 0000 0000 0000 001a 1209 cdcc -cccc cccc ec3f 1100 0000 0000 0000 001a -1209 ae47 e17a 14ae ef3f 1100 0000 0000 -0000 0022 660a 1b0a 0768 616e 646c 6572 -1210 2f61 7069 2f71 7565 7279 5f72 616e -6765 2247 0800 1100 0000 0000 0000 001a -1209 0000 0000 0000 e03f 1100 0000 0000 -0000 001a 1209 cdcc cccc cccc ec3f 1100 -0000 0000 0000 001a 1209 ae47 e17a 14ae -ef3f 1100 0000 0000 0000 0022 620a 170a -0768 616e 646c 6572 120c 2f61 7069 2f74 -6172 6765 7473 2247 0800 1100 0000 0000 -0000 001a 1209 0000 0000 0000 e03f 1100 -0000 0000 0000 001a 1209 cdcc cccc cccc -ec3f 1100 0000 0000 0000 001a 1209 ae47 -e17a 14ae ef3f 1100 0000 0000 0000 0022 -600a 150a 0768 616e 646c 6572 120a 2f63 -6f6e 736f 6c65 732f 2247 0800 1100 0000 -0000 0000 001a 1209 0000 0000 0000 e03f -1100 0000 0000 0000 001a 1209 cdcc cccc -cccc ec3f 1100 0000 0000 0000 001a 1209 -ae47 e17a 14ae ef3f 1100 0000 0000 0000 -0022 5c0a 110a 0768 616e 646c 6572 1206 -2f67 7261 7068 2247 0800 1100 0000 0000 -0000 001a 1209 0000 0000 0000 e03f 1100 -0000 0000 0000 001a 1209 cdcc cccc cccc -ec3f 1100 0000 0000 0000 001a 1209 ae47 -e17a 14ae ef3f 1100 0000 0000 0000 0022 -5b0a 100a 0768 616e 646c 6572 1205 2f68 -6561 7022 4708 0011 0000 0000 0000 0000 -1a12 0900 0000 0000 00e0 3f11 0000 0000 -0000 0000 1a12 09cd cccc cccc ccec 3f11 -0000 0000 0000 0000 1a12 09ae 47e1 7a14 -aeef 3f11 0000 0000 0000 0000 225e 0a13 -0a07 6861 6e64 6c65 7212 082f 7374 6174 -6963 2f22 4708 0011 0000 0000 0000 0000 -1a12 0900 0000 0000 00e0 3f11 0000 0000 -0000 0000 1a12 09cd cccc cccc ccec 3f11 -0000 0000 0000 0000 1a12 09ae 47e1 7a14 -aeef 3f11 0000 0000 0000 0000 2260 0a15 -0a07 6861 6e64 6c65 7212 0a70 726f 6d65 -7468 6575 7322 4708 3b11 5b8f c2f5 083f -f440 1a12 0900 0000 0000 00e0 3f11 e17a -14ae c7af 9340 1a12 09cd cccc cccc ccec -3f11 2fdd 2406 81f0 9640 1a12 09ae 47e1 -7a14 aeef 3f11 3d0a d7a3 b095 a740 e608 -0a17 6874 7470 5f72 6571 7565 7374 5f73 -697a 655f 6279 7465 7312 2054 6865 2048 -5454 5020 7265 7175 6573 7420 7369 7a65 -7320 696e 2062 7974 6573 2e18 0222 570a -0c0a 0768 616e 646c 6572 1201 2f22 4708 -0011 0000 0000 0000 0000 1a12 0900 0000 -0000 00e0 3f11 0000 0000 0000 0000 1a12 -09cd cccc cccc ccec 3f11 0000 0000 0000 -0000 1a12 09ae 47e1 7a14 aeef 3f11 0000 -0000 0000 0000 225d 0a12 0a07 6861 6e64 -6c65 7212 072f 616c 6572 7473 2247 0800 -1100 0000 0000 0000 001a 1209 0000 0000 -0000 e03f 1100 0000 0000 0000 001a 1209 -cdcc cccc cccc ec3f 1100 0000 0000 0000 -001a 1209 ae47 e17a 14ae ef3f 1100 0000 -0000 0000 0022 620a 170a 0768 616e 646c -6572 120c 2f61 7069 2f6d 6574 7269 6373 -2247 0800 1100 0000 0000 0000 001a 1209 -0000 0000 0000 e03f 1100 0000 0000 0000 -001a 1209 cdcc cccc cccc ec3f 1100 0000 -0000 0000 001a 1209 ae47 e17a 14ae ef3f -1100 0000 0000 0000 0022 600a 150a 0768 -616e 646c 6572 120a 2f61 7069 2f71 7565 -7279 2247 0800 1100 0000 0000 0000 001a -1209 0000 0000 0000 e03f 1100 0000 0000 -0000 001a 1209 cdcc cccc cccc ec3f 1100 -0000 0000 0000 001a 1209 ae47 e17a 14ae -ef3f 1100 0000 0000 0000 0022 660a 1b0a -0768 616e 646c 6572 1210 2f61 7069 2f71 -7565 7279 5f72 616e 6765 2247 0800 1100 -0000 0000 0000 001a 1209 0000 0000 0000 -e03f 1100 0000 0000 0000 001a 1209 cdcc -cccc cccc ec3f 1100 0000 0000 0000 001a -1209 ae47 e17a 14ae ef3f 1100 0000 0000 -0000 0022 620a 170a 0768 616e 646c 6572 -120c 2f61 7069 2f74 6172 6765 7473 2247 -0800 1100 0000 0000 0000 001a 1209 0000 -0000 0000 e03f 1100 0000 0000 0000 001a -1209 cdcc cccc cccc ec3f 1100 0000 0000 -0000 001a 1209 ae47 e17a 14ae ef3f 1100 -0000 0000 0000 0022 600a 150a 0768 616e -646c 6572 120a 2f63 6f6e 736f 6c65 732f -2247 0800 1100 0000 0000 0000 001a 1209 -0000 0000 0000 e03f 1100 0000 0000 0000 -001a 1209 cdcc cccc cccc ec3f 1100 0000 -0000 0000 001a 1209 ae47 e17a 14ae ef3f -1100 0000 0000 0000 0022 5c0a 110a 0768 -616e 646c 6572 1206 2f67 7261 7068 2247 -0800 1100 0000 0000 0000 001a 1209 0000 -0000 0000 e03f 1100 0000 0000 0000 001a -1209 cdcc cccc cccc ec3f 1100 0000 0000 -0000 001a 1209 ae47 e17a 14ae ef3f 1100 -0000 0000 0000 0022 5b0a 100a 0768 616e -646c 6572 1205 2f68 6561 7022 4708 0011 -0000 0000 0000 0000 1a12 0900 0000 0000 -00e0 3f11 0000 0000 0000 0000 1a12 09cd -cccc cccc ccec 3f11 0000 0000 0000 0000 -1a12 09ae 47e1 7a14 aeef 3f11 0000 0000 -0000 0000 225e 0a13 0a07 6861 6e64 6c65 -7212 082f 7374 6174 6963 2f22 4708 0011 -0000 0000 0000 0000 1a12 0900 0000 0000 -00e0 3f11 0000 0000 0000 0000 1a12 09cd -cccc cccc ccec 3f11 0000 0000 0000 0000 -1a12 09ae 47e1 7a14 aeef 3f11 0000 0000 -0000 0000 2260 0a15 0a07 6861 6e64 6c65 -7212 0a70 726f 6d65 7468 6575 7322 4708 -3b11 0000 0000 40c4 d040 1a12 0900 0000 -0000 00e0 3f11 0000 0000 0030 7240 1a12 -09cd cccc cccc ccec 3f11 0000 0000 0030 -7240 1a12 09ae 47e1 7a14 aeef 3f11 0000 -0000 0030 7240 7c0a 1368 7474 705f 7265 -7175 6573 7473 5f74 6f74 616c 1223 546f -7461 6c20 6e75 6d62 6572 206f 6620 4854 -5450 2072 6571 7565 7374 7320 6d61 6465 -2e18 0022 3e0a 0b0a 0463 6f64 6512 0332 -3030 0a15 0a07 6861 6e64 6c65 7212 0a70 -726f 6d65 7468 6575 730a 0d0a 066d 6574 -686f 6412 0367 6574 1a09 0900 0000 0000 -804d 40e8 080a 1868 7474 705f 7265 7370 -6f6e 7365 5f73 697a 655f 6279 7465 7312 -2154 6865 2048 5454 5020 7265 7370 6f6e -7365 2073 697a 6573 2069 6e20 6279 7465 -732e 1802 2257 0a0c 0a07 6861 6e64 6c65 -7212 012f 2247 0800 1100 0000 0000 0000 -001a 1209 0000 0000 0000 e03f 1100 0000 -0000 0000 001a 1209 cdcc cccc cccc ec3f -1100 0000 0000 0000 001a 1209 ae47 e17a -14ae ef3f 1100 0000 0000 0000 0022 5d0a -120a 0768 616e 646c 6572 1207 2f61 6c65 -7274 7322 4708 0011 0000 0000 0000 0000 -1a12 0900 0000 0000 00e0 3f11 0000 0000 -0000 0000 1a12 09cd cccc cccc ccec 3f11 -0000 0000 0000 0000 1a12 09ae 47e1 7a14 -aeef 3f11 0000 0000 0000 0000 2262 0a17 -0a07 6861 6e64 6c65 7212 0c2f 6170 692f -6d65 7472 6963 7322 4708 0011 0000 0000 -0000 0000 1a12 0900 0000 0000 00e0 3f11 -0000 0000 0000 0000 1a12 09cd cccc cccc -ccec 3f11 0000 0000 0000 0000 1a12 09ae -47e1 7a14 aeef 3f11 0000 0000 0000 0000 -2260 0a15 0a07 6861 6e64 6c65 7212 0a2f -6170 692f 7175 6572 7922 4708 0011 0000 -0000 0000 0000 1a12 0900 0000 0000 00e0 -3f11 0000 0000 0000 0000 1a12 09cd cccc -cccc ccec 3f11 0000 0000 0000 0000 1a12 -09ae 47e1 7a14 aeef 3f11 0000 0000 0000 -0000 2266 0a1b 0a07 6861 6e64 6c65 7212 -102f 6170 692f 7175 6572 795f 7261 6e67 -6522 4708 0011 0000 0000 0000 0000 1a12 -0900 0000 0000 00e0 3f11 0000 0000 0000 -0000 1a12 09cd cccc cccc ccec 3f11 0000 -0000 0000 0000 1a12 09ae 47e1 7a14 aeef -3f11 0000 0000 0000 0000 2262 0a17 0a07 -6861 6e64 6c65 7212 0c2f 6170 692f 7461 -7267 6574 7322 4708 0011 0000 0000 0000 -0000 1a12 0900 0000 0000 00e0 3f11 0000 -0000 0000 0000 1a12 09cd cccc cccc ccec -3f11 0000 0000 0000 0000 1a12 09ae 47e1 -7a14 aeef 3f11 0000 0000 0000 0000 2260 -0a15 0a07 6861 6e64 6c65 7212 0a2f 636f -6e73 6f6c 6573 2f22 4708 0011 0000 0000 -0000 0000 1a12 0900 0000 0000 00e0 3f11 -0000 0000 0000 0000 1a12 09cd cccc cccc -ccec 3f11 0000 0000 0000 0000 1a12 09ae -47e1 7a14 aeef 3f11 0000 0000 0000 0000 -225c 0a11 0a07 6861 6e64 6c65 7212 062f -6772 6170 6822 4708 0011 0000 0000 0000 -0000 1a12 0900 0000 0000 00e0 3f11 0000 -0000 0000 0000 1a12 09cd cccc cccc ccec -3f11 0000 0000 0000 0000 1a12 09ae 47e1 -7a14 aeef 3f11 0000 0000 0000 0000 225b -0a10 0a07 6861 6e64 6c65 7212 052f 6865 -6170 2247 0800 1100 0000 0000 0000 001a -1209 0000 0000 0000 e03f 1100 0000 0000 -0000 001a 1209 cdcc cccc cccc ec3f 1100 -0000 0000 0000 001a 1209 ae47 e17a 14ae -ef3f 1100 0000 0000 0000 0022 5e0a 130a -0768 616e 646c 6572 1208 2f73 7461 7469 -632f 2247 0800 1100 0000 0000 0000 001a -1209 0000 0000 0000 e03f 1100 0000 0000 -0000 001a 1209 cdcc cccc cccc ec3f 1100 -0000 0000 0000 001a 1209 ae47 e17a 14ae -ef3f 1100 0000 0000 0000 0022 600a 150a -0768 616e 646c 6572 120a 7072 6f6d 6574 -6865 7573 2247 083b 1100 0000 00e0 b4fc -401a 1209 0000 0000 0000 e03f 1100 0000 -0000 349f 401a 1209 cdcc cccc cccc ec3f -1100 0000 0000 08a0 401a 1209 ae47 e17a -14ae ef3f 1100 0000 0000 0aa0 405c 0a19 -7072 6f63 6573 735f 6370 755f 7365 636f -6e64 735f 746f 7461 6c12 3054 6f74 616c -2075 7365 7220 616e 6420 7379 7374 656d -2043 5055 2074 696d 6520 7370 656e 7420 -696e 2073 6563 6f6e 6473 2e18 0022 0b1a -0909 a470 3d0a d7a3 d03f 4f0a 1270 726f -6365 7373 5f67 6f72 6f75 7469 6e65 7312 -2a4e 756d 6265 7220 6f66 2067 6f72 6f75 -7469 6e65 7320 7468 6174 2063 7572 7265 -6e74 6c79 2065 7869 7374 2e18 0122 0b12 -0909 0000 0000 0000 5140 4a0a 0f70 726f -6365 7373 5f6d 6178 5f66 6473 1228 4d61 -7869 6d75 6d20 6e75 6d62 6572 206f 6620 -6f70 656e 2066 696c 6520 6465 7363 7269 -7074 6f72 732e 1801 220b 1209 0900 0000 -0000 00c0 4043 0a10 7072 6f63 6573 735f -6f70 656e 5f66 6473 1220 4e75 6d62 6572 -206f 6620 6f70 656e 2066 696c 6520 6465 -7363 7269 7074 6f72 732e 1801 220b 1209 -0900 0000 0000 003d 404e 0a1d 7072 6f63 -6573 735f 7265 7369 6465 6e74 5f6d 656d -6f72 795f 6279 7465 7312 1e52 6573 6964 -656e 7420 6d65 6d6f 7279 2073 697a 6520 -696e 2062 7974 6573 2e18 0122 0b12 0909 -0000 0000 004b 8841 630a 1a70 726f 6365 -7373 5f73 7461 7274 5f74 696d 655f 7365 -636f 6e64 7312 3653 7461 7274 2074 696d -6520 6f66 2074 6865 2070 726f 6365 7373 -2073 696e 6365 2075 6e69 7820 6570 6f63 -6820 696e 2073 6563 6f6e 6473 2e18 0122 -0b12 0909 3d0a 172d e831 d541 4c0a 1c70 -726f 6365 7373 5f76 6972 7475 616c 5f6d -656d 6f72 795f 6279 7465 7312 1d56 6972 -7475 616c 206d 656d 6f72 7920 7369 7a65 -2069 6e20 6279 7465 732e 1801 220b 1209 -0900 0000 0020 12c0 415f 0a27 7072 6f6d -6574 6865 7573 5f64 6e73 5f73 645f 6c6f -6f6b 7570 5f66 6169 6c75 7265 735f 746f -7461 6c12 2554 6865 206e 756d 6265 7220 -6f66 2044 4e53 2d53 4420 6c6f 6f6b 7570 -2066 6169 6c75 7265 732e 1800 220b 1a09 -0900 0000 0000 0000 004f 0a1f 7072 6f6d -6574 6865 7573 5f64 6e73 5f73 645f 6c6f -6f6b 7570 735f 746f 7461 6c12 1d54 6865 -206e 756d 6265 7220 6f66 2044 4e53 2d53 -4420 6c6f 6f6b 7570 732e 1800 220b 1a09 -0900 0000 0000 0008 40cf 010a 2a70 726f -6d65 7468 6575 735f 6576 616c 7561 746f -725f 6475 7261 7469 6f6e 5f6d 696c 6c69 -7365 636f 6e64 7312 2c54 6865 2064 7572 -6174 696f 6e20 666f 7220 616c 6c20 6576 -616c 7561 7469 6f6e 7320 746f 2065 7865 -6375 7465 2e18 0222 7122 6f08 0b11 0000 -0000 0000 2240 1a12 097b 14ae 47e1 7a84 -3f11 0000 0000 0000 0000 1a12 099a 9999 -9999 99a9 3f11 0000 0000 0000 0000 1a12 -0900 0000 0000 00e0 3f11 0000 0000 0000 -0000 1a12 09cd cccc cccc ccec 3f11 0000 -0000 0000 f03f 1a12 09ae 47e1 7a14 aeef -3f11 0000 0000 0000 f03f a301 0a39 7072 -6f6d 6574 6865 7573 5f6c 6f63 616c 5f73 -746f 7261 6765 5f63 6865 636b 706f 696e -745f 6475 7261 7469 6f6e 5f6d 696c 6c69 -7365 636f 6e64 7312 5754 6865 2064 7572 -6174 696f 6e20 2869 6e20 6d69 6c6c 6973 -6563 6f6e 6473 2920 6974 2074 6f6f 6b20 -746f 2063 6865 636b 706f 696e 7420 696e -2d6d 656d 6f72 7920 6d65 7472 6963 7320 -616e 6420 6865 6164 2063 6875 6e6b 732e -1801 220b 1209 0900 0000 0000 0000 00f2 -010a 2870 726f 6d65 7468 6575 735f 6c6f -6361 6c5f 7374 6f72 6167 655f 6368 756e -6b5f 6f70 735f 746f 7461 6c12 3354 6865 -2074 6f74 616c 206e 756d 6265 7220 6f66 -2063 6875 6e6b 206f 7065 7261 7469 6f6e -7320 6279 2074 6865 6972 2074 7970 652e -1800 221b 0a0e 0a04 7479 7065 1206 6372 -6561 7465 1a09 0900 0000 0000 b880 4022 -1c0a 0f0a 0474 7970 6512 0770 6572 7369 -7374 1a09 0900 0000 0000 c05b 4022 180a -0b0a 0474 7970 6512 0370 696e 1a09 0900 -0000 0000 807b 4022 1e0a 110a 0474 7970 -6512 0974 7261 6e73 636f 6465 1a09 0900 -0000 0000 a06b 4022 1a0a 0d0a 0474 7970 -6512 0575 6e70 696e 1a09 0900 0000 0000 -807b 40c4 010a 3c70 726f 6d65 7468 6575 -735f 6c6f 6361 6c5f 7374 6f72 6167 655f -696e 6465 7869 6e67 5f62 6174 6368 5f6c -6174 656e 6379 5f6d 696c 6c69 7365 636f -6e64 7312 3751 7561 6e74 696c 6573 2066 -6f72 2062 6174 6368 2069 6e64 6578 696e -6720 6c61 7465 6e63 6965 7320 696e 206d -696c 6c69 7365 636f 6e64 732e 1802 2249 -2247 0801 1100 0000 0000 0000 001a 1209 -0000 0000 0000 e03f 1100 0000 0000 0000 -001a 1209 cdcc cccc cccc ec3f 1100 0000 -0000 0000 001a 1209 ae47 e17a 14ae ef3f -1100 0000 0000 0000 00bf 010a 2d70 726f -6d65 7468 6575 735f 6c6f 6361 6c5f 7374 -6f72 6167 655f 696e 6465 7869 6e67 5f62 -6174 6368 5f73 697a 6573 1241 5175 616e -7469 6c65 7320 666f 7220 696e 6465 7869 -6e67 2062 6174 6368 2073 697a 6573 2028 -6e75 6d62 6572 206f 6620 6d65 7472 6963 -7320 7065 7220 6261 7463 6829 2e18 0222 -4922 4708 0111 0000 0000 0000 0040 1a12 -0900 0000 0000 00e0 3f11 0000 0000 0000 -0040 1a12 09cd cccc cccc ccec 3f11 0000 -0000 0000 0040 1a12 09ae 47e1 7a14 aeef -3f11 0000 0000 0000 0040 660a 3070 726f -6d65 7468 6575 735f 6c6f 6361 6c5f 7374 -6f72 6167 655f 696e 6465 7869 6e67 5f71 -7565 7565 5f63 6170 6163 6974 7912 2354 -6865 2063 6170 6163 6974 7920 6f66 2074 -6865 2069 6e64 6578 696e 6720 7175 6575 -652e 1801 220b 1209 0900 0000 0000 00d0 -406d 0a2e 7072 6f6d 6574 6865 7573 5f6c -6f63 616c 5f73 746f 7261 6765 5f69 6e64 -6578 696e 675f 7175 6575 655f 6c65 6e67 -7468 122c 5468 6520 6e75 6d62 6572 206f -6620 6d65 7472 6963 7320 7761 6974 696e -6720 746f 2062 6520 696e 6465 7865 642e -1801 220b 1209 0900 0000 0000 0000 0067 -0a2f 7072 6f6d 6574 6865 7573 5f6c 6f63 -616c 5f73 746f 7261 6765 5f69 6e67 6573 -7465 645f 7361 6d70 6c65 735f 746f 7461 -6c12 2554 6865 2074 6f74 616c 206e 756d -6265 7220 6f66 2073 616d 706c 6573 2069 -6e67 6573 7465 642e 1800 220b 1a09 0900 -0000 0080 27cd 40c3 010a 3770 726f 6d65 -7468 6575 735f 6c6f 6361 6c5f 7374 6f72 -6167 655f 696e 7661 6c69 645f 7072 656c -6f61 645f 7265 7175 6573 7473 5f74 6f74 -616c 1279 5468 6520 746f 7461 6c20 6e75 -6d62 6572 206f 6620 7072 656c 6f61 6420 -7265 7175 6573 7473 2072 6566 6572 7269 -6e67 2074 6f20 6120 6e6f 6e2d 6578 6973 -7465 6e74 2073 6572 6965 732e 2054 6869 -7320 6973 2061 6e20 696e 6469 6361 7469 -6f6e 206f 6620 6f75 7464 6174 6564 206c -6162 656c 2069 6e64 6578 6573 2e18 0022 -0b1a 0909 0000 0000 0000 0000 6f0a 2a70 -726f 6d65 7468 6575 735f 6c6f 6361 6c5f -7374 6f72 6167 655f 6d65 6d6f 7279 5f63 -6875 6e6b 6465 7363 7312 3254 6865 2063 -7572 7265 6e74 206e 756d 6265 7220 6f66 -2063 6875 6e6b 2064 6573 6372 6970 746f -7273 2069 6e20 6d65 6d6f 7279 2e18 0122 -0b12 0909 0000 0000 0020 8f40 9c01 0a26 -7072 6f6d 6574 6865 7573 5f6c 6f63 616c -5f73 746f 7261 6765 5f6d 656d 6f72 795f -6368 756e 6b73 1263 5468 6520 6375 7272 -656e 7420 6e75 6d62 6572 206f 6620 6368 -756e 6b73 2069 6e20 6d65 6d6f 7279 2c20 -6578 636c 7564 696e 6720 636c 6f6e 6564 -2063 6875 6e6b 7320 2869 2e65 2e20 6368 -756e 6b73 2077 6974 686f 7574 2061 2064 -6573 6372 6970 746f 7229 2e18 0122 0b12 -0909 0000 0000 00e8 8d40 600a 2670 726f -6d65 7468 6575 735f 6c6f 6361 6c5f 7374 -6f72 6167 655f 6d65 6d6f 7279 5f73 6572 -6965 7312 2754 6865 2063 7572 7265 6e74 -206e 756d 6265 7220 6f66 2073 6572 6965 -7320 696e 206d 656d 6f72 792e 1801 220b -1209 0900 0000 0000 807a 40b7 010a 3570 -726f 6d65 7468 6575 735f 6c6f 6361 6c5f -7374 6f72 6167 655f 7065 7273 6973 745f -6c61 7465 6e63 795f 6d69 6372 6f73 6563 -6f6e 6473 1231 4120 7375 6d6d 6172 7920 -6f66 206c 6174 656e 6369 6573 2066 6f72 -2070 6572 7369 7374 696e 6720 6561 6368 -2063 6875 6e6b 2e18 0222 4922 4708 6f11 -1c2f dd24 e68c cc40 1a12 0900 0000 0000 -00e0 3f11 8d97 6e12 8360 3e40 1a12 09cd -cccc cccc ccec 3f11 0ad7 a370 3d62 6b40 -1a12 09ae 47e1 7a14 aeef 3f11 7b14 ae47 -e1b6 7240 6a0a 2f70 726f 6d65 7468 6575 -735f 6c6f 6361 6c5f 7374 6f72 6167 655f -7065 7273 6973 745f 7175 6575 655f 6361 -7061 6369 7479 1228 5468 6520 746f 7461 -6c20 6361 7061 6369 7479 206f 6620 7468 -6520 7065 7273 6973 7420 7175 6575 652e -1801 220b 1209 0900 0000 0000 0090 407a -0a2d 7072 6f6d 6574 6865 7573 5f6c 6f63 -616c 5f73 746f 7261 6765 5f70 6572 7369 -7374 5f71 7565 7565 5f6c 656e 6774 6812 -3a54 6865 2063 7572 7265 6e74 206e 756d -6265 7220 6f66 2063 6875 6e6b 7320 7761 -6974 696e 6720 696e 2074 6865 2070 6572 -7369 7374 2071 7565 7565 2e18 0122 0b12 -0909 0000 0000 0000 0000 ac01 0a29 7072 -6f6d 6574 6865 7573 5f6c 6f63 616c 5f73 -746f 7261 6765 5f73 6572 6965 735f 6f70 -735f 746f 7461 6c12 3454 6865 2074 6f74 -616c 206e 756d 6265 7220 6f66 2073 6572 -6965 7320 6f70 6572 6174 696f 6e73 2062 -7920 7468 6569 7220 7479 7065 2e18 0022 -1b0a 0e0a 0474 7970 6512 0663 7265 6174 -651a 0909 0000 0000 0000 0040 222a 0a1d -0a04 7479 7065 1215 6d61 696e 7465 6e61 -6e63 655f 696e 5f6d 656d 6f72 791a 0909 -0000 0000 0000 1440 d601 0a2d 7072 6f6d -6574 6865 7573 5f6e 6f74 6966 6963 6174 -696f 6e73 5f6c 6174 656e 6379 5f6d 696c -6c69 7365 636f 6e64 7312 584c 6174 656e -6379 2071 7561 6e74 696c 6573 2066 6f72 -2073 656e 6469 6e67 2061 6c65 7274 206e -6f74 6966 6963 6174 696f 6e73 2028 6e6f -7420 696e 636c 7564 696e 6720 6472 6f70 -7065 6420 6e6f 7469 6669 6361 7469 6f6e -7329 2e18 0222 4922 4708 0011 0000 0000 -0000 0000 1a12 0900 0000 0000 00e0 3f11 -0000 0000 0000 0000 1a12 09cd cccc cccc -ccec 3f11 0000 0000 0000 0000 1a12 09ae -47e1 7a14 aeef 3f11 0000 0000 0000 0000 -680a 2770 726f 6d65 7468 6575 735f 6e6f -7469 6669 6361 7469 6f6e 735f 7175 6575 -655f 6361 7061 6369 7479 122e 5468 6520 -6361 7061 6369 7479 206f 6620 7468 6520 -616c 6572 7420 6e6f 7469 6669 6361 7469 -6f6e 7320 7175 6575 652e 1801 220b 1209 -0900 0000 0000 0059 4067 0a25 7072 6f6d -6574 6865 7573 5f6e 6f74 6966 6963 6174 -696f 6e73 5f71 7565 7565 5f6c 656e 6774 -6812 2f54 6865 206e 756d 6265 7220 6f66 -2061 6c65 7274 206e 6f74 6966 6963 6174 -696f 6e73 2069 6e20 7468 6520 7175 6575 -652e 1801 220b 1209 0900 0000 0000 0000 -009e 020a 3070 726f 6d65 7468 6575 735f -7275 6c65 5f65 7661 6c75 6174 696f 6e5f -6475 7261 7469 6f6e 5f6d 696c 6c69 7365 -636f 6e64 7312 2354 6865 2064 7572 6174 -696f 6e20 666f 7220 6120 7275 6c65 2074 -6f20 6578 6563 7574 652e 1802 2260 0a15 -0a09 7275 6c65 5f74 7970 6512 0861 6c65 -7274 696e 6722 4708 3711 0000 0000 0000 -2840 1a12 0900 0000 0000 00e0 3f11 0000 -0000 0000 0000 1a12 09cd cccc cccc ccec -3f11 0000 0000 0000 0000 1a12 09ae 47e1 -7a14 aeef 3f11 0000 0000 0000 0840 2261 -0a16 0a09 7275 6c65 5f74 7970 6512 0972 -6563 6f72 6469 6e67 2247 0837 1100 0000 -0000 002e 401a 1209 0000 0000 0000 e03f -1100 0000 0000 0000 001a 1209 cdcc cccc -cccc ec3f 1100 0000 0000 0000 001a 1209 -ae47 e17a 14ae ef3f 1100 0000 0000 0008 -4069 0a29 7072 6f6d 6574 6865 7573 5f72 -756c 655f 6576 616c 7561 7469 6f6e 5f66 -6169 6c75 7265 735f 746f 7461 6c12 2d54 -6865 2074 6f74 616c 206e 756d 6265 7220 -6f66 2072 756c 6520 6576 616c 7561 7469 -6f6e 2066 6169 6c75 7265 732e 1800 220b -1a09 0900 0000 0000 0000 0060 0a21 7072 -6f6d 6574 6865 7573 5f73 616d 706c 6573 -5f71 7565 7565 5f63 6170 6163 6974 7912 -2c43 6170 6163 6974 7920 6f66 2074 6865 -2071 7565 7565 2066 6f72 2075 6e77 7269 -7474 656e 2073 616d 706c 6573 2e18 0122 -0b12 0909 0000 0000 0000 b040 da01 0a1f -7072 6f6d 6574 6865 7573 5f73 616d 706c -6573 5f71 7565 7565 5f6c 656e 6774 6812 -a701 4375 7272 656e 7420 6e75 6d62 6572 -206f 6620 6974 656d 7320 696e 2074 6865 -2071 7565 7565 2066 6f72 2075 6e77 7269 -7474 656e 2073 616d 706c 6573 2e20 4561 -6368 2069 7465 6d20 636f 6d70 7269 7365 -7320 616c 6c20 7361 6d70 6c65 7320 6578 -706f 7365 6420 6279 206f 6e65 2074 6172 -6765 7420 6173 206f 6e65 206d 6574 7269 -6320 6661 6d69 6c79 2028 692e 652e 206d -6574 7269 6373 206f 6620 7468 6520 7361 -6d65 206e 616d 6529 2e18 0122 0b12 0909 -0000 0000 0000 0000 d902 0a29 7072 6f6d -6574 6865 7573 5f74 6172 6765 745f 696e -7465 7276 616c 5f6c 656e 6774 685f 7365 -636f 6e64 7312 2141 6374 7561 6c20 696e -7465 7276 616c 7320 6265 7477 6565 6e20 -7363 7261 7065 732e 1802 2282 010a 0f0a -0869 6e74 6572 7661 6c12 0331 3573 226f -0804 1100 0000 0000 804d 401a 1209 7b14 -ae47 e17a 843f 1100 0000 0000 002c 401a -1209 9a99 9999 9999 a93f 1100 0000 0000 -002c 401a 1209 0000 0000 0000 e03f 1100 -0000 0000 002e 401a 1209 cdcc cccc cccc -ec3f 1100 0000 0000 002e 401a 1209 ae47 -e17a 14ae ef3f 1100 0000 0000 002e 4022 -8101 0a0e 0a08 696e 7465 7276 616c 1202 -3173 226f 083a 1100 0000 0000 003c 401a -1209 7b14 ae47 e17a 843f 1100 0000 0000 -0000 001a 1209 9a99 9999 9999 a93f 1100 -0000 0000 0000 001a 1209 0000 0000 0000 -e03f 1100 0000 0000 0000 001a 1209 cdcc -cccc cccc ec3f 1100 0000 0000 00f0 3f1a -1209 ae47 e17a 14ae ef3f 1100 0000 0000 -00f0 3f \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf.gz b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf.gz deleted file mode 100644 index 62fccb616..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf.gz +++ /dev/null @@ -1,129 +0,0 @@ -1f8b 0808 efa0 c754 0003 7072 6f74 6f62 -7566 00ed 594d 8c1c c515 9eb1 8d3d 5b86 -6037 265e 8c4d ca03 c4bb ceee cc9a 9f58 -01cc f6ca 4424 041b 8837 21c8 24ed daee -9a99 cef6 1f55 d578 c7e4 b004 0e39 8088 -8448 048a 124b 4442 9110 e110 25b9 c54a -9072 01c5 9724 4a24 2472 413e 448a 8592 -1b87 bcea aeda eeea 99d9 3530 49a4 68e7 -b0bb 5355 fdde abf7 bef7 bdf7 7a3f 6ca0 -664f 88c4 61f4 8994 72e1 7829 23c2 8f23 -27f4 5d16 73ea c691 c7ad cf2d f628 fed2 -e2e2 c358 9dc3 0111 3472 7dca b11f e1f2 -d9d6 e496 e6a3 e86a b4a3 4722 2fa0 ccaa -b79b f737 6abb 6bea b3cf 9ac8 ff78 6fbe -bcf6 cedb f2f3 7763 ed8d fbff 766e cf1b -ff28 d69a df44 5621 7847 9bc0 2fc1 c727 -7e09 ed2d c45f dd26 89df 0ea9 60be 3b46 -1d67 d0f5 850e 94e9 008f b2fe f834 74d0 -8d85 865d 8506 8791 a84b ffa3 de12 8475 -e938 2352 f116 208c c701 e563 84d4 e368 -77a1 617b bbcb 48d2 1b9f f4d3 6857 21fd -aa76 8f92 647c c2bf 85ae 2b84 37da 5c40 -e6ba 6374 8de9 fc84 c590 0c3d 9aca f0de -bdfb f40b bffd 5763 fe9f 7659 8314 f0fb -9fbf 6897 35b4 dfbd 65fb d397 7f60 9735 -1c43 7f7e f5cd 975e b3df 6fa0 bd06 fb70 -ff1c 7596 fa82 720b 0f50 8edc cce8 263b -b0c9 339b 3cb3 c933 5afa ff2f cfc8 13f6 -5b17 ed01 0d73 cc1e d090 af99 1a60 ed3b -e8ba 32cd 7047 c482 04d6 cd8b f217 8ed2 -7089 321c 770c bae1 3824 1e6d 4dd6 9af7 -a29d 689b 1b7b d4da 7adb dcdc 085b d135 -68bb fc33 f6ac ad00 cd7d 13b9 b5ab 27ec -4b0d 34a9 b4f3 0470 45cb 2c77 b0c4 72f9 -ee26 cd7d 02ec 6cd2 dc26 cd7d 6ce1 ff73 -9a7b ef17 1f0e d2dc 1d3f 19a4 b9c6 f941 -9a43 e7ed c7d1 0d20 d5a5 9c3b 6e92 3a6a -2053 6437 9793 5dca 81ea c006 ccfb 5cd0 -101f 7ff8 6b58 f821 d04e 4223 2169 676d -8eab 3577 028d fd34 91dd dac5 f987 90a5 -8577 6316 a7c2 8f80 bf0e 9f5c 23cf 6215 -8b1e 11d8 4d19 0391 411f d315 9f8b d664 -bdb9 d352 b458 7bc4 7e00 5dab e585 64c5 -e9c0 9439 7582 acf8 611a 9618 3906 ab70 -c70f 28f6 2877 999f 8898 7153 d405 fb38 -daa5 45c9 f399 2c7c f2a3 c838 669f 4407 -b40c 6062 df03 cb9d 9086 31e4 79ce d437 -7d55 2de3 7c39 e3e9 124d 97c4 7de5 7b0b -2eda a7c5 018e 9870 a48f 7544 accf 9f92 -6bb9 dfc1 4040 0156 a741 6ae4 529c 46fe -0aa6 49ec f68c 88e4 3a8e a1bd b397 8efc -71e1 41b4 5feb 78d2 6722 2581 69f1 81af -e7ab 1b1a 8cad 0b0b 0e3a 5420 d2f1 22b0 -db73 8238 5e4e 13a7 43fc 2005 af28 24dd -2a6b 5611 a2fb 4e9e 9a3d 751f cecf 627d -56c3 47a3 ff21 f499 51f2 b5dc 03eb c8ad -c86b d87f a8a3 c325 81f4 4912 a404 025b -7e81 1104 bef6 f88c 94ad b770 2786 1c08 -02ac 9e82 25c0 6c0c 38a5 6e2a a82c b94f -34e3 c64e 95ba 4d99 6c4f ed91 e9f6 ac91 -e2af bc2c 3f3f 9bff 88f4 7079 7e90 1e2e -cfbf 5a47 5f28 5d28 885d 8827 871b 912e -75dc 1e75 9793 d88f c488 fb3d 6adc 6f2a -7b27 536c 4f63 1fd0 068e 94b7 2c64 0118 -6615 3654 5dce 9801 58d5 8353 69b4 5cc9 -925a ed83 3a9a 5ac7 4878 0432 50c7 f376 -6993 a8b4 58d9 2199 924c f97d a92f f1ef -332c fa49 d66e dd88 3e85 b6c9 2fd6 7697 -5122 a88e faaf 57ed e67e 74ad dadc 0122 -38f0 8ade bd70 da6e 4eca 4e2d dbdd 9af8 -d15a 0ff6 94dd bc09 ca52 be33 21a0 6e73 -d9ce e9fd f3cb 7673 1ff4 6ff9 fe55 6964 -3efb 561d dd33 f2ce 7ee4 01bb 455d 6789 -08b7 e7e4 6fc5 fa66 6c8e 3e92 9248 00ff -f00c 78d9 49ac 1fac be48 2b9e 9330 fc32 -d486 fa58 aacf 6fea 68f6 4a6f 9175 a0d6 -8269 f69a c1b9 fd79 973a 5504 5623 08c2 -921f 991e b8c0 6071 cbd7 aa17 182c 6eb0 -d641 731b db0f 8d59 0a40 2409 717d d187 -061f 10a8 bf69 a65d bb48 76d8 44f8 453b -44ad 2b55 13d0 a82b 7a39 b50c fae1 2cf1 -85d4 0219 b7a4 9452 af9a 4f5d d45e 475b -17c6 10ea 399c 8449 60b2 6f35 abd4 11ac -9f29 b3e5 eaa1 77ec dfd5 d1d1 7514 010d -fa9e 9330 1ac4 c4ab 4e49 fd61 0ad5 d962 -5862 b443 1953 1726 388a a3d9 acec cb82 -092d 07e0 bb85 177b 3e98 2849 46fa c377 -73b2 9215 3a15 1ea4 8107 c9b0 4403 e5ac -8112 121b 8c6f de41 15be 8c5d 6495 e7d6 -6d59 ecf3 1e64 807f 4a8d 4096 76d9 d346 -70f0 0bf6 8fea e8b3 57a4 905b ee3a ca4a -1a66 a0c4 b841 ea49 37b9 411c 51cd b3c0 -d82d dad2 5fce fa30 47a6 02dc 58d8 396d -5877 e979 fbcc c6c6 e57e b70e 0d37 2edf -1d71 fdd5 73f6 afea e8ce 911a 14f9 9608 -aff4 df82 230b 98a7 6148 5896 7305 c149 -1a51 0f4a 0f50 023c 925d 5933 45bc 7b7f -fbdd 5bde 7fee 6d83 299e ff61 643d 73e6 -5e83 29a0 254d 8e2d 2d1b 4c91 95e8 5f32 -fbdb eb24 95b6 bb42 1453 05c6 ab74 a19e -18c6 16df b7cf ad43 aaa6 2a45 1677 ad0b -14cd 1910 930d 54d7 6aaf d7d1 f448 dd79 -6c4b b5f8 8ea1 ac91 23e0 6315 6360 e4e6 -6174 406d 5e1f 12e8 2768 44a0 7905 3e51 -005c 3bbb c7fe 9359 7ea2 58f8 1d45 007c -78d5 fcc6 83f9 2adc be5c 8638 8db2 f4c9 -de55 6043 0e54 a358 f634 3ac3 3c16 2709 -a498 7168 ad2a 8d67 a8eb 196d b379 ad0a -c65a c38a d1b0 6b0c 09f7 6376 17dd ba81 -2285 b0b6 598e 8629 50f0 1a0a ab1f 6f31 -ea2c 4b03 ea14 6df2 88ee f3e6 c1ee 1acb -272b 4db5 1c80 2732 8919 681a 996d 1029 -88c6 51e5 d1a9 613d c215 46a3 6137 09fa -7459 c304 0303 9967 aa68 7d22 15be 9175 -55f7 5426 a5d9 6159 9739 a678 66e4 c474 -061d 2c69 d24d 4005 5433 c72b 80ca f6b3 -10a4 d159 e60b c821 dd1d 98a1 7ed3 fe6b -dd98 c94c 0d0a 4daf d58f 0f90 952f 6868 -8268 843e fc45 c9f0 f238 76e3 3061 8017 -9ecd 5dba 5da1 2b09 140d 4fd2 0e14 439c -bfee c284 67df f246 0adc 0350 ebab 02a9 -9b2b 7559 9003 5887 1fd3 5518 ff65 8b11 -a75c b223 398a 81e7 d5ed d6e6 f183 0b6e -3628 eb7d 2042 2ace 5279 1597 9124 7f0b -fbdd 3acc 1e0d 7dc4 da7a e44e 0e43 e2b6 -1c19 ab27 860c 8933 f6e0 9038 3304 7dad -214d 706b 4813 dcb2 9b4f d781 900b 23b6 -1c91 36dc a5f6 eff9 af0c aaff 06f1 48e5 -4433 2000 00 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/test.gz b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/test.gz deleted file mode 100644 index 3f8199dfb..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/test.gz +++ /dev/null @@ -1,163 +0,0 @@ -1f8b 0808 2aa1 c754 0003 7465 7874 00b5 -5b5d 939b 3816 7def 5fa1 ea79 99a9 4d3c -601b db3c f4c3 5426 55f3 309b ca6e 7ab7 -6a9e 281a d436 150c 04c4 a4bd 5df3 dff7 -4a88 361f 025d 094f 1e92 34e8 1cae 8ea4 -ab7b 04fd 03f9 ede3 ef9f c989 b122 28e9 -b79a 562c 88eb 3264 499e 05e7 242a f38a -4679 1657 e4f1 44c9 6f8f 8f9f 896c 46d2 -90d1 2c4a 6845 928c 749b aeee 7e20 8f7f -7cfe 8861 adea f339 2c2f 77fa a6af a730 -8b53 5a3e dcff 7cff ee5b 1d66 2c49 e9c3 -bdb3 f2ee ff22 ce12 027f 3101 9621 80ee -7659 90a8 28af 3366 8eeb 2042 f887 558b -7553 d158 a8a7 a4b1 d450 7259 2a69 84ee -e28a e4e7 3365 6512 dd40 d429 2e1b 6527 -b96c e5ed 10da 6a6c 4c31 0043 cbf2 7213 -9915 4c96 22ab 9816 48dc d02d 10d8 8440 -050d ca30 3bd2 db89 ace2 5b22 b592 6fa9 -e092 74a9 ec46 3403 0216 9647 7a8b cc3c -c565 29ba 9a6b 81e0 2de1 02b1 cd28 3a60 -f8b9 ca53 5a2d 2f1c 2698 2c44 9e62 b294 -f84a 6729 b029 4107 7a2c c3e2 b458 5a05 -8b85 ac2a 164b 491b 2a4b 394d c01d d889 -86c5 6225 c724 1642 2a48 2c75 144c 9632 -1a60 3ba8 8ac1 ed68 f96a 57f2 5868 a9e6 -b194 b325 b354 d40c 7e05 1665 0e45 dc89 -d68a bdca dd38 fbd5 7aef dd84 90cb e21e -bcc3 6ab7 59df 8690 336e 9cc3 7eb5 396c -8df5 eeb0 425c 7bff 70d8 ad3c 47fe 712d -46a0 4fe8 fa60 96c7 16bc 4afe 4783 a70b -a30a dfcd ef09 cf2d eeab cd76 07af 74d8 -d7fb 26b6 1a81 524c 6a0c 6a16 a675 cd9d -a67a abac 0c07 e98f d158 ac0c 5827 3c29 -c694 819d 9144 0fb1 34ba 6604 6889 4c2c -edb4 4e73 2674 4e2c 1cce cab1 9ac0 4dd4 -427a d359 ad26 fca4 4629 2d6a 81f5 3427 -31d6 0c6b 32f5 ca4d 5942 8c7e 7aac a587 -3423 3051 0fed 1667 959b f477 1ad5 1038 -2b33 6802 c7aa 6560 fb26 b59a b16a 334a -a150 c6ae 0e0b c5ea 83f4 6f93 da4c f8ae -195d b408 537b 8644 6215 c119 b149 41d4 -0e6a 460f 1dc0 c267 e1c1 5851 d08e 6a52 -9749 1f34 230d 0283 334c 6bdf b527 f017 -1368 1866 0cd0 66bb 3d1c b07a 619c 4e15 -b09c 8529 7914 7f67 f5f9 8996 247f ee39 -9e8a 9cc3 982a 8d4e 0b17 4fa6 e59d e2de -6b94 c7d0 edb5 e3dc bf53 4ac3 ff93 c70f -f7b0 8728 e3ac 0ac8 9c74 c292 3537 359e -6ccc 3030 65a3 0638 5786 87f9 96b0 79dc -8c31 1bb7 9d73 6673 1169 ad99 2918 ad85 -de9c e914 195b 2dbd 2e08 8cb1 3fb3 62c0 -eb84 7368 5ab1 d456 0ba1 1812 6868 d22c -f046 9269 6d1a 46b0 91e3 c2c9 a587 5939 -356b 1673 e1f4 5e0d 2ddf d870 1988 8800 -1bdb 352b 0623 0911 860d 239f c279 e1a4 -c300 0d3d 9b05 1e2d 19ca b5e9 0453 1a30 -bd5c 3898 8171 33c4 a245 d25a 379d 4023 -27a6 1747 0fc1 bb37 3328 5a16 9d7f d3a9 -32f4 637a 51b4 0823 0b67 8c46 2b83 3071 -3a71 148e 4caf 0f06 84f4 71ce d65f 4021 -7c98 e31d 9650 341c bb2d 52b1 9e27 5b6f -f79d 7758 5ae1 a6fc 1c5c 8f68 05cd 8b3a -685f 7a75 5d5d 5d81 a703 1252 5d2a 46cf -e4c3 e7ff 1096 9cc1 3515 3463 dc35 0d3f -1c9d 666c 8dde 740b 1819 6f18 d931 2ff3 -9a25 1938 af4f 6f16 b373 919d 4246 a2ba -2c21 9ef4 42e8 4b52 b151 309d f6c7 b03e -d23b c58d bd33 7cf4 397c 099e e38a fc33 -7c49 cef5 b963 7173 e83d 7986 7124 31ad -a232 2958 5e8e 2568 f1fd 47b6 570f aebf -1e3e 91f3 8a9b 9f0c 1ff5 06ec 3feb edf2 -7a34 e230 6992 1834 0bce f49c 432d d498 -db7f cbab a4b9 2acc f1d8 1bcf 73f4 4350 -b7f1 569b c3de f1fc 35fd 87b3 1f86 068b -bc64 019f 66ed fc20 5ff8 a566 e681 2630 -91db c610 6116 5152 67c9 0ba1 451e 9de6 -e6a4 82b8 1fac a281 bbda aed7 9bdd c1df -1e36 3b88 7624 e49f 49c9 ea30 edf7 efbf -cd45 9c8c 4a86 7e60 ca26 de6a eb6e f707 -dfe5 2a1e 3a71 c9a5 1ec4 1974 290e d23c -ff5a 17c1 7398 a435 0c47 bbc0 41c4 eb8c -fef5 d397 f75f 7e25 4d53 d236 ed86 8a22 -edac 7154 7b47 1735 225a 7d94 d8e8 da76 -7b45 54f4 cf30 ad43 587c dd4f 05d2 34e9 -7e63 dfde 21cf 3964 cd34 2512 0497 2051 -e590 9c68 5433 aa8a 5747 df9e 3ae1 21af -ddbd c671 c596 698b f696 a017 81c5 2725 -d660 5334 df70 89bb 3641 8839 45d6 1bc5 -9449 f308 966c 05d8 f048 83e8 44a3 af45 -9e64 0c33 837e 14bf 9871 bdfb 1349 20ff -c12c e5f3 e84a 0549 e5bd cc31 f218 45ec -d650 46c6 d0aa cebe 2a17 8761 606f a9c8 -12af 5ae4 430a 0815 76ab ee6a 6783 6365 -d186 6f87 a55c 504f 17be 1124 2561 9742 -b9a6 e69f a148 06b3 8057 fe98 87fb a8a4 -21e3 8706 9e7f 30c5 42ec 1594 27e2 6ba4 -ad31 38c9 00e8 af1d 5320 2bc3 ace2 27e9 -00df ba9e 29bc ceae 4fd6 8d63 92c5 5080 -65c7 e029 64d1 2968 7ecd e8d2 9f0d ff92 -0bb4 1259 5234 242d 6ef8 8b49 5798 7e7c -31cf 5664 5163 92f9 dcb6 8cce bf31 dd72 -3e91 1117 5234 29d2 359d 3dcd 8b99 fe74 -799b 28cd bc69 9afc 784d 126d 1284 95d6 -34f9 c978 e234 9ca6 3345 a046 5363 bd00 -ef2f c55b 1088 d136 c518 0fef b79a d690 -6dc2 228c 1276 11c9 feed 0759 ddbf 8db3 -686b 3086 036e cdd6 3505 7377 fc7b 53c3 -0ea5 343b b2d3 a052 6d27 e4f7 3061 bc3f -b07b 3fc9 eed1 d8b8 5ff2 1166 bd92 204c -f63e 5270 f971 5085 e722 a573 9bb1 6c41 -5a08 a627 4a72 ed2e 3c81 db38 dbbd bee6 -4a32 a8de 9238 284a 9ae6 613c 7a73 ade8 -996c 7a7d 815d d267 5a96 72ec 4292 e5d9 -7b71 c8c0 5d72 454b d8ab 5640 9480 16bc -f6e2 439b 444d 0dc7 dd7b cd62 4889 316c -6c4f 3495 e38e dacc 6603 47a8 368b d7cf -0569 3445 49c0 0f1e 9af2 549e b38c aab2 -ced1 84d8 b805 58df cbf1 4334 337b 0c70 -1dcf 37ea cc6c 473a d1bf 03b7 16a5 75cc -073e 4af3 8cb6 0535 94e6 2bba 6a7f f89e -b013 0c32 4c8c ab06 883d a71f 9141 af79 -8f11 8598 8434 f373 a2c7 f2a6 f978 4920 -2e6a d978 bbd6 e753 591e 778a 88ce 6f9b -ffd2 6ec9 3cf4 6b99 c88b 0289 e323 4543 -a80a 8450 fade cc3e 4ebb ffcf a147 75c0 -c659 6df6 fb1b 9035 47c6 9b95 b7f1 6fc1 -26e8 76eb dd6a bbdb d8f1 3515 8303 c3bb -9af5 16b3 1cb2 82d8 e3a7 88a2 8490 9971 -5048 4800 b68e 98e0 d74c f509 14ac 54d3 -1e75 6a88 c914 d596 12b0 7017 f710 5750 -2831 fa24 d42c 7d8d ad97 f9c1 ded7 8f9e -a2dd 1c87 88a1 b39f 2980 27a0 e730 8147 -6661 16f1 ad57 a63e f1a6 4521 5296 b3e4 -59d6 0895 daa7 fede 5c24 df7a e6a7 a299 -d88e c467 46a4 4703 1e28 e787 41ed 8e15 -9779 51c0 96d5 6ba4 dc97 10d1 2872 a11e -356f 930d f123 1f6b 8ab7 2018 3b5f 04a6 -c964 aaa5 d107 232c 906a 9427 d7f8 2cfb -6875 cfb6 761d 6cf8 4ac3 a30a 5b66 2aa3 -e8a7 32d3 4c5b 55dc 659d d2e0 7a0c 8f3e -bc27 1ca8 39b3 c771 2b56 0f0a f82a 5a35 -f945 880a eb5a f5ae fff6 bca3 c572 2bde -d189 048a 58bc 0557 91ff 3538 aac7 b135 -6fc6 27f8 fa25 8c71 bf4b b854 c67f c340 -4d10 2f1f a929 62f1 8bb7 8b87 eaca 0eda -9a4b 3b1e ab1e a1eb 2116 bce2 ade7 b004 -114b fd0a 997d fba9 a157 d41e 1a84 2a69 -b547 1d83 ccfc 61b0 4388 db22 5dd5 d9f7 -3261 b01f b507 33aa d027 5847 1976 a2dd -d6f1 77da 5865 26fe 30aa 5d13 46cf fd8d -6022 70f2 915b 38de 1cc4 3c17 25cc 854a -bc4b 6d8f 9ce8 4b01 c621 e665 22b8 72d2 -7c8e 48c2 4afc d41c b7c1 08c2 34ba 48a7 -de1e c149 d580 07f6 2bf8 4b59 0e29 bba3 -9168 66fb 69a2 0b78 7558 c214 904d df3e -2ef8 2512 5f09 b4b7 a1f6 a5ec 3be5 6a44 -6558 a887 5143 a9d8 6ee6 11af edf5 877b -d71b 7ca2 245e 1bbb db1b 9179 3724 f346 -19c5 9ecb bf25 9729 9948 997d 42fe 7ad0 -84a1 c992 238e b55d 8f54 53c0 b90d d568 -1fb4 a6ba 1dd3 e813 017b 2643 aae1 c8f3 -41f3 168d 7bf3 71df feee ff2d f9e8 431a -5200 00 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/text b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/text deleted file mode 100644 index f3d8c3784..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/text +++ /dev/null @@ -1,322 +0,0 @@ -# HELP http_request_duration_microseconds The HTTP request latencies in microseconds. -# TYPE http_request_duration_microseconds summary -http_request_duration_microseconds{handler="/",quantile="0.5"} 0 -http_request_duration_microseconds{handler="/",quantile="0.9"} 0 -http_request_duration_microseconds{handler="/",quantile="0.99"} 0 -http_request_duration_microseconds_sum{handler="/"} 0 -http_request_duration_microseconds_count{handler="/"} 0 -http_request_duration_microseconds{handler="/alerts",quantile="0.5"} 0 -http_request_duration_microseconds{handler="/alerts",quantile="0.9"} 0 -http_request_duration_microseconds{handler="/alerts",quantile="0.99"} 0 -http_request_duration_microseconds_sum{handler="/alerts"} 0 -http_request_duration_microseconds_count{handler="/alerts"} 0 -http_request_duration_microseconds{handler="/api/metrics",quantile="0.5"} 0 -http_request_duration_microseconds{handler="/api/metrics",quantile="0.9"} 0 -http_request_duration_microseconds{handler="/api/metrics",quantile="0.99"} 0 -http_request_duration_microseconds_sum{handler="/api/metrics"} 0 -http_request_duration_microseconds_count{handler="/api/metrics"} 0 -http_request_duration_microseconds{handler="/api/query",quantile="0.5"} 0 -http_request_duration_microseconds{handler="/api/query",quantile="0.9"} 0 -http_request_duration_microseconds{handler="/api/query",quantile="0.99"} 0 -http_request_duration_microseconds_sum{handler="/api/query"} 0 -http_request_duration_microseconds_count{handler="/api/query"} 0 -http_request_duration_microseconds{handler="/api/query_range",quantile="0.5"} 0 -http_request_duration_microseconds{handler="/api/query_range",quantile="0.9"} 0 -http_request_duration_microseconds{handler="/api/query_range",quantile="0.99"} 0 -http_request_duration_microseconds_sum{handler="/api/query_range"} 0 -http_request_duration_microseconds_count{handler="/api/query_range"} 0 -http_request_duration_microseconds{handler="/api/targets",quantile="0.5"} 0 -http_request_duration_microseconds{handler="/api/targets",quantile="0.9"} 0 -http_request_duration_microseconds{handler="/api/targets",quantile="0.99"} 0 -http_request_duration_microseconds_sum{handler="/api/targets"} 0 -http_request_duration_microseconds_count{handler="/api/targets"} 0 -http_request_duration_microseconds{handler="/consoles/",quantile="0.5"} 0 -http_request_duration_microseconds{handler="/consoles/",quantile="0.9"} 0 -http_request_duration_microseconds{handler="/consoles/",quantile="0.99"} 0 -http_request_duration_microseconds_sum{handler="/consoles/"} 0 -http_request_duration_microseconds_count{handler="/consoles/"} 0 -http_request_duration_microseconds{handler="/graph",quantile="0.5"} 0 -http_request_duration_microseconds{handler="/graph",quantile="0.9"} 0 -http_request_duration_microseconds{handler="/graph",quantile="0.99"} 0 -http_request_duration_microseconds_sum{handler="/graph"} 0 -http_request_duration_microseconds_count{handler="/graph"} 0 -http_request_duration_microseconds{handler="/heap",quantile="0.5"} 0 -http_request_duration_microseconds{handler="/heap",quantile="0.9"} 0 -http_request_duration_microseconds{handler="/heap",quantile="0.99"} 0 -http_request_duration_microseconds_sum{handler="/heap"} 0 -http_request_duration_microseconds_count{handler="/heap"} 0 -http_request_duration_microseconds{handler="/static/",quantile="0.5"} 0 -http_request_duration_microseconds{handler="/static/",quantile="0.9"} 0 -http_request_duration_microseconds{handler="/static/",quantile="0.99"} 0 -http_request_duration_microseconds_sum{handler="/static/"} 0 -http_request_duration_microseconds_count{handler="/static/"} 0 -http_request_duration_microseconds{handler="prometheus",quantile="0.5"} 1307.275 -http_request_duration_microseconds{handler="prometheus",quantile="0.9"} 1858.632 -http_request_duration_microseconds{handler="prometheus",quantile="0.99"} 3087.384 -http_request_duration_microseconds_sum{handler="prometheus"} 179886.5000000001 -http_request_duration_microseconds_count{handler="prometheus"} 119 -# HELP http_request_size_bytes The HTTP request sizes in bytes. -# TYPE http_request_size_bytes summary -http_request_size_bytes{handler="/",quantile="0.5"} 0 -http_request_size_bytes{handler="/",quantile="0.9"} 0 -http_request_size_bytes{handler="/",quantile="0.99"} 0 -http_request_size_bytes_sum{handler="/"} 0 -http_request_size_bytes_count{handler="/"} 0 -http_request_size_bytes{handler="/alerts",quantile="0.5"} 0 -http_request_size_bytes{handler="/alerts",quantile="0.9"} 0 -http_request_size_bytes{handler="/alerts",quantile="0.99"} 0 -http_request_size_bytes_sum{handler="/alerts"} 0 -http_request_size_bytes_count{handler="/alerts"} 0 -http_request_size_bytes{handler="/api/metrics",quantile="0.5"} 0 -http_request_size_bytes{handler="/api/metrics",quantile="0.9"} 0 -http_request_size_bytes{handler="/api/metrics",quantile="0.99"} 0 -http_request_size_bytes_sum{handler="/api/metrics"} 0 -http_request_size_bytes_count{handler="/api/metrics"} 0 -http_request_size_bytes{handler="/api/query",quantile="0.5"} 0 -http_request_size_bytes{handler="/api/query",quantile="0.9"} 0 -http_request_size_bytes{handler="/api/query",quantile="0.99"} 0 -http_request_size_bytes_sum{handler="/api/query"} 0 -http_request_size_bytes_count{handler="/api/query"} 0 -http_request_size_bytes{handler="/api/query_range",quantile="0.5"} 0 -http_request_size_bytes{handler="/api/query_range",quantile="0.9"} 0 -http_request_size_bytes{handler="/api/query_range",quantile="0.99"} 0 -http_request_size_bytes_sum{handler="/api/query_range"} 0 -http_request_size_bytes_count{handler="/api/query_range"} 0 -http_request_size_bytes{handler="/api/targets",quantile="0.5"} 0 -http_request_size_bytes{handler="/api/targets",quantile="0.9"} 0 -http_request_size_bytes{handler="/api/targets",quantile="0.99"} 0 -http_request_size_bytes_sum{handler="/api/targets"} 0 -http_request_size_bytes_count{handler="/api/targets"} 0 -http_request_size_bytes{handler="/consoles/",quantile="0.5"} 0 -http_request_size_bytes{handler="/consoles/",quantile="0.9"} 0 -http_request_size_bytes{handler="/consoles/",quantile="0.99"} 0 -http_request_size_bytes_sum{handler="/consoles/"} 0 -http_request_size_bytes_count{handler="/consoles/"} 0 -http_request_size_bytes{handler="/graph",quantile="0.5"} 0 -http_request_size_bytes{handler="/graph",quantile="0.9"} 0 -http_request_size_bytes{handler="/graph",quantile="0.99"} 0 -http_request_size_bytes_sum{handler="/graph"} 0 -http_request_size_bytes_count{handler="/graph"} 0 -http_request_size_bytes{handler="/heap",quantile="0.5"} 0 -http_request_size_bytes{handler="/heap",quantile="0.9"} 0 -http_request_size_bytes{handler="/heap",quantile="0.99"} 0 -http_request_size_bytes_sum{handler="/heap"} 0 -http_request_size_bytes_count{handler="/heap"} 0 -http_request_size_bytes{handler="/static/",quantile="0.5"} 0 -http_request_size_bytes{handler="/static/",quantile="0.9"} 0 -http_request_size_bytes{handler="/static/",quantile="0.99"} 0 -http_request_size_bytes_sum{handler="/static/"} 0 -http_request_size_bytes_count{handler="/static/"} 0 -http_request_size_bytes{handler="prometheus",quantile="0.5"} 291 -http_request_size_bytes{handler="prometheus",quantile="0.9"} 291 -http_request_size_bytes{handler="prometheus",quantile="0.99"} 291 -http_request_size_bytes_sum{handler="prometheus"} 34488 -http_request_size_bytes_count{handler="prometheus"} 119 -# HELP http_requests_total Total number of HTTP requests made. -# TYPE http_requests_total counter -http_requests_total{code="200",handler="prometheus",method="get"} 119 -# HELP http_response_size_bytes The HTTP response sizes in bytes. -# TYPE http_response_size_bytes summary -http_response_size_bytes{handler="/",quantile="0.5"} 0 -http_response_size_bytes{handler="/",quantile="0.9"} 0 -http_response_size_bytes{handler="/",quantile="0.99"} 0 -http_response_size_bytes_sum{handler="/"} 0 -http_response_size_bytes_count{handler="/"} 0 -http_response_size_bytes{handler="/alerts",quantile="0.5"} 0 -http_response_size_bytes{handler="/alerts",quantile="0.9"} 0 -http_response_size_bytes{handler="/alerts",quantile="0.99"} 0 -http_response_size_bytes_sum{handler="/alerts"} 0 -http_response_size_bytes_count{handler="/alerts"} 0 -http_response_size_bytes{handler="/api/metrics",quantile="0.5"} 0 -http_response_size_bytes{handler="/api/metrics",quantile="0.9"} 0 -http_response_size_bytes{handler="/api/metrics",quantile="0.99"} 0 -http_response_size_bytes_sum{handler="/api/metrics"} 0 -http_response_size_bytes_count{handler="/api/metrics"} 0 -http_response_size_bytes{handler="/api/query",quantile="0.5"} 0 -http_response_size_bytes{handler="/api/query",quantile="0.9"} 0 -http_response_size_bytes{handler="/api/query",quantile="0.99"} 0 -http_response_size_bytes_sum{handler="/api/query"} 0 -http_response_size_bytes_count{handler="/api/query"} 0 -http_response_size_bytes{handler="/api/query_range",quantile="0.5"} 0 -http_response_size_bytes{handler="/api/query_range",quantile="0.9"} 0 -http_response_size_bytes{handler="/api/query_range",quantile="0.99"} 0 -http_response_size_bytes_sum{handler="/api/query_range"} 0 -http_response_size_bytes_count{handler="/api/query_range"} 0 -http_response_size_bytes{handler="/api/targets",quantile="0.5"} 0 -http_response_size_bytes{handler="/api/targets",quantile="0.9"} 0 -http_response_size_bytes{handler="/api/targets",quantile="0.99"} 0 -http_response_size_bytes_sum{handler="/api/targets"} 0 -http_response_size_bytes_count{handler="/api/targets"} 0 -http_response_size_bytes{handler="/consoles/",quantile="0.5"} 0 -http_response_size_bytes{handler="/consoles/",quantile="0.9"} 0 -http_response_size_bytes{handler="/consoles/",quantile="0.99"} 0 -http_response_size_bytes_sum{handler="/consoles/"} 0 -http_response_size_bytes_count{handler="/consoles/"} 0 -http_response_size_bytes{handler="/graph",quantile="0.5"} 0 -http_response_size_bytes{handler="/graph",quantile="0.9"} 0 -http_response_size_bytes{handler="/graph",quantile="0.99"} 0 -http_response_size_bytes_sum{handler="/graph"} 0 -http_response_size_bytes_count{handler="/graph"} 0 -http_response_size_bytes{handler="/heap",quantile="0.5"} 0 -http_response_size_bytes{handler="/heap",quantile="0.9"} 0 -http_response_size_bytes{handler="/heap",quantile="0.99"} 0 -http_response_size_bytes_sum{handler="/heap"} 0 -http_response_size_bytes_count{handler="/heap"} 0 -http_response_size_bytes{handler="/static/",quantile="0.5"} 0 -http_response_size_bytes{handler="/static/",quantile="0.9"} 0 -http_response_size_bytes{handler="/static/",quantile="0.99"} 0 -http_response_size_bytes_sum{handler="/static/"} 0 -http_response_size_bytes_count{handler="/static/"} 0 -http_response_size_bytes{handler="prometheus",quantile="0.5"} 2049 -http_response_size_bytes{handler="prometheus",quantile="0.9"} 2058 -http_response_size_bytes{handler="prometheus",quantile="0.99"} 2064 -http_response_size_bytes_sum{handler="prometheus"} 247001 -http_response_size_bytes_count{handler="prometheus"} 119 -# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds. -# TYPE process_cpu_seconds_total counter -process_cpu_seconds_total 0.55 -# HELP go_goroutines Number of goroutines that currently exist. -# TYPE go_goroutines gauge -go_goroutines 70 -# HELP process_max_fds Maximum number of open file descriptors. -# TYPE process_max_fds gauge -process_max_fds 8192 -# HELP process_open_fds Number of open file descriptors. -# TYPE process_open_fds gauge -process_open_fds 29 -# HELP process_resident_memory_bytes Resident memory size in bytes. -# TYPE process_resident_memory_bytes gauge -process_resident_memory_bytes 5.3870592e+07 -# HELP process_start_time_seconds Start time of the process since unix epoch in seconds. -# TYPE process_start_time_seconds gauge -process_start_time_seconds 1.42236894836e+09 -# HELP process_virtual_memory_bytes Virtual memory size in bytes. -# TYPE process_virtual_memory_bytes gauge -process_virtual_memory_bytes 5.41478912e+08 -# HELP prometheus_dns_sd_lookup_failures_total The number of DNS-SD lookup failures. -# TYPE prometheus_dns_sd_lookup_failures_total counter -prometheus_dns_sd_lookup_failures_total 0 -# HELP prometheus_dns_sd_lookups_total The number of DNS-SD lookups. -# TYPE prometheus_dns_sd_lookups_total counter -prometheus_dns_sd_lookups_total 7 -# HELP prometheus_evaluator_duration_milliseconds The duration for all evaluations to execute. -# TYPE prometheus_evaluator_duration_milliseconds summary -prometheus_evaluator_duration_milliseconds{quantile="0.01"} 0 -prometheus_evaluator_duration_milliseconds{quantile="0.05"} 0 -prometheus_evaluator_duration_milliseconds{quantile="0.5"} 0 -prometheus_evaluator_duration_milliseconds{quantile="0.9"} 1 -prometheus_evaluator_duration_milliseconds{quantile="0.99"} 1 -prometheus_evaluator_duration_milliseconds_sum 12 -prometheus_evaluator_duration_milliseconds_count 23 -# HELP prometheus_local_storage_checkpoint_duration_milliseconds The duration (in milliseconds) it took to checkpoint in-memory metrics and head chunks. -# TYPE prometheus_local_storage_checkpoint_duration_milliseconds gauge -prometheus_local_storage_checkpoint_duration_milliseconds 0 -# HELP prometheus_local_storage_chunk_ops_total The total number of chunk operations by their type. -# TYPE prometheus_local_storage_chunk_ops_total counter -prometheus_local_storage_chunk_ops_total{type="create"} 598 -prometheus_local_storage_chunk_ops_total{type="persist"} 174 -prometheus_local_storage_chunk_ops_total{type="pin"} 920 -prometheus_local_storage_chunk_ops_total{type="transcode"} 415 -prometheus_local_storage_chunk_ops_total{type="unpin"} 920 -# HELP prometheus_local_storage_indexing_batch_latency_milliseconds Quantiles for batch indexing latencies in milliseconds. -# TYPE prometheus_local_storage_indexing_batch_latency_milliseconds summary -prometheus_local_storage_indexing_batch_latency_milliseconds{quantile="0.5"} 0 -prometheus_local_storage_indexing_batch_latency_milliseconds{quantile="0.9"} 0 -prometheus_local_storage_indexing_batch_latency_milliseconds{quantile="0.99"} 0 -prometheus_local_storage_indexing_batch_latency_milliseconds_sum 0 -prometheus_local_storage_indexing_batch_latency_milliseconds_count 1 -# HELP prometheus_local_storage_indexing_batch_sizes Quantiles for indexing batch sizes (number of metrics per batch). -# TYPE prometheus_local_storage_indexing_batch_sizes summary -prometheus_local_storage_indexing_batch_sizes{quantile="0.5"} 2 -prometheus_local_storage_indexing_batch_sizes{quantile="0.9"} 2 -prometheus_local_storage_indexing_batch_sizes{quantile="0.99"} 2 -prometheus_local_storage_indexing_batch_sizes_sum 2 -prometheus_local_storage_indexing_batch_sizes_count 1 -# HELP prometheus_local_storage_indexing_queue_capacity The capacity of the indexing queue. -# TYPE prometheus_local_storage_indexing_queue_capacity gauge -prometheus_local_storage_indexing_queue_capacity 16384 -# HELP prometheus_local_storage_indexing_queue_length The number of metrics waiting to be indexed. -# TYPE prometheus_local_storage_indexing_queue_length gauge -prometheus_local_storage_indexing_queue_length 0 -# HELP prometheus_local_storage_ingested_samples_total The total number of samples ingested. -# TYPE prometheus_local_storage_ingested_samples_total counter -prometheus_local_storage_ingested_samples_total 30473 -# HELP prometheus_local_storage_invalid_preload_requests_total The total number of preload requests referring to a non-existent series. This is an indication of outdated label indexes. -# TYPE prometheus_local_storage_invalid_preload_requests_total counter -prometheus_local_storage_invalid_preload_requests_total 0 -# HELP prometheus_local_storage_memory_chunkdescs The current number of chunk descriptors in memory. -# TYPE prometheus_local_storage_memory_chunkdescs gauge -prometheus_local_storage_memory_chunkdescs 1059 -# HELP prometheus_local_storage_memory_chunks The current number of chunks in memory, excluding cloned chunks (i.e. chunks without a descriptor). -# TYPE prometheus_local_storage_memory_chunks gauge -prometheus_local_storage_memory_chunks 1020 -# HELP prometheus_local_storage_memory_series The current number of series in memory. -# TYPE prometheus_local_storage_memory_series gauge -prometheus_local_storage_memory_series 424 -# HELP prometheus_local_storage_persist_latency_microseconds A summary of latencies for persisting each chunk. -# TYPE prometheus_local_storage_persist_latency_microseconds summary -prometheus_local_storage_persist_latency_microseconds{quantile="0.5"} 30.377 -prometheus_local_storage_persist_latency_microseconds{quantile="0.9"} 203.539 -prometheus_local_storage_persist_latency_microseconds{quantile="0.99"} 2626.463 -prometheus_local_storage_persist_latency_microseconds_sum 20424.415 -prometheus_local_storage_persist_latency_microseconds_count 174 -# HELP prometheus_local_storage_persist_queue_capacity The total capacity of the persist queue. -# TYPE prometheus_local_storage_persist_queue_capacity gauge -prometheus_local_storage_persist_queue_capacity 1024 -# HELP prometheus_local_storage_persist_queue_length The current number of chunks waiting in the persist queue. -# TYPE prometheus_local_storage_persist_queue_length gauge -prometheus_local_storage_persist_queue_length 0 -# HELP prometheus_local_storage_series_ops_total The total number of series operations by their type. -# TYPE prometheus_local_storage_series_ops_total counter -prometheus_local_storage_series_ops_total{type="create"} 2 -prometheus_local_storage_series_ops_total{type="maintenance_in_memory"} 11 -# HELP prometheus_notifications_latency_milliseconds Latency quantiles for sending alert notifications (not including dropped notifications). -# TYPE prometheus_notifications_latency_milliseconds summary -prometheus_notifications_latency_milliseconds{quantile="0.5"} 0 -prometheus_notifications_latency_milliseconds{quantile="0.9"} 0 -prometheus_notifications_latency_milliseconds{quantile="0.99"} 0 -prometheus_notifications_latency_milliseconds_sum 0 -prometheus_notifications_latency_milliseconds_count 0 -# HELP prometheus_notifications_queue_capacity The capacity of the alert notifications queue. -# TYPE prometheus_notifications_queue_capacity gauge -prometheus_notifications_queue_capacity 100 -# HELP prometheus_notifications_queue_length The number of alert notifications in the queue. -# TYPE prometheus_notifications_queue_length gauge -prometheus_notifications_queue_length 0 -# HELP prometheus_rule_evaluation_duration_milliseconds The duration for a rule to execute. -# TYPE prometheus_rule_evaluation_duration_milliseconds summary -prometheus_rule_evaluation_duration_milliseconds{rule_type="alerting",quantile="0.5"} 0 -prometheus_rule_evaluation_duration_milliseconds{rule_type="alerting",quantile="0.9"} 0 -prometheus_rule_evaluation_duration_milliseconds{rule_type="alerting",quantile="0.99"} 2 -prometheus_rule_evaluation_duration_milliseconds_sum{rule_type="alerting"} 12 -prometheus_rule_evaluation_duration_milliseconds_count{rule_type="alerting"} 115 -prometheus_rule_evaluation_duration_milliseconds{rule_type="recording",quantile="0.5"} 0 -prometheus_rule_evaluation_duration_milliseconds{rule_type="recording",quantile="0.9"} 0 -prometheus_rule_evaluation_duration_milliseconds{rule_type="recording",quantile="0.99"} 3 -prometheus_rule_evaluation_duration_milliseconds_sum{rule_type="recording"} 15 -prometheus_rule_evaluation_duration_milliseconds_count{rule_type="recording"} 115 -# HELP prometheus_rule_evaluation_failures_total The total number of rule evaluation failures. -# TYPE prometheus_rule_evaluation_failures_total counter -prometheus_rule_evaluation_failures_total 0 -# HELP prometheus_samples_queue_capacity Capacity of the queue for unwritten samples. -# TYPE prometheus_samples_queue_capacity gauge -prometheus_samples_queue_capacity 4096 -# HELP prometheus_samples_queue_length Current number of items in the queue for unwritten samples. Each item comprises all samples exposed by one target as one metric family (i.e. metrics of the same name). -# TYPE prometheus_samples_queue_length gauge -prometheus_samples_queue_length 0 -# HELP prometheus_target_interval_length_seconds Actual intervals between scrapes. -# TYPE prometheus_target_interval_length_seconds summary -prometheus_target_interval_length_seconds{interval="15s",quantile="0.01"} 14 -prometheus_target_interval_length_seconds{interval="15s",quantile="0.05"} 14 -prometheus_target_interval_length_seconds{interval="15s",quantile="0.5"} 15 -prometheus_target_interval_length_seconds{interval="15s",quantile="0.9"} 15 -prometheus_target_interval_length_seconds{interval="15s",quantile="0.99"} 15 -prometheus_target_interval_length_seconds_sum{interval="15s"} 175 -prometheus_target_interval_length_seconds_count{interval="15s"} 12 -prometheus_target_interval_length_seconds{interval="1s",quantile="0.01"} 0 -prometheus_target_interval_length_seconds{interval="1s",quantile="0.05"} 0 -prometheus_target_interval_length_seconds{interval="1s",quantile="0.5"} 0 -prometheus_target_interval_length_seconds{interval="1s",quantile="0.9"} 1 -prometheus_target_interval_length_seconds{interval="1s",quantile="0.99"} 1 -prometheus_target_interval_length_seconds_sum{interval="1s"} 55 -prometheus_target_interval_length_seconds_count{interval="1s"} 117 diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create_test.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create_test.go deleted file mode 100644 index e4cc5d803..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create_test.go +++ /dev/null @@ -1,443 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package expfmt - -import ( - "bytes" - "math" - "strings" - "testing" - - "github.com/golang/protobuf/proto" - - dto "github.com/prometheus/client_model/go" -) - -func testCreate(t testing.TB) { - var scenarios = []struct { - in *dto.MetricFamily - out string - }{ - // 0: Counter, NaN as value, timestamp given. - { - in: &dto.MetricFamily{ - Name: proto.String("name"), - Help: proto.String("two-line\n doc str\\ing"), - Type: dto.MetricType_COUNTER.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("labelname"), - Value: proto.String("val1"), - }, - &dto.LabelPair{ - Name: proto.String("basename"), - Value: proto.String("basevalue"), - }, - }, - Counter: &dto.Counter{ - Value: proto.Float64(math.NaN()), - }, - }, - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("labelname"), - Value: proto.String("val2"), - }, - &dto.LabelPair{ - Name: proto.String("basename"), - Value: proto.String("basevalue"), - }, - }, - Counter: &dto.Counter{ - Value: proto.Float64(.23), - }, - TimestampMs: proto.Int64(1234567890), - }, - }, - }, - out: `# HELP name two-line\n doc str\\ing -# TYPE name counter -name{labelname="val1",basename="basevalue"} NaN -name{labelname="val2",basename="basevalue"} 0.23 1234567890 -`, - }, - // 1: Gauge, some escaping required, +Inf as value, multi-byte characters in label values. - { - in: &dto.MetricFamily{ - Name: proto.String("gauge_name"), - Help: proto.String("gauge\ndoc\nstr\"ing"), - Type: dto.MetricType_GAUGE.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("name_1"), - Value: proto.String("val with\nnew line"), - }, - &dto.LabelPair{ - Name: proto.String("name_2"), - Value: proto.String("val with \\backslash and \"quotes\""), - }, - }, - Gauge: &dto.Gauge{ - Value: proto.Float64(math.Inf(+1)), - }, - }, - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("name_1"), - Value: proto.String("Björn"), - }, - &dto.LabelPair{ - Name: proto.String("name_2"), - Value: proto.String("佖佥"), - }, - }, - Gauge: &dto.Gauge{ - Value: proto.Float64(3.14E42), - }, - }, - }, - }, - out: `# HELP gauge_name gauge\ndoc\nstr"ing -# TYPE gauge_name gauge -gauge_name{name_1="val with\nnew line",name_2="val with \\backslash and \"quotes\""} +Inf -gauge_name{name_1="Björn",name_2="佖佥"} 3.14e+42 -`, - }, - // 2: Untyped, no help, one sample with no labels and -Inf as value, another sample with one label. - { - in: &dto.MetricFamily{ - Name: proto.String("untyped_name"), - Type: dto.MetricType_UNTYPED.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Untyped: &dto.Untyped{ - Value: proto.Float64(math.Inf(-1)), - }, - }, - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("name_1"), - Value: proto.String("value 1"), - }, - }, - Untyped: &dto.Untyped{ - Value: proto.Float64(-1.23e-45), - }, - }, - }, - }, - out: `# TYPE untyped_name untyped -untyped_name -Inf -untyped_name{name_1="value 1"} -1.23e-45 -`, - }, - // 3: Summary. - { - in: &dto.MetricFamily{ - Name: proto.String("summary_name"), - Help: proto.String("summary docstring"), - Type: dto.MetricType_SUMMARY.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Summary: &dto.Summary{ - SampleCount: proto.Uint64(42), - SampleSum: proto.Float64(-3.4567), - Quantile: []*dto.Quantile{ - &dto.Quantile{ - Quantile: proto.Float64(0.5), - Value: proto.Float64(-1.23), - }, - &dto.Quantile{ - Quantile: proto.Float64(0.9), - Value: proto.Float64(.2342354), - }, - &dto.Quantile{ - Quantile: proto.Float64(0.99), - Value: proto.Float64(0), - }, - }, - }, - }, - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("name_1"), - Value: proto.String("value 1"), - }, - &dto.LabelPair{ - Name: proto.String("name_2"), - Value: proto.String("value 2"), - }, - }, - Summary: &dto.Summary{ - SampleCount: proto.Uint64(4711), - SampleSum: proto.Float64(2010.1971), - Quantile: []*dto.Quantile{ - &dto.Quantile{ - Quantile: proto.Float64(0.5), - Value: proto.Float64(1), - }, - &dto.Quantile{ - Quantile: proto.Float64(0.9), - Value: proto.Float64(2), - }, - &dto.Quantile{ - Quantile: proto.Float64(0.99), - Value: proto.Float64(3), - }, - }, - }, - }, - }, - }, - out: `# HELP summary_name summary docstring -# TYPE summary_name summary -summary_name{quantile="0.5"} -1.23 -summary_name{quantile="0.9"} 0.2342354 -summary_name{quantile="0.99"} 0 -summary_name_sum -3.4567 -summary_name_count 42 -summary_name{name_1="value 1",name_2="value 2",quantile="0.5"} 1 -summary_name{name_1="value 1",name_2="value 2",quantile="0.9"} 2 -summary_name{name_1="value 1",name_2="value 2",quantile="0.99"} 3 -summary_name_sum{name_1="value 1",name_2="value 2"} 2010.1971 -summary_name_count{name_1="value 1",name_2="value 2"} 4711 -`, - }, - // 4: Histogram - { - in: &dto.MetricFamily{ - Name: proto.String("request_duration_microseconds"), - Help: proto.String("The response latency."), - Type: dto.MetricType_HISTOGRAM.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Histogram: &dto.Histogram{ - SampleCount: proto.Uint64(2693), - SampleSum: proto.Float64(1756047.3), - Bucket: []*dto.Bucket{ - &dto.Bucket{ - UpperBound: proto.Float64(100), - CumulativeCount: proto.Uint64(123), - }, - &dto.Bucket{ - UpperBound: proto.Float64(120), - CumulativeCount: proto.Uint64(412), - }, - &dto.Bucket{ - UpperBound: proto.Float64(144), - CumulativeCount: proto.Uint64(592), - }, - &dto.Bucket{ - UpperBound: proto.Float64(172.8), - CumulativeCount: proto.Uint64(1524), - }, - &dto.Bucket{ - UpperBound: proto.Float64(math.Inf(+1)), - CumulativeCount: proto.Uint64(2693), - }, - }, - }, - }, - }, - }, - out: `# HELP request_duration_microseconds The response latency. -# TYPE request_duration_microseconds histogram -request_duration_microseconds_bucket{le="100"} 123 -request_duration_microseconds_bucket{le="120"} 412 -request_duration_microseconds_bucket{le="144"} 592 -request_duration_microseconds_bucket{le="172.8"} 1524 -request_duration_microseconds_bucket{le="+Inf"} 2693 -request_duration_microseconds_sum 1.7560473e+06 -request_duration_microseconds_count 2693 -`, - }, - // 5: Histogram with missing +Inf bucket. - { - in: &dto.MetricFamily{ - Name: proto.String("request_duration_microseconds"), - Help: proto.String("The response latency."), - Type: dto.MetricType_HISTOGRAM.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Histogram: &dto.Histogram{ - SampleCount: proto.Uint64(2693), - SampleSum: proto.Float64(1756047.3), - Bucket: []*dto.Bucket{ - &dto.Bucket{ - UpperBound: proto.Float64(100), - CumulativeCount: proto.Uint64(123), - }, - &dto.Bucket{ - UpperBound: proto.Float64(120), - CumulativeCount: proto.Uint64(412), - }, - &dto.Bucket{ - UpperBound: proto.Float64(144), - CumulativeCount: proto.Uint64(592), - }, - &dto.Bucket{ - UpperBound: proto.Float64(172.8), - CumulativeCount: proto.Uint64(1524), - }, - }, - }, - }, - }, - }, - out: `# HELP request_duration_microseconds The response latency. -# TYPE request_duration_microseconds histogram -request_duration_microseconds_bucket{le="100"} 123 -request_duration_microseconds_bucket{le="120"} 412 -request_duration_microseconds_bucket{le="144"} 592 -request_duration_microseconds_bucket{le="172.8"} 1524 -request_duration_microseconds_bucket{le="+Inf"} 2693 -request_duration_microseconds_sum 1.7560473e+06 -request_duration_microseconds_count 2693 -`, - }, - // 6: No metric type, should result in default type Counter. - { - in: &dto.MetricFamily{ - Name: proto.String("name"), - Help: proto.String("doc string"), - Metric: []*dto.Metric{ - &dto.Metric{ - Counter: &dto.Counter{ - Value: proto.Float64(math.Inf(-1)), - }, - }, - }, - }, - out: `# HELP name doc string -# TYPE name counter -name -Inf -`, - }, - } - - for i, scenario := range scenarios { - out := bytes.NewBuffer(make([]byte, 0, len(scenario.out))) - n, err := MetricFamilyToText(out, scenario.in) - if err != nil { - t.Errorf("%d. error: %s", i, err) - continue - } - if expected, got := len(scenario.out), n; expected != got { - t.Errorf( - "%d. expected %d bytes written, got %d", - i, expected, got, - ) - } - if expected, got := scenario.out, out.String(); expected != got { - t.Errorf( - "%d. expected out=%q, got %q", - i, expected, got, - ) - } - } - -} - -func TestCreate(t *testing.T) { - testCreate(t) -} - -func BenchmarkCreate(b *testing.B) { - for i := 0; i < b.N; i++ { - testCreate(b) - } -} - -func testCreateError(t testing.TB) { - var scenarios = []struct { - in *dto.MetricFamily - err string - }{ - // 0: No metric. - { - in: &dto.MetricFamily{ - Name: proto.String("name"), - Help: proto.String("doc string"), - Type: dto.MetricType_COUNTER.Enum(), - Metric: []*dto.Metric{}, - }, - err: "MetricFamily has no metrics", - }, - // 1: No metric name. - { - in: &dto.MetricFamily{ - Help: proto.String("doc string"), - Type: dto.MetricType_UNTYPED.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Untyped: &dto.Untyped{ - Value: proto.Float64(math.Inf(-1)), - }, - }, - }, - }, - err: "MetricFamily has no name", - }, - // 2: Wrong type. - { - in: &dto.MetricFamily{ - Name: proto.String("name"), - Help: proto.String("doc string"), - Type: dto.MetricType_COUNTER.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Untyped: &dto.Untyped{ - Value: proto.Float64(math.Inf(-1)), - }, - }, - }, - }, - err: "expected counter in metric", - }, - } - - for i, scenario := range scenarios { - var out bytes.Buffer - _, err := MetricFamilyToText(&out, scenario.in) - if err == nil { - t.Errorf("%d. expected error, got nil", i) - continue - } - if expected, got := scenario.err, err.Error(); strings.Index(got, expected) != 0 { - t.Errorf( - "%d. expected error starting with %q, got %q", - i, expected, got, - ) - } - } - -} - -func TestCreateError(t *testing.T) { - testCreateError(t) -} - -func BenchmarkCreateError(b *testing.B) { - for i := 0; i < b.N; i++ { - testCreateError(b) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse_test.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse_test.go deleted file mode 100644 index 589c87a9d..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse_test.go +++ /dev/null @@ -1,586 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package expfmt - -import ( - "math" - "strings" - "testing" - - "github.com/golang/protobuf/proto" - dto "github.com/prometheus/client_model/go" -) - -func testTextParse(t testing.TB) { - var scenarios = []struct { - in string - out []*dto.MetricFamily - }{ - // 0: Empty lines as input. - { - in: ` - -`, - out: []*dto.MetricFamily{}, - }, - // 1: Minimal case. - { - in: ` -minimal_metric 1.234 -another_metric -3e3 103948 -# Even that: -no_labels{} 3 -# HELP line for non-existing metric will be ignored. -`, - out: []*dto.MetricFamily{ - &dto.MetricFamily{ - Name: proto.String("minimal_metric"), - Type: dto.MetricType_UNTYPED.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Untyped: &dto.Untyped{ - Value: proto.Float64(1.234), - }, - }, - }, - }, - &dto.MetricFamily{ - Name: proto.String("another_metric"), - Type: dto.MetricType_UNTYPED.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Untyped: &dto.Untyped{ - Value: proto.Float64(-3e3), - }, - TimestampMs: proto.Int64(103948), - }, - }, - }, - &dto.MetricFamily{ - Name: proto.String("no_labels"), - Type: dto.MetricType_UNTYPED.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Untyped: &dto.Untyped{ - Value: proto.Float64(3), - }, - }, - }, - }, - }, - }, - // 2: Counters & gauges, docstrings, various whitespace, escape sequences. - { - in: ` -# A normal comment. -# -# TYPE name counter -name{labelname="val1",basename="basevalue"} NaN -name {labelname="val2",basename="base\"v\\al\nue"} 0.23 1234567890 -# HELP name two-line\n doc str\\ing - - # HELP name2 doc str"ing 2 - # TYPE name2 gauge -name2{labelname="val2" ,basename = "basevalue2" } +Inf 54321 -name2{ labelname = "val1" , }-Inf -`, - out: []*dto.MetricFamily{ - &dto.MetricFamily{ - Name: proto.String("name"), - Help: proto.String("two-line\n doc str\\ing"), - Type: dto.MetricType_COUNTER.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("labelname"), - Value: proto.String("val1"), - }, - &dto.LabelPair{ - Name: proto.String("basename"), - Value: proto.String("basevalue"), - }, - }, - Counter: &dto.Counter{ - Value: proto.Float64(math.NaN()), - }, - }, - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("labelname"), - Value: proto.String("val2"), - }, - &dto.LabelPair{ - Name: proto.String("basename"), - Value: proto.String("base\"v\\al\nue"), - }, - }, - Counter: &dto.Counter{ - Value: proto.Float64(.23), - }, - TimestampMs: proto.Int64(1234567890), - }, - }, - }, - &dto.MetricFamily{ - Name: proto.String("name2"), - Help: proto.String("doc str\"ing 2"), - Type: dto.MetricType_GAUGE.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("labelname"), - Value: proto.String("val2"), - }, - &dto.LabelPair{ - Name: proto.String("basename"), - Value: proto.String("basevalue2"), - }, - }, - Gauge: &dto.Gauge{ - Value: proto.Float64(math.Inf(+1)), - }, - TimestampMs: proto.Int64(54321), - }, - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("labelname"), - Value: proto.String("val1"), - }, - }, - Gauge: &dto.Gauge{ - Value: proto.Float64(math.Inf(-1)), - }, - }, - }, - }, - }, - }, - // 3: The evil summary, mixed with other types and funny comments. - { - in: ` -# TYPE my_summary summary -my_summary{n1="val1",quantile="0.5"} 110 -decoy -1 -2 -my_summary{n1="val1",quantile="0.9"} 140 1 -my_summary_count{n1="val1"} 42 -# Latest timestamp wins in case of a summary. -my_summary_sum{n1="val1"} 4711 2 -fake_sum{n1="val1"} 2001 -# TYPE another_summary summary -another_summary_count{n2="val2",n1="val1"} 20 -my_summary_count{n2="val2",n1="val1"} 5 5 -another_summary{n1="val1",n2="val2",quantile=".3"} -1.2 -my_summary_sum{n1="val2"} 08 15 -my_summary{n1="val3", quantile="0.2"} 4711 - my_summary{n1="val1",n2="val2",quantile="-12.34",} NaN -# some -# funny comments -# HELP -# HELP -# HELP my_summary -# HELP my_summary -`, - out: []*dto.MetricFamily{ - &dto.MetricFamily{ - Name: proto.String("fake_sum"), - Type: dto.MetricType_UNTYPED.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("n1"), - Value: proto.String("val1"), - }, - }, - Untyped: &dto.Untyped{ - Value: proto.Float64(2001), - }, - }, - }, - }, - &dto.MetricFamily{ - Name: proto.String("decoy"), - Type: dto.MetricType_UNTYPED.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Untyped: &dto.Untyped{ - Value: proto.Float64(-1), - }, - TimestampMs: proto.Int64(-2), - }, - }, - }, - &dto.MetricFamily{ - Name: proto.String("my_summary"), - Type: dto.MetricType_SUMMARY.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("n1"), - Value: proto.String("val1"), - }, - }, - Summary: &dto.Summary{ - SampleCount: proto.Uint64(42), - SampleSum: proto.Float64(4711), - Quantile: []*dto.Quantile{ - &dto.Quantile{ - Quantile: proto.Float64(0.5), - Value: proto.Float64(110), - }, - &dto.Quantile{ - Quantile: proto.Float64(0.9), - Value: proto.Float64(140), - }, - }, - }, - TimestampMs: proto.Int64(2), - }, - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("n2"), - Value: proto.String("val2"), - }, - &dto.LabelPair{ - Name: proto.String("n1"), - Value: proto.String("val1"), - }, - }, - Summary: &dto.Summary{ - SampleCount: proto.Uint64(5), - Quantile: []*dto.Quantile{ - &dto.Quantile{ - Quantile: proto.Float64(-12.34), - Value: proto.Float64(math.NaN()), - }, - }, - }, - TimestampMs: proto.Int64(5), - }, - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("n1"), - Value: proto.String("val2"), - }, - }, - Summary: &dto.Summary{ - SampleSum: proto.Float64(8), - }, - TimestampMs: proto.Int64(15), - }, - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("n1"), - Value: proto.String("val3"), - }, - }, - Summary: &dto.Summary{ - Quantile: []*dto.Quantile{ - &dto.Quantile{ - Quantile: proto.Float64(0.2), - Value: proto.Float64(4711), - }, - }, - }, - }, - }, - }, - &dto.MetricFamily{ - Name: proto.String("another_summary"), - Type: dto.MetricType_SUMMARY.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Label: []*dto.LabelPair{ - &dto.LabelPair{ - Name: proto.String("n2"), - Value: proto.String("val2"), - }, - &dto.LabelPair{ - Name: proto.String("n1"), - Value: proto.String("val1"), - }, - }, - Summary: &dto.Summary{ - SampleCount: proto.Uint64(20), - Quantile: []*dto.Quantile{ - &dto.Quantile{ - Quantile: proto.Float64(0.3), - Value: proto.Float64(-1.2), - }, - }, - }, - }, - }, - }, - }, - }, - // 4: The histogram. - { - in: ` -# HELP request_duration_microseconds The response latency. -# TYPE request_duration_microseconds histogram -request_duration_microseconds_bucket{le="100"} 123 -request_duration_microseconds_bucket{le="120"} 412 -request_duration_microseconds_bucket{le="144"} 592 -request_duration_microseconds_bucket{le="172.8"} 1524 -request_duration_microseconds_bucket{le="+Inf"} 2693 -request_duration_microseconds_sum 1.7560473e+06 -request_duration_microseconds_count 2693 -`, - out: []*dto.MetricFamily{ - { - Name: proto.String("request_duration_microseconds"), - Help: proto.String("The response latency."), - Type: dto.MetricType_HISTOGRAM.Enum(), - Metric: []*dto.Metric{ - &dto.Metric{ - Histogram: &dto.Histogram{ - SampleCount: proto.Uint64(2693), - SampleSum: proto.Float64(1756047.3), - Bucket: []*dto.Bucket{ - &dto.Bucket{ - UpperBound: proto.Float64(100), - CumulativeCount: proto.Uint64(123), - }, - &dto.Bucket{ - UpperBound: proto.Float64(120), - CumulativeCount: proto.Uint64(412), - }, - &dto.Bucket{ - UpperBound: proto.Float64(144), - CumulativeCount: proto.Uint64(592), - }, - &dto.Bucket{ - UpperBound: proto.Float64(172.8), - CumulativeCount: proto.Uint64(1524), - }, - &dto.Bucket{ - UpperBound: proto.Float64(math.Inf(+1)), - CumulativeCount: proto.Uint64(2693), - }, - }, - }, - }, - }, - }, - }, - }, - } - - for i, scenario := range scenarios { - out, err := parser.TextToMetricFamilies(strings.NewReader(scenario.in)) - if err != nil { - t.Errorf("%d. error: %s", i, err) - continue - } - if expected, got := len(scenario.out), len(out); expected != got { - t.Errorf( - "%d. expected %d MetricFamilies, got %d", - i, expected, got, - ) - } - for _, expected := range scenario.out { - got, ok := out[expected.GetName()] - if !ok { - t.Errorf( - "%d. expected MetricFamily %q, found none", - i, expected.GetName(), - ) - continue - } - if expected.String() != got.String() { - t.Errorf( - "%d. expected MetricFamily %s, got %s", - i, expected, got, - ) - } - } - } -} - -func TestTextParse(t *testing.T) { - testTextParse(t) -} - -func BenchmarkTextParse(b *testing.B) { - for i := 0; i < b.N; i++ { - testTextParse(b) - } -} - -func testTextParseError(t testing.TB) { - var scenarios = []struct { - in string - err string - }{ - // 0: No new-line at end of input. - { - in: `bla 3.14`, - err: "EOF", - }, - // 1: Invalid escape sequence in label value. - { - in: `metric{label="\t"} 3.14`, - err: "text format parsing error in line 1: invalid escape sequence", - }, - // 2: Newline in label value. - { - in: ` -metric{label="new -line"} 3.14 -`, - err: `text format parsing error in line 2: label value "new" contains unescaped new-line`, - }, - // 3: - { - in: `metric{@="bla"} 3.14`, - err: "text format parsing error in line 1: invalid label name for metric", - }, - // 4: - { - in: `metric{__name__="bla"} 3.14`, - err: `text format parsing error in line 1: label name "__name__" is reserved`, - }, - // 5: - { - in: `metric{label+="bla"} 3.14`, - err: "text format parsing error in line 1: expected '=' after label name", - }, - // 6: - { - in: `metric{label=bla} 3.14`, - err: "text format parsing error in line 1: expected '\"' at start of label value", - }, - // 7: - { - in: ` -# TYPE metric summary -metric{quantile="bla"} 3.14 -`, - err: "text format parsing error in line 3: expected float as value for 'quantile' label", - }, - // 8: - { - in: `metric{label="bla"+} 3.14`, - err: "text format parsing error in line 1: unexpected end of label value", - }, - // 9: - { - in: `metric{label="bla"} 3.14 2.72 -`, - err: "text format parsing error in line 1: expected integer as timestamp", - }, - // 10: - { - in: `metric{label="bla"} 3.14 2 3 -`, - err: "text format parsing error in line 1: spurious string after timestamp", - }, - // 11: - { - in: `metric{label="bla"} blubb -`, - err: "text format parsing error in line 1: expected float as value", - }, - // 12: - { - in: ` -# HELP metric one -# HELP metric two -`, - err: "text format parsing error in line 3: second HELP line for metric name", - }, - // 13: - { - in: ` -# TYPE metric counter -# TYPE metric untyped -`, - err: `text format parsing error in line 3: second TYPE line for metric name "metric", or TYPE reported after samples`, - }, - // 14: - { - in: ` -metric 4.12 -# TYPE metric counter -`, - err: `text format parsing error in line 3: second TYPE line for metric name "metric", or TYPE reported after samples`, - }, - // 14: - { - in: ` -# TYPE metric bla -`, - err: "text format parsing error in line 2: unknown metric type", - }, - // 15: - { - in: ` -# TYPE met-ric -`, - err: "text format parsing error in line 2: invalid metric name in comment", - }, - // 16: - { - in: `@invalidmetric{label="bla"} 3.14 2`, - err: "text format parsing error in line 1: invalid metric name", - }, - // 17: - { - in: `{label="bla"} 3.14 2`, - err: "text format parsing error in line 1: invalid metric name", - }, - // 18: - { - in: ` -# TYPE metric histogram -metric_bucket{le="bla"} 3.14 -`, - err: "text format parsing error in line 3: expected float as value for 'le' label", - }, - } - - for i, scenario := range scenarios { - _, err := parser.TextToMetricFamilies(strings.NewReader(scenario.in)) - if err == nil { - t.Errorf("%d. expected error, got nil", i) - continue - } - if expected, got := scenario.err, err.Error(); strings.Index(got, expected) != 0 { - t.Errorf( - "%d. expected error starting with %q, got %q", - i, expected, got, - ) - } - } - -} - -func TestTextParseError(t *testing.T) { - testTextParseError(t) -} - -func BenchmarkParseError(b *testing.B) { - for i := 0; i < b.N; i++ { - testTextParseError(b) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/labels_test.go b/Godeps/_workspace/src/github.com/prometheus/common/model/labels_test.go deleted file mode 100644 index ab17025c7..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/model/labels_test.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2013 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "sort" - "testing" -) - -func testLabelNames(t testing.TB) { - var scenarios = []struct { - in LabelNames - out LabelNames - }{ - { - in: LabelNames{"ZZZ", "zzz"}, - out: LabelNames{"ZZZ", "zzz"}, - }, - { - in: LabelNames{"aaa", "AAA"}, - out: LabelNames{"AAA", "aaa"}, - }, - } - - for i, scenario := range scenarios { - sort.Sort(scenario.in) - - for j, expected := range scenario.out { - if expected != scenario.in[j] { - t.Errorf("%d.%d expected %s, got %s", i, j, expected, scenario.in[j]) - } - } - } -} - -func TestLabelNames(t *testing.T) { - testLabelNames(t) -} - -func BenchmarkLabelNames(b *testing.B) { - for i := 0; i < b.N; i++ { - testLabelNames(b) - } -} - -func testLabelValues(t testing.TB) { - var scenarios = []struct { - in LabelValues - out LabelValues - }{ - { - in: LabelValues{"ZZZ", "zzz"}, - out: LabelValues{"ZZZ", "zzz"}, - }, - { - in: LabelValues{"aaa", "AAA"}, - out: LabelValues{"AAA", "aaa"}, - }, - } - - for i, scenario := range scenarios { - sort.Sort(scenario.in) - - for j, expected := range scenario.out { - if expected != scenario.in[j] { - t.Errorf("%d.%d expected %s, got %s", i, j, expected, scenario.in[j]) - } - } - } -} - -func TestLabelValues(t *testing.T) { - testLabelValues(t) -} - -func BenchmarkLabelValues(b *testing.B) { - for i := 0; i < b.N; i++ { - testLabelValues(b) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go b/Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go deleted file mode 100644 index 5c7cfceaf..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2013 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import "testing" - -func testMetric(t testing.TB) { - var scenarios = []struct { - input LabelSet - fingerprint Fingerprint - fastFingerprint Fingerprint - }{ - { - input: LabelSet{}, - fingerprint: 14695981039346656037, - fastFingerprint: 14695981039346656037, - }, - { - input: LabelSet{ - "first_name": "electro", - "occupation": "robot", - "manufacturer": "westinghouse", - }, - fingerprint: 5911716720268894962, - fastFingerprint: 11310079640881077873, - }, - { - input: LabelSet{ - "x": "y", - }, - fingerprint: 8241431561484471700, - fastFingerprint: 13948396922932177635, - }, - { - input: LabelSet{ - "a": "bb", - "b": "c", - }, - fingerprint: 3016285359649981711, - fastFingerprint: 3198632812309449502, - }, - { - input: LabelSet{ - "a": "b", - "bb": "c", - }, - fingerprint: 7122421792099404749, - fastFingerprint: 5774953389407657638, - }, - } - - for i, scenario := range scenarios { - input := Metric(scenario.input) - - if scenario.fingerprint != input.Fingerprint() { - t.Errorf("%d. expected %d, got %d", i, scenario.fingerprint, input.Fingerprint()) - } - if scenario.fastFingerprint != input.FastFingerprint() { - t.Errorf("%d. expected %d, got %d", i, scenario.fastFingerprint, input.FastFingerprint()) - } - } -} - -func TestMetric(t *testing.T) { - testMetric(t) -} - -func BenchmarkMetric(b *testing.B) { - for i := 0; i < b.N; i++ { - testMetric(b) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go b/Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go deleted file mode 100644 index d9c665f8c..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright 2014 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "runtime" - "sync" - "testing" -) - -func TestLabelsToSignature(t *testing.T) { - var scenarios = []struct { - in map[string]string - out uint64 - }{ - { - in: map[string]string{}, - out: 14695981039346656037, - }, - { - in: map[string]string{"name": "garland, briggs", "fear": "love is not enough"}, - out: 5799056148416392346, - }, - } - - for i, scenario := range scenarios { - actual := LabelsToSignature(scenario.in) - - if actual != scenario.out { - t.Errorf("%d. expected %d, got %d", i, scenario.out, actual) - } - } -} - -func TestMetricToFingerprint(t *testing.T) { - var scenarios = []struct { - in LabelSet - out Fingerprint - }{ - { - in: LabelSet{}, - out: 14695981039346656037, - }, - { - in: LabelSet{"name": "garland, briggs", "fear": "love is not enough"}, - out: 5799056148416392346, - }, - } - - for i, scenario := range scenarios { - actual := labelSetToFingerprint(scenario.in) - - if actual != scenario.out { - t.Errorf("%d. expected %d, got %d", i, scenario.out, actual) - } - } -} - -func TestMetricToFastFingerprint(t *testing.T) { - var scenarios = []struct { - in LabelSet - out Fingerprint - }{ - { - in: LabelSet{}, - out: 14695981039346656037, - }, - { - in: LabelSet{"name": "garland, briggs", "fear": "love is not enough"}, - out: 12952432476264840823, - }, - } - - for i, scenario := range scenarios { - actual := labelSetToFastFingerprint(scenario.in) - - if actual != scenario.out { - t.Errorf("%d. expected %d, got %d", i, scenario.out, actual) - } - } -} - -func TestSignatureForLabels(t *testing.T) { - var scenarios = []struct { - in Metric - labels LabelNames - out uint64 - }{ - { - in: Metric{}, - labels: nil, - out: 14695981039346656037, - }, - { - in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, - labels: LabelNames{"fear", "name"}, - out: 5799056148416392346, - }, - { - in: Metric{"name": "garland, briggs", "fear": "love is not enough", "foo": "bar"}, - labels: LabelNames{"fear", "name"}, - out: 5799056148416392346, - }, - { - in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, - labels: LabelNames{}, - out: 14695981039346656037, - }, - { - in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, - labels: nil, - out: 14695981039346656037, - }, - } - - for i, scenario := range scenarios { - actual := SignatureForLabels(scenario.in, scenario.labels...) - - if actual != scenario.out { - t.Errorf("%d. expected %d, got %d", i, scenario.out, actual) - } - } -} - -func TestSignatureWithoutLabels(t *testing.T) { - var scenarios = []struct { - in Metric - labels map[LabelName]struct{} - out uint64 - }{ - { - in: Metric{}, - labels: nil, - out: 14695981039346656037, - }, - { - in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, - labels: map[LabelName]struct{}{"fear": struct{}{}, "name": struct{}{}}, - out: 14695981039346656037, - }, - { - in: Metric{"name": "garland, briggs", "fear": "love is not enough", "foo": "bar"}, - labels: map[LabelName]struct{}{"foo": struct{}{}}, - out: 5799056148416392346, - }, - { - in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, - labels: map[LabelName]struct{}{}, - out: 5799056148416392346, - }, - { - in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, - labels: nil, - out: 5799056148416392346, - }, - } - - for i, scenario := range scenarios { - actual := SignatureWithoutLabels(scenario.in, scenario.labels) - - if actual != scenario.out { - t.Errorf("%d. expected %d, got %d", i, scenario.out, actual) - } - } -} - -func benchmarkLabelToSignature(b *testing.B, l map[string]string, e uint64) { - for i := 0; i < b.N; i++ { - if a := LabelsToSignature(l); a != e { - b.Fatalf("expected signature of %d for %s, got %d", e, l, a) - } - } -} - -func BenchmarkLabelToSignatureScalar(b *testing.B) { - benchmarkLabelToSignature(b, nil, 14695981039346656037) -} - -func BenchmarkLabelToSignatureSingle(b *testing.B) { - benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value"}, 5146282821936882169) -} - -func BenchmarkLabelToSignatureDouble(b *testing.B) { - benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value"}, 3195800080984914717) -} - -func BenchmarkLabelToSignatureTriple(b *testing.B) { - benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 13843036195897128121) -} - -func benchmarkMetricToFingerprint(b *testing.B, ls LabelSet, e Fingerprint) { - for i := 0; i < b.N; i++ { - if a := labelSetToFingerprint(ls); a != e { - b.Fatalf("expected signature of %d for %s, got %d", e, ls, a) - } - } -} - -func BenchmarkMetricToFingerprintScalar(b *testing.B) { - benchmarkMetricToFingerprint(b, nil, 14695981039346656037) -} - -func BenchmarkMetricToFingerprintSingle(b *testing.B) { - benchmarkMetricToFingerprint(b, LabelSet{"first-label": "first-label-value"}, 5146282821936882169) -} - -func BenchmarkMetricToFingerprintDouble(b *testing.B) { - benchmarkMetricToFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value"}, 3195800080984914717) -} - -func BenchmarkMetricToFingerprintTriple(b *testing.B) { - benchmarkMetricToFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 13843036195897128121) -} - -func benchmarkMetricToFastFingerprint(b *testing.B, ls LabelSet, e Fingerprint) { - for i := 0; i < b.N; i++ { - if a := labelSetToFastFingerprint(ls); a != e { - b.Fatalf("expected signature of %d for %s, got %d", e, ls, a) - } - } -} - -func BenchmarkMetricToFastFingerprintScalar(b *testing.B) { - benchmarkMetricToFastFingerprint(b, nil, 14695981039346656037) -} - -func BenchmarkMetricToFastFingerprintSingle(b *testing.B) { - benchmarkMetricToFastFingerprint(b, LabelSet{"first-label": "first-label-value"}, 5147259542624943964) -} - -func BenchmarkMetricToFastFingerprintDouble(b *testing.B) { - benchmarkMetricToFastFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value"}, 18269973311206963528) -} - -func BenchmarkMetricToFastFingerprintTriple(b *testing.B) { - benchmarkMetricToFastFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676) -} - -func BenchmarkEmptyLabelSignature(b *testing.B) { - input := []map[string]string{nil, {}} - - var ms runtime.MemStats - runtime.ReadMemStats(&ms) - - alloc := ms.Alloc - - for _, labels := range input { - LabelsToSignature(labels) - } - - runtime.ReadMemStats(&ms) - - if got := ms.Alloc; alloc != got { - b.Fatal("expected LabelsToSignature with empty labels not to perform allocations") - } -} - -func benchmarkMetricToFastFingerprintConc(b *testing.B, ls LabelSet, e Fingerprint, concLevel int) { - var start, end sync.WaitGroup - start.Add(1) - end.Add(concLevel) - - for i := 0; i < concLevel; i++ { - go func() { - start.Wait() - for j := b.N / concLevel; j >= 0; j-- { - if a := labelSetToFastFingerprint(ls); a != e { - b.Fatalf("expected signature of %d for %s, got %d", e, ls, a) - } - } - end.Done() - }() - } - b.ResetTimer() - start.Done() - end.Wait() -} - -func BenchmarkMetricToFastFingerprintTripleConc1(b *testing.B) { - benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 1) -} - -func BenchmarkMetricToFastFingerprintTripleConc2(b *testing.B) { - benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 2) -} - -func BenchmarkMetricToFastFingerprintTripleConc4(b *testing.B) { - benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 4) -} - -func BenchmarkMetricToFastFingerprintTripleConc8(b *testing.B) { - benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 8) -} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/time_test.go b/Godeps/_workspace/src/github.com/prometheus/common/model/time_test.go deleted file mode 100644 index 9013a6277..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/model/time_test.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2013 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "testing" - "time" -) - -func TestComparators(t *testing.T) { - t1a := TimeFromUnix(0) - t1b := TimeFromUnix(0) - t2 := TimeFromUnix(2*second - 1) - - if !t1a.Equal(t1b) { - t.Fatalf("Expected %s to be equal to %s", t1a, t1b) - } - if t1a.Equal(t2) { - t.Fatalf("Expected %s to not be equal to %s", t1a, t2) - } - - if !t1a.Before(t2) { - t.Fatalf("Expected %s to be before %s", t1a, t2) - } - if t1a.Before(t1b) { - t.Fatalf("Expected %s to not be before %s", t1a, t1b) - } - - if !t2.After(t1a) { - t.Fatalf("Expected %s to be after %s", t2, t1a) - } - if t1b.After(t1a) { - t.Fatalf("Expected %s to not be after %s", t1b, t1a) - } -} - -func TestTimeConversions(t *testing.T) { - unixSecs := int64(1136239445) - unixNsecs := int64(123456789) - unixNano := unixSecs*1e9 + unixNsecs - - t1 := time.Unix(unixSecs, unixNsecs-unixNsecs%nanosPerTick) - t2 := time.Unix(unixSecs, unixNsecs) - - ts := TimeFromUnixNano(unixNano) - if !ts.Time().Equal(t1) { - t.Fatalf("Expected %s, got %s", t1, ts.Time()) - } - - // Test available precision. - ts = TimeFromUnixNano(t2.UnixNano()) - if !ts.Time().Equal(t1) { - t.Fatalf("Expected %s, got %s", t1, ts.Time()) - } - - if ts.UnixNano() != unixNano-unixNano%nanosPerTick { - t.Fatalf("Expected %d, got %d", unixNano, ts.UnixNano()) - } -} - -func TestDuration(t *testing.T) { - duration := time.Second + time.Minute + time.Hour - goTime := time.Unix(1136239445, 0) - - ts := TimeFromUnix(goTime.Unix()) - if !goTime.Add(duration).Equal(ts.Add(duration).Time()) { - t.Fatalf("Expected %s to be equal to %s", goTime.Add(duration), ts.Add(duration)) - } - - earlier := ts.Add(-duration) - delta := ts.Sub(earlier) - if delta != duration { - t.Fatalf("Expected %s to be equal to %s", delta, duration) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go b/Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go deleted file mode 100644 index 2e9c7eb09..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright 2013 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "encoding/json" - "math" - "reflect" - "sort" - "testing" -) - -func TestSamplePairJSON(t *testing.T) { - input := []struct { - plain string - value SamplePair - }{ - { - plain: `[1234.567,"123.1"]`, - value: SamplePair{ - Value: 123.1, - Timestamp: 1234567, - }, - }, - } - - for _, test := range input { - b, err := json.Marshal(test.value) - if err != nil { - t.Error(err) - continue - } - - if string(b) != test.plain { - t.Errorf("encoding error: expected %q, got %q", test.plain, b) - continue - } - - var sp SamplePair - err = json.Unmarshal(b, &sp) - if err != nil { - t.Error(err) - continue - } - - if sp != test.value { - t.Errorf("decoding error: expected %v, got %v", test.value, sp) - } - } -} - -func TestSampleJSON(t *testing.T) { - input := []struct { - plain string - value Sample - }{ - { - plain: `{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]}`, - value: Sample{ - Metric: Metric{ - MetricNameLabel: "test_metric", - }, - Value: 123.1, - Timestamp: 1234567, - }, - }, - } - - for _, test := range input { - b, err := json.Marshal(test.value) - if err != nil { - t.Error(err) - continue - } - - if string(b) != test.plain { - t.Errorf("encoding error: expected %q, got %q", test.plain, b) - continue - } - - var sv Sample - err = json.Unmarshal(b, &sv) - if err != nil { - t.Error(err) - continue - } - - if !reflect.DeepEqual(sv, test.value) { - t.Errorf("decoding error: expected %v, got %v", test.value, sv) - } - } -} - -func TestVectorJSON(t *testing.T) { - input := []struct { - plain string - value Vector - }{ - { - plain: `[]`, - value: Vector{}, - }, - { - plain: `[{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]}]`, - value: Vector{&Sample{ - Metric: Metric{ - MetricNameLabel: "test_metric", - }, - Value: 123.1, - Timestamp: 1234567, - }}, - }, - { - plain: `[{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]},{"metric":{"foo":"bar"},"value":[1.234,"+Inf"]}]`, - value: Vector{ - &Sample{ - Metric: Metric{ - MetricNameLabel: "test_metric", - }, - Value: 123.1, - Timestamp: 1234567, - }, - &Sample{ - Metric: Metric{ - "foo": "bar", - }, - Value: SampleValue(math.Inf(1)), - Timestamp: 1234, - }, - }, - }, - } - - for _, test := range input { - b, err := json.Marshal(test.value) - if err != nil { - t.Error(err) - continue - } - - if string(b) != test.plain { - t.Errorf("encoding error: expected %q, got %q", test.plain, b) - continue - } - - var vec Vector - err = json.Unmarshal(b, &vec) - if err != nil { - t.Error(err) - continue - } - - if !reflect.DeepEqual(vec, test.value) { - t.Errorf("decoding error: expected %v, got %v", test.value, vec) - } - } -} - -func TestScalarJSON(t *testing.T) { - input := []struct { - plain string - value Scalar - }{ - { - plain: `[123.456,"456"]`, - value: Scalar{ - Timestamp: 123456, - Value: 456, - }, - }, - { - plain: `[123123.456,"+Inf"]`, - value: Scalar{ - Timestamp: 123123456, - Value: SampleValue(math.Inf(1)), - }, - }, - { - plain: `[123123.456,"-Inf"]`, - value: Scalar{ - Timestamp: 123123456, - Value: SampleValue(math.Inf(-1)), - }, - }, - } - - for _, test := range input { - b, err := json.Marshal(test.value) - if err != nil { - t.Error(err) - continue - } - - if string(b) != test.plain { - t.Errorf("encoding error: expected %q, got %q", test.plain, b) - continue - } - - var sv Scalar - err = json.Unmarshal(b, &sv) - if err != nil { - t.Error(err) - continue - } - - if sv != test.value { - t.Errorf("decoding error: expected %v, got %v", test.value, sv) - } - } -} - -func TestStringJSON(t *testing.T) { - input := []struct { - plain string - value String - }{ - { - plain: `[123.456,"test"]`, - value: String{ - Timestamp: 123456, - Value: "test", - }, - }, - { - plain: `[123123.456,"台北"]`, - value: String{ - Timestamp: 123123456, - Value: "台北", - }, - }, - } - - for _, test := range input { - b, err := json.Marshal(test.value) - if err != nil { - t.Error(err) - continue - } - - if string(b) != test.plain { - t.Errorf("encoding error: expected %q, got %q", test.plain, b) - continue - } - - var sv String - err = json.Unmarshal(b, &sv) - if err != nil { - t.Error(err) - continue - } - - if sv != test.value { - t.Errorf("decoding error: expected %v, got %v", test.value, sv) - } - } -} - -func TestVectorSort(t *testing.T) { - input := Vector{ - &Sample{ - Metric: Metric{ - MetricNameLabel: "A", - }, - Timestamp: 1, - }, - &Sample{ - Metric: Metric{ - MetricNameLabel: "A", - }, - Timestamp: 2, - }, - &Sample{ - Metric: Metric{ - MetricNameLabel: "C", - }, - Timestamp: 1, - }, - &Sample{ - Metric: Metric{ - MetricNameLabel: "C", - }, - Timestamp: 2, - }, - &Sample{ - Metric: Metric{ - MetricNameLabel: "B", - }, - Timestamp: 1, - }, - &Sample{ - Metric: Metric{ - MetricNameLabel: "B", - }, - Timestamp: 2, - }, - } - - expected := Vector{ - &Sample{ - Metric: Metric{ - MetricNameLabel: "A", - }, - Timestamp: 1, - }, - &Sample{ - Metric: Metric{ - MetricNameLabel: "A", - }, - Timestamp: 2, - }, - &Sample{ - Metric: Metric{ - MetricNameLabel: "B", - }, - Timestamp: 1, - }, - &Sample{ - Metric: Metric{ - MetricNameLabel: "B", - }, - Timestamp: 2, - }, - &Sample{ - Metric: Metric{ - MetricNameLabel: "C", - }, - Timestamp: 1, - }, - &Sample{ - Metric: Metric{ - MetricNameLabel: "C", - }, - Timestamp: 2, - }, - } - - sort.Sort(input) - - for i, actual := range input { - actualFp := actual.Metric.Fingerprint() - expectedFp := expected[i].Metric.Fingerprint() - - if actualFp != expectedFp { - t.Fatalf("%d. Incorrect fingerprint. Got %s; want %s", i, actualFp.String(), expectedFp.String()) - } - - if actual.Timestamp != expected[i].Timestamp { - t.Fatalf("%d. Incorrect timestamp. Got %s; want %s", i, actual.Timestamp, expected[i].Timestamp) - } - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/cmdline b/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/cmdline deleted file mode 100644 index d2d8ef88764f6c9f819285f534aa70835a54950c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16 XcmXTR%w;G^EiTbZ&u7p!G++P#E?@+m diff --git a/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/fd/0 b/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/fd/0 deleted file mode 100644 index e69de29bb..000000000 diff --git a/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/fd/1 b/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/fd/1 deleted file mode 100644 index e69de29bb..000000000 diff --git a/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/fd/2 b/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/fd/2 deleted file mode 100644 index e69de29bb..000000000 diff --git a/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/fd/3 b/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/fd/3 deleted file mode 100644 index e69de29bb..000000000 diff --git a/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/fd/4 b/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/fd/4 deleted file mode 100644 index e69de29bb..000000000 diff --git a/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/limits b/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/limits deleted file mode 100644 index 23c6b6898..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/limits +++ /dev/null @@ -1,17 +0,0 @@ -Limit Soft Limit Hard Limit Units -Max cpu time unlimited unlimited seconds -Max file size unlimited unlimited bytes -Max data size unlimited unlimited bytes -Max stack size 8388608 unlimited bytes -Max core file size 0 unlimited bytes -Max resident set unlimited unlimited bytes -Max processes 62898 62898 processes -Max open files 2048 4096 files -Max locked memory 65536 65536 bytes -Max address space unlimited unlimited bytes -Max file locks unlimited unlimited locks -Max pending signals 62898 62898 signals -Max msgqueue size 819200 819200 bytes -Max nice priority 0 0 -Max realtime priority 0 0 -Max realtime timeout unlimited unlimited us diff --git a/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/stat b/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/stat deleted file mode 100644 index 438aaa9dc..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/stat +++ /dev/null @@ -1 +0,0 @@ -26231 (vim) R 5392 7446 5392 34835 7446 4218880 32533 309516 26 82 1677 44 158 99 20 0 1 0 82375 56274944 1981 18446744073709551615 4194304 6294284 140736914091744 140736914087944 139965136429984 0 0 12288 1870679807 0 0 0 17 0 0 0 31 0 0 8391624 8481048 16420864 140736914093252 140736914093279 140736914093279 140736914096107 0 diff --git a/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/584/stat b/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/584/stat deleted file mode 100644 index 65b9369d1..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/584/stat +++ /dev/null @@ -1,2 +0,0 @@ -1020 ((a b ) ( c d) ) R 28378 1020 28378 34842 1020 4218880 286 0 0 0 0 0 0 0 20 0 1 0 10839175 10395648 155 18446744073709551615 4194304 4238788 140736466511168 140736466511168 140609271124624 0 0 0 0 0 0 0 17 5 0 0 0 0 0 6336016 6337300 25579520 140736466515030 140736466515061 140736466515061 140736466518002 0 -#!/bin/cat /proc/self/stat diff --git a/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/stat b/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/stat deleted file mode 100644 index dabb96f74..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/stat +++ /dev/null @@ -1,16 +0,0 @@ -cpu 301854 612 111922 8979004 3552 2 3944 0 0 0 -cpu0 44490 19 21045 1087069 220 1 3410 0 0 0 -cpu1 47869 23 16474 1110787 591 0 46 0 0 0 -cpu2 46504 36 15916 1112321 441 0 326 0 0 0 -cpu3 47054 102 15683 1113230 533 0 60 0 0 0 -cpu4 28413 25 10776 1140321 217 0 8 0 0 0 -cpu5 29271 101 11586 1136270 672 0 30 0 0 0 -cpu6 29152 36 10276 1139721 319 0 29 0 0 0 -cpu7 29098 268 10164 1139282 555 0 31 0 0 0 -intr 8885917 17 0 0 0 0 0 0 0 1 79281 0 0 0 0 0 0 0 231237 0 0 0 0 250586 103 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 223424 190745 13 906 1283803 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -ctxt 38014093 -btime 1418183276 -processes 26442 -procs_running 2 -procs_blocked 0 -softirq 5057579 250191 1481983 1647 211099 186066 0 1783454 622196 12499 508444 diff --git a/Godeps/_workspace/src/github.com/prometheus/procfs/fs_test.go b/Godeps/_workspace/src/github.com/prometheus/procfs/fs_test.go deleted file mode 100644 index 91f1c6c97..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/procfs/fs_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package procfs - -import "testing" - -func TestNewFS(t *testing.T) { - if _, err := NewFS("foobar"); err == nil { - t.Error("want NewFS to fail for non-existing mount point") - } - - if _, err := NewFS("procfs.go"); err == nil { - t.Error("want NewFS to fail if mount point is not a directory") - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/procfs/proc_limits_test.go b/Godeps/_workspace/src/github.com/prometheus/procfs/proc_limits_test.go deleted file mode 100644 index ca7a254da..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/procfs/proc_limits_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package procfs - -import "testing" - -func TestNewLimits(t *testing.T) { - fs, err := NewFS("fixtures") - if err != nil { - t.Fatal(err) - } - - p, err := fs.NewProc(26231) - if err != nil { - t.Fatal(err) - } - - l, err := p.NewLimits() - if err != nil { - t.Fatal(err) - } - - for _, test := range []struct { - name string - want int - got int - }{ - {name: "cpu time", want: -1, got: l.CPUTime}, - {name: "open files", want: 2048, got: l.OpenFiles}, - {name: "msgqueue size", want: 819200, got: l.MsqqueueSize}, - {name: "nice priority", want: 0, got: l.NicePriority}, - {name: "address space", want: -1, got: l.AddressSpace}, - } { - if test.want != test.got { - t.Errorf("want %s %d, got %d", test.name, test.want, test.got) - } - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/procfs/proc_stat_test.go b/Godeps/_workspace/src/github.com/prometheus/procfs/proc_stat_test.go deleted file mode 100644 index e4d5cacfa..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/procfs/proc_stat_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package procfs - -import "testing" - -func TestProcStat(t *testing.T) { - fs, err := NewFS("fixtures") - if err != nil { - t.Fatal(err) - } - - p, err := fs.NewProc(26231) - if err != nil { - t.Fatal(err) - } - - s, err := p.NewStat() - if err != nil { - t.Fatal(err) - } - - for _, test := range []struct { - name string - want int - got int - }{ - {name: "pid", want: 26231, got: s.PID}, - {name: "user time", want: 1677, got: int(s.UTime)}, - {name: "system time", want: 44, got: int(s.STime)}, - {name: "start time", want: 82375, got: int(s.Starttime)}, - {name: "virtual memory size", want: 56274944, got: s.VSize}, - {name: "resident set size", want: 1981, got: s.RSS}, - } { - if test.want != test.got { - t.Errorf("want %s %d, got %d", test.name, test.want, test.got) - } - } -} - -func TestProcStatComm(t *testing.T) { - s1, err := testProcStat(26231) - if err != nil { - t.Fatal(err) - } - if want, got := "vim", s1.Comm; want != got { - t.Errorf("want comm %s, got %s", want, got) - } - - s2, err := testProcStat(584) - if err != nil { - t.Fatal(err) - } - if want, got := "(a b ) ( c d) ", s2.Comm; want != got { - t.Errorf("want comm %s, got %s", want, got) - } -} - -func TestProcStatVirtualMemory(t *testing.T) { - s, err := testProcStat(26231) - if err != nil { - t.Fatal(err) - } - - if want, got := 56274944, s.VirtualMemory(); want != got { - t.Errorf("want virtual memory %d, got %d", want, got) - } -} - -func TestProcStatResidentMemory(t *testing.T) { - s, err := testProcStat(26231) - if err != nil { - t.Fatal(err) - } - - if want, got := 1981*4096, s.ResidentMemory(); want != got { - t.Errorf("want resident memory %d, got %d", want, got) - } -} - -func TestProcStatStartTime(t *testing.T) { - s, err := testProcStat(26231) - if err != nil { - t.Fatal(err) - } - - time, err := s.StartTime() - if err != nil { - t.Fatal(err) - } - if want, got := 1418184099.75, time; want != got { - t.Errorf("want start time %f, got %f", want, got) - } -} - -func TestProcStatCPUTime(t *testing.T) { - s, err := testProcStat(26231) - if err != nil { - t.Fatal(err) - } - - if want, got := 17.21, s.CPUTime(); want != got { - t.Errorf("want cpu time %f, got %f", want, got) - } -} - -func testProcStat(pid int) (ProcStat, error) { - p, err := testProcess(pid) - if err != nil { - return ProcStat{}, err - } - - return p.NewStat() -} diff --git a/Godeps/_workspace/src/github.com/prometheus/procfs/proc_test.go b/Godeps/_workspace/src/github.com/prometheus/procfs/proc_test.go deleted file mode 100644 index bd234a14c..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/procfs/proc_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package procfs - -import ( - "os" - "reflect" - "sort" - "testing" -) - -func TestSelf(t *testing.T) { - p1, err := NewProc(os.Getpid()) - if err != nil { - t.Fatal(err) - } - p2, err := Self() - if err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(p1, p2) { - t.Errorf("want process %v to equal %v", p1, p2) - } -} - -func TestAllProcs(t *testing.T) { - fs, err := NewFS("fixtures") - if err != nil { - t.Fatal(err) - } - procs, err := fs.AllProcs() - if err != nil { - t.Fatal(err) - } - sort.Sort(procs) - for i, p := range []*Proc{{PID: 584}, {PID: 26231}} { - if want, got := p.PID, procs[i].PID; want != got { - t.Errorf("want processes %d, got %d", want, got) - } - } -} - -func TestCmdLine(t *testing.T) { - p1, err := testProcess(26231) - if err != nil { - t.Fatal(err) - } - c, err := p1.CmdLine() - if err != nil { - t.Fatal(err) - } - if want := []string{"vim", "test.go", "+10"}; !reflect.DeepEqual(want, c) { - t.Errorf("want cmdline %v, got %v", want, c) - } -} - -func TestFileDescriptors(t *testing.T) { - p1, err := testProcess(26231) - if err != nil { - t.Fatal(err) - } - fds, err := p1.FileDescriptors() - if err != nil { - t.Fatal(err) - } - sort.Sort(byUintptr(fds)) - if want := []uintptr{0, 1, 2, 3, 4}; !reflect.DeepEqual(want, fds) { - t.Errorf("want fds %v, got %v", want, fds) - } - - p2, err := Self() - if err != nil { - t.Fatal(err) - } - - fdsBefore, err := p2.FileDescriptors() - if err != nil { - t.Fatal(err) - } - - s, err := os.Open("fixtures") - if err != nil { - t.Fatal(err) - } - defer s.Close() - - fdsAfter, err := p2.FileDescriptors() - if err != nil { - t.Fatal(err) - } - - if len(fdsBefore)+1 != len(fdsAfter) { - t.Errorf("want fds %v+1 to equal %v", fdsBefore, fdsAfter) - } -} - -func TestFileDescriptorsLen(t *testing.T) { - p1, err := testProcess(26231) - if err != nil { - t.Fatal(err) - } - l, err := p1.FileDescriptorsLen() - if err != nil { - t.Fatal(err) - } - if want, got := 5, l; want != got { - t.Errorf("want fds %d, got %d", want, got) - } -} - -func testProcess(pid int) (Proc, error) { - fs, err := NewFS("fixtures") - if err != nil { - return Proc{}, err - } - - return fs.NewProc(pid) -} - -type byUintptr []uintptr - -func (a byUintptr) Len() int { return len(a) } -func (a byUintptr) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a byUintptr) Less(i, j int) bool { return a[i] < a[j] } diff --git a/Godeps/_workspace/src/github.com/prometheus/procfs/stat_test.go b/Godeps/_workspace/src/github.com/prometheus/procfs/stat_test.go deleted file mode 100644 index 24b5d61f8..000000000 --- a/Godeps/_workspace/src/github.com/prometheus/procfs/stat_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package procfs - -import "testing" - -func TestStat(t *testing.T) { - fs, err := NewFS("fixtures") - if err != nil { - t.Fatal(err) - } - - s, err := fs.NewStat() - if err != nil { - t.Fatal(err) - } - - if want, got := int64(1418183276), s.BootTime; want != got { - t.Errorf("want boot time %d, got %d", want, got) - } -} diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/block_test.go b/Godeps/_workspace/src/github.com/russross/blackfriday/block_test.go deleted file mode 100644 index b726c070c..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/block_test.go +++ /dev/null @@ -1,1296 +0,0 @@ -// -// Blackfriday Markdown Processor -// Available at http://github.com/russross/blackfriday -// -// Copyright © 2011 Russ Ross . -// Distributed under the Simplified BSD License. -// See README.md for details. -// - -// -// Unit tests for block parsing -// - -package blackfriday - -import ( - "testing" -) - -func runMarkdownBlockWithRenderer(input string, extensions int, renderer Renderer) string { - return string(Markdown([]byte(input), renderer, extensions)) -} - -func runMarkdownBlock(input string, extensions int) string { - htmlFlags := 0 - htmlFlags |= HTML_USE_XHTML - - renderer := HtmlRenderer(htmlFlags, "", "") - - return runMarkdownBlockWithRenderer(input, extensions, renderer) -} - -func runnerWithRendererParameters(parameters HtmlRendererParameters) func(string, int) string { - return func(input string, extensions int) string { - htmlFlags := 0 - htmlFlags |= HTML_USE_XHTML - - renderer := HtmlRendererWithParameters(htmlFlags, "", "", parameters) - - return runMarkdownBlockWithRenderer(input, extensions, renderer) - } -} - -func doTestsBlock(t *testing.T, tests []string, extensions int) { - doTestsBlockWithRunner(t, tests, extensions, runMarkdownBlock) -} - -func doTestsBlockWithRunner(t *testing.T, tests []string, extensions int, runner func(string, int) string) { - // catch and report panics - var candidate string - defer func() { - if err := recover(); err != nil { - t.Errorf("\npanic while processing [%#v]: %s\n", candidate, err) - } - }() - - for i := 0; i+1 < len(tests); i += 2 { - input := tests[i] - candidate = input - expected := tests[i+1] - actual := runner(candidate, extensions) - if actual != expected { - t.Errorf("\nInput [%#v]\nExpected[%#v]\nActual [%#v]", - candidate, expected, actual) - } - - // now test every substring to stress test bounds checking - if !testing.Short() { - for start := 0; start < len(input); start++ { - for end := start + 1; end <= len(input); end++ { - candidate = input[start:end] - _ = runMarkdownBlock(candidate, extensions) - } - } - } - } -} - -func TestPrefixHeaderNoExtensions(t *testing.T) { - var tests = []string{ - "# Header 1\n", - "

Header 1

\n", - - "## Header 2\n", - "

Header 2

\n", - - "### Header 3\n", - "

Header 3

\n", - - "#### Header 4\n", - "

Header 4

\n", - - "##### Header 5\n", - "
Header 5
\n", - - "###### Header 6\n", - "
Header 6
\n", - - "####### Header 7\n", - "
# Header 7
\n", - - "#Header 1\n", - "

Header 1

\n", - - "##Header 2\n", - "

Header 2

\n", - - "###Header 3\n", - "

Header 3

\n", - - "####Header 4\n", - "

Header 4

\n", - - "#####Header 5\n", - "
Header 5
\n", - - "######Header 6\n", - "
Header 6
\n", - - "#######Header 7\n", - "
#Header 7
\n", - - "Hello\n# Header 1\nGoodbye\n", - "

Hello

\n\n

Header 1

\n\n

Goodbye

\n", - - "* List\n# Header\n* List\n", - "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", - - "* List\n#Header\n* List\n", - "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", - - "* List\n * Nested list\n # Nested header\n", - "
    \n
  • List

    \n\n
      \n
    • Nested list

      \n\n" + - "

      Nested header

    • \n
  • \n
\n", - } - doTestsBlock(t, tests, 0) -} - -func TestPrefixHeaderSpaceExtension(t *testing.T) { - var tests = []string{ - "# Header 1\n", - "

Header 1

\n", - - "## Header 2\n", - "

Header 2

\n", - - "### Header 3\n", - "

Header 3

\n", - - "#### Header 4\n", - "

Header 4

\n", - - "##### Header 5\n", - "
Header 5
\n", - - "###### Header 6\n", - "
Header 6
\n", - - "####### Header 7\n", - "

####### Header 7

\n", - - "#Header 1\n", - "

#Header 1

\n", - - "##Header 2\n", - "

##Header 2

\n", - - "###Header 3\n", - "

###Header 3

\n", - - "####Header 4\n", - "

####Header 4

\n", - - "#####Header 5\n", - "

#####Header 5

\n", - - "######Header 6\n", - "

######Header 6

\n", - - "#######Header 7\n", - "

#######Header 7

\n", - - "Hello\n# Header 1\nGoodbye\n", - "

Hello

\n\n

Header 1

\n\n

Goodbye

\n", - - "* List\n# Header\n* List\n", - "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", - - "* List\n#Header\n* List\n", - "
    \n
  • List\n#Header
  • \n
  • List
  • \n
\n", - - "* List\n * Nested list\n # Nested header\n", - "
    \n
  • List

    \n\n
      \n
    • Nested list

      \n\n" + - "

      Nested header

    • \n
  • \n
\n", - } - doTestsBlock(t, tests, EXTENSION_SPACE_HEADERS) -} - -func TestPrefixHeaderIdExtension(t *testing.T) { - var tests = []string{ - "# Header 1 {#someid}\n", - "

Header 1

\n", - - "# Header 1 {#someid} \n", - "

Header 1

\n", - - "# Header 1 {#someid}\n", - "

Header 1

\n", - - "# Header 1 {#someid\n", - "

Header 1 {#someid

\n", - - "# Header 1 {#someid\n", - "

Header 1 {#someid

\n", - - "# Header 1 {#someid}}\n", - "

Header 1

\n\n

}

\n", - - "## Header 2 {#someid}\n", - "

Header 2

\n", - - "### Header 3 {#someid}\n", - "

Header 3

\n", - - "#### Header 4 {#someid}\n", - "

Header 4

\n", - - "##### Header 5 {#someid}\n", - "
Header 5
\n", - - "###### Header 6 {#someid}\n", - "
Header 6
\n", - - "####### Header 7 {#someid}\n", - "
# Header 7
\n", - - "# Header 1 # {#someid}\n", - "

Header 1

\n", - - "## Header 2 ## {#someid}\n", - "

Header 2

\n", - - "Hello\n# Header 1\nGoodbye\n", - "

Hello

\n\n

Header 1

\n\n

Goodbye

\n", - - "* List\n# Header {#someid}\n* List\n", - "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", - - "* List\n#Header {#someid}\n* List\n", - "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", - - "* List\n * Nested list\n # Nested header {#someid}\n", - "
    \n
  • List

    \n\n
      \n
    • Nested list

      \n\n" + - "

      Nested header

    • \n
  • \n
\n", - } - doTestsBlock(t, tests, EXTENSION_HEADER_IDS) -} - -func TestPrefixHeaderIdExtensionWithPrefixAndSuffix(t *testing.T) { - var tests = []string{ - "# header 1 {#someid}\n", - "

header 1

\n", - - "## header 2 {#someid}\n", - "

header 2

\n", - - "### header 3 {#someid}\n", - "

header 3

\n", - - "#### header 4 {#someid}\n", - "

header 4

\n", - - "##### header 5 {#someid}\n", - "
header 5
\n", - - "###### header 6 {#someid}\n", - "
header 6
\n", - - "####### header 7 {#someid}\n", - "
# header 7
\n", - - "# header 1 # {#someid}\n", - "

header 1

\n", - - "## header 2 ## {#someid}\n", - "

header 2

\n", - - "* List\n# Header {#someid}\n* List\n", - "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", - - "* List\n#Header {#someid}\n* List\n", - "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", - - "* List\n * Nested list\n # Nested header {#someid}\n", - "
    \n
  • List

    \n\n
      \n
    • Nested list

      \n\n" + - "

      Nested header

    • \n
  • \n
\n", - } - - parameters := HtmlRendererParameters{ - HeaderIDPrefix: "PRE:", - HeaderIDSuffix: ":POST", - } - - doTestsBlockWithRunner(t, tests, EXTENSION_HEADER_IDS, runnerWithRendererParameters(parameters)) -} - -func TestPrefixAutoHeaderIdExtension(t *testing.T) { - var tests = []string{ - "# Header 1\n", - "

Header 1

\n", - - "# Header 1 \n", - "

Header 1

\n", - - "## Header 2\n", - "

Header 2

\n", - - "### Header 3\n", - "

Header 3

\n", - - "#### Header 4\n", - "

Header 4

\n", - - "##### Header 5\n", - "
Header 5
\n", - - "###### Header 6\n", - "
Header 6
\n", - - "####### Header 7\n", - "
# Header 7
\n", - - "Hello\n# Header 1\nGoodbye\n", - "

Hello

\n\n

Header 1

\n\n

Goodbye

\n", - - "* List\n# Header\n* List\n", - "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", - - "* List\n#Header\n* List\n", - "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", - - "* List\n * Nested list\n # Nested header\n", - "
    \n
  • List

    \n\n
      \n
    • Nested list

      \n\n" + - "

      Nested header

    • \n
  • \n
\n", - - "# Header\n\n# Header\n", - "

Header

\n\n

Header

\n", - - "# Header 1\n\n# Header 1", - "

Header 1

\n\n

Header 1

\n", - - "# Header\n\n# Header 1\n\n# Header\n\n# Header", - "

Header

\n\n

Header 1

\n\n

Header

\n\n

Header

\n", - } - doTestsBlock(t, tests, EXTENSION_AUTO_HEADER_IDS) -} - -func TestPrefixAutoHeaderIdExtensionWithPrefixAndSuffix(t *testing.T) { - var tests = []string{ - "# Header 1\n", - "

Header 1

\n", - - "# Header 1 \n", - "

Header 1

\n", - - "## Header 2\n", - "

Header 2

\n", - - "### Header 3\n", - "

Header 3

\n", - - "#### Header 4\n", - "

Header 4

\n", - - "##### Header 5\n", - "
Header 5
\n", - - "###### Header 6\n", - "
Header 6
\n", - - "####### Header 7\n", - "
# Header 7
\n", - - "Hello\n# Header 1\nGoodbye\n", - "

Hello

\n\n

Header 1

\n\n

Goodbye

\n", - - "* List\n# Header\n* List\n", - "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", - - "* List\n#Header\n* List\n", - "
    \n
  • List

    \n\n

    Header

  • \n\n
  • List

  • \n
\n", - - "* List\n * Nested list\n # Nested header\n", - "
    \n
  • List

    \n\n
      \n
    • Nested list

      \n\n" + - "

      Nested header

    • \n
  • \n
\n", - - "# Header\n\n# Header\n", - "

Header

\n\n

Header

\n", - - "# Header 1\n\n# Header 1", - "

Header 1

\n\n

Header 1

\n", - - "# Header\n\n# Header 1\n\n# Header\n\n# Header", - "

Header

\n\n

Header 1

\n\n

Header

\n\n

Header

\n", - } - - parameters := HtmlRendererParameters{ - HeaderIDPrefix: "PRE:", - HeaderIDSuffix: ":POST", - } - - doTestsBlockWithRunner(t, tests, EXTENSION_AUTO_HEADER_IDS, runnerWithRendererParameters(parameters)) -} - -func TestPrefixMultipleHeaderExtensions(t *testing.T) { - var tests = []string{ - "# Header\n\n# Header {#header}\n\n# Header 1", - "

Header

\n\n

Header

\n\n

Header 1

\n", - } - doTestsBlock(t, tests, EXTENSION_AUTO_HEADER_IDS|EXTENSION_HEADER_IDS) -} - -func TestUnderlineHeaders(t *testing.T) { - var tests = []string{ - "Header 1\n========\n", - "

Header 1

\n", - - "Header 2\n--------\n", - "

Header 2

\n", - - "A\n=\n", - "

A

\n", - - "B\n-\n", - "

B

\n", - - "Paragraph\nHeader\n=\n", - "

Paragraph

\n\n

Header

\n", - - "Header\n===\nParagraph\n", - "

Header

\n\n

Paragraph

\n", - - "Header\n===\nAnother header\n---\n", - "

Header

\n\n

Another header

\n", - - " Header\n======\n", - "

Header

\n", - - " Code\n========\n", - "
Code\n
\n\n

========

\n", - - "Header with *inline*\n=====\n", - "

Header with inline

\n", - - "* List\n * Sublist\n Not a header\n ------\n", - "
    \n
  • List\n\n
      \n
    • Sublist\nNot a header\n------
    • \n
  • \n
\n", - - "Paragraph\n\n\n\n\nHeader\n===\n", - "

Paragraph

\n\n

Header

\n", - - "Trailing space \n==== \n\n", - "

Trailing space

\n", - - "Trailing spaces\n==== \n\n", - "

Trailing spaces

\n", - - "Double underline\n=====\n=====\n", - "

Double underline

\n\n

=====

\n", - } - doTestsBlock(t, tests, 0) -} - -func TestUnderlineHeadersAutoIDs(t *testing.T) { - var tests = []string{ - "Header 1\n========\n", - "

Header 1

\n", - - "Header 2\n--------\n", - "

Header 2

\n", - - "A\n=\n", - "

A

\n", - - "B\n-\n", - "

B

\n", - - "Paragraph\nHeader\n=\n", - "

Paragraph

\n\n

Header

\n", - - "Header\n===\nParagraph\n", - "

Header

\n\n

Paragraph

\n", - - "Header\n===\nAnother header\n---\n", - "

Header

\n\n

Another header

\n", - - " Header\n======\n", - "

Header

\n", - - "Header with *inline*\n=====\n", - "

Header with inline

\n", - - "Paragraph\n\n\n\n\nHeader\n===\n", - "

Paragraph

\n\n

Header

\n", - - "Trailing space \n==== \n\n", - "

Trailing space

\n", - - "Trailing spaces\n==== \n\n", - "

Trailing spaces

\n", - - "Double underline\n=====\n=====\n", - "

Double underline

\n\n

=====

\n", - - "Header\n======\n\nHeader\n======\n", - "

Header

\n\n

Header

\n", - - "Header 1\n========\n\nHeader 1\n========\n", - "

Header 1

\n\n

Header 1

\n", - } - doTestsBlock(t, tests, EXTENSION_AUTO_HEADER_IDS) -} - -func TestHorizontalRule(t *testing.T) { - var tests = []string{ - "-\n", - "

-

\n", - - "--\n", - "

--

\n", - - "---\n", - "
\n", - - "----\n", - "
\n", - - "*\n", - "

*

\n", - - "**\n", - "

**

\n", - - "***\n", - "
\n", - - "****\n", - "
\n", - - "_\n", - "

_

\n", - - "__\n", - "

__

\n", - - "___\n", - "
\n", - - "____\n", - "
\n", - - "-*-\n", - "

-*-

\n", - - "- - -\n", - "
\n", - - "* * *\n", - "
\n", - - "_ _ _\n", - "
\n", - - "-----*\n", - "

-----*

\n", - - " ------ \n", - "
\n", - - "Hello\n***\n", - "

Hello

\n\n
\n", - - "---\n***\n___\n", - "
\n\n
\n\n
\n", - } - doTestsBlock(t, tests, 0) -} - -func TestUnorderedList(t *testing.T) { - var tests = []string{ - "* Hello\n", - "
    \n
  • Hello
  • \n
\n", - - "* Yin\n* Yang\n", - "
    \n
  • Yin
  • \n
  • Yang
  • \n
\n", - - "* Ting\n* Bong\n* Goo\n", - "
    \n
  • Ting
  • \n
  • Bong
  • \n
  • Goo
  • \n
\n", - - "* Yin\n\n* Yang\n", - "
    \n
  • Yin

  • \n\n
  • Yang

  • \n
\n", - - "* Ting\n\n* Bong\n* Goo\n", - "
    \n
  • Ting

  • \n\n
  • Bong

  • \n\n
  • Goo

  • \n
\n", - - "+ Hello\n", - "
    \n
  • Hello
  • \n
\n", - - "+ Yin\n+ Yang\n", - "
    \n
  • Yin
  • \n
  • Yang
  • \n
\n", - - "+ Ting\n+ Bong\n+ Goo\n", - "
    \n
  • Ting
  • \n
  • Bong
  • \n
  • Goo
  • \n
\n", - - "+ Yin\n\n+ Yang\n", - "
    \n
  • Yin

  • \n\n
  • Yang

  • \n
\n", - - "+ Ting\n\n+ Bong\n+ Goo\n", - "
    \n
  • Ting

  • \n\n
  • Bong

  • \n\n
  • Goo

  • \n
\n", - - "- Hello\n", - "
    \n
  • Hello
  • \n
\n", - - "- Yin\n- Yang\n", - "
    \n
  • Yin
  • \n
  • Yang
  • \n
\n", - - "- Ting\n- Bong\n- Goo\n", - "
    \n
  • Ting
  • \n
  • Bong
  • \n
  • Goo
  • \n
\n", - - "- Yin\n\n- Yang\n", - "
    \n
  • Yin

  • \n\n
  • Yang

  • \n
\n", - - "- Ting\n\n- Bong\n- Goo\n", - "
    \n
  • Ting

  • \n\n
  • Bong

  • \n\n
  • Goo

  • \n
\n", - - "*Hello\n", - "

*Hello

\n", - - "* Hello \n", - "
    \n
  • Hello
  • \n
\n", - - "* Hello \n Next line \n", - "
    \n
  • Hello\nNext line
  • \n
\n", - - "Paragraph\n* No linebreak\n", - "

Paragraph\n* No linebreak

\n", - - "Paragraph\n\n* Linebreak\n", - "

Paragraph

\n\n
    \n
  • Linebreak
  • \n
\n", - - "* List\n * Nested list\n", - "
    \n
  • List\n\n
      \n
    • Nested list
    • \n
  • \n
\n", - - "* List\n\n * Nested list\n", - "
    \n
  • List

    \n\n
      \n
    • Nested list
    • \n
  • \n
\n", - - "* List\n Second line\n\n + Nested\n", - "
    \n
  • List\nSecond line

    \n\n
      \n
    • Nested
    • \n
  • \n
\n", - - "* List\n + Nested\n\n Continued\n", - "
    \n
  • List

    \n\n
      \n
    • Nested
    • \n
    \n\n

    Continued

  • \n
\n", - - "* List\n * shallow indent\n", - "
    \n
  • List\n\n
      \n
    • shallow indent
    • \n
  • \n
\n", - - "* List\n" + - " * shallow indent\n" + - " * part of second list\n" + - " * still second\n" + - " * almost there\n" + - " * third level\n", - "
    \n" + - "
  • List\n\n" + - "
      \n" + - "
    • shallow indent
    • \n" + - "
    • part of second list
    • \n" + - "
    • still second
    • \n" + - "
    • almost there\n\n" + - "
        \n" + - "
      • third level
      • \n" + - "
    • \n" + - "
  • \n" + - "
\n", - - "* List\n extra indent, same paragraph\n", - "
    \n
  • List\n extra indent, same paragraph
  • \n
\n", - - "* List\n\n code block\n", - "
    \n
  • List

    \n\n
    code block\n
  • \n
\n", - - "* List\n\n code block with spaces\n", - "
    \n
  • List

    \n\n
      code block with spaces\n
  • \n
\n", - - "* List\n\n * sublist\n\n normal text\n\n * another sublist\n", - "
    \n
  • List

    \n\n
      \n
    • sublist
    • \n
    \n\n

    normal text

    \n\n
      \n
    • another sublist
    • \n
  • \n
\n", - } - doTestsBlock(t, tests, 0) -} - -func TestOrderedList(t *testing.T) { - var tests = []string{ - "1. Hello\n", - "
    \n
  1. Hello
  2. \n
\n", - - "1. Yin\n2. Yang\n", - "
    \n
  1. Yin
  2. \n
  3. Yang
  4. \n
\n", - - "1. Ting\n2. Bong\n3. Goo\n", - "
    \n
  1. Ting
  2. \n
  3. Bong
  4. \n
  5. Goo
  6. \n
\n", - - "1. Yin\n\n2. Yang\n", - "
    \n
  1. Yin

  2. \n\n
  3. Yang

  4. \n
\n", - - "1. Ting\n\n2. Bong\n3. Goo\n", - "
    \n
  1. Ting

  2. \n\n
  3. Bong

  4. \n\n
  5. Goo

  6. \n
\n", - - "1 Hello\n", - "

1 Hello

\n", - - "1.Hello\n", - "

1.Hello

\n", - - "1. Hello \n", - "
    \n
  1. Hello
  2. \n
\n", - - "1. Hello \n Next line \n", - "
    \n
  1. Hello\nNext line
  2. \n
\n", - - "Paragraph\n1. No linebreak\n", - "

Paragraph\n1. No linebreak

\n", - - "Paragraph\n\n1. Linebreak\n", - "

Paragraph

\n\n
    \n
  1. Linebreak
  2. \n
\n", - - "1. List\n 1. Nested list\n", - "
    \n
  1. List\n\n
      \n
    1. Nested list
    2. \n
  2. \n
\n", - - "1. List\n\n 1. Nested list\n", - "
    \n
  1. List

    \n\n
      \n
    1. Nested list
    2. \n
  2. \n
\n", - - "1. List\n Second line\n\n 1. Nested\n", - "
    \n
  1. List\nSecond line

    \n\n
      \n
    1. Nested
    2. \n
  2. \n
\n", - - "1. List\n 1. Nested\n\n Continued\n", - "
    \n
  1. List

    \n\n
      \n
    1. Nested
    2. \n
    \n\n

    Continued

  2. \n
\n", - - "1. List\n 1. shallow indent\n", - "
    \n
  1. List\n\n
      \n
    1. shallow indent
    2. \n
  2. \n
\n", - - "1. List\n" + - " 1. shallow indent\n" + - " 2. part of second list\n" + - " 3. still second\n" + - " 4. almost there\n" + - " 1. third level\n", - "
    \n" + - "
  1. List\n\n" + - "
      \n" + - "
    1. shallow indent
    2. \n" + - "
    3. part of second list
    4. \n" + - "
    5. still second
    6. \n" + - "
    7. almost there\n\n" + - "
        \n" + - "
      1. third level
      2. \n" + - "
    8. \n" + - "
  2. \n" + - "
\n", - - "1. List\n extra indent, same paragraph\n", - "
    \n
  1. List\n extra indent, same paragraph
  2. \n
\n", - - "1. List\n\n code block\n", - "
    \n
  1. List

    \n\n
    code block\n
  2. \n
\n", - - "1. List\n\n code block with spaces\n", - "
    \n
  1. List

    \n\n
      code block with spaces\n
  2. \n
\n", - - "1. List\n * Mixted list\n", - "
    \n
  1. List\n\n
      \n
    • Mixted list
    • \n
  2. \n
\n", - - "1. List\n * Mixed list\n", - "
    \n
  1. List\n\n
      \n
    • Mixed list
    • \n
  2. \n
\n", - - "* Start with unordered\n 1. Ordered\n", - "
    \n
  • Start with unordered\n\n
      \n
    1. Ordered
    2. \n
  • \n
\n", - - "* Start with unordered\n 1. Ordered\n", - "
    \n
  • Start with unordered\n\n
      \n
    1. Ordered
    2. \n
  • \n
\n", - - "1. numbers\n1. are ignored\n", - "
    \n
  1. numbers
  2. \n
  3. are ignored
  4. \n
\n", - } - doTestsBlock(t, tests, 0) -} - -func TestPreformattedHtml(t *testing.T) { - var tests = []string{ - "
\n", - "
\n", - - "
\n
\n", - "
\n
\n", - - "
\n
\nParagraph\n", - "

\n
\nParagraph

\n", - - "
\n
\n", - "
\n
\n", - - "
\nAnything here\n
\n", - "
\nAnything here\n
\n", - - "
\n Anything here\n
\n", - "
\n Anything here\n
\n", - - "
\nAnything here\n
\n", - "
\nAnything here\n
\n", - - "
\nThis is *not* &proceessed\n
\n", - "
\nThis is *not* &proceessed\n
\n", - - "\n Something\n\n", - "

\n Something\n

\n", - - "
\n Something here\n\n", - "

\n Something here\n

\n", - - "Paragraph\n
\nHere? >&<\n
\n", - "

Paragraph\n

\nHere? >&<\n

\n", - - "Paragraph\n\n
\nHow about here? >&<\n
\n", - "

Paragraph

\n\n
\nHow about here? >&<\n
\n", - - "Paragraph\n
\nHere? >&<\n
\nAnd here?\n", - "

Paragraph\n

\nHere? >&<\n
\nAnd here?

\n", - - "Paragraph\n\n
\nHow about here? >&<\n
\nAnd here?\n", - "

Paragraph

\n\n

\nHow about here? >&<\n
\nAnd here?

\n", - - "Paragraph\n
\nHere? >&<\n
\n\nAnd here?\n", - "

Paragraph\n

\nHere? >&<\n

\n\n

And here?

\n", - - "Paragraph\n\n
\nHow about here? >&<\n
\n\nAnd here?\n", - "

Paragraph

\n\n
\nHow about here? >&<\n
\n\n

And here?

\n", - } - doTestsBlock(t, tests, 0) -} - -func TestPreformattedHtmlLax(t *testing.T) { - var tests = []string{ - "Paragraph\n
\nHere? >&<\n
\n", - "

Paragraph

\n\n
\nHere? >&<\n
\n", - - "Paragraph\n\n
\nHow about here? >&<\n
\n", - "

Paragraph

\n\n
\nHow about here? >&<\n
\n", - - "Paragraph\n
\nHere? >&<\n
\nAnd here?\n", - "

Paragraph

\n\n
\nHere? >&<\n
\n\n

And here?

\n", - - "Paragraph\n\n
\nHow about here? >&<\n
\nAnd here?\n", - "

Paragraph

\n\n
\nHow about here? >&<\n
\n\n

And here?

\n", - - "Paragraph\n
\nHere? >&<\n
\n\nAnd here?\n", - "

Paragraph

\n\n
\nHere? >&<\n
\n\n

And here?

\n", - - "Paragraph\n\n
\nHow about here? >&<\n
\n\nAnd here?\n", - "

Paragraph

\n\n
\nHow about here? >&<\n
\n\n

And here?

\n", - } - doTestsBlock(t, tests, EXTENSION_LAX_HTML_BLOCKS) -} - -func TestFencedCodeBlock(t *testing.T) { - var tests = []string{ - "``` go\nfunc foo() bool {\n\treturn true;\n}\n```\n", - "
func foo() bool {\n\treturn true;\n}\n
\n", - - "``` c\n/* special & char < > \" escaping */\n```\n", - "
/* special & char < > " escaping */\n
\n", - - "``` c\nno *inline* processing ~~of text~~\n```\n", - "
no *inline* processing ~~of text~~\n
\n", - - "```\nNo language\n```\n", - "
No language\n
\n", - - "``` {ocaml}\nlanguage in braces\n```\n", - "
language in braces\n
\n", - - "``` {ocaml} \nwith extra whitespace\n```\n", - "
with extra whitespace\n
\n", - - "```{ ocaml }\nwith extra whitespace\n```\n", - "
with extra whitespace\n
\n", - - "~ ~~ java\nWith whitespace\n~~~\n", - "

~ ~~ java\nWith whitespace\n~~~

\n", - - "~~\nonly two\n~~\n", - "

~~\nonly two\n~~

\n", - - "```` python\nextra\n````\n", - "
extra\n
\n", - - "~~~ perl\nthree to start, four to end\n~~~~\n", - "

~~~ perl\nthree to start, four to end\n~~~~

\n", - - "~~~~ perl\nfour to start, three to end\n~~~\n", - "

~~~~ perl\nfour to start, three to end\n~~~

\n", - - "~~~ bash\ntildes\n~~~\n", - "
tildes\n
\n", - - "``` lisp\nno ending\n", - "

``` lisp\nno ending

\n", - - "~~~ lisp\nend with language\n~~~ lisp\n", - "

~~~ lisp\nend with language\n~~~ lisp

\n", - - "```\nmismatched begin and end\n~~~\n", - "

```\nmismatched begin and end\n~~~

\n", - - "~~~\nmismatched begin and end\n```\n", - "

~~~\nmismatched begin and end\n```

\n", - - " ``` oz\nleading spaces\n```\n", - "
leading spaces\n
\n", - - " ``` oz\nleading spaces\n ```\n", - "
leading spaces\n
\n", - - " ``` oz\nleading spaces\n ```\n", - "
leading spaces\n
\n", - - "``` oz\nleading spaces\n ```\n", - "
leading spaces\n
\n", - - " ``` oz\nleading spaces\n ```\n", - "
``` oz\n
\n\n

leading spaces\n ```

\n", - - "Bla bla\n\n``` oz\ncode blocks breakup paragraphs\n```\n\nBla Bla\n", - "

Bla bla

\n\n
code blocks breakup paragraphs\n
\n\n

Bla Bla

\n", - - "Some text before a fenced code block\n``` oz\ncode blocks breakup paragraphs\n```\nAnd some text after a fenced code block", - "

Some text before a fenced code block

\n\n
code blocks breakup paragraphs\n
\n\n

And some text after a fenced code block

\n", - - "`", - "

`

\n", - - "Bla bla\n\n``` oz\ncode blocks breakup paragraphs\n```\n\nBla Bla\n\n``` oz\nmultiple code blocks work okay\n```\n\nBla Bla\n", - "

Bla bla

\n\n
code blocks breakup paragraphs\n
\n\n

Bla Bla

\n\n
multiple code blocks work okay\n
\n\n

Bla Bla

\n", - - "Some text before a fenced code block\n``` oz\ncode blocks breakup paragraphs\n```\nSome text in between\n``` oz\nmultiple code blocks work okay\n```\nAnd some text after a fenced code block", - "

Some text before a fenced code block

\n\n
code blocks breakup paragraphs\n
\n\n

Some text in between

\n\n
multiple code blocks work okay\n
\n\n

And some text after a fenced code block

\n", - } - doTestsBlock(t, tests, EXTENSION_FENCED_CODE) -} - -func TestTable(t *testing.T) { - var tests = []string{ - "a | b\n---|---\nc | d\n", - "\n\n\n\n\n\n\n\n" + - "\n\n\n\n\n\n
ab
cd
\n", - - "a | b\n---|--\nc | d\n", - "

a | b\n---|--\nc | d

\n", - - "|a|b|c|d|\n|----|----|----|---|\n|e|f|g|h|\n", - "\n\n\n\n\n\n\n\n\n\n" + - "\n\n\n\n\n\n\n\n
abcd
efgh
\n", - - "*a*|__b__|[c](C)|d\n---|---|---|---\ne|f|g|h\n", - "\n\n\n\n\n\n\n\n\n\n" + - "\n\n\n\n\n\n\n\n
abcd
efgh
\n", - - "a|b|c\n---|---|---\nd|e|f\ng|h\ni|j|k|l|m\nn|o|p\n", - "\n\n\n\n\n\n\n\n\n" + - "\n\n\n\n\n\n\n" + - "\n\n\n\n\n\n" + - "\n\n\n\n\n\n" + - "\n\n\n\n\n\n
abc
def
gh
ijk
nop
\n", - - "a|b|c\n---|---|---\n*d*|__e__|f\n", - "\n\n\n\n\n\n\n\n\n" + - "\n\n\n\n\n\n\n
abc
def
\n", - - "a|b|c|d\n:--|--:|:-:|---\ne|f|g|h\n", - "\n\n\n\n\n" + - "\n\n\n\n\n" + - "\n\n\n\n" + - "\n\n\n\n
abcd
efgh
\n", - - "a|b|c\n---|---|---\n", - "\n\n\n\n\n\n\n\n\n\n\n
abc
\n", - - "a| b|c | d | e\n---|---|---|---|---\nf| g|h | i |j\n", - "\n\n\n\n\n\n\n\n\n\n\n" + - "\n\n\n\n\n\n\n\n\n
abcde
fghij
\n", - - "a|b\\|c|d\n---|---|---\nf|g\\|h|i\n", - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
ab|cd
fg|hi
\n", - } - doTestsBlock(t, tests, EXTENSION_TABLES) -} - -func TestUnorderedListWith_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) { - var tests = []string{ - "* Hello\n", - "
    \n
  • Hello
  • \n
\n", - - "* Yin\n* Yang\n", - "
    \n
  • Yin
  • \n
  • Yang
  • \n
\n", - - "* Ting\n* Bong\n* Goo\n", - "
    \n
  • Ting
  • \n
  • Bong
  • \n
  • Goo
  • \n
\n", - - "* Yin\n\n* Yang\n", - "
    \n
  • Yin

  • \n\n
  • Yang

  • \n
\n", - - "* Ting\n\n* Bong\n* Goo\n", - "
    \n
  • Ting

  • \n\n
  • Bong

  • \n\n
  • Goo

  • \n
\n", - - "+ Hello\n", - "
    \n
  • Hello
  • \n
\n", - - "+ Yin\n+ Yang\n", - "
    \n
  • Yin
  • \n
  • Yang
  • \n
\n", - - "+ Ting\n+ Bong\n+ Goo\n", - "
    \n
  • Ting
  • \n
  • Bong
  • \n
  • Goo
  • \n
\n", - - "+ Yin\n\n+ Yang\n", - "
    \n
  • Yin

  • \n\n
  • Yang

  • \n
\n", - - "+ Ting\n\n+ Bong\n+ Goo\n", - "
    \n
  • Ting

  • \n\n
  • Bong

  • \n\n
  • Goo

  • \n
\n", - - "- Hello\n", - "
    \n
  • Hello
  • \n
\n", - - "- Yin\n- Yang\n", - "
    \n
  • Yin
  • \n
  • Yang
  • \n
\n", - - "- Ting\n- Bong\n- Goo\n", - "
    \n
  • Ting
  • \n
  • Bong
  • \n
  • Goo
  • \n
\n", - - "- Yin\n\n- Yang\n", - "
    \n
  • Yin

  • \n\n
  • Yang

  • \n
\n", - - "- Ting\n\n- Bong\n- Goo\n", - "
    \n
  • Ting

  • \n\n
  • Bong

  • \n\n
  • Goo

  • \n
\n", - - "*Hello\n", - "

*Hello

\n", - - "* Hello \n", - "
    \n
  • Hello
  • \n
\n", - - "* Hello \n Next line \n", - "
    \n
  • Hello\nNext line
  • \n
\n", - - "Paragraph\n* No linebreak\n", - "

Paragraph

\n\n
    \n
  • No linebreak
  • \n
\n", - - "Paragraph\n\n* Linebreak\n", - "

Paragraph

\n\n
    \n
  • Linebreak
  • \n
\n", - - "* List\n * Nested list\n", - "
    \n
  • List\n\n
      \n
    • Nested list
    • \n
  • \n
\n", - - "* List\n\n * Nested list\n", - "
    \n
  • List

    \n\n
      \n
    • Nested list
    • \n
  • \n
\n", - - "* List\n Second line\n\n + Nested\n", - "
    \n
  • List\nSecond line

    \n\n
      \n
    • Nested
    • \n
  • \n
\n", - - "* List\n + Nested\n\n Continued\n", - "
    \n
  • List

    \n\n
      \n
    • Nested
    • \n
    \n\n

    Continued

  • \n
\n", - - "* List\n * shallow indent\n", - "
    \n
  • List\n\n
      \n
    • shallow indent
    • \n
  • \n
\n", - - "* List\n" + - " * shallow indent\n" + - " * part of second list\n" + - " * still second\n" + - " * almost there\n" + - " * third level\n", - "
    \n" + - "
  • List\n\n" + - "
      \n" + - "
    • shallow indent
    • \n" + - "
    • part of second list
    • \n" + - "
    • still second
    • \n" + - "
    • almost there\n\n" + - "
        \n" + - "
      • third level
      • \n" + - "
    • \n" + - "
  • \n" + - "
\n", - - "* List\n extra indent, same paragraph\n", - "
    \n
  • List\n extra indent, same paragraph
  • \n
\n", - - "* List\n\n code block\n", - "
    \n
  • List

    \n\n
    code block\n
  • \n
\n", - - "* List\n\n code block with spaces\n", - "
    \n
  • List

    \n\n
      code block with spaces\n
  • \n
\n", - - "* List\n\n * sublist\n\n normal text\n\n * another sublist\n", - "
    \n
  • List

    \n\n
      \n
    • sublist
    • \n
    \n\n

    normal text

    \n\n
      \n
    • another sublist
    • \n
  • \n
\n", - } - doTestsBlock(t, tests, EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK) -} - -func TestOrderedList_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) { - var tests = []string{ - "1. Hello\n", - "
    \n
  1. Hello
  2. \n
\n", - - "1. Yin\n2. Yang\n", - "
    \n
  1. Yin
  2. \n
  3. Yang
  4. \n
\n", - - "1. Ting\n2. Bong\n3. Goo\n", - "
    \n
  1. Ting
  2. \n
  3. Bong
  4. \n
  5. Goo
  6. \n
\n", - - "1. Yin\n\n2. Yang\n", - "
    \n
  1. Yin

  2. \n\n
  3. Yang

  4. \n
\n", - - "1. Ting\n\n2. Bong\n3. Goo\n", - "
    \n
  1. Ting

  2. \n\n
  3. Bong

  4. \n\n
  5. Goo

  6. \n
\n", - - "1 Hello\n", - "

1 Hello

\n", - - "1.Hello\n", - "

1.Hello

\n", - - "1. Hello \n", - "
    \n
  1. Hello
  2. \n
\n", - - "1. Hello \n Next line \n", - "
    \n
  1. Hello\nNext line
  2. \n
\n", - - "Paragraph\n1. No linebreak\n", - "

Paragraph

\n\n
    \n
  1. No linebreak
  2. \n
\n", - - "Paragraph\n\n1. Linebreak\n", - "

Paragraph

\n\n
    \n
  1. Linebreak
  2. \n
\n", - - "1. List\n 1. Nested list\n", - "
    \n
  1. List\n\n
      \n
    1. Nested list
    2. \n
  2. \n
\n", - - "1. List\n\n 1. Nested list\n", - "
    \n
  1. List

    \n\n
      \n
    1. Nested list
    2. \n
  2. \n
\n", - - "1. List\n Second line\n\n 1. Nested\n", - "
    \n
  1. List\nSecond line

    \n\n
      \n
    1. Nested
    2. \n
  2. \n
\n", - - "1. List\n 1. Nested\n\n Continued\n", - "
    \n
  1. List

    \n\n
      \n
    1. Nested
    2. \n
    \n\n

    Continued

  2. \n
\n", - - "1. List\n 1. shallow indent\n", - "
    \n
  1. List\n\n
      \n
    1. shallow indent
    2. \n
  2. \n
\n", - - "1. List\n" + - " 1. shallow indent\n" + - " 2. part of second list\n" + - " 3. still second\n" + - " 4. almost there\n" + - " 1. third level\n", - "
    \n" + - "
  1. List\n\n" + - "
      \n" + - "
    1. shallow indent
    2. \n" + - "
    3. part of second list
    4. \n" + - "
    5. still second
    6. \n" + - "
    7. almost there\n\n" + - "
        \n" + - "
      1. third level
      2. \n" + - "
    8. \n" + - "
  2. \n" + - "
\n", - - "1. List\n extra indent, same paragraph\n", - "
    \n
  1. List\n extra indent, same paragraph
  2. \n
\n", - - "1. List\n\n code block\n", - "
    \n
  1. List

    \n\n
    code block\n
  2. \n
\n", - - "1. List\n\n code block with spaces\n", - "
    \n
  1. List

    \n\n
      code block with spaces\n
  2. \n
\n", - - "1. List\n * Mixted list\n", - "
    \n
  1. List\n\n
      \n
    • Mixted list
    • \n
  2. \n
\n", - - "1. List\n * Mixed list\n", - "
    \n
  1. List\n\n
      \n
    • Mixed list
    • \n
  2. \n
\n", - - "* Start with unordered\n 1. Ordered\n", - "
    \n
  • Start with unordered\n\n
      \n
    1. Ordered
    2. \n
  • \n
\n", - - "* Start with unordered\n 1. Ordered\n", - "
    \n
  • Start with unordered\n\n
      \n
    1. Ordered
    2. \n
  • \n
\n", - - "1. numbers\n1. are ignored\n", - "
    \n
  1. numbers
  2. \n
  3. are ignored
  4. \n
\n", - } - doTestsBlock(t, tests, EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK) -} - -func TestFencedCodeBlock_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) { - var tests = []string{ - "``` go\nfunc foo() bool {\n\treturn true;\n}\n```\n", - "
func foo() bool {\n\treturn true;\n}\n
\n", - - "``` c\n/* special & char < > \" escaping */\n```\n", - "
/* special & char < > " escaping */\n
\n", - - "``` c\nno *inline* processing ~~of text~~\n```\n", - "
no *inline* processing ~~of text~~\n
\n", - - "```\nNo language\n```\n", - "
No language\n
\n", - - "``` {ocaml}\nlanguage in braces\n```\n", - "
language in braces\n
\n", - - "``` {ocaml} \nwith extra whitespace\n```\n", - "
with extra whitespace\n
\n", - - "```{ ocaml }\nwith extra whitespace\n```\n", - "
with extra whitespace\n
\n", - - "~ ~~ java\nWith whitespace\n~~~\n", - "

~ ~~ java\nWith whitespace\n~~~

\n", - - "~~\nonly two\n~~\n", - "

~~\nonly two\n~~

\n", - - "```` python\nextra\n````\n", - "
extra\n
\n", - - "~~~ perl\nthree to start, four to end\n~~~~\n", - "

~~~ perl\nthree to start, four to end\n~~~~

\n", - - "~~~~ perl\nfour to start, three to end\n~~~\n", - "

~~~~ perl\nfour to start, three to end\n~~~

\n", - - "~~~ bash\ntildes\n~~~\n", - "
tildes\n
\n", - - "``` lisp\nno ending\n", - "

``` lisp\nno ending

\n", - - "~~~ lisp\nend with language\n~~~ lisp\n", - "

~~~ lisp\nend with language\n~~~ lisp

\n", - - "```\nmismatched begin and end\n~~~\n", - "

```\nmismatched begin and end\n~~~

\n", - - "~~~\nmismatched begin and end\n```\n", - "

~~~\nmismatched begin and end\n```

\n", - - " ``` oz\nleading spaces\n```\n", - "
leading spaces\n
\n", - - " ``` oz\nleading spaces\n ```\n", - "
leading spaces\n
\n", - - " ``` oz\nleading spaces\n ```\n", - "
leading spaces\n
\n", - - "``` oz\nleading spaces\n ```\n", - "
leading spaces\n
\n", - - " ``` oz\nleading spaces\n ```\n", - "
``` oz\n
\n\n

leading spaces

\n\n
```\n
\n", - } - doTestsBlock(t, tests, EXTENSION_FENCED_CODE|EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK) -} - -func TestTitleBlock_EXTENSION_TITLEBLOCK(t *testing.T) { - var tests = []string{ - "% Some title\n" + - "% Another title line\n" + - "% Yep, more here too\n", - "

" + - "Some title\n" + - "Another title line\n" + - "Yep, more here too\n" + - "

", - } - - doTestsBlock(t, tests, EXTENSION_TITLEBLOCK) - -} diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/inline_test.go b/Godeps/_workspace/src/github.com/russross/blackfriday/inline_test.go deleted file mode 100644 index fb3bdec8c..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/inline_test.go +++ /dev/null @@ -1,859 +0,0 @@ -// -// Blackfriday Markdown Processor -// Available at http://github.com/russross/blackfriday -// -// Copyright © 2011 Russ Ross . -// Distributed under the Simplified BSD License. -// See README.md for details. -// - -// -// Unit tests for inline parsing -// - -package blackfriday - -import ( - "regexp" - "testing" - - "strings" -) - -func runMarkdownInline(input string, extensions, htmlFlags int, params HtmlRendererParameters) string { - extensions |= EXTENSION_AUTOLINK - extensions |= EXTENSION_STRIKETHROUGH - - htmlFlags |= HTML_USE_XHTML - - renderer := HtmlRendererWithParameters(htmlFlags, "", "", params) - - return string(Markdown([]byte(input), renderer, extensions)) -} - -func doTestsInline(t *testing.T, tests []string) { - doTestsInlineParam(t, tests, 0, 0, HtmlRendererParameters{}) -} - -func doLinkTestsInline(t *testing.T, tests []string) { - doTestsInline(t, tests) - - prefix := "http://localhost" - params := HtmlRendererParameters{AbsolutePrefix: prefix} - transformTests := transformLinks(tests, prefix) - doTestsInlineParam(t, transformTests, 0, 0, params) - doTestsInlineParam(t, transformTests, 0, commonHtmlFlags, params) -} - -func doSafeTestsInline(t *testing.T, tests []string) { - doTestsInlineParam(t, tests, 0, HTML_SAFELINK, HtmlRendererParameters{}) - - // All the links in this test should not have the prefix appended, so - // just rerun it with different parameters and the same expectations. - prefix := "http://localhost" - params := HtmlRendererParameters{AbsolutePrefix: prefix} - transformTests := transformLinks(tests, prefix) - doTestsInlineParam(t, transformTests, 0, HTML_SAFELINK, params) -} - -func doTestsInlineParam(t *testing.T, tests []string, extensions, htmlFlags int, - params HtmlRendererParameters) { - // catch and report panics - var candidate string - /* - defer func() { - if err := recover(); err != nil { - t.Errorf("\npanic while processing [%#v] (%v)\n", candidate, err) - } - }() - */ - - for i := 0; i+1 < len(tests); i += 2 { - input := tests[i] - candidate = input - expected := tests[i+1] - actual := runMarkdownInline(candidate, extensions, htmlFlags, params) - if actual != expected { - t.Errorf("\nInput [%#v]\nExpected[%#v]\nActual [%#v]", - candidate, expected, actual) - } - - // now test every substring to stress test bounds checking - if !testing.Short() { - for start := 0; start < len(input); start++ { - for end := start + 1; end <= len(input); end++ { - candidate = input[start:end] - _ = runMarkdownInline(candidate, extensions, htmlFlags, params) - } - } - } - } -} - -func transformLinks(tests []string, prefix string) []string { - newTests := make([]string, len(tests)) - anchorRe := regexp.MustCompile(`nothing inline

\n", - - "simple *inline* test\n", - "

simple inline test

\n", - - "*at the* beginning\n", - "

at the beginning

\n", - - "at the *end*\n", - "

at the end

\n", - - "*try two* in *one line*\n", - "

try two in one line

\n", - - "over *two\nlines* test\n", - "

over two\nlines test

\n", - - "odd *number of* markers* here\n", - "

odd number of markers* here

\n", - - "odd *number\nof* markers* here\n", - "

odd number\nof markers* here

\n", - - "simple _inline_ test\n", - "

simple inline test

\n", - - "_at the_ beginning\n", - "

at the beginning

\n", - - "at the _end_\n", - "

at the end

\n", - - "_try two_ in _one line_\n", - "

try two in one line

\n", - - "over _two\nlines_ test\n", - "

over two\nlines test

\n", - - "odd _number of_ markers_ here\n", - "

odd number of markers_ here

\n", - - "odd _number\nof_ markers_ here\n", - "

odd number\nof markers_ here

\n", - - "mix of *markers_\n", - "

mix of *markers_

\n", - } - doTestsInline(t, tests) -} - -func TestStrong(t *testing.T) { - var tests = []string{ - "nothing inline\n", - "

nothing inline

\n", - - "simple **inline** test\n", - "

simple inline test

\n", - - "**at the** beginning\n", - "

at the beginning

\n", - - "at the **end**\n", - "

at the end

\n", - - "**try two** in **one line**\n", - "

try two in one line

\n", - - "over **two\nlines** test\n", - "

over two\nlines test

\n", - - "odd **number of** markers** here\n", - "

odd number of markers** here

\n", - - "odd **number\nof** markers** here\n", - "

odd number\nof markers** here

\n", - - "simple __inline__ test\n", - "

simple inline test

\n", - - "__at the__ beginning\n", - "

at the beginning

\n", - - "at the __end__\n", - "

at the end

\n", - - "__try two__ in __one line__\n", - "

try two in one line

\n", - - "over __two\nlines__ test\n", - "

over two\nlines test

\n", - - "odd __number of__ markers__ here\n", - "

odd number of markers__ here

\n", - - "odd __number\nof__ markers__ here\n", - "

odd number\nof markers__ here

\n", - - "mix of **markers__\n", - "

mix of **markers__

\n", - } - doTestsInline(t, tests) -} - -func TestEmphasisMix(t *testing.T) { - var tests = []string{ - "***triple emphasis***\n", - "

triple emphasis

\n", - - "***triple\nemphasis***\n", - "

triple\nemphasis

\n", - - "___triple emphasis___\n", - "

triple emphasis

\n", - - "***triple emphasis___\n", - "

***triple emphasis___

\n", - - "*__triple emphasis__*\n", - "

triple emphasis

\n", - - "__*triple emphasis*__\n", - "

triple emphasis

\n", - - "**improper *nesting** is* bad\n", - "

improper *nesting is* bad

\n", - - "*improper **nesting* is** bad\n", - "

improper **nesting is** bad

\n", - } - doTestsInline(t, tests) -} - -func TestEmphasisLink(t *testing.T) { - var tests = []string{ - "[first](before) *text[second] (inside)text* [third](after)\n", - "

first textsecondtext third

\n", - - "*incomplete [link] definition*\n", - "

incomplete [link] definition

\n", - - "*it's [emphasis*] (not link)\n", - "

it's [emphasis] (not link)

\n", - - "*it's [emphasis*] and *[asterisk]\n", - "

it's [emphasis] and *[asterisk]

\n", - } - doTestsInline(t, tests) -} - -func TestStrikeThrough(t *testing.T) { - var tests = []string{ - "nothing inline\n", - "

nothing inline

\n", - - "simple ~~inline~~ test\n", - "

simple inline test

\n", - - "~~at the~~ beginning\n", - "

at the beginning

\n", - - "at the ~~end~~\n", - "

at the end

\n", - - "~~try two~~ in ~~one line~~\n", - "

try two in one line

\n", - - "over ~~two\nlines~~ test\n", - "

over two\nlines test

\n", - - "odd ~~number of~~ markers~~ here\n", - "

odd number of markers~~ here

\n", - - "odd ~~number\nof~~ markers~~ here\n", - "

odd number\nof markers~~ here

\n", - } - doTestsInline(t, tests) -} - -func TestCodeSpan(t *testing.T) { - var tests = []string{ - "`source code`\n", - "

source code

\n", - - "` source code with spaces `\n", - "

source code with spaces

\n", - - "` source code with spaces `not here\n", - "

source code with spacesnot here

\n", - - "a `single marker\n", - "

a `single marker

\n", - - "a single multi-tick marker with ``` no text\n", - "

a single multi-tick marker with ``` no text

\n", - - "markers with ` ` a space\n", - "

markers with a space

\n", - - "`source code` and a `stray\n", - "

source code and a `stray

\n", - - "`source *with* _awkward characters_ in it`\n", - "

source *with* _awkward characters_ in it

\n", - - "`split over\ntwo lines`\n", - "

split over\ntwo lines

\n", - - "```multiple ticks``` for the marker\n", - "

multiple ticks for the marker

\n", - - "```multiple ticks `with` ticks inside```\n", - "

multiple ticks `with` ticks inside

\n", - } - doTestsInline(t, tests) -} - -func TestLineBreak(t *testing.T) { - var tests = []string{ - "this line \nhas a break\n", - "

this line
\nhas a break

\n", - - "this line \ndoes not\n", - "

this line\ndoes not

\n", - - "this has an \nextra space\n", - "

this has an
\nextra space

\n", - } - doTestsInline(t, tests) -} - -func TestInlineLink(t *testing.T) { - var tests = []string{ - "[foo](/bar/)\n", - "

foo

\n", - - "[foo with a title](/bar/ \"title\")\n", - "

foo with a title

\n", - - "[foo with a title](/bar/\t\"title\")\n", - "

foo with a title

\n", - - "[foo with a title](/bar/ \"title\" )\n", - "

foo with a title

\n", - - "[foo with a title](/bar/ title with no quotes)\n", - "

foo with a title

\n", - - "[foo]()\n", - "

[foo]()

\n", - - "![foo](/bar/)\n", - "

\"foo\"\n

\n", - - "![foo with a title](/bar/ \"title\")\n", - "

\"foo\n

\n", - - "![foo with a title](/bar/\t\"title\")\n", - "

\"foo\n

\n", - - "![foo with a title](/bar/ \"title\" )\n", - "

\"foo\n

\n", - - "![foo with a title](/bar/ title with no quotes)\n", - "

\"foo\n

\n", - - "![](img.jpg)\n", - "

\"\"\n

\n", - - "[link](url)\n", - "

link

\n", - - "![foo]()\n", - "

![foo]()

\n", - - "[a link]\t(/with_a_tab/)\n", - "

a link

\n", - - "[a link] (/with_spaces/)\n", - "

a link

\n", - - "[text (with) [[nested] (brackets)]](/url/)\n", - "

text (with) [[nested] (brackets)]

\n", - - "[text (with) [broken nested] (brackets)]](/url/)\n", - "

[text (with) broken nested]](/url/)

\n", - - "[text\nwith a newline](/link/)\n", - "

text\nwith a newline

\n", - - "[text in brackets] [followed](/by a link/)\n", - "

[text in brackets] followed

\n", - - "[link with\\] a closing bracket](/url/)\n", - "

link with] a closing bracket

\n", - - "[link with\\[ an opening bracket](/url/)\n", - "

link with[ an opening bracket

\n", - - "[link with\\) a closing paren](/url/)\n", - "

link with) a closing paren

\n", - - "[link with\\( an opening paren](/url/)\n", - "

link with( an opening paren

\n", - - "[link]( with whitespace)\n", - "

link

\n", - - "[link]( with whitespace )\n", - "

link

\n", - - "[![image](someimage)](with image)\n", - "

\"image\"\n

\n", - - "[link](url \"one quote)\n", - "

link

\n", - - "[link](url 'one quote)\n", - "

link

\n", - - "[link]()\n", - "

link

\n", - - "[link & ampersand](/url/)\n", - "

link & ampersand

\n", - - "[link & ampersand](/url/)\n", - "

link & ampersand

\n", - - "[link](/url/&query)\n", - "

link

\n", - - "[[t]](/t)\n", - "

[t]

\n", - } - doLinkTestsInline(t, tests) - -} - -func TestNofollowLink(t *testing.T) { - var tests = []string{ - "[foo](http://bar.com/foo/)\n", - "

foo

\n", - - "[foo](/bar/)\n", - "

foo

\n", - } - doTestsInlineParam(t, tests, 0, HTML_SAFELINK|HTML_NOFOLLOW_LINKS, - HtmlRendererParameters{}) -} - -func TestHrefTargetBlank(t *testing.T) { - var tests = []string{ - // internal link - "[foo](/bar/)\n", - "

foo

\n", - - "[foo](http://example.com)\n", - "

foo

\n", - } - doTestsInlineParam(t, tests, 0, HTML_SAFELINK|HTML_HREF_TARGET_BLANK, HtmlRendererParameters{}) -} - -func TestSafeInlineLink(t *testing.T) { - var tests = []string{ - "[foo](/bar/)\n", - "

foo

\n", - - "[foo](http://bar/)\n", - "

foo

\n", - - "[foo](https://bar/)\n", - "

foo

\n", - - "[foo](ftp://bar/)\n", - "

foo

\n", - - "[foo](mailto://bar/)\n", - "

foo

\n", - - // Not considered safe - "[foo](baz://bar/)\n", - "

foo

\n", - } - doSafeTestsInline(t, tests) -} - -func TestReferenceLink(t *testing.T) { - var tests = []string{ - "[link][ref]\n", - "

[link][ref]

\n", - - "[link][ref]\n [ref]: /url/ \"title\"\n", - "

link

\n", - - "[link][ref]\n [ref]: /url/\n", - "

link

\n", - - " [ref]: /url/\n", - "", - - " [ref]: /url/\n[ref2]: /url/\n [ref3]: /url/\n", - "", - - " [ref]: /url/\n[ref2]: /url/\n [ref3]: /url/\n [4spaces]: /url/\n", - "
[4spaces]: /url/\n
\n", - - "[hmm](ref2)\n [ref]: /url/\n[ref2]: /url/\n [ref3]: /url/\n", - "

hmm

\n", - - "[ref]\n", - "

[ref]

\n", - - "[ref]\n [ref]: /url/ \"title\"\n", - "

ref

\n", - } - doLinkTestsInline(t, tests) -} - -func TestTags(t *testing.T) { - var tests = []string{ - "a tag\n", - "

a tag

\n", - - "tag\n", - "

tag

\n", - - "mismatch\n", - "

mismatch

\n", - - "a tag\n", - "

a tag

\n", - } - doTestsInline(t, tests) -} - -func TestAutoLink(t *testing.T) { - var tests = []string{ - "http://foo.com/\n", - "

http://foo.com/

\n", - - "1 http://foo.com/\n", - "

1 http://foo.com/

\n", - - "1http://foo.com/\n", - "

1http://foo.com/

\n", - - "1.http://foo.com/\n", - "

1.http://foo.com/

\n", - - "1. http://foo.com/\n", - "
    \n
  1. http://foo.com/
  2. \n
\n", - - "-http://foo.com/\n", - "

-http://foo.com/

\n", - - "- http://foo.com/\n", - "\n", - - "_http://foo.com/\n", - "

_http://foo.com/

\n", - - "令狐http://foo.com/\n", - "

令狐http://foo.com/

\n", - - "令狐 http://foo.com/\n", - "

令狐 http://foo.com/

\n", - - "ahttp://foo.com/\n", - "

ahttp://foo.com/

\n", - - ">http://foo.com/\n", - "
\n

http://foo.com/

\n
\n", - - "> http://foo.com/\n", - "
\n

http://foo.com/

\n
\n", - - "go to \n", - "

go to http://foo.com/

\n", - - "a secure \n", - "

a secure https://link.org

\n", - - "an email \n", - "

an email some@one.com

\n", - - "an email \n", - "

an email some@one.com

\n", - - "an email \n", - "

an email some@one.com

\n", - - "an ftp \n", - "

an ftp ftp://old.com

\n", - - "an ftp \n", - "

an ftp ftp:old.com

\n", - - "a link with \n", - "

a link with " + - "http://new.com?query=foo&bar

\n", - - "quotes mean a tag \n", - "

quotes mean a tag

\n", - - "quotes mean a tag \n", - "

quotes mean a tag

\n", - - "unless escaped \n", - "

unless escaped " + - "http://new.com?query="foo"&bar

\n", - - "even a > can be escaped &etc>\n", - "

even a > can be escaped " + - "http://new.com?q=>&etc

\n", - - "http://fancy.com\n", - "

http://fancy.com

\n", - - "This is a link\n", - "

This is a link

\n", - - "http://www.fancy.com/A_B.pdf\n", - "

http://www.fancy.com/A_B.pdf

\n", - - "(http://www.fancy.com/A_B (\n", - "

(http://www.fancy.com/A_B (

\n", - - "(http://www.fancy.com/A_B (part two: http://www.fancy.com/A_B)).\n", - "

(http://www.fancy.com/A_B (part two: http://www.fancy.com/A_B)).

\n", - - "http://www.foo.com
\n", - "

http://www.foo.com

\n", - - "http://foo.com/viewtopic.php?f=18&t=297", - "

http://foo.com/viewtopic.php?f=18&t=297

\n", - - "http://foo.com/viewtopic.php?param="18"zz", - "

http://foo.com/viewtopic.php?param="18"zz

\n", - - "http://foo.com/viewtopic.php?param="18"", - "

http://foo.com/viewtopic.php?param="18"

\n", - } - doLinkTestsInline(t, tests) -} - -var footnoteTests = []string{ - "testing footnotes.[^a]\n\n[^a]: This is the note\n", - `

testing footnotes.1

-
- -
- -
    -
  1. This is the note -
  2. -
-
-`, - - `testing long[^b] notes. - -[^b]: Paragraph 1 - - Paragraph 2 - - ` + "```\n\tsome code\n\t```" + ` - - Paragraph 3 - -No longer in the footnote -`, - `

testing long1 notes.

- -

No longer in the footnote

-
- -
- -
    -
  1. Paragraph 1

    - -

    Paragraph 2

    - -

    -some code -

    - -

    Paragraph 3

    -
  2. -
-
-`, - - `testing[^c] multiple[^d] notes. - -[^c]: this is [note] c - - -omg - -[^d]: this is note d - -what happens here - -[note]: /link/c - -`, - `

testing1 multiple2 notes.

- -

omg

- -

what happens here

-
- -
- -
    -
  1. this is note c -
  2. -
  3. this is note d -
  4. -
-
-`, - - "testing inline^[this is the note] notes.\n", - `

testing inline1 notes.

-
- -
- -
    -
  1. this is the note
  2. -
-
-`, - - "testing multiple[^1] types^[inline note] of notes[^2]\n\n[^2]: the second deferred note\n[^1]: the first deferred note\n\n\twhich happens to be a block\n", - `

testing multiple1 types2 of notes3

-
- -
- -
    -
  1. the first deferred note

    - -

    which happens to be a block

    -
  2. -
  3. inline note
  4. -
  5. the second deferred note -
  6. -
-
-`, - - `This is a footnote[^1]^[and this is an inline footnote] - -[^1]: the footnote text. - - may be multiple paragraphs. -`, - `

This is a footnote12

-
- -
- -
    -
  1. the footnote text.

    - -

    may be multiple paragraphs.

    -
  2. -
  3. and this is an inline footnote
  4. -
-
-`, - - "empty footnote[^]\n\n[^]: fn text", - "

empty footnote1

\n
\n\n
\n\n
    \n
  1. fn text\n
  2. \n
\n
\n", -} - -func TestFootnotes(t *testing.T) { - doTestsInlineParam(t, footnoteTests, EXTENSION_FOOTNOTES, 0, HtmlRendererParameters{}) -} - -func TestFootnotesWithParameters(t *testing.T) { - tests := make([]string, len(footnoteTests)) - - prefix := "testPrefix" - returnText := "ret" - re := regexp.MustCompile(`(?ms)
  • (.*?)
  • `) - - // Transform the test expectations to match the parameters we're using. - for i, test := range footnoteTests { - if i%2 == 1 { - test = strings.Replace(test, "fn:", "fn:"+prefix, -1) - test = strings.Replace(test, "fnref:", "fnref:"+prefix, -1) - test = re.ReplaceAllString(test, `
  • $2 ret
  • `) - } - tests[i] = test - } - - params := HtmlRendererParameters{ - FootnoteAnchorPrefix: prefix, - FootnoteReturnLinkContents: returnText, - } - - doTestsInlineParam(t, tests, EXTENSION_FOOTNOTES, HTML_FOOTNOTE_RETURN_LINKS, params) -} - -func TestSmartDoubleQuotes(t *testing.T) { - var tests = []string{ - "this should be normal \"quoted\" text.\n", - "

    this should be normal “quoted” text.

    \n", - "this \" single double\n", - "

    this “ single double

    \n", - "two pair of \"some\" quoted \"text\".\n", - "

    two pair of “some” quoted “text”.

    \n"} - - doTestsInlineParam(t, tests, 0, HTML_USE_SMARTYPANTS, HtmlRendererParameters{}) -} - -func TestSmartAngledDoubleQuotes(t *testing.T) { - var tests = []string{ - "this should be angled \"quoted\" text.\n", - "

    this should be angled «quoted» text.

    \n", - "this \" single double\n", - "

    this « single double

    \n", - "two pair of \"some\" quoted \"text\".\n", - "

    two pair of «some» quoted «text».

    \n"} - - doTestsInlineParam(t, tests, 0, HTML_USE_SMARTYPANTS|HTML_SMARTYPANTS_ANGLED_QUOTES, HtmlRendererParameters{}) -} - -func TestSmartFractions(t *testing.T) { - var tests = []string{ - "1/2, 1/4 and 3/4; 1/4th and 3/4ths\n", - "

    ½, ¼ and ¾; ¼th and ¾ths

    \n", - "1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.\n", - "

    1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.

    \n"} - - doTestsInlineParam(t, tests, 0, HTML_USE_SMARTYPANTS, HtmlRendererParameters{}) - - tests = []string{ - "1/2, 2/3, 81/100 and 1000000/1048576.\n", - "

    12, 23, 81100 and 10000001048576.

    \n", - "1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.\n", - "

    1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.

    \n"} - - doTestsInlineParam(t, tests, 0, HTML_USE_SMARTYPANTS|HTML_SMARTYPANTS_FRACTIONS, HtmlRendererParameters{}) -} diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Amps and angle encoding.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Amps and angle encoding.html deleted file mode 100644 index 483f8ffa1..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Amps and angle encoding.html +++ /dev/null @@ -1,17 +0,0 @@ -

    AT&T has an ampersand in their name.

    - -

    AT&T is another way to write it.

    - -

    This & that.

    - -

    4 < 5.

    - -

    6 > 5.

    - -

    Here's a link with an ampersand in the URL.

    - -

    Here's a link with an amersand in the link text: AT&T.

    - -

    Here's an inline link.

    - -

    Here's an inline link.

    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Amps and angle encoding.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Amps and angle encoding.text deleted file mode 100644 index 0e9527f93..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Amps and angle encoding.text +++ /dev/null @@ -1,21 +0,0 @@ -AT&T has an ampersand in their name. - -AT&T is another way to write it. - -This & that. - -4 < 5. - -6 > 5. - -Here's a [link] [1] with an ampersand in the URL. - -Here's a link with an amersand in the link text: [AT&T] [2]. - -Here's an inline [link](/script?foo=1&bar=2). - -Here's an inline [link](). - - -[1]: http://example.com/?foo=1&bar=2 -[2]: http://att.com/ "AT&T" \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Auto links.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Auto links.html deleted file mode 100644 index b1791e7f9..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Auto links.html +++ /dev/null @@ -1,18 +0,0 @@ -

    Link: http://example.com/.

    - -

    With an ampersand: http://example.com/?foo=1&bar=2

    - - - -
    -

    Blockquoted: http://example.com/

    -
    - -

    Auto-links should not occur here: <http://example.com/>

    - -
    or here: <http://example.com/>
    -
    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Auto links.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Auto links.text deleted file mode 100644 index abbc48869..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Auto links.text +++ /dev/null @@ -1,13 +0,0 @@ -Link: . - -With an ampersand: - -* In a list? -* -* It should. - -> Blockquoted: - -Auto-links should not occur here: `` - - or here: \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Backslash escapes.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Backslash escapes.html deleted file mode 100644 index a73c998f6..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Backslash escapes.html +++ /dev/null @@ -1,123 +0,0 @@ -

    These should all get escaped:

    - -

    Backslash: \

    - -

    Backtick: `

    - -

    Asterisk: *

    - -

    Underscore: _

    - -

    Left brace: {

    - -

    Right brace: }

    - -

    Left bracket: [

    - -

    Right bracket: ]

    - -

    Left paren: (

    - -

    Right paren: )

    - -

    Greater-than: >

    - -

    Hash: #

    - -

    Period: .

    - -

    Bang: !

    - -

    Plus: +

    - -

    Minus: -

    - -

    Tilde: ~

    - -

    These should not, because they occur within a code block:

    - -
    Backslash: \\
    -
    -Backtick: \`
    -
    -Asterisk: \*
    -
    -Underscore: \_
    -
    -Left brace: \{
    -
    -Right brace: \}
    -
    -Left bracket: \[
    -
    -Right bracket: \]
    -
    -Left paren: \(
    -
    -Right paren: \)
    -
    -Greater-than: \>
    -
    -Hash: \#
    -
    -Period: \.
    -
    -Bang: \!
    -
    -Plus: \+
    -
    -Minus: \-
    -
    -Tilde: \~
    -
    - -

    Nor should these, which occur in code spans:

    - -

    Backslash: \\

    - -

    Backtick: \`

    - -

    Asterisk: \*

    - -

    Underscore: \_

    - -

    Left brace: \{

    - -

    Right brace: \}

    - -

    Left bracket: \[

    - -

    Right bracket: \]

    - -

    Left paren: \(

    - -

    Right paren: \)

    - -

    Greater-than: \>

    - -

    Hash: \#

    - -

    Period: \.

    - -

    Bang: \!

    - -

    Plus: \+

    - -

    Minus: \-

    - -

    Tilde: \~

    - -

    These should get escaped, even though they're matching pairs for -other Markdown constructs:

    - -

    *asterisks*

    - -

    _underscores_

    - -

    `backticks`

    - -

    This is a code span with a literal backslash-backtick sequence: \`

    - -

    This is a tag with unescaped backticks bar.

    - -

    This is a tag with backslashes bar.

    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Backslash escapes.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Backslash escapes.text deleted file mode 100644 index 04c20bd3b..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Backslash escapes.text +++ /dev/null @@ -1,126 +0,0 @@ -These should all get escaped: - -Backslash: \\ - -Backtick: \` - -Asterisk: \* - -Underscore: \_ - -Left brace: \{ - -Right brace: \} - -Left bracket: \[ - -Right bracket: \] - -Left paren: \( - -Right paren: \) - -Greater-than: \> - -Hash: \# - -Period: \. - -Bang: \! - -Plus: \+ - -Minus: \- - -Tilde: \~ - - - -These should not, because they occur within a code block: - - Backslash: \\ - - Backtick: \` - - Asterisk: \* - - Underscore: \_ - - Left brace: \{ - - Right brace: \} - - Left bracket: \[ - - Right bracket: \] - - Left paren: \( - - Right paren: \) - - Greater-than: \> - - Hash: \# - - Period: \. - - Bang: \! - - Plus: \+ - - Minus: \- - - Tilde: \~ - - -Nor should these, which occur in code spans: - -Backslash: `\\` - -Backtick: `` \` `` - -Asterisk: `\*` - -Underscore: `\_` - -Left brace: `\{` - -Right brace: `\}` - -Left bracket: `\[` - -Right bracket: `\]` - -Left paren: `\(` - -Right paren: `\)` - -Greater-than: `\>` - -Hash: `\#` - -Period: `\.` - -Bang: `\!` - -Plus: `\+` - -Minus: `\-` - -Tilde: `\~` - - -These should get escaped, even though they're matching pairs for -other Markdown constructs: - -\*asterisks\* - -\_underscores\_ - -\`backticks\` - -This is a code span with a literal backslash-backtick sequence: `` \` `` - -This is a tag with unescaped backticks bar. - -This is a tag with backslashes bar. diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Blockquotes with code blocks.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Blockquotes with code blocks.html deleted file mode 100644 index 360fa9b14..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Blockquotes with code blocks.html +++ /dev/null @@ -1,15 +0,0 @@ -
    -

    Example:

    - -
    sub status {
    -    print "working";
    -}
    -
    - -

    Or:

    - -
    sub status {
    -    return "working";
    -}
    -
    -
    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Blockquotes with code blocks.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Blockquotes with code blocks.text deleted file mode 100644 index c31d17104..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Blockquotes with code blocks.text +++ /dev/null @@ -1,11 +0,0 @@ -> Example: -> -> sub status { -> print "working"; -> } -> -> Or: -> -> sub status { -> return "working"; -> } diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Blocks.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Blocks.html deleted file mode 100644 index 32703f5cb..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Blocks.html +++ /dev/null @@ -1,18 +0,0 @@ -
    code block on the first line
    -
    - -

    Regular text.

    - -
    code block indented by spaces
    -
    - -

    Regular text.

    - -
    the lines in this block  
    -all contain trailing spaces  
    -
    - -

    Regular Text.

    - -
    code block on the last line
    -
    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Blocks.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Blocks.text deleted file mode 100644 index b54b09285..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Blocks.text +++ /dev/null @@ -1,14 +0,0 @@ - code block on the first line - -Regular text. - - code block indented by spaces - -Regular text. - - the lines in this block - all contain trailing spaces - -Regular Text. - - code block on the last line \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Spans.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Spans.html deleted file mode 100644 index ef85f95e7..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Spans.html +++ /dev/null @@ -1,5 +0,0 @@ -

    <test a=" content of attribute ">

    - -

    Fix for backticks within HTML tag: like this

    - -

    Here's how you put `backticks` in a code span.

    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Spans.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Spans.text deleted file mode 100644 index 750a1973d..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Code Spans.text +++ /dev/null @@ -1,6 +0,0 @@ -`` - -Fix for backticks within HTML tag: like this - -Here's how you put `` `backticks` `` in a code span. - diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines no empty line before block.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines no empty line before block.html deleted file mode 100644 index fc253194c..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines no empty line before block.html +++ /dev/null @@ -1,14 +0,0 @@ -

    In Markdown 1.0.0 and earlier. Version

    - -
      -
    1. This line turns into a list item. -Because a hard-wrapped line in the -middle of a paragraph looked like a -list item.
    2. -
    - -

    Here's one with a bullet.

    - -
      -
    • criminey.
    • -
    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines no empty line before block.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines no empty line before block.text deleted file mode 100644 index f8a5b27bf..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines no empty line before block.text +++ /dev/null @@ -1,8 +0,0 @@ -In Markdown 1.0.0 and earlier. Version -8. This line turns into a list item. -Because a hard-wrapped line in the -middle of a paragraph looked like a -list item. - -Here's one with a bullet. -* criminey. diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines.html deleted file mode 100644 index e21ac79a2..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines.html +++ /dev/null @@ -1,8 +0,0 @@ -

    In Markdown 1.0.0 and earlier. Version -8. This line turns into a list item. -Because a hard-wrapped line in the -middle of a paragraph looked like a -list item.

    - -

    Here's one with a bullet. -* criminey.

    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines.text deleted file mode 100644 index f8a5b27bf..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Hard-wrapped paragraphs with list-like lines.text +++ /dev/null @@ -1,8 +0,0 @@ -In Markdown 1.0.0 and earlier. Version -8. This line turns into a list item. -Because a hard-wrapped line in the -middle of a paragraph looked like a -list item. - -Here's one with a bullet. -* criminey. diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Horizontal rules.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Horizontal rules.html deleted file mode 100644 index e60d4ba25..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Horizontal rules.html +++ /dev/null @@ -1,71 +0,0 @@ -

    Dashes:

    - -
    - -
    - -
    - -
    - -
    ---
    -
    - -
    - -
    - -
    - -
    - -
    - - -
    -
    - -

    Asterisks:

    - -
    - -
    - -
    - -
    - -
    ***
    -
    - -
    - -
    - -
    - -
    - -
    * * *
    -
    - -

    Underscores:

    - -
    - -
    - -
    - -
    - -
    ___
    -
    - -
    - -
    - -
    - -
    - -
    _ _ _
    -
    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Horizontal rules.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Horizontal rules.text deleted file mode 100644 index 1594bda27..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Horizontal rules.text +++ /dev/null @@ -1,67 +0,0 @@ -Dashes: - ---- - - --- - - --- - - --- - - --- - -- - - - - - - - - - - - - - - - - - - - - - - - - -Asterisks: - -*** - - *** - - *** - - *** - - *** - -* * * - - * * * - - * * * - - * * * - - * * * - - -Underscores: - -___ - - ___ - - ___ - - ___ - - ___ - -_ _ _ - - _ _ _ - - _ _ _ - - _ _ _ - - _ _ _ diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Advanced).html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Advanced).html deleted file mode 100644 index 3af9cafb1..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Advanced).html +++ /dev/null @@ -1,15 +0,0 @@ -

    Simple block on one line:

    - -
    foo
    - -

    And nested without indentation:

    - -
    -
    -
    -foo -
    -
    -
    -
    bar
    -
    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Advanced).text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Advanced).text deleted file mode 100644 index 86b7206d2..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Advanced).text +++ /dev/null @@ -1,15 +0,0 @@ -Simple block on one line: - -
    foo
    - -And nested without indentation: - -
    -
    -
    -foo -
    -
    -
    -
    bar
    -
    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Simple).html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Simple).html deleted file mode 100644 index 6bf78f8fc..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Simple).html +++ /dev/null @@ -1,72 +0,0 @@ -

    Here's a simple block:

    - -
    - foo -
    - -

    This should be a code block, though:

    - -
    <div>
    -    foo
    -</div>
    -
    - -

    As should this:

    - -
    <div>foo</div>
    -
    - -

    Now, nested:

    - -
    -
    -
    - foo -
    -
    -
    - -

    This should just be an HTML comment:

    - - - -

    Multiline:

    - - - -

    Code block:

    - -
    <!-- Comment -->
    -
    - -

    Just plain comment, with trailing spaces on the line:

    - - - -

    Code:

    - -
    <hr />
    -
    - -

    Hr's:

    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Simple).text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Simple).text deleted file mode 100644 index 14aa2dc27..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML (Simple).text +++ /dev/null @@ -1,69 +0,0 @@ -Here's a simple block: - -
    - foo -
    - -This should be a code block, though: - -
    - foo -
    - -As should this: - -
    foo
    - -Now, nested: - -
    -
    -
    - foo -
    -
    -
    - -This should just be an HTML comment: - - - -Multiline: - - - -Code block: - - - -Just plain comment, with trailing spaces on the line: - - - -Code: - -
    - -Hr's: - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML comments.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML comments.html deleted file mode 100644 index 3f167a161..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML comments.html +++ /dev/null @@ -1,13 +0,0 @@ -

    Paragraph one.

    - - - - - -

    Paragraph two.

    - - - -

    The end.

    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML comments.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML comments.text deleted file mode 100644 index 41d830d03..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Inline HTML comments.text +++ /dev/null @@ -1,13 +0,0 @@ -Paragraph one. - - - - - -Paragraph two. - - - -The end. diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, inline style.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, inline style.html deleted file mode 100644 index 5802f2deb..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, inline style.html +++ /dev/null @@ -1,11 +0,0 @@ -

    Just a URL.

    - -

    URL and title.

    - -

    URL and title.

    - -

    URL and title.

    - -

    URL and title.

    - -

    [Empty]().

    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, inline style.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, inline style.text deleted file mode 100644 index 09017a90c..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, inline style.text +++ /dev/null @@ -1,12 +0,0 @@ -Just a [URL](/url/). - -[URL and title](/url/ "title"). - -[URL and title](/url/ "title preceded by two spaces"). - -[URL and title](/url/ "title preceded by a tab"). - -[URL and title](/url/ "title has spaces afterward" ). - - -[Empty](). diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, reference style.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, reference style.html deleted file mode 100644 index bebefdee2..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, reference style.html +++ /dev/null @@ -1,52 +0,0 @@ -

    Foo bar.

    - -

    Foo bar.

    - -

    Foo bar.

    - -

    With embedded [brackets].

    - -

    Indented once.

    - -

    Indented twice.

    - -

    Indented thrice.

    - -

    Indented [four][] times.

    - -
    [four]: /url
    -
    - -
    - -

    this should work

    - -

    So should this.

    - -

    And this.

    - -

    And this.

    - -

    And this.

    - -

    But not [that] [].

    - -

    Nor [that][].

    - -

    Nor [that].

    - -

    [Something in brackets like this should work]

    - -

    [Same with this.]

    - -

    In this case, this points to something else.

    - -

    Backslashing should suppress [this] and [this].

    - -
    - -

    Here's one where the link -breaks across lines.

    - -

    Here's another where the link -breaks across lines, but with a line-ending space.

    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, reference style.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, reference style.text deleted file mode 100644 index 341ec88e3..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, reference style.text +++ /dev/null @@ -1,71 +0,0 @@ -Foo [bar] [1]. - -Foo [bar][1]. - -Foo [bar] -[1]. - -[1]: /url/ "Title" - - -With [embedded [brackets]] [b]. - - -Indented [once][]. - -Indented [twice][]. - -Indented [thrice][]. - -Indented [four][] times. - - [once]: /url - - [twice]: /url - - [thrice]: /url - - [four]: /url - - -[b]: /url/ - -* * * - -[this] [this] should work - -So should [this][this]. - -And [this] []. - -And [this][]. - -And [this]. - -But not [that] []. - -Nor [that][]. - -Nor [that]. - -[Something in brackets like [this][] should work] - -[Same with [this].] - -In this case, [this](/somethingelse/) points to something else. - -Backslashing should suppress \[this] and [this\]. - -[this]: foo - - -* * * - -Here's one where the [link -breaks] across lines. - -Here's another where the [link -breaks] across lines, but with a line-ending space. - - -[link breaks]: /url/ diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, shortcut references.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, shortcut references.html deleted file mode 100644 index 0b5e1d64e..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, shortcut references.html +++ /dev/null @@ -1,9 +0,0 @@ -

    This is the simple case.

    - -

    This one has a line -break.

    - -

    This one has a line -break with a line-ending space.

    - -

    this and the other

    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, shortcut references.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, shortcut references.text deleted file mode 100644 index 8c44c98fe..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Links, shortcut references.text +++ /dev/null @@ -1,20 +0,0 @@ -This is the [simple case]. - -[simple case]: /simple - - - -This one has a [line -break]. - -This one has a [line -break] with a line-ending space. - -[line break]: /foo - - -[this] [that] and the [other] - -[this]: /this -[that]: /that -[other]: /other diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Literal quotes in titles.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Literal quotes in titles.html deleted file mode 100644 index 611c1ac61..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Literal quotes in titles.html +++ /dev/null @@ -1,3 +0,0 @@ -

    Foo bar.

    - -

    Foo bar.

    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Literal quotes in titles.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Literal quotes in titles.text deleted file mode 100644 index 29d0e4235..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Literal quotes in titles.text +++ /dev/null @@ -1,7 +0,0 @@ -Foo [bar][]. - -Foo [bar](/url/ "Title with "quotes" inside"). - - - [bar]: /url/ "Title with "quotes" inside" - diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Basics.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Basics.html deleted file mode 100644 index ea3a61c39..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Basics.html +++ /dev/null @@ -1,314 +0,0 @@ -

    Markdown: Basics

    - - - -

    Getting the Gist of Markdown's Formatting Syntax

    - -

    This page offers a brief overview of what it's like to use Markdown. -The syntax page provides complete, detailed documentation for -every feature, but Markdown should be very easy to pick up simply by -looking at a few examples of it in action. The examples on this page -are written in a before/after style, showing example syntax and the -HTML output produced by Markdown.

    - -

    It's also helpful to simply try Markdown out; the Dingus is a -web application that allows you type your own Markdown-formatted text -and translate it to XHTML.

    - -

    Note: This document is itself written using Markdown; you -can see the source for it by adding '.text' to the URL.

    - -

    Paragraphs, Headers, Blockquotes

    - -

    A paragraph is simply one or more consecutive lines of text, separated -by one or more blank lines. (A blank line is any line that looks like a -blank line -- a line containing nothing spaces or tabs is considered -blank.) Normal paragraphs should not be intended with spaces or tabs.

    - -

    Markdown offers two styles of headers: Setext and atx. -Setext-style headers for <h1> and <h2> are created by -"underlining" with equal signs (=) and hyphens (-), respectively. -To create an atx-style header, you put 1-6 hash marks (#) at the -beginning of the line -- the number of hashes equals the resulting -HTML header level.

    - -

    Blockquotes are indicated using email-style '>' angle brackets.

    - -

    Markdown:

    - -
    A First Level Header
    -====================
    -
    -A Second Level Header
    ----------------------
    -
    -Now is the time for all good men to come to
    -the aid of their country. This is just a
    -regular paragraph.
    -
    -The quick brown fox jumped over the lazy
    -dog's back.
    -
    -### Header 3
    -
    -> This is a blockquote.
    -> 
    -> This is the second paragraph in the blockquote.
    ->
    -> ## This is an H2 in a blockquote
    -
    - -

    Output:

    - -
    <h1>A First Level Header</h1>
    -
    -<h2>A Second Level Header</h2>
    -
    -<p>Now is the time for all good men to come to
    -the aid of their country. This is just a
    -regular paragraph.</p>
    -
    -<p>The quick brown fox jumped over the lazy
    -dog's back.</p>
    -
    -<h3>Header 3</h3>
    -
    -<blockquote>
    -    <p>This is a blockquote.</p>
    -
    -    <p>This is the second paragraph in the blockquote.</p>
    -
    -    <h2>This is an H2 in a blockquote</h2>
    -</blockquote>
    -
    - -

    Phrase Emphasis

    - -

    Markdown uses asterisks and underscores to indicate spans of emphasis.

    - -

    Markdown:

    - -
    Some of these words *are emphasized*.
    -Some of these words _are emphasized also_.
    -
    -Use two asterisks for **strong emphasis**.
    -Or, if you prefer, __use two underscores instead__.
    -
    - -

    Output:

    - -
    <p>Some of these words <em>are emphasized</em>.
    -Some of these words <em>are emphasized also</em>.</p>
    -
    -<p>Use two asterisks for <strong>strong emphasis</strong>.
    -Or, if you prefer, <strong>use two underscores instead</strong>.</p>
    -
    - -

    Lists

    - -

    Unordered (bulleted) lists use asterisks, pluses, and hyphens (*, -+, and -) as list markers. These three markers are -interchangable; this:

    - -
    *   Candy.
    -*   Gum.
    -*   Booze.
    -
    - -

    this:

    - -
    +   Candy.
    -+   Gum.
    -+   Booze.
    -
    - -

    and this:

    - -
    -   Candy.
    --   Gum.
    --   Booze.
    -
    - -

    all produce the same output:

    - -
    <ul>
    -<li>Candy.</li>
    -<li>Gum.</li>
    -<li>Booze.</li>
    -</ul>
    -
    - -

    Ordered (numbered) lists use regular numbers, followed by periods, as -list markers:

    - -
    1.  Red
    -2.  Green
    -3.  Blue
    -
    - -

    Output:

    - -
    <ol>
    -<li>Red</li>
    -<li>Green</li>
    -<li>Blue</li>
    -</ol>
    -
    - -

    If you put blank lines between items, you'll get <p> tags for the -list item text. You can create multi-paragraph list items by indenting -the paragraphs by 4 spaces or 1 tab:

    - -
    *   A list item.
    -
    -    With multiple paragraphs.
    -
    -*   Another item in the list.
    -
    - -

    Output:

    - -
    <ul>
    -<li><p>A list item.</p>
    -<p>With multiple paragraphs.</p></li>
    -<li><p>Another item in the list.</p></li>
    -</ul>
    -
    - -

    Links

    - -

    Markdown supports two styles for creating links: inline and -reference. With both styles, you use square brackets to delimit the -text you want to turn into a link.

    - -

    Inline-style links use parentheses immediately after the link text. -For example:

    - -
    This is an [example link](http://example.com/).
    -
    - -

    Output:

    - -
    <p>This is an <a href="http://example.com/">
    -example link</a>.</p>
    -
    - -

    Optionally, you may include a title attribute in the parentheses:

    - -
    This is an [example link](http://example.com/ "With a Title").
    -
    - -

    Output:

    - -
    <p>This is an <a href="http://example.com/" title="With a Title">
    -example link</a>.</p>
    -
    - -

    Reference-style links allow you to refer to your links by names, which -you define elsewhere in your document:

    - -
    I get 10 times more traffic from [Google][1] than from
    -[Yahoo][2] or [MSN][3].
    -
    -[1]: http://google.com/        "Google"
    -[2]: http://search.yahoo.com/  "Yahoo Search"
    -[3]: http://search.msn.com/    "MSN Search"
    -
    - -

    Output:

    - -
    <p>I get 10 times more traffic from <a href="http://google.com/"
    -title="Google">Google</a> than from <a href="http://search.yahoo.com/"
    -title="Yahoo Search">Yahoo</a> or <a href="http://search.msn.com/"
    -title="MSN Search">MSN</a>.</p>
    -
    - -

    The title attribute is optional. Link names may contain letters, -numbers and spaces, but are not case sensitive:

    - -
    I start my morning with a cup of coffee and
    -[The New York Times][NY Times].
    -
    -[ny times]: http://www.nytimes.com/
    -
    - -

    Output:

    - -
    <p>I start my morning with a cup of coffee and
    -<a href="http://www.nytimes.com/">The New York Times</a>.</p>
    -
    - -

    Images

    - -

    Image syntax is very much like link syntax.

    - -

    Inline (titles are optional):

    - -
    ![alt text](/path/to/img.jpg "Title")
    -
    - -

    Reference-style:

    - -
    ![alt text][id]
    -
    -[id]: /path/to/img.jpg "Title"
    -
    - -

    Both of the above examples produce the same output:

    - -
    <img src="/path/to/img.jpg" alt="alt text" title="Title" />
    -
    - -

    Code

    - -

    In a regular paragraph, you can create code span by wrapping text in -backtick quotes. Any ampersands (&) and angle brackets (< or ->) will automatically be translated into HTML entities. This makes -it easy to use Markdown to write about HTML example code:

    - -
    I strongly recommend against using any `<blink>` tags.
    -
    -I wish SmartyPants used named entities like `&mdash;`
    -instead of decimal-encoded entites like `&#8212;`.
    -
    - -

    Output:

    - -
    <p>I strongly recommend against using any
    -<code>&lt;blink&gt;</code> tags.</p>
    -
    -<p>I wish SmartyPants used named entities like
    -<code>&amp;mdash;</code> instead of decimal-encoded
    -entites like <code>&amp;#8212;</code>.</p>
    -
    - -

    To specify an entire block of pre-formatted code, indent every line of -the block by 4 spaces or 1 tab. Just like with code spans, &, <, -and > characters will be escaped automatically.

    - -

    Markdown:

    - -
    If you want your page to validate under XHTML 1.0 Strict,
    -you've got to put paragraph tags in your blockquotes:
    -
    -    <blockquote>
    -        <p>For example.</p>
    -    </blockquote>
    -
    - -

    Output:

    - -
    <p>If you want your page to validate under XHTML 1.0 Strict,
    -you've got to put paragraph tags in your blockquotes:</p>
    -
    -<pre><code>&lt;blockquote&gt;
    -    &lt;p&gt;For example.&lt;/p&gt;
    -&lt;/blockquote&gt;
    -</code></pre>
    -
    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Basics.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Basics.text deleted file mode 100644 index 486055ca7..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Basics.text +++ /dev/null @@ -1,306 +0,0 @@ -Markdown: Basics -================ - - - - -Getting the Gist of Markdown's Formatting Syntax ------------------------------------------------- - -This page offers a brief overview of what it's like to use Markdown. -The [syntax page] [s] provides complete, detailed documentation for -every feature, but Markdown should be very easy to pick up simply by -looking at a few examples of it in action. The examples on this page -are written in a before/after style, showing example syntax and the -HTML output produced by Markdown. - -It's also helpful to simply try Markdown out; the [Dingus] [d] is a -web application that allows you type your own Markdown-formatted text -and translate it to XHTML. - -**Note:** This document is itself written using Markdown; you -can [see the source for it by adding '.text' to the URL] [src]. - - [s]: /projects/markdown/syntax "Markdown Syntax" - [d]: /projects/markdown/dingus "Markdown Dingus" - [src]: /projects/markdown/basics.text - - -## Paragraphs, Headers, Blockquotes ## - -A paragraph is simply one or more consecutive lines of text, separated -by one or more blank lines. (A blank line is any line that looks like a -blank line -- a line containing nothing spaces or tabs is considered -blank.) Normal paragraphs should not be intended with spaces or tabs. - -Markdown offers two styles of headers: *Setext* and *atx*. -Setext-style headers for `

    ` and `

    ` are created by -"underlining" with equal signs (`=`) and hyphens (`-`), respectively. -To create an atx-style header, you put 1-6 hash marks (`#`) at the -beginning of the line -- the number of hashes equals the resulting -HTML header level. - -Blockquotes are indicated using email-style '`>`' angle brackets. - -Markdown: - - A First Level Header - ==================== - - A Second Level Header - --------------------- - - Now is the time for all good men to come to - the aid of their country. This is just a - regular paragraph. - - The quick brown fox jumped over the lazy - dog's back. - - ### Header 3 - - > This is a blockquote. - > - > This is the second paragraph in the blockquote. - > - > ## This is an H2 in a blockquote - - -Output: - -

    A First Level Header

    - -

    A Second Level Header

    - -

    Now is the time for all good men to come to - the aid of their country. This is just a - regular paragraph.

    - -

    The quick brown fox jumped over the lazy - dog's back.

    - -

    Header 3

    - -
    -

    This is a blockquote.

    - -

    This is the second paragraph in the blockquote.

    - -

    This is an H2 in a blockquote

    -
    - - - -### Phrase Emphasis ### - -Markdown uses asterisks and underscores to indicate spans of emphasis. - -Markdown: - - Some of these words *are emphasized*. - Some of these words _are emphasized also_. - - Use two asterisks for **strong emphasis**. - Or, if you prefer, __use two underscores instead__. - -Output: - -

    Some of these words are emphasized. - Some of these words are emphasized also.

    - -

    Use two asterisks for strong emphasis. - Or, if you prefer, use two underscores instead.

    - - - -## Lists ## - -Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`, -`+`, and `-`) as list markers. These three markers are -interchangable; this: - - * Candy. - * Gum. - * Booze. - -this: - - + Candy. - + Gum. - + Booze. - -and this: - - - Candy. - - Gum. - - Booze. - -all produce the same output: - -
      -
    • Candy.
    • -
    • Gum.
    • -
    • Booze.
    • -
    - -Ordered (numbered) lists use regular numbers, followed by periods, as -list markers: - - 1. Red - 2. Green - 3. Blue - -Output: - -
      -
    1. Red
    2. -
    3. Green
    4. -
    5. Blue
    6. -
    - -If you put blank lines between items, you'll get `

    ` tags for the -list item text. You can create multi-paragraph list items by indenting -the paragraphs by 4 spaces or 1 tab: - - * A list item. - - With multiple paragraphs. - - * Another item in the list. - -Output: - -

      -
    • A list item.

      -

      With multiple paragraphs.

    • -
    • Another item in the list.

    • -
    - - - -### Links ### - -Markdown supports two styles for creating links: *inline* and -*reference*. With both styles, you use square brackets to delimit the -text you want to turn into a link. - -Inline-style links use parentheses immediately after the link text. -For example: - - This is an [example link](http://example.com/). - -Output: - -

    This is an - example link.

    - -Optionally, you may include a title attribute in the parentheses: - - This is an [example link](http://example.com/ "With a Title"). - -Output: - -

    This is an - example link.

    - -Reference-style links allow you to refer to your links by names, which -you define elsewhere in your document: - - I get 10 times more traffic from [Google][1] than from - [Yahoo][2] or [MSN][3]. - - [1]: http://google.com/ "Google" - [2]: http://search.yahoo.com/ "Yahoo Search" - [3]: http://search.msn.com/ "MSN Search" - -Output: - -

    I get 10 times more traffic from Google than from Yahoo or MSN.

    - -The title attribute is optional. Link names may contain letters, -numbers and spaces, but are *not* case sensitive: - - I start my morning with a cup of coffee and - [The New York Times][NY Times]. - - [ny times]: http://www.nytimes.com/ - -Output: - -

    I start my morning with a cup of coffee and - The New York Times.

    - - -### Images ### - -Image syntax is very much like link syntax. - -Inline (titles are optional): - - ![alt text](/path/to/img.jpg "Title") - -Reference-style: - - ![alt text][id] - - [id]: /path/to/img.jpg "Title" - -Both of the above examples produce the same output: - - alt text - - - -### Code ### - -In a regular paragraph, you can create code span by wrapping text in -backtick quotes. Any ampersands (`&`) and angle brackets (`<` or -`>`) will automatically be translated into HTML entities. This makes -it easy to use Markdown to write about HTML example code: - - I strongly recommend against using any `` tags. - - I wish SmartyPants used named entities like `—` - instead of decimal-encoded entites like `—`. - -Output: - -

    I strongly recommend against using any - <blink> tags.

    - -

    I wish SmartyPants used named entities like - &mdash; instead of decimal-encoded - entites like &#8212;.

    - - -To specify an entire block of pre-formatted code, indent every line of -the block by 4 spaces or 1 tab. Just like with code spans, `&`, `<`, -and `>` characters will be escaped automatically. - -Markdown: - - If you want your page to validate under XHTML 1.0 Strict, - you've got to put paragraph tags in your blockquotes: - -
    -

    For example.

    -
    - -Output: - -

    If you want your page to validate under XHTML 1.0 Strict, - you've got to put paragraph tags in your blockquotes:

    - -
    <blockquote>
    -        <p>For example.</p>
    -    </blockquote>
    -    
    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Syntax.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Syntax.html deleted file mode 100644 index 61dde593d..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Syntax.html +++ /dev/null @@ -1,946 +0,0 @@ -

    Markdown: Syntax

    - - - - - -

    Note: This document is itself written using Markdown; you -can see the source for it by adding '.text' to the URL.

    - -
    - -

    Overview

    - -

    Philosophy

    - -

    Markdown is intended to be as easy-to-read and easy-to-write as is feasible.

    - -

    Readability, however, is emphasized above all else. A Markdown-formatted -document should be publishable as-is, as plain text, without looking -like it's been marked up with tags or formatting instructions. While -Markdown's syntax has been influenced by several existing text-to-HTML -filters -- including Setext, atx, Textile, reStructuredText, -Grutatext, and EtText -- the single biggest source of -inspiration for Markdown's syntax is the format of plain text email.

    - -

    To this end, Markdown's syntax is comprised entirely of punctuation -characters, which punctuation characters have been carefully chosen so -as to look like what they mean. E.g., asterisks around a word actually -look like *emphasis*. Markdown lists look like, well, lists. Even -blockquotes look like quoted passages of text, assuming you've ever -used email.

    - -

    Inline HTML

    - -

    Markdown's syntax is intended for one purpose: to be used as a -format for writing for the web.

    - -

    Markdown is not a replacement for HTML, or even close to it. Its -syntax is very small, corresponding only to a very small subset of -HTML tags. The idea is not to create a syntax that makes it easier -to insert HTML tags. In my opinion, HTML tags are already easy to -insert. The idea for Markdown is to make it easy to read, write, and -edit prose. HTML is a publishing format; Markdown is a writing -format. Thus, Markdown's formatting syntax only addresses issues that -can be conveyed in plain text.

    - -

    For any markup that is not covered by Markdown's syntax, you simply -use HTML itself. There's no need to preface it or delimit it to -indicate that you're switching from Markdown to HTML; you just use -the tags.

    - -

    The only restrictions are that block-level HTML elements -- e.g. <div>, -<table>, <pre>, <p>, etc. -- must be separated from surrounding -content by blank lines, and the start and end tags of the block should -not be indented with tabs or spaces. Markdown is smart enough not -to add extra (unwanted) <p> tags around HTML block-level tags.

    - -

    For example, to add an HTML table to a Markdown article:

    - -
    This is a regular paragraph.
    -
    -<table>
    -    <tr>
    -        <td>Foo</td>
    -    </tr>
    -</table>
    -
    -This is another regular paragraph.
    -
    - -

    Note that Markdown formatting syntax is not processed within block-level -HTML tags. E.g., you can't use Markdown-style *emphasis* inside an -HTML block.

    - -

    Span-level HTML tags -- e.g. <span>, <cite>, or <del> -- can be -used anywhere in a Markdown paragraph, list item, or header. If you -want, you can even use HTML tags instead of Markdown formatting; e.g. if -you'd prefer to use HTML <a> or <img> tags instead of Markdown's -link or image syntax, go right ahead.

    - -

    Unlike block-level HTML tags, Markdown syntax is processed within -span-level tags.

    - -

    Automatic Escaping for Special Characters

    - -

    In HTML, there are two characters that demand special treatment: < -and &. Left angle brackets are used to start tags; ampersands are -used to denote HTML entities. If you want to use them as literal -characters, you must escape them as entities, e.g. &lt;, and -&amp;.

    - -

    Ampersands in particular are bedeviling for web writers. If you want to -write about 'AT&T', you need to write 'AT&amp;T'. You even need to -escape ampersands within URLs. Thus, if you want to link to:

    - -
    http://images.google.com/images?num=30&q=larry+bird
    -
    - -

    you need to encode the URL as:

    - -
    http://images.google.com/images?num=30&amp;q=larry+bird
    -
    - -

    in your anchor tag href attribute. Needless to say, this is easy to -forget, and is probably the single most common source of HTML validation -errors in otherwise well-marked-up web sites.

    - -

    Markdown allows you to use these characters naturally, taking care of -all the necessary escaping for you. If you use an ampersand as part of -an HTML entity, it remains unchanged; otherwise it will be translated -into &amp;.

    - -

    So, if you want to include a copyright symbol in your article, you can write:

    - -
    &copy;
    -
    - -

    and Markdown will leave it alone. But if you write:

    - -
    AT&T
    -
    - -

    Markdown will translate it to:

    - -
    AT&amp;T
    -
    - -

    Similarly, because Markdown supports inline HTML, if you use -angle brackets as delimiters for HTML tags, Markdown will treat them as -such. But if you write:

    - -
    4 < 5
    -
    - -

    Markdown will translate it to:

    - -
    4 &lt; 5
    -
    - -

    However, inside Markdown code spans and blocks, angle brackets and -ampersands are always encoded automatically. This makes it easy to use -Markdown to write about HTML code. (As opposed to raw HTML, which is a -terrible format for writing about HTML syntax, because every single < -and & in your example code needs to be escaped.)

    - -
    - -

    Block Elements

    - -

    Paragraphs and Line Breaks

    - -

    A paragraph is simply one or more consecutive lines of text, separated -by one or more blank lines. (A blank line is any line that looks like a -blank line -- a line containing nothing but spaces or tabs is considered -blank.) Normal paragraphs should not be intended with spaces or tabs.

    - -

    The implication of the "one or more consecutive lines of text" rule is -that Markdown supports "hard-wrapped" text paragraphs. This differs -significantly from most other text-to-HTML formatters (including Movable -Type's "Convert Line Breaks" option) which translate every line break -character in a paragraph into a <br /> tag.

    - -

    When you do want to insert a <br /> break tag using Markdown, you -end a line with two or more spaces, then type return.

    - -

    Yes, this takes a tad more effort to create a <br />, but a simplistic -"every line break is a <br />" rule wouldn't work for Markdown. -Markdown's email-style blockquoting and multi-paragraph list items -work best -- and look better -- when you format them with hard breaks.

    - - - -

    Markdown supports two styles of headers, Setext and atx.

    - -

    Setext-style headers are "underlined" using equal signs (for first-level -headers) and dashes (for second-level headers). For example:

    - -
    This is an H1
    -=============
    -
    -This is an H2
    --------------
    -
    - -

    Any number of underlining ='s or -'s will work.

    - -

    Atx-style headers use 1-6 hash characters at the start of the line, -corresponding to header levels 1-6. For example:

    - -
    # This is an H1
    -
    -## This is an H2
    -
    -###### This is an H6
    -
    - -

    Optionally, you may "close" atx-style headers. This is purely -cosmetic -- you can use this if you think it looks better. The -closing hashes don't even need to match the number of hashes -used to open the header. (The number of opening hashes -determines the header level.) :

    - -
    # This is an H1 #
    -
    -## This is an H2 ##
    -
    -### This is an H3 ######
    -
    - -

    Blockquotes

    - -

    Markdown uses email-style > characters for blockquoting. If you're -familiar with quoting passages of text in an email message, then you -know how to create a blockquote in Markdown. It looks best if you hard -wrap the text and put a > before every line:

    - -
    > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
    -> consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
    -> Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
    -> 
    -> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
    -> id sem consectetuer libero luctus adipiscing.
    -
    - -

    Markdown allows you to be lazy and only put the > before the first -line of a hard-wrapped paragraph:

    - -
    > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
    -consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
    -Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
    -
    -> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
    -id sem consectetuer libero luctus adipiscing.
    -
    - -

    Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by -adding additional levels of >:

    - -
    > This is the first level of quoting.
    ->
    -> > This is nested blockquote.
    ->
    -> Back to the first level.
    -
    - -

    Blockquotes can contain other Markdown elements, including headers, lists, -and code blocks:

    - -
    > ## This is a header.
    -> 
    -> 1.   This is the first list item.
    -> 2.   This is the second list item.
    -> 
    -> Here's some example code:
    -> 
    ->     return shell_exec("echo $input | $markdown_script");
    -
    - -

    Any decent text editor should make email-style quoting easy. For -example, with BBEdit, you can make a selection and choose Increase -Quote Level from the Text menu.

    - -

    Lists

    - -

    Markdown supports ordered (numbered) and unordered (bulleted) lists.

    - -

    Unordered lists use asterisks, pluses, and hyphens -- interchangably --- as list markers:

    - -
    *   Red
    -*   Green
    -*   Blue
    -
    - -

    is equivalent to:

    - -
    +   Red
    -+   Green
    -+   Blue
    -
    - -

    and:

    - -
    -   Red
    --   Green
    --   Blue
    -
    - -

    Ordered lists use numbers followed by periods:

    - -
    1.  Bird
    -2.  McHale
    -3.  Parish
    -
    - -

    It's important to note that the actual numbers you use to mark the -list have no effect on the HTML output Markdown produces. The HTML -Markdown produces from the above list is:

    - -
    <ol>
    -<li>Bird</li>
    -<li>McHale</li>
    -<li>Parish</li>
    -</ol>
    -
    - -

    If you instead wrote the list in Markdown like this:

    - -
    1.  Bird
    -1.  McHale
    -1.  Parish
    -
    - -

    or even:

    - -
    3. Bird
    -1. McHale
    -8. Parish
    -
    - -

    you'd get the exact same HTML output. The point is, if you want to, -you can use ordinal numbers in your ordered Markdown lists, so that -the numbers in your source match the numbers in your published HTML. -But if you want to be lazy, you don't have to.

    - -

    If you do use lazy list numbering, however, you should still start the -list with the number 1. At some point in the future, Markdown may support -starting ordered lists at an arbitrary number.

    - -

    List markers typically start at the left margin, but may be indented by -up to three spaces. List markers must be followed by one or more spaces -or a tab.

    - -

    To make lists look nice, you can wrap items with hanging indents:

    - -
    *   Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
    -    Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
    -    viverra nec, fringilla in, laoreet vitae, risus.
    -*   Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
    -    Suspendisse id sem consectetuer libero luctus adipiscing.
    -
    - -

    But if you want to be lazy, you don't have to:

    - -
    *   Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
    -Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
    -viverra nec, fringilla in, laoreet vitae, risus.
    -*   Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
    -Suspendisse id sem consectetuer libero luctus adipiscing.
    -
    - -

    If list items are separated by blank lines, Markdown will wrap the -items in <p> tags in the HTML output. For example, this input:

    - -
    *   Bird
    -*   Magic
    -
    - -

    will turn into:

    - -
    <ul>
    -<li>Bird</li>
    -<li>Magic</li>
    -</ul>
    -
    - -

    But this:

    - -
    *   Bird
    -
    -*   Magic
    -
    - -

    will turn into:

    - -
    <ul>
    -<li><p>Bird</p></li>
    -<li><p>Magic</p></li>
    -</ul>
    -
    - -

    List items may consist of multiple paragraphs. Each subsequent -paragraph in a list item must be intended by either 4 spaces -or one tab:

    - -
    1.  This is a list item with two paragraphs. Lorem ipsum dolor
    -    sit amet, consectetuer adipiscing elit. Aliquam hendrerit
    -    mi posuere lectus.
    -
    -    Vestibulum enim wisi, viverra nec, fringilla in, laoreet
    -    vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
    -    sit amet velit.
    -
    -2.  Suspendisse id sem consectetuer libero luctus adipiscing.
    -
    - -

    It looks nice if you indent every line of the subsequent -paragraphs, but here again, Markdown will allow you to be -lazy:

    - -
    *   This is a list item with two paragraphs.
    -
    -    This is the second paragraph in the list item. You're
    -only required to indent the first line. Lorem ipsum dolor
    -sit amet, consectetuer adipiscing elit.
    -
    -*   Another item in the same list.
    -
    - -

    To put a blockquote within a list item, the blockquote's > -delimiters need to be indented:

    - -
    *   A list item with a blockquote:
    -
    -    > This is a blockquote
    -    > inside a list item.
    -
    - -

    To put a code block within a list item, the code block needs -to be indented twice -- 8 spaces or two tabs:

    - -
    *   A list item with a code block:
    -
    -        <code goes here>
    -
    - -

    It's worth noting that it's possible to trigger an ordered list by -accident, by writing something like this:

    - -
    1986. What a great season.
    -
    - -

    In other words, a number-period-space sequence at the beginning of a -line. To avoid this, you can backslash-escape the period:

    - -
    1986\. What a great season.
    -
    - -

    Code Blocks

    - -

    Pre-formatted code blocks are used for writing about programming or -markup source code. Rather than forming normal paragraphs, the lines -of a code block are interpreted literally. Markdown wraps a code block -in both <pre> and <code> tags.

    - -

    To produce a code block in Markdown, simply indent every line of the -block by at least 4 spaces or 1 tab. For example, given this input:

    - -
    This is a normal paragraph:
    -
    -    This is a code block.
    -
    - -

    Markdown will generate:

    - -
    <p>This is a normal paragraph:</p>
    -
    -<pre><code>This is a code block.
    -</code></pre>
    -
    - -

    One level of indentation -- 4 spaces or 1 tab -- is removed from each -line of the code block. For example, this:

    - -
    Here is an example of AppleScript:
    -
    -    tell application "Foo"
    -        beep
    -    end tell
    -
    - -

    will turn into:

    - -
    <p>Here is an example of AppleScript:</p>
    -
    -<pre><code>tell application "Foo"
    -    beep
    -end tell
    -</code></pre>
    -
    - -

    A code block continues until it reaches a line that is not indented -(or the end of the article).

    - -

    Within a code block, ampersands (&) and angle brackets (< and >) -are automatically converted into HTML entities. This makes it very -easy to include example HTML source code using Markdown -- just paste -it and indent it, and Markdown will handle the hassle of encoding the -ampersands and angle brackets. For example, this:

    - -
        <div class="footer">
    -        &copy; 2004 Foo Corporation
    -    </div>
    -
    - -

    will turn into:

    - -
    <pre><code>&lt;div class="footer"&gt;
    -    &amp;copy; 2004 Foo Corporation
    -&lt;/div&gt;
    -</code></pre>
    -
    - -

    Regular Markdown syntax is not processed within code blocks. E.g., -asterisks are just literal asterisks within a code block. This means -it's also easy to use Markdown to write about Markdown's own syntax.

    - -

    Horizontal Rules

    - -

    You can produce a horizontal rule tag (<hr />) by placing three or -more hyphens, asterisks, or underscores on a line by themselves. If you -wish, you may use spaces between the hyphens or asterisks. Each of the -following lines will produce a horizontal rule:

    - -
    * * *
    -
    -***
    -
    -*****
    -
    -- - -
    -
    ----------------------------------------
    -
    -_ _ _
    -
    - -
    - -

    Span Elements

    - - - -

    Markdown supports two style of links: inline and reference.

    - -

    In both styles, the link text is delimited by [square brackets].

    - -

    To create an inline link, use a set of regular parentheses immediately -after the link text's closing square bracket. Inside the parentheses, -put the URL where you want the link to point, along with an optional -title for the link, surrounded in quotes. For example:

    - -
    This is [an example](http://example.com/ "Title") inline link.
    -
    -[This link](http://example.net/) has no title attribute.
    -
    - -

    Will produce:

    - -
    <p>This is <a href="http://example.com/" title="Title">
    -an example</a> inline link.</p>
    -
    -<p><a href="http://example.net/">This link</a> has no
    -title attribute.</p>
    -
    - -

    If you're referring to a local resource on the same server, you can -use relative paths:

    - -
    See my [About](/about/) page for details.
    -
    - -

    Reference-style links use a second set of square brackets, inside -which you place a label of your choosing to identify the link:

    - -
    This is [an example][id] reference-style link.
    -
    - -

    You can optionally use a space to separate the sets of brackets:

    - -
    This is [an example] [id] reference-style link.
    -
    - -

    Then, anywhere in the document, you define your link label like this, -on a line by itself:

    - -
    [id]: http://example.com/  "Optional Title Here"
    -
    - -

    That is:

    - -
      -
    • Square brackets containing the link identifier (optionally -indented from the left margin using up to three spaces);
    • -
    • followed by a colon;
    • -
    • followed by one or more spaces (or tabs);
    • -
    • followed by the URL for the link;
    • -
    • optionally followed by a title attribute for the link, enclosed -in double or single quotes.
    • -
    - -

    The link URL may, optionally, be surrounded by angle brackets:

    - -
    [id]: <http://example.com/>  "Optional Title Here"
    -
    - -

    You can put the title attribute on the next line and use extra spaces -or tabs for padding, which tends to look better with longer URLs:

    - -
    [id]: http://example.com/longish/path/to/resource/here
    -    "Optional Title Here"
    -
    - -

    Link definitions are only used for creating links during Markdown -processing, and are stripped from your document in the HTML output.

    - -

    Link definition names may constist of letters, numbers, spaces, and punctuation -- but they are not case sensitive. E.g. these two links:

    - -
    [link text][a]
    -[link text][A]
    -
    - -

    are equivalent.

    - -

    The implicit link name shortcut allows you to omit the name of the -link, in which case the link text itself is used as the name. -Just use an empty set of square brackets -- e.g., to link the word -"Google" to the google.com web site, you could simply write:

    - -
    [Google][]
    -
    - -

    And then define the link:

    - -
    [Google]: http://google.com/
    -
    - -

    Because link names may contain spaces, this shortcut even works for -multiple words in the link text:

    - -
    Visit [Daring Fireball][] for more information.
    -
    - -

    And then define the link:

    - -
    [Daring Fireball]: http://daringfireball.net/
    -
    - -

    Link definitions can be placed anywhere in your Markdown document. I -tend to put them immediately after each paragraph in which they're -used, but if you want, you can put them all at the end of your -document, sort of like footnotes.

    - -

    Here's an example of reference links in action:

    - -
    I get 10 times more traffic from [Google] [1] than from
    -[Yahoo] [2] or [MSN] [3].
    -
    -  [1]: http://google.com/        "Google"
    -  [2]: http://search.yahoo.com/  "Yahoo Search"
    -  [3]: http://search.msn.com/    "MSN Search"
    -
    - -

    Using the implicit link name shortcut, you could instead write:

    - -
    I get 10 times more traffic from [Google][] than from
    -[Yahoo][] or [MSN][].
    -
    -  [google]: http://google.com/        "Google"
    -  [yahoo]:  http://search.yahoo.com/  "Yahoo Search"
    -  [msn]:    http://search.msn.com/    "MSN Search"
    -
    - -

    Both of the above examples will produce the following HTML output:

    - -
    <p>I get 10 times more traffic from <a href="http://google.com/"
    -title="Google">Google</a> than from
    -<a href="http://search.yahoo.com/" title="Yahoo Search">Yahoo</a>
    -or <a href="http://search.msn.com/" title="MSN Search">MSN</a>.</p>
    -
    - -

    For comparison, here is the same paragraph written using -Markdown's inline link style:

    - -
    I get 10 times more traffic from [Google](http://google.com/ "Google")
    -than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or
    -[MSN](http://search.msn.com/ "MSN Search").
    -
    - -

    The point of reference-style links is not that they're easier to -write. The point is that with reference-style links, your document -source is vastly more readable. Compare the above examples: using -reference-style links, the paragraph itself is only 81 characters -long; with inline-style links, it's 176 characters; and as raw HTML, -it's 234 characters. In the raw HTML, there's more markup than there -is text.

    - -

    With Markdown's reference-style links, a source document much more -closely resembles the final output, as rendered in a browser. By -allowing you to move the markup-related metadata out of the paragraph, -you can add links without interrupting the narrative flow of your -prose.

    - -

    Emphasis

    - -

    Markdown treats asterisks (*) and underscores (_) as indicators of -emphasis. Text wrapped with one * or _ will be wrapped with an -HTML <em> tag; double *'s or _'s will be wrapped with an HTML -<strong> tag. E.g., this input:

    - -
    *single asterisks*
    -
    -_single underscores_
    -
    -**double asterisks**
    -
    -__double underscores__
    -
    - -

    will produce:

    - -
    <em>single asterisks</em>
    -
    -<em>single underscores</em>
    -
    -<strong>double asterisks</strong>
    -
    -<strong>double underscores</strong>
    -
    - -

    You can use whichever style you prefer; the lone restriction is that -the same character must be used to open and close an emphasis span.

    - -

    Emphasis can be used in the middle of a word:

    - -
    un*fucking*believable
    -
    - -

    But if you surround an * or _ with spaces, it'll be treated as a -literal asterisk or underscore.

    - -

    To produce a literal asterisk or underscore at a position where it -would otherwise be used as an emphasis delimiter, you can backslash -escape it:

    - -
    \*this text is surrounded by literal asterisks\*
    -
    - -

    Code

    - -

    To indicate a span of code, wrap it with backtick quotes (`). -Unlike a pre-formatted code block, a code span indicates code within a -normal paragraph. For example:

    - -
    Use the `printf()` function.
    -
    - -

    will produce:

    - -
    <p>Use the <code>printf()</code> function.</p>
    -
    - -

    To include a literal backtick character within a code span, you can use -multiple backticks as the opening and closing delimiters:

    - -
    ``There is a literal backtick (`) here.``
    -
    - -

    which will produce this:

    - -
    <p><code>There is a literal backtick (`) here.</code></p>
    -
    - -

    The backtick delimiters surrounding a code span may include spaces -- -one after the opening, one before the closing. This allows you to place -literal backtick characters at the beginning or end of a code span:

    - -
    A single backtick in a code span: `` ` ``
    -
    -A backtick-delimited string in a code span: `` `foo` ``
    -
    - -

    will produce:

    - -
    <p>A single backtick in a code span: <code>`</code></p>
    -
    -<p>A backtick-delimited string in a code span: <code>`foo`</code></p>
    -
    - -

    With a code span, ampersands and angle brackets are encoded as HTML -entities automatically, which makes it easy to include example HTML -tags. Markdown will turn this:

    - -
    Please don't use any `<blink>` tags.
    -
    - -

    into:

    - -
    <p>Please don't use any <code>&lt;blink&gt;</code> tags.</p>
    -
    - -

    You can write this:

    - -
    `&#8212;` is the decimal-encoded equivalent of `&mdash;`.
    -
    - -

    to produce:

    - -
    <p><code>&amp;#8212;</code> is the decimal-encoded
    -equivalent of <code>&amp;mdash;</code>.</p>
    -
    - -

    Images

    - -

    Admittedly, it's fairly difficult to devise a "natural" syntax for -placing images into a plain text document format.

    - -

    Markdown uses an image syntax that is intended to resemble the syntax -for links, allowing for two styles: inline and reference.

    - -

    Inline image syntax looks like this:

    - -
    ![Alt text](/path/to/img.jpg)
    -
    -![Alt text](/path/to/img.jpg "Optional title")
    -
    - -

    That is:

    - -
      -
    • An exclamation mark: !;
    • -
    • followed by a set of square brackets, containing the alt -attribute text for the image;
    • -
    • followed by a set of parentheses, containing the URL or path to -the image, and an optional title attribute enclosed in double -or single quotes.
    • -
    - -

    Reference-style image syntax looks like this:

    - -
    ![Alt text][id]
    -
    - -

    Where "id" is the name of a defined image reference. Image references -are defined using syntax identical to link references:

    - -
    [id]: url/to/image  "Optional title attribute"
    -
    - -

    As of this writing, Markdown has no syntax for specifying the -dimensions of an image; if this is important to you, you can simply -use regular HTML <img> tags.

    - -
    - -

    Miscellaneous

    - - - -

    Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this:

    - -
    <http://example.com/>
    -
    - -

    Markdown will turn this into:

    - -
    <a href="http://example.com/">http://example.com/</a>
    -
    - -

    Automatic links for email addresses work similarly, except that -Markdown will also perform a bit of randomized decimal and hex -entity-encoding to help obscure your address from address-harvesting -spambots. For example, Markdown will turn this:

    - -
    <address@example.com>
    -
    - -

    into something like this:

    - -
    <a href="&#x6D;&#x61;i&#x6C;&#x74;&#x6F;:&#x61;&#x64;&#x64;&#x72;&#x65;
    -&#115;&#115;&#64;&#101;&#120;&#x61;&#109;&#x70;&#x6C;e&#x2E;&#99;&#111;
    -&#109;">&#x61;&#x64;&#x64;&#x72;&#x65;&#115;&#115;&#64;&#101;&#120;&#x61;
    -&#109;&#x70;&#x6C;e&#x2E;&#99;&#111;&#109;</a>
    -
    - -

    which will render in a browser as a clickable link to "address@example.com".

    - -

    (This sort of entity-encoding trick will indeed fool many, if not -most, address-harvesting bots, but it definitely won't fool all of -them. It's better than nothing, but an address published in this way -will probably eventually start receiving spam.)

    - -

    Backslash Escapes

    - -

    Markdown allows you to use backslash escapes to generate literal -characters which would otherwise have special meaning in Markdown's -formatting syntax. For example, if you wanted to surround a word with -literal asterisks (instead of an HTML <em> tag), you can backslashes -before the asterisks, like this:

    - -
    \*literal asterisks\*
    -
    - -

    Markdown provides backslash escapes for the following characters:

    - -
    \   backslash
    -`   backtick
    -*   asterisk
    -_   underscore
    -{}  curly braces
    -[]  square brackets
    -()  parentheses
    -#   hash mark
    -+   plus sign
    --   minus sign (hyphen)
    -.   dot
    -!   exclamation mark
    -
    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Syntax.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Syntax.text deleted file mode 100644 index 57360a16c..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Markdown Documentation - Syntax.text +++ /dev/null @@ -1,888 +0,0 @@ -Markdown: Syntax -================ - - - - -* [Overview](#overview) - * [Philosophy](#philosophy) - * [Inline HTML](#html) - * [Automatic Escaping for Special Characters](#autoescape) -* [Block Elements](#block) - * [Paragraphs and Line Breaks](#p) - * [Headers](#header) - * [Blockquotes](#blockquote) - * [Lists](#list) - * [Code Blocks](#precode) - * [Horizontal Rules](#hr) -* [Span Elements](#span) - * [Links](#link) - * [Emphasis](#em) - * [Code](#code) - * [Images](#img) -* [Miscellaneous](#misc) - * [Backslash Escapes](#backslash) - * [Automatic Links](#autolink) - - -**Note:** This document is itself written using Markdown; you -can [see the source for it by adding '.text' to the URL][src]. - - [src]: /projects/markdown/syntax.text - -* * * - -

    Overview

    - -

    Philosophy

    - -Markdown is intended to be as easy-to-read and easy-to-write as is feasible. - -Readability, however, is emphasized above all else. A Markdown-formatted -document should be publishable as-is, as plain text, without looking -like it's been marked up with tags or formatting instructions. While -Markdown's syntax has been influenced by several existing text-to-HTML -filters -- including [Setext] [1], [atx] [2], [Textile] [3], [reStructuredText] [4], -[Grutatext] [5], and [EtText] [6] -- the single biggest source of -inspiration for Markdown's syntax is the format of plain text email. - - [1]: http://docutils.sourceforge.net/mirror/setext.html - [2]: http://www.aaronsw.com/2002/atx/ - [3]: http://textism.com/tools/textile/ - [4]: http://docutils.sourceforge.net/rst.html - [5]: http://www.triptico.com/software/grutatxt.html - [6]: http://ettext.taint.org/doc/ - -To this end, Markdown's syntax is comprised entirely of punctuation -characters, which punctuation characters have been carefully chosen so -as to look like what they mean. E.g., asterisks around a word actually -look like \*emphasis\*. Markdown lists look like, well, lists. Even -blockquotes look like quoted passages of text, assuming you've ever -used email. - - - -

    Inline HTML

    - -Markdown's syntax is intended for one purpose: to be used as a -format for *writing* for the web. - -Markdown is not a replacement for HTML, or even close to it. Its -syntax is very small, corresponding only to a very small subset of -HTML tags. The idea is *not* to create a syntax that makes it easier -to insert HTML tags. In my opinion, HTML tags are already easy to -insert. The idea for Markdown is to make it easy to read, write, and -edit prose. HTML is a *publishing* format; Markdown is a *writing* -format. Thus, Markdown's formatting syntax only addresses issues that -can be conveyed in plain text. - -For any markup that is not covered by Markdown's syntax, you simply -use HTML itself. There's no need to preface it or delimit it to -indicate that you're switching from Markdown to HTML; you just use -the tags. - -The only restrictions are that block-level HTML elements -- e.g. `
    `, -``, `
    `, `

    `, etc. -- must be separated from surrounding -content by blank lines, and the start and end tags of the block should -not be indented with tabs or spaces. Markdown is smart enough not -to add extra (unwanted) `

    ` tags around HTML block-level tags. - -For example, to add an HTML table to a Markdown article: - - This is a regular paragraph. - -

    - - - -
    Foo
    - - This is another regular paragraph. - -Note that Markdown formatting syntax is not processed within block-level -HTML tags. E.g., you can't use Markdown-style `*emphasis*` inside an -HTML block. - -Span-level HTML tags -- e.g. ``, ``, or `` -- can be -used anywhere in a Markdown paragraph, list item, or header. If you -want, you can even use HTML tags instead of Markdown formatting; e.g. if -you'd prefer to use HTML `` or `` tags instead of Markdown's -link or image syntax, go right ahead. - -Unlike block-level HTML tags, Markdown syntax *is* processed within -span-level tags. - - -

    Automatic Escaping for Special Characters

    - -In HTML, there are two characters that demand special treatment: `<` -and `&`. Left angle brackets are used to start tags; ampersands are -used to denote HTML entities. If you want to use them as literal -characters, you must escape them as entities, e.g. `<`, and -`&`. - -Ampersands in particular are bedeviling for web writers. If you want to -write about 'AT&T', you need to write '`AT&T`'. You even need to -escape ampersands within URLs. Thus, if you want to link to: - - http://images.google.com/images?num=30&q=larry+bird - -you need to encode the URL as: - - http://images.google.com/images?num=30&q=larry+bird - -in your anchor tag `href` attribute. Needless to say, this is easy to -forget, and is probably the single most common source of HTML validation -errors in otherwise well-marked-up web sites. - -Markdown allows you to use these characters naturally, taking care of -all the necessary escaping for you. If you use an ampersand as part of -an HTML entity, it remains unchanged; otherwise it will be translated -into `&`. - -So, if you want to include a copyright symbol in your article, you can write: - - © - -and Markdown will leave it alone. But if you write: - - AT&T - -Markdown will translate it to: - - AT&T - -Similarly, because Markdown supports [inline HTML](#html), if you use -angle brackets as delimiters for HTML tags, Markdown will treat them as -such. But if you write: - - 4 < 5 - -Markdown will translate it to: - - 4 < 5 - -However, inside Markdown code spans and blocks, angle brackets and -ampersands are *always* encoded automatically. This makes it easy to use -Markdown to write about HTML code. (As opposed to raw HTML, which is a -terrible format for writing about HTML syntax, because every single `<` -and `&` in your example code needs to be escaped.) - - -* * * - - -

    Block Elements

    - - -

    Paragraphs and Line Breaks

    - -A paragraph is simply one or more consecutive lines of text, separated -by one or more blank lines. (A blank line is any line that looks like a -blank line -- a line containing nothing but spaces or tabs is considered -blank.) Normal paragraphs should not be intended with spaces or tabs. - -The implication of the "one or more consecutive lines of text" rule is -that Markdown supports "hard-wrapped" text paragraphs. This differs -significantly from most other text-to-HTML formatters (including Movable -Type's "Convert Line Breaks" option) which translate every line break -character in a paragraph into a `
    ` tag. - -When you *do* want to insert a `
    ` break tag using Markdown, you -end a line with two or more spaces, then type return. - -Yes, this takes a tad more effort to create a `
    `, but a simplistic -"every line break is a `
    `" rule wouldn't work for Markdown. -Markdown's email-style [blockquoting][bq] and multi-paragraph [list items][l] -work best -- and look better -- when you format them with hard breaks. - - [bq]: #blockquote - [l]: #list - - - - - -Markdown supports two styles of headers, [Setext] [1] and [atx] [2]. - -Setext-style headers are "underlined" using equal signs (for first-level -headers) and dashes (for second-level headers). For example: - - This is an H1 - ============= - - This is an H2 - ------------- - -Any number of underlining `=`'s or `-`'s will work. - -Atx-style headers use 1-6 hash characters at the start of the line, -corresponding to header levels 1-6. For example: - - # This is an H1 - - ## This is an H2 - - ###### This is an H6 - -Optionally, you may "close" atx-style headers. This is purely -cosmetic -- you can use this if you think it looks better. The -closing hashes don't even need to match the number of hashes -used to open the header. (The number of opening hashes -determines the header level.) : - - # This is an H1 # - - ## This is an H2 ## - - ### This is an H3 ###### - - -

    Blockquotes

    - -Markdown uses email-style `>` characters for blockquoting. If you're -familiar with quoting passages of text in an email message, then you -know how to create a blockquote in Markdown. It looks best if you hard -wrap the text and put a `>` before every line: - - > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, - > consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. - > Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. - > - > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse - > id sem consectetuer libero luctus adipiscing. - -Markdown allows you to be lazy and only put the `>` before the first -line of a hard-wrapped paragraph: - - > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, - consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. - Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. - - > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse - id sem consectetuer libero luctus adipiscing. - -Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by -adding additional levels of `>`: - - > This is the first level of quoting. - > - > > This is nested blockquote. - > - > Back to the first level. - -Blockquotes can contain other Markdown elements, including headers, lists, -and code blocks: - - > ## This is a header. - > - > 1. This is the first list item. - > 2. This is the second list item. - > - > Here's some example code: - > - > return shell_exec("echo $input | $markdown_script"); - -Any decent text editor should make email-style quoting easy. For -example, with BBEdit, you can make a selection and choose Increase -Quote Level from the Text menu. - - -

    Lists

    - -Markdown supports ordered (numbered) and unordered (bulleted) lists. - -Unordered lists use asterisks, pluses, and hyphens -- interchangably --- as list markers: - - * Red - * Green - * Blue - -is equivalent to: - - + Red - + Green - + Blue - -and: - - - Red - - Green - - Blue - -Ordered lists use numbers followed by periods: - - 1. Bird - 2. McHale - 3. Parish - -It's important to note that the actual numbers you use to mark the -list have no effect on the HTML output Markdown produces. The HTML -Markdown produces from the above list is: - -
      -
    1. Bird
    2. -
    3. McHale
    4. -
    5. Parish
    6. -
    - -If you instead wrote the list in Markdown like this: - - 1. Bird - 1. McHale - 1. Parish - -or even: - - 3. Bird - 1. McHale - 8. Parish - -you'd get the exact same HTML output. The point is, if you want to, -you can use ordinal numbers in your ordered Markdown lists, so that -the numbers in your source match the numbers in your published HTML. -But if you want to be lazy, you don't have to. - -If you do use lazy list numbering, however, you should still start the -list with the number 1. At some point in the future, Markdown may support -starting ordered lists at an arbitrary number. - -List markers typically start at the left margin, but may be indented by -up to three spaces. List markers must be followed by one or more spaces -or a tab. - -To make lists look nice, you can wrap items with hanging indents: - - * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. - Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, - viverra nec, fringilla in, laoreet vitae, risus. - * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. - Suspendisse id sem consectetuer libero luctus adipiscing. - -But if you want to be lazy, you don't have to: - - * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. - Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, - viverra nec, fringilla in, laoreet vitae, risus. - * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. - Suspendisse id sem consectetuer libero luctus adipiscing. - -If list items are separated by blank lines, Markdown will wrap the -items in `

    ` tags in the HTML output. For example, this input: - - * Bird - * Magic - -will turn into: - -

      -
    • Bird
    • -
    • Magic
    • -
    - -But this: - - * Bird - - * Magic - -will turn into: - -
      -
    • Bird

    • -
    • Magic

    • -
    - -List items may consist of multiple paragraphs. Each subsequent -paragraph in a list item must be intended by either 4 spaces -or one tab: - - 1. This is a list item with two paragraphs. Lorem ipsum dolor - sit amet, consectetuer adipiscing elit. Aliquam hendrerit - mi posuere lectus. - - Vestibulum enim wisi, viverra nec, fringilla in, laoreet - vitae, risus. Donec sit amet nisl. Aliquam semper ipsum - sit amet velit. - - 2. Suspendisse id sem consectetuer libero luctus adipiscing. - -It looks nice if you indent every line of the subsequent -paragraphs, but here again, Markdown will allow you to be -lazy: - - * This is a list item with two paragraphs. - - This is the second paragraph in the list item. You're - only required to indent the first line. Lorem ipsum dolor - sit amet, consectetuer adipiscing elit. - - * Another item in the same list. - -To put a blockquote within a list item, the blockquote's `>` -delimiters need to be indented: - - * A list item with a blockquote: - - > This is a blockquote - > inside a list item. - -To put a code block within a list item, the code block needs -to be indented *twice* -- 8 spaces or two tabs: - - * A list item with a code block: - - - - -It's worth noting that it's possible to trigger an ordered list by -accident, by writing something like this: - - 1986. What a great season. - -In other words, a *number-period-space* sequence at the beginning of a -line. To avoid this, you can backslash-escape the period: - - 1986\. What a great season. - - - -

    Code Blocks

    - -Pre-formatted code blocks are used for writing about programming or -markup source code. Rather than forming normal paragraphs, the lines -of a code block are interpreted literally. Markdown wraps a code block -in both `
    ` and `` tags.
    -
    -To produce a code block in Markdown, simply indent every line of the
    -block by at least 4 spaces or 1 tab. For example, given this input:
    -
    -    This is a normal paragraph:
    -
    -        This is a code block.
    -
    -Markdown will generate:
    -
    -    

    This is a normal paragraph:

    - -
    This is a code block.
    -    
    - -One level of indentation -- 4 spaces or 1 tab -- is removed from each -line of the code block. For example, this: - - Here is an example of AppleScript: - - tell application "Foo" - beep - end tell - -will turn into: - -

    Here is an example of AppleScript:

    - -
    tell application "Foo"
    -        beep
    -    end tell
    -    
    - -A code block continues until it reaches a line that is not indented -(or the end of the article). - -Within a code block, ampersands (`&`) and angle brackets (`<` and `>`) -are automatically converted into HTML entities. This makes it very -easy to include example HTML source code using Markdown -- just paste -it and indent it, and Markdown will handle the hassle of encoding the -ampersands and angle brackets. For example, this: - - - -will turn into: - -
    <div class="footer">
    -        &copy; 2004 Foo Corporation
    -    </div>
    -    
    - -Regular Markdown syntax is not processed within code blocks. E.g., -asterisks are just literal asterisks within a code block. This means -it's also easy to use Markdown to write about Markdown's own syntax. - - - -

    Horizontal Rules

    - -You can produce a horizontal rule tag (`
    `) by placing three or -more hyphens, asterisks, or underscores on a line by themselves. If you -wish, you may use spaces between the hyphens or asterisks. Each of the -following lines will produce a horizontal rule: - - * * * - - *** - - ***** - - - - - - - --------------------------------------- - - _ _ _ - - -* * * - -

    Span Elements

    - - - -Markdown supports two style of links: *inline* and *reference*. - -In both styles, the link text is delimited by [square brackets]. - -To create an inline link, use a set of regular parentheses immediately -after the link text's closing square bracket. Inside the parentheses, -put the URL where you want the link to point, along with an *optional* -title for the link, surrounded in quotes. For example: - - This is [an example](http://example.com/ "Title") inline link. - - [This link](http://example.net/) has no title attribute. - -Will produce: - -

    This is - an example inline link.

    - -

    This link has no - title attribute.

    - -If you're referring to a local resource on the same server, you can -use relative paths: - - See my [About](/about/) page for details. - -Reference-style links use a second set of square brackets, inside -which you place a label of your choosing to identify the link: - - This is [an example][id] reference-style link. - -You can optionally use a space to separate the sets of brackets: - - This is [an example] [id] reference-style link. - -Then, anywhere in the document, you define your link label like this, -on a line by itself: - - [id]: http://example.com/ "Optional Title Here" - -That is: - -* Square brackets containing the link identifier (optionally - indented from the left margin using up to three spaces); -* followed by a colon; -* followed by one or more spaces (or tabs); -* followed by the URL for the link; -* optionally followed by a title attribute for the link, enclosed - in double or single quotes. - -The link URL may, optionally, be surrounded by angle brackets: - - [id]: "Optional Title Here" - -You can put the title attribute on the next line and use extra spaces -or tabs for padding, which tends to look better with longer URLs: - - [id]: http://example.com/longish/path/to/resource/here - "Optional Title Here" - -Link definitions are only used for creating links during Markdown -processing, and are stripped from your document in the HTML output. - -Link definition names may constist of letters, numbers, spaces, and punctuation -- but they are *not* case sensitive. E.g. these two links: - - [link text][a] - [link text][A] - -are equivalent. - -The *implicit link name* shortcut allows you to omit the name of the -link, in which case the link text itself is used as the name. -Just use an empty set of square brackets -- e.g., to link the word -"Google" to the google.com web site, you could simply write: - - [Google][] - -And then define the link: - - [Google]: http://google.com/ - -Because link names may contain spaces, this shortcut even works for -multiple words in the link text: - - Visit [Daring Fireball][] for more information. - -And then define the link: - - [Daring Fireball]: http://daringfireball.net/ - -Link definitions can be placed anywhere in your Markdown document. I -tend to put them immediately after each paragraph in which they're -used, but if you want, you can put them all at the end of your -document, sort of like footnotes. - -Here's an example of reference links in action: - - I get 10 times more traffic from [Google] [1] than from - [Yahoo] [2] or [MSN] [3]. - - [1]: http://google.com/ "Google" - [2]: http://search.yahoo.com/ "Yahoo Search" - [3]: http://search.msn.com/ "MSN Search" - -Using the implicit link name shortcut, you could instead write: - - I get 10 times more traffic from [Google][] than from - [Yahoo][] or [MSN][]. - - [google]: http://google.com/ "Google" - [yahoo]: http://search.yahoo.com/ "Yahoo Search" - [msn]: http://search.msn.com/ "MSN Search" - -Both of the above examples will produce the following HTML output: - -

    I get 10 times more traffic from Google than from - Yahoo - or MSN.

    - -For comparison, here is the same paragraph written using -Markdown's inline link style: - - I get 10 times more traffic from [Google](http://google.com/ "Google") - than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or - [MSN](http://search.msn.com/ "MSN Search"). - -The point of reference-style links is not that they're easier to -write. The point is that with reference-style links, your document -source is vastly more readable. Compare the above examples: using -reference-style links, the paragraph itself is only 81 characters -long; with inline-style links, it's 176 characters; and as raw HTML, -it's 234 characters. In the raw HTML, there's more markup than there -is text. - -With Markdown's reference-style links, a source document much more -closely resembles the final output, as rendered in a browser. By -allowing you to move the markup-related metadata out of the paragraph, -you can add links without interrupting the narrative flow of your -prose. - - -

    Emphasis

    - -Markdown treats asterisks (`*`) and underscores (`_`) as indicators of -emphasis. Text wrapped with one `*` or `_` will be wrapped with an -HTML `` tag; double `*`'s or `_`'s will be wrapped with an HTML -`` tag. E.g., this input: - - *single asterisks* - - _single underscores_ - - **double asterisks** - - __double underscores__ - -will produce: - - single asterisks - - single underscores - - double asterisks - - double underscores - -You can use whichever style you prefer; the lone restriction is that -the same character must be used to open and close an emphasis span. - -Emphasis can be used in the middle of a word: - - un*fucking*believable - -But if you surround an `*` or `_` with spaces, it'll be treated as a -literal asterisk or underscore. - -To produce a literal asterisk or underscore at a position where it -would otherwise be used as an emphasis delimiter, you can backslash -escape it: - - \*this text is surrounded by literal asterisks\* - - - -

    Code

    - -To indicate a span of code, wrap it with backtick quotes (`` ` ``). -Unlike a pre-formatted code block, a code span indicates code within a -normal paragraph. For example: - - Use the `printf()` function. - -will produce: - -

    Use the printf() function.

    - -To include a literal backtick character within a code span, you can use -multiple backticks as the opening and closing delimiters: - - ``There is a literal backtick (`) here.`` - -which will produce this: - -

    There is a literal backtick (`) here.

    - -The backtick delimiters surrounding a code span may include spaces -- -one after the opening, one before the closing. This allows you to place -literal backtick characters at the beginning or end of a code span: - - A single backtick in a code span: `` ` `` - - A backtick-delimited string in a code span: `` `foo` `` - -will produce: - -

    A single backtick in a code span: `

    - -

    A backtick-delimited string in a code span: `foo`

    - -With a code span, ampersands and angle brackets are encoded as HTML -entities automatically, which makes it easy to include example HTML -tags. Markdown will turn this: - - Please don't use any `` tags. - -into: - -

    Please don't use any <blink> tags.

    - -You can write this: - - `—` is the decimal-encoded equivalent of `—`. - -to produce: - -

    &#8212; is the decimal-encoded - equivalent of &mdash;.

    - - - -

    Images

    - -Admittedly, it's fairly difficult to devise a "natural" syntax for -placing images into a plain text document format. - -Markdown uses an image syntax that is intended to resemble the syntax -for links, allowing for two styles: *inline* and *reference*. - -Inline image syntax looks like this: - - ![Alt text](/path/to/img.jpg) - - ![Alt text](/path/to/img.jpg "Optional title") - -That is: - -* An exclamation mark: `!`; -* followed by a set of square brackets, containing the `alt` - attribute text for the image; -* followed by a set of parentheses, containing the URL or path to - the image, and an optional `title` attribute enclosed in double - or single quotes. - -Reference-style image syntax looks like this: - - ![Alt text][id] - -Where "id" is the name of a defined image reference. Image references -are defined using syntax identical to link references: - - [id]: url/to/image "Optional title attribute" - -As of this writing, Markdown has no syntax for specifying the -dimensions of an image; if this is important to you, you can simply -use regular HTML `` tags. - - -* * * - - -

    Miscellaneous

    - - - -Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this: - - - -Markdown will turn this into: - - http://example.com/ - -Automatic links for email addresses work similarly, except that -Markdown will also perform a bit of randomized decimal and hex -entity-encoding to help obscure your address from address-harvesting -spambots. For example, Markdown will turn this: - - - -into something like this: - - address@exa - mple.com - -which will render in a browser as a clickable link to "address@example.com". - -(This sort of entity-encoding trick will indeed fool many, if not -most, address-harvesting bots, but it definitely won't fool all of -them. It's better than nothing, but an address published in this way -will probably eventually start receiving spam.) - - - -

    Backslash Escapes

    - -Markdown allows you to use backslash escapes to generate literal -characters which would otherwise have special meaning in Markdown's -formatting syntax. For example, if you wanted to surround a word with -literal asterisks (instead of an HTML `` tag), you can backslashes -before the asterisks, like this: - - \*literal asterisks\* - -Markdown provides backslash escapes for the following characters: - - \ backslash - ` backtick - * asterisk - _ underscore - {} curly braces - [] square brackets - () parentheses - # hash mark - + plus sign - - minus sign (hyphen) - . dot - ! exclamation mark - diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Nested blockquotes.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Nested blockquotes.html deleted file mode 100644 index 538bb4fe7..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Nested blockquotes.html +++ /dev/null @@ -1,9 +0,0 @@ -
    -

    foo

    - -
    -

    bar

    -
    - -

    foo

    -
    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Nested blockquotes.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Nested blockquotes.text deleted file mode 100644 index ed3c624ff..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Nested blockquotes.text +++ /dev/null @@ -1,5 +0,0 @@ -> foo -> -> > bar -> -> foo diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Ordered and unordered lists.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Ordered and unordered lists.html deleted file mode 100644 index d6fa42780..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Ordered and unordered lists.html +++ /dev/null @@ -1,166 +0,0 @@ -

    Unordered

    - -

    Asterisks tight:

    - -
      -
    • asterisk 1
    • -
    • asterisk 2
    • -
    • asterisk 3
    • -
    - -

    Asterisks loose:

    - -
      -
    • asterisk 1

    • - -
    • asterisk 2

    • - -
    • asterisk 3

    • -
    - -
    - -

    Pluses tight:

    - -
      -
    • Plus 1
    • -
    • Plus 2
    • -
    • Plus 3
    • -
    - -

    Pluses loose:

    - -
      -
    • Plus 1

    • - -
    • Plus 2

    • - -
    • Plus 3

    • -
    - -
    - -

    Minuses tight:

    - -
      -
    • Minus 1
    • -
    • Minus 2
    • -
    • Minus 3
    • -
    - -

    Minuses loose:

    - -
      -
    • Minus 1

    • - -
    • Minus 2

    • - -
    • Minus 3

    • -
    - -

    Ordered

    - -

    Tight:

    - -
      -
    1. First
    2. -
    3. Second
    4. -
    5. Third
    6. -
    - -

    and:

    - -
      -
    1. One
    2. -
    3. Two
    4. -
    5. Three
    6. -
    - -

    Loose using tabs:

    - -
      -
    1. First

    2. - -
    3. Second

    4. - -
    5. Third

    6. -
    - -

    and using spaces:

    - -
      -
    1. One

    2. - -
    3. Two

    4. - -
    5. Three

    6. -
    - -

    Multiple paragraphs:

    - -
      -
    1. Item 1, graf one.

      - -

      Item 2. graf two. The quick brown fox jumped over the lazy dog's -back.

    2. - -
    3. Item 2.

    4. - -
    5. Item 3.

    6. -
    - -

    Nested

    - -
      -
    • Tab - -
        -
      • Tab - -
          -
        • Tab
        • -
      • -
    • -
    - -

    Here's another:

    - -
      -
    1. First
    2. -
    3. Second: - -
        -
      • Fee
      • -
      • Fie
      • -
      • Foe
      • -
    4. -
    5. Third
    6. -
    - -

    Same thing but with paragraphs:

    - -
      -
    1. First

    2. - -
    3. Second:

      - -
        -
      • Fee
      • -
      • Fie
      • -
      • Foe
      • -
    4. - -
    5. Third

    6. -
    - -

    This was an error in Markdown 1.0.1:

    - -
      -
    • this

      - -
        -
      • sub
      • -
      - -

      that

    • -
    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Ordered and unordered lists.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Ordered and unordered lists.text deleted file mode 100644 index 7f3b49777..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Ordered and unordered lists.text +++ /dev/null @@ -1,131 +0,0 @@ -## Unordered - -Asterisks tight: - -* asterisk 1 -* asterisk 2 -* asterisk 3 - - -Asterisks loose: - -* asterisk 1 - -* asterisk 2 - -* asterisk 3 - -* * * - -Pluses tight: - -+ Plus 1 -+ Plus 2 -+ Plus 3 - - -Pluses loose: - -+ Plus 1 - -+ Plus 2 - -+ Plus 3 - -* * * - - -Minuses tight: - -- Minus 1 -- Minus 2 -- Minus 3 - - -Minuses loose: - -- Minus 1 - -- Minus 2 - -- Minus 3 - - -## Ordered - -Tight: - -1. First -2. Second -3. Third - -and: - -1. One -2. Two -3. Three - - -Loose using tabs: - -1. First - -2. Second - -3. Third - -and using spaces: - -1. One - -2. Two - -3. Three - -Multiple paragraphs: - -1. Item 1, graf one. - - Item 2. graf two. The quick brown fox jumped over the lazy dog's - back. - -2. Item 2. - -3. Item 3. - - - -## Nested - -* Tab - * Tab - * Tab - -Here's another: - -1. First -2. Second: - * Fee - * Fie - * Foe -3. Third - -Same thing but with paragraphs: - -1. First - -2. Second: - * Fee - * Fie - * Foe - -3. Third - - -This was an error in Markdown 1.0.1: - -* this - - * sub - - that diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Strong and em together.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Strong and em together.html deleted file mode 100644 index 71ec78c70..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Strong and em together.html +++ /dev/null @@ -1,7 +0,0 @@ -

    This is strong and em.

    - -

    So is this word.

    - -

    This is strong and em.

    - -

    So is this word.

    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Strong and em together.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Strong and em together.text deleted file mode 100644 index 95ee690db..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Strong and em together.text +++ /dev/null @@ -1,7 +0,0 @@ -***This is strong and em.*** - -So is ***this*** word. - -___This is strong and em.___ - -So is ___this___ word. diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tabs.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tabs.html deleted file mode 100644 index 64006d96a..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tabs.html +++ /dev/null @@ -1,26 +0,0 @@ -
      -
    • this is a list item -indented with tabs

    • - -
    • this is a list item -indented with spaces

    • -
    - -

    Code:

    - -
    this code block is indented by one tab
    -
    - -

    And:

    - -
        this code block is indented by two tabs
    -
    - -

    And:

    - -
    +   this is an example list item
    -    indented with tabs
    -
    -+   this is an example list item
    -    indented with spaces
    -
    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tabs.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tabs.text deleted file mode 100644 index 589d1136e..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tabs.text +++ /dev/null @@ -1,21 +0,0 @@ -+ this is a list item - indented with tabs - -+ this is a list item - indented with spaces - -Code: - - this code block is indented by one tab - -And: - - this code block is indented by two tabs - -And: - - + this is an example list item - indented with tabs - - + this is an example list item - indented with spaces diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tidyness.html b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tidyness.html deleted file mode 100644 index 9c45b69c2..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tidyness.html +++ /dev/null @@ -1,9 +0,0 @@ -
    -

    A list within a blockquote:

    - -
      -
    • asterisk 1
    • -
    • asterisk 2
    • -
    • asterisk 3
    • -
    -
    diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tidyness.text b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tidyness.text deleted file mode 100644 index 5f18b8da2..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref/Tidyness.text +++ /dev/null @@ -1,5 +0,0 @@ -> A list within a blockquote: -> -> * asterisk 1 -> * asterisk 2 -> * asterisk 3 diff --git a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref_test.go b/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref_test.go deleted file mode 100644 index 42a0bdd52..000000000 --- a/Godeps/_workspace/src/github.com/russross/blackfriday/upskirtref_test.go +++ /dev/null @@ -1,128 +0,0 @@ -// -// Blackfriday Markdown Processor -// Available at http://github.com/russross/blackfriday -// -// Copyright © 2011 Russ Ross . -// Distributed under the Simplified BSD License. -// See README.md for details. -// - -// -// Markdown 1.0.3 reference tests -// - -package blackfriday - -import ( - "io/ioutil" - "path/filepath" - "testing" -) - -func runMarkdownReference(input string, flag int) string { - renderer := HtmlRenderer(0, "", "") - return string(Markdown([]byte(input), renderer, flag)) -} - -func doTestsReference(t *testing.T, files []string, flag int) { - // catch and report panics - var candidate string - defer func() { - if err := recover(); err != nil { - t.Errorf("\npanic while processing [%#v]\n", candidate) - } - }() - - for _, basename := range files { - filename := filepath.Join("upskirtref", basename+".text") - inputBytes, err := ioutil.ReadFile(filename) - if err != nil { - t.Errorf("Couldn't open '%s', error: %v\n", filename, err) - continue - } - input := string(inputBytes) - - filename = filepath.Join("upskirtref", basename+".html") - expectedBytes, err := ioutil.ReadFile(filename) - if err != nil { - t.Errorf("Couldn't open '%s', error: %v\n", filename, err) - continue - } - expected := string(expectedBytes) - - // fmt.Fprintf(os.Stderr, "processing %s ...", filename) - actual := string(runMarkdownReference(input, flag)) - if actual != expected { - t.Errorf("\n [%#v]\nExpected[%#v]\nActual [%#v]", - basename+".text", expected, actual) - } - // fmt.Fprintf(os.Stderr, " ok\n") - - // now test every prefix of every input to check for - // bounds checking - if !testing.Short() { - start, max := 0, len(input) - for end := start + 1; end <= max; end++ { - candidate = input[start:end] - // fmt.Fprintf(os.Stderr, " %s %d:%d/%d\n", filename, start, end, max) - _ = runMarkdownReference(candidate, flag) - } - } - } -} - -func TestReference(t *testing.T) { - files := []string{ - "Amps and angle encoding", - "Auto links", - "Backslash escapes", - "Blockquotes with code blocks", - "Code Blocks", - "Code Spans", - "Hard-wrapped paragraphs with list-like lines", - "Horizontal rules", - "Inline HTML (Advanced)", - "Inline HTML (Simple)", - "Inline HTML comments", - "Links, inline style", - "Links, reference style", - "Links, shortcut references", - "Literal quotes in titles", - "Markdown Documentation - Basics", - "Markdown Documentation - Syntax", - "Nested blockquotes", - "Ordered and unordered lists", - "Strong and em together", - "Tabs", - "Tidyness", - } - doTestsReference(t, files, 0) -} - -func TestReference_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) { - files := []string{ - "Amps and angle encoding", - "Auto links", - "Backslash escapes", - "Blockquotes with code blocks", - "Code Blocks", - "Code Spans", - "Hard-wrapped paragraphs with list-like lines no empty line before block", - "Horizontal rules", - "Inline HTML (Advanced)", - "Inline HTML (Simple)", - "Inline HTML comments", - "Links, inline style", - "Links, reference style", - "Links, shortcut references", - "Literal quotes in titles", - "Markdown Documentation - Basics", - "Markdown Documentation - Syntax", - "Nested blockquotes", - "Ordered and unordered lists", - "Strong and em together", - "Tabs", - "Tidyness", - } - doTestsReference(t, files, EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK) -} diff --git a/Godeps/_workspace/src/github.com/scalingdata/gcfg/example_test.go b/Godeps/_workspace/src/github.com/scalingdata/gcfg/example_test.go deleted file mode 100644 index 7fa554c08..000000000 --- a/Godeps/_workspace/src/github.com/scalingdata/gcfg/example_test.go +++ /dev/null @@ -1,132 +0,0 @@ -package gcfg_test - -import ( - "fmt" - "log" -) - -import "github.com/scalingdata/gcfg" - -func ExampleReadStringInto() { - cfgStr := `; Comment line -[section] -name=value # comment` - cfg := struct { - Section struct { - Name string - } - }{} - err := gcfg.ReadStringInto(&cfg, cfgStr) - if err != nil { - log.Fatalf("Failed to parse gcfg data: %s", err) - } - fmt.Println(cfg.Section.Name) - // Output: value -} - -func ExampleReadStringInto_bool() { - cfgStr := `; Comment line -[section] -switch=on` - cfg := struct { - Section struct { - Switch bool - } - }{} - err := gcfg.ReadStringInto(&cfg, cfgStr) - if err != nil { - log.Fatalf("Failed to parse gcfg data: %s", err) - } - fmt.Println(cfg.Section.Switch) - // Output: true -} - -func ExampleReadStringInto_hyphens() { - cfgStr := `; Comment line -[section-name] -variable-name=value # comment` - cfg := struct { - Section_Name struct { - Variable_Name string - } - }{} - err := gcfg.ReadStringInto(&cfg, cfgStr) - if err != nil { - log.Fatalf("Failed to parse gcfg data: %s", err) - } - fmt.Println(cfg.Section_Name.Variable_Name) - // Output: value -} - -func ExampleReadStringInto_tags() { - cfgStr := `; Comment line -[section] -var-name=value # comment` - cfg := struct { - Section struct { - FieldName string `gcfg:"var-name"` - } - }{} - err := gcfg.ReadStringInto(&cfg, cfgStr) - if err != nil { - log.Fatalf("Failed to parse gcfg data: %s", err) - } - fmt.Println(cfg.Section.FieldName) - // Output: value -} - -func ExampleReadStringInto_subsections() { - cfgStr := `; Comment line -[profile "A"] -color = white - -[profile "B"] -color = black -` - cfg := struct { - Profile map[string]*struct { - Color string - } - }{} - err := gcfg.ReadStringInto(&cfg, cfgStr) - if err != nil { - log.Fatalf("Failed to parse gcfg data: %s", err) - } - fmt.Printf("%s %s\n", cfg.Profile["A"].Color, cfg.Profile["B"].Color) - // Output: white black -} - -func ExampleReadStringInto_multivalue() { - cfgStr := `; Comment line -[section] -multi=value1 -multi=value2` - cfg := struct { - Section struct { - Multi []string - } - }{} - err := gcfg.ReadStringInto(&cfg, cfgStr) - if err != nil { - log.Fatalf("Failed to parse gcfg data: %s", err) - } - fmt.Println(cfg.Section.Multi) - // Output: [value1 value2] -} - -func ExampleReadStringInto_unicode() { - cfgStr := `; Comment line -[甲] -乙=丙 # comment` - cfg := struct { - X甲 struct { - X乙 string - } - }{} - err := gcfg.ReadStringInto(&cfg, cfgStr) - if err != nil { - log.Fatalf("Failed to parse gcfg data: %s", err) - } - fmt.Println(cfg.X甲.X乙) - // Output: 丙 -} diff --git a/Godeps/_workspace/src/github.com/scalingdata/gcfg/issues_test.go b/Godeps/_workspace/src/github.com/scalingdata/gcfg/issues_test.go deleted file mode 100644 index 796dd10b6..000000000 --- a/Godeps/_workspace/src/github.com/scalingdata/gcfg/issues_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package gcfg - -import ( - "fmt" - "math/big" - "strings" - "testing" -) - -type Config1 struct { - Section struct { - Int int - BigInt big.Int - } -} - -var testsIssue1 = []struct { - cfg string - typename string -}{ - {"[section]\nint=X", "int"}, - {"[section]\nint=", "int"}, - {"[section]\nint=1A", "int"}, - {"[section]\nbigint=X", "big.Int"}, - {"[section]\nbigint=", "big.Int"}, - {"[section]\nbigint=1A", "big.Int"}, -} - -// Value parse error should: -// - include plain type name -// - not include reflect internals -func TestIssue1(t *testing.T) { - for i, tt := range testsIssue1 { - var c Config1 - err := ReadStringInto(&c, tt.cfg) - switch { - case err == nil: - t.Errorf("%d fail: got ok; wanted error", i) - case !strings.Contains(err.Error(), tt.typename): - t.Errorf("%d fail: error message doesn't contain type name %q: %v", - i, tt.typename, err) - case strings.Contains(err.Error(), "reflect"): - t.Errorf("%d fail: error message includes reflect internals: %v", - i, err) - default: - t.Logf("%d pass: %v", i, err) - } - } -} - -type confIssue2 struct{ Main struct{ Foo string } } - -var testsIssue2 = []readtest{ - {"[main]\n;\nfoo = bar\n", &confIssue2{struct{ Foo string }{"bar"}}, true}, - {"[main]\r\n;\r\nfoo = bar\r\n", &confIssue2{struct{ Foo string }{"bar"}}, true}, -} - -func TestIssue2(t *testing.T) { - for i, tt := range testsIssue2 { - id := fmt.Sprintf("issue2:%d", i) - testRead(t, id, tt) - } -} diff --git a/Godeps/_workspace/src/github.com/scalingdata/gcfg/read_test.go b/Godeps/_workspace/src/github.com/scalingdata/gcfg/read_test.go deleted file mode 100644 index 4a7d8e191..000000000 --- a/Godeps/_workspace/src/github.com/scalingdata/gcfg/read_test.go +++ /dev/null @@ -1,333 +0,0 @@ -package gcfg - -import ( - "fmt" - "math/big" - "os" - "reflect" - "testing" -) - -const ( - // 64 spaces - sp64 = " " - // 512 spaces - sp512 = sp64 + sp64 + sp64 + sp64 + sp64 + sp64 + sp64 + sp64 - // 4096 spaces - sp4096 = sp512 + sp512 + sp512 + sp512 + sp512 + sp512 + sp512 + sp512 -) - -type cBasic struct { - Section cBasicS1 - Hyphen_In_Section cBasicS2 - unexported cBasicS1 - Exported cBasicS3 - TagName cBasicS1 `gcfg:"tag-name"` -} -type cBasicS1 struct { - Name string - Int int - PName *string -} -type cBasicS2 struct { - Hyphen_In_Name string -} -type cBasicS3 struct { - unexported string -} - -type nonMulti []string - -type unmarshalable string - -func (u *unmarshalable) UnmarshalText(text []byte) error { - s := string(text) - if s == "error" { - return fmt.Errorf("%s", s) - } - *u = unmarshalable(s) - return nil -} - -var _ textUnmarshaler = new(unmarshalable) - -type cUni struct { - X甲 cUniS1 - XSection cUniS2 -} -type cUniS1 struct { - X乙 string -} -type cUniS2 struct { - XName string -} - -type cMulti struct { - M1 cMultiS1 - M2 cMultiS2 - M3 cMultiS3 -} -type cMultiS1 struct{ Multi []string } -type cMultiS2 struct{ NonMulti nonMulti } -type cMultiS3 struct{ MultiInt []int } - -type cSubs struct{ Sub map[string]*cSubsS1 } -type cSubsS1 struct{ Name string } - -type cBool struct{ Section cBoolS1 } -type cBoolS1 struct{ Bool bool } - -type cTxUnm struct{ Section cTxUnmS1 } -type cTxUnmS1 struct{ Name unmarshalable } - -type cNum struct { - N1 cNumS1 - N2 cNumS2 - N3 cNumS3 -} -type cNumS1 struct { - Int int - IntDHO int `gcfg:",int=dho"` - Big *big.Int -} -type cNumS2 struct { - MultiInt []int - MultiBig []*big.Int -} -type cNumS3 struct{ FileMode os.FileMode } -type readtest struct { - gcfg string - exp interface{} - ok bool -} - -func newString(s string) *string { - return &s -} - -var readtests = []struct { - group string - tests []readtest -}{{"scanning", []readtest{ - {"[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - // hyphen in name - {"[hyphen-in-section]\nhyphen-in-name=value", &cBasic{Hyphen_In_Section: cBasicS2{Hyphen_In_Name: "value"}}, true}, - // quoted string value - {"[section]\nname=\"\"", &cBasic{Section: cBasicS1{Name: ""}}, true}, - {"[section]\nname=\" \"", &cBasic{Section: cBasicS1{Name: " "}}, true}, - {"[section]\nname=\"value\"", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[section]\nname=\" value \"", &cBasic{Section: cBasicS1{Name: " value "}}, true}, - {"\n[section]\nname=\"va ; lue\"", &cBasic{Section: cBasicS1{Name: "va ; lue"}}, true}, - {"[section]\nname=\"val\" \"ue\"", &cBasic{Section: cBasicS1{Name: "val ue"}}, true}, - {"[section]\nname=\"value", &cBasic{}, false}, - // escape sequences - {"[section]\nname=\"va\\\\lue\"", &cBasic{Section: cBasicS1{Name: "va\\lue"}}, true}, - {"[section]\nname=\"va\\\"lue\"", &cBasic{Section: cBasicS1{Name: "va\"lue"}}, true}, - {"[section]\nname=\"va\\nlue\"", &cBasic{Section: cBasicS1{Name: "va\nlue"}}, true}, - {"[section]\nname=\"va\\tlue\"", &cBasic{Section: cBasicS1{Name: "va\tlue"}}, true}, - {"\n[section]\nname=\\", &cBasic{}, false}, - {"\n[section]\nname=\\a", &cBasic{}, false}, - {"\n[section]\nname=\"val\\a\"", &cBasic{}, false}, - {"\n[section]\nname=val\\", &cBasic{}, false}, - {"\n[sub \"A\\\n\"]\nname=value", &cSubs{}, false}, - {"\n[sub \"A\\\t\"]\nname=value", &cSubs{}, false}, - // broken line - {"[section]\nname=value \\\n value", &cBasic{Section: cBasicS1{Name: "value value"}}, true}, - {"[section]\nname=\"value \\\n value\"", &cBasic{}, false}, -}}, {"scanning:whitespace", []readtest{ - {" \n[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {" [section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"\t[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[ section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[section ]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[section]\n name=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[section]\nname =value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[section]\nname= value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[section]\nname=value ", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[section]\r\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[section]\r\nname=value\r\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {";cmnt\r\n[section]\r\nname=value\r\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - // long lines - {sp4096 + "[section]\nname=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[" + sp4096 + "section]\nname=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[section" + sp4096 + "]\nname=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[section]" + sp4096 + "\nname=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[section]\n" + sp4096 + "name=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[section]\nname" + sp4096 + "=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[section]\nname=" + sp4096 + "value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[section]\nname=value\n" + sp4096, &cBasic{Section: cBasicS1{Name: "value"}}, true}, -}}, {"scanning:comments", []readtest{ - {"; cmnt\n[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"# cmnt\n[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {" ; cmnt\n[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"\t; cmnt\n[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"\n[section]; cmnt\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"\n[section] ; cmnt\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"\n[section]\nname=value; cmnt", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"\n[section]\nname=value ; cmnt", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"\n[section]\nname=\"value\" ; cmnt", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"\n[section]\nname=value ; \"cmnt", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"\n[section]\nname=\"va ; lue\" ; cmnt", &cBasic{Section: cBasicS1{Name: "va ; lue"}}, true}, - {"\n[section]\nname=; cmnt", &cBasic{Section: cBasicS1{Name: ""}}, true}, -}}, {"scanning:subsections", []readtest{ - {"\n[sub \"A\"]\nname=value", &cSubs{map[string]*cSubsS1{"A": &cSubsS1{"value"}}}, true}, - {"\n[sub \"b\"]\nname=value", &cSubs{map[string]*cSubsS1{"b": &cSubsS1{"value"}}}, true}, - {"\n[sub \"A\\\\\"]\nname=value", &cSubs{map[string]*cSubsS1{"A\\": &cSubsS1{"value"}}}, true}, - {"\n[sub \"A\\\"\"]\nname=value", &cSubs{map[string]*cSubsS1{"A\"": &cSubsS1{"value"}}}, true}, -}}, {"syntax", []readtest{ - // invalid line - {"\n[section]\n=", &cBasic{}, false}, - // no section - {"name=value", &cBasic{}, false}, - // empty section - {"\n[]\nname=value", &cBasic{}, false}, - // empty subsection - {"\n[sub \"\"]\nname=value", &cSubs{}, false}, -}}, {"setting", []readtest{ - {"[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - // pointer - {"[section]", &cBasic{Section: cBasicS1{PName: nil}}, true}, - {"[section]\npname=value", &cBasic{Section: cBasicS1{PName: newString("value")}}, true}, - // section name not matched - {"\n[nonexistent]\nname=value", &cBasic{}, false}, - // subsection name not matched - {"\n[section \"nonexistent\"]\nname=value", &cBasic{}, false}, - // variable name not matched - {"\n[section]\nnonexistent=value", &cBasic{}, false}, - // hyphen in name - {"[hyphen-in-section]\nhyphen-in-name=value", &cBasic{Hyphen_In_Section: cBasicS2{Hyphen_In_Name: "value"}}, true}, - // ignore unexported fields - {"[unexported]\nname=value", &cBasic{}, false}, - {"[exported]\nunexported=value", &cBasic{}, false}, - // 'X' prefix for non-upper/lower-case letters - {"[甲]\n乙=丙", &cUni{X甲: cUniS1{X乙: "丙"}}, true}, - //{"[section]\nxname=value", &cBasic{XSection: cBasicS4{XName: "value"}}, false}, - //{"[xsection]\nname=value", &cBasic{XSection: cBasicS4{XName: "value"}}, false}, - // name specified as struct tag - {"[tag-name]\nname=value", &cBasic{TagName: cBasicS1{Name: "value"}}, true}, -}}, {"multivalue", []readtest{ - // unnamed slice type: treat as multi-value - {"\n[m1]", &cMulti{M1: cMultiS1{}}, true}, - {"\n[m1]\nmulti=value", &cMulti{M1: cMultiS1{[]string{"value"}}}, true}, - {"\n[m1]\nmulti=value1\nmulti=value2", &cMulti{M1: cMultiS1{[]string{"value1", "value2"}}}, true}, - // "blank" empties multi-valued slice -- here same result as above - {"\n[m1]\nmulti\nmulti=value1\nmulti=value2", &cMulti{M1: cMultiS1{[]string{"value1", "value2"}}}, true}, - // named slice type: do not treat as multi-value - {"\n[m2]", &cMulti{}, true}, - {"\n[m2]\nmulti=value", &cMulti{}, false}, - {"\n[m2]\nmulti=value1\nmulti=value2", &cMulti{}, false}, -}}, {"type:string", []readtest{ - {"[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, - {"[section]\nname=", &cBasic{Section: cBasicS1{Name: ""}}, true}, -}}, {"type:bool", []readtest{ - // explicit values - {"[section]\nbool=true", &cBool{cBoolS1{true}}, true}, - {"[section]\nbool=yes", &cBool{cBoolS1{true}}, true}, - {"[section]\nbool=on", &cBool{cBoolS1{true}}, true}, - {"[section]\nbool=1", &cBool{cBoolS1{true}}, true}, - {"[section]\nbool=tRuE", &cBool{cBoolS1{true}}, true}, - {"[section]\nbool=false", &cBool{cBoolS1{false}}, true}, - {"[section]\nbool=no", &cBool{cBoolS1{false}}, true}, - {"[section]\nbool=off", &cBool{cBoolS1{false}}, true}, - {"[section]\nbool=0", &cBool{cBoolS1{false}}, true}, - {"[section]\nbool=NO", &cBool{cBoolS1{false}}, true}, - // "blank" value handled as true - {"[section]\nbool", &cBool{cBoolS1{true}}, true}, - // bool parse errors - {"[section]\nbool=maybe", &cBool{}, false}, - {"[section]\nbool=t", &cBool{}, false}, - {"[section]\nbool=truer", &cBool{}, false}, - {"[section]\nbool=2", &cBool{}, false}, - {"[section]\nbool=-1", &cBool{}, false}, -}}, {"type:numeric", []readtest{ - {"[section]\nint=0", &cBasic{Section: cBasicS1{Int: 0}}, true}, - {"[section]\nint=1", &cBasic{Section: cBasicS1{Int: 1}}, true}, - {"[section]\nint=-1", &cBasic{Section: cBasicS1{Int: -1}}, true}, - {"[section]\nint=0.2", &cBasic{}, false}, - {"[section]\nint=1e3", &cBasic{}, false}, - // primitive [u]int(|8|16|32|64) and big.Int is parsed as dec or hex (not octal) - {"[n1]\nint=010", &cNum{N1: cNumS1{Int: 10}}, true}, - {"[n1]\nint=0x10", &cNum{N1: cNumS1{Int: 0x10}}, true}, - {"[n1]\nbig=1", &cNum{N1: cNumS1{Big: big.NewInt(1)}}, true}, - {"[n1]\nbig=0x10", &cNum{N1: cNumS1{Big: big.NewInt(0x10)}}, true}, - {"[n1]\nbig=010", &cNum{N1: cNumS1{Big: big.NewInt(10)}}, true}, - {"[n2]\nmultiint=010", &cNum{N2: cNumS2{MultiInt: []int{10}}}, true}, - {"[n2]\nmultibig=010", &cNum{N2: cNumS2{MultiBig: []*big.Int{big.NewInt(10)}}}, true}, - // set parse mode for int types via struct tag - {"[n1]\nintdho=010", &cNum{N1: cNumS1{IntDHO: 010}}, true}, - // octal allowed for named type - {"[n3]\nfilemode=0777", &cNum{N3: cNumS3{FileMode: 0777}}, true}, -}}, {"type:textUnmarshaler", []readtest{ - {"[section]\nname=value", &cTxUnm{Section: cTxUnmS1{Name: "value"}}, true}, - {"[section]\nname=error", &cTxUnm{}, false}, -}}, -} - -func TestReadStringInto(t *testing.T) { - for _, tg := range readtests { - for i, tt := range tg.tests { - id := fmt.Sprintf("%s:%d", tg.group, i) - testRead(t, id, tt) - } - } -} - -func TestReadStringIntoMultiBlankPreset(t *testing.T) { - tt := readtest{"\n[m1]\nmulti\nmulti=value1\nmulti=value2", &cMulti{M1: cMultiS1{[]string{"value1", "value2"}}}, true} - cfg := &cMulti{M1: cMultiS1{[]string{"preset1", "preset2"}}} - testReadInto(t, "multi:blank", tt, cfg) -} - -func testRead(t *testing.T, id string, tt readtest) { - // get the type of the expected result - restyp := reflect.TypeOf(tt.exp).Elem() - // create a new instance to hold the actual result - res := reflect.New(restyp).Interface() - testReadInto(t, id, tt, res) -} - -func testReadInto(t *testing.T, id string, tt readtest, res interface{}) { - err := ReadStringInto(res, tt.gcfg) - if tt.ok { - if err != nil { - t.Errorf("%s fail: got error %v, wanted ok", id, err) - return - } else if !reflect.DeepEqual(res, tt.exp) { - t.Errorf("%s fail: got value %#v, wanted value %#v", id, res, tt.exp) - return - } - if !testing.Short() { - t.Logf("%s pass: got value %#v", id, res) - } - } else { // !tt.ok - if err == nil { - t.Errorf("%s fail: got value %#v, wanted error", id, res) - return - } - if !testing.Short() { - t.Logf("%s pass: got error %v", id, err) - } - } -} - -func TestReadFileInto(t *testing.T) { - res := &struct{ Section struct{ Name string } }{} - err := ReadFileInto(res, "testdata/gcfg_test.gcfg") - if err != nil { - t.Errorf(err.Error()) - } - if "value" != res.Section.Name { - t.Errorf("got %q, wanted %q", res.Section.Name, "value") - } -} - -func TestReadFileIntoUnicode(t *testing.T) { - res := &struct{ X甲 struct{ X乙 string } }{} - err := ReadFileInto(res, "testdata/gcfg_unicode_test.gcfg") - if err != nil { - t.Errorf(err.Error()) - } - if "丙" != res.X甲.X乙 { - t.Errorf("got %q, wanted %q", res.X甲.X乙, "丙") - } -} diff --git a/Godeps/_workspace/src/github.com/scalingdata/gcfg/scanner/example_test.go b/Godeps/_workspace/src/github.com/scalingdata/gcfg/scanner/example_test.go deleted file mode 100644 index 1a15f6b78..000000000 --- a/Godeps/_workspace/src/github.com/scalingdata/gcfg/scanner/example_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// 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 scanner_test - -import ( - "fmt" -) - -import ( - "github.com/scalingdata/gcfg/scanner" - "github.com/scalingdata/gcfg/token" -) - -func ExampleScanner_Scan() { - // src is the input that we want to tokenize. - src := []byte(`[profile "A"] -color = blue ; Comment`) - - // Initialize the scanner. - var s scanner.Scanner - fset := token.NewFileSet() // positions are relative to fset - file := fset.AddFile("", fset.Base(), len(src)) // register input "file" - s.Init(file, src, nil /* no error handler */, scanner.ScanComments) - - // Repeated calls to Scan yield the token sequence found in the input. - for { - pos, tok, lit := s.Scan() - if tok == token.EOF { - break - } - fmt.Printf("%s\t%q\t%q\n", fset.Position(pos), tok, lit) - } - - // output: - // 1:1 "[" "" - // 1:2 "IDENT" "profile" - // 1:10 "STRING" "\"A\"" - // 1:13 "]" "" - // 1:14 "\n" "" - // 2:1 "IDENT" "color" - // 2:7 "=" "" - // 2:9 "STRING" "blue" - // 2:14 "COMMENT" "; Comment" -} diff --git a/Godeps/_workspace/src/github.com/scalingdata/gcfg/scanner/scanner_test.go b/Godeps/_workspace/src/github.com/scalingdata/gcfg/scanner/scanner_test.go deleted file mode 100644 index e4015736e..000000000 --- a/Godeps/_workspace/src/github.com/scalingdata/gcfg/scanner/scanner_test.go +++ /dev/null @@ -1,417 +0,0 @@ -// 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 ( - "os" - "strings" - "testing" -) - -import ( - "github.com/scalingdata/gcfg/token" -) - -var fset = token.NewFileSet() - -const /* class */ ( - special = iota - literal - operator -) - -func tokenclass(tok token.Token) int { - switch { - case tok.IsLiteral(): - return literal - case tok.IsOperator(): - return operator - } - return special -} - -type elt struct { - tok token.Token - lit string - class int - pre string - suf string -} - -var tokens = [...]elt{ - // Special tokens - {token.COMMENT, "; a comment", special, "", "\n"}, - {token.COMMENT, "# a comment", special, "", "\n"}, - - // Operators and delimiters - {token.ASSIGN, "=", operator, "", "value"}, - {token.LBRACK, "[", operator, "", ""}, - {token.RBRACK, "]", operator, "", ""}, - {token.EOL, "\n", operator, "", ""}, - - // Identifiers - {token.IDENT, "foobar", literal, "", ""}, - {token.IDENT, "a۰۱۸", literal, "", ""}, - {token.IDENT, "foo६४", literal, "", ""}, - {token.IDENT, "bar9876", literal, "", ""}, - {token.IDENT, "foo-bar", literal, "", ""}, - {token.IDENT, "foo", literal, ";\n", ""}, - // String literals (subsection names) - {token.STRING, `"foobar"`, literal, "", ""}, - {token.STRING, `"\""`, literal, "", ""}, - // String literals (values) - {token.STRING, `"\n"`, literal, "=", ""}, - {token.STRING, `"foobar"`, literal, "=", ""}, - {token.STRING, `"foo\nbar"`, literal, "=", ""}, - {token.STRING, `"foo\"bar"`, literal, "=", ""}, - {token.STRING, `"foo\\bar"`, literal, "=", ""}, - {token.STRING, `"foobar"`, literal, "=", ""}, - {token.STRING, `"foobar"`, literal, "= ", ""}, - {token.STRING, `"foobar"`, literal, "=", "\n"}, - {token.STRING, `"foobar"`, literal, "=", ";"}, - {token.STRING, `"foobar"`, literal, "=", " ;"}, - {token.STRING, `"foobar"`, literal, "=", "#"}, - {token.STRING, `"foobar"`, literal, "=", " #"}, - {token.STRING, "foobar", literal, "=", ""}, - {token.STRING, "foobar", literal, "= ", ""}, - {token.STRING, "foobar", literal, "=", " "}, - {token.STRING, `"foo" "bar"`, literal, "=", " "}, - {token.STRING, "foo\\\nbar", literal, "=", ""}, - {token.STRING, "foo\\\r\nbar", literal, "=", ""}, -} - -const whitespace = " \t \n\n\n" // to separate tokens - -var source = func() []byte { - var src []byte - for _, t := range tokens { - src = append(src, t.pre...) - src = append(src, t.lit...) - src = append(src, t.suf...) - src = append(src, whitespace...) - } - return src -}() - -func newlineCount(s string) int { - n := 0 - for i := 0; i < len(s); i++ { - if s[i] == '\n' { - n++ - } - } - return n -} - -func checkPos(t *testing.T, lit string, p token.Pos, expected token.Position) { - pos := fset.Position(p) - if pos.Filename != expected.Filename { - t.Errorf("bad filename for %q: got %s, expected %s", lit, pos.Filename, expected.Filename) - } - if pos.Offset != expected.Offset { - t.Errorf("bad position for %q: got %d, expected %d", lit, pos.Offset, expected.Offset) - } - if pos.Line != expected.Line { - t.Errorf("bad line for %q: got %d, expected %d", lit, pos.Line, expected.Line) - } - if pos.Column != expected.Column { - t.Errorf("bad column for %q: got %d, expected %d", lit, pos.Column, expected.Column) - } -} - -// Verify that calling Scan() provides the correct results. -func TestScan(t *testing.T) { - // make source - src_linecount := newlineCount(string(source)) - whitespace_linecount := newlineCount(whitespace) - - index := 0 - - // error handler - eh := func(_ token.Position, msg string) { - t.Errorf("%d: error handler called (msg = %s)", index, msg) - } - - // verify scan - var s Scanner - s.Init(fset.AddFile("", fset.Base(), len(source)), source, eh, ScanComments) - // epos is the expected position - epos := token.Position{ - Filename: "", - Offset: 0, - Line: 1, - Column: 1, - } - for { - pos, tok, lit := s.Scan() - if lit == "" { - // no literal value for non-literal tokens - lit = tok.String() - } - e := elt{token.EOF, "", special, "", ""} - if index < len(tokens) { - e = tokens[index] - } - if tok == token.EOF { - lit = "" - epos.Line = src_linecount - epos.Column = 2 - } - if e.pre != "" && strings.ContainsRune("=;#", rune(e.pre[0])) { - epos.Column = 1 - checkPos(t, lit, pos, epos) - var etok token.Token - if e.pre[0] == '=' { - etok = token.ASSIGN - } else { - etok = token.COMMENT - } - if tok != etok { - t.Errorf("bad token for %q: got %q, expected %q", lit, tok, etok) - } - pos, tok, lit = s.Scan() - } - epos.Offset += len(e.pre) - if tok != token.EOF { - epos.Column = 1 + len(e.pre) - } - if e.pre != "" && e.pre[len(e.pre)-1] == '\n' { - epos.Offset-- - epos.Column-- - checkPos(t, lit, pos, epos) - if tok != token.EOL { - t.Errorf("bad token for %q: got %q, expected %q", lit, tok, token.EOL) - } - epos.Line++ - epos.Offset++ - epos.Column = 1 - pos, tok, lit = s.Scan() - } - checkPos(t, lit, pos, epos) - if tok != e.tok { - t.Errorf("bad token for %q: got %q, expected %q", lit, tok, e.tok) - } - if e.tok.IsLiteral() { - // no CRs in value string literals - elit := e.lit - if strings.ContainsRune(e.pre, '=') { - elit = string(stripCR([]byte(elit))) - epos.Offset += len(e.lit) - len(lit) // correct position - } - if lit != elit { - t.Errorf("bad literal for %q: got %q, expected %q", lit, lit, elit) - } - } - if tokenclass(tok) != e.class { - t.Errorf("bad class for %q: got %d, expected %d", lit, tokenclass(tok), e.class) - } - epos.Offset += len(lit) + len(e.suf) + len(whitespace) - epos.Line += newlineCount(lit) + newlineCount(e.suf) + whitespace_linecount - index++ - if tok == token.EOF { - break - } - if e.suf == "value" { - pos, tok, lit = s.Scan() - if tok != token.STRING { - t.Errorf("bad token for %q: got %q, expected %q", lit, tok, token.STRING) - } - } else if strings.ContainsRune(e.suf, ';') || strings.ContainsRune(e.suf, '#') { - pos, tok, lit = s.Scan() - if tok != token.COMMENT { - t.Errorf("bad token for %q: got %q, expected %q", lit, tok, token.COMMENT) - } - } - // skip EOLs - for i := 0; i < whitespace_linecount+newlineCount(e.suf); i++ { - pos, tok, lit = s.Scan() - if tok != token.EOL { - t.Errorf("bad token for %q: got %q, expected %q", lit, tok, token.EOL) - } - } - } - if s.ErrorCount != 0 { - t.Errorf("found %d errors", s.ErrorCount) - } -} - -func TestScanValStringEOF(t *testing.T) { - var s Scanner - src := "= value" - f := fset.AddFile("src", fset.Base(), len(src)) - s.Init(f, []byte(src), nil, 0) - s.Scan() // = - s.Scan() // value - _, tok, _ := s.Scan() // EOF - if tok != token.EOF { - t.Errorf("bad token: got %s, expected %s", tok, token.EOF) - } - if s.ErrorCount > 0 { - t.Error("scanning error") - } -} - -// Verify that initializing the same scanner more then once works correctly. -func TestInit(t *testing.T) { - var s Scanner - - // 1st init - src1 := "\nname = value" - f1 := fset.AddFile("src1", fset.Base(), len(src1)) - s.Init(f1, []byte(src1), nil, 0) - if f1.Size() != len(src1) { - t.Errorf("bad file size: got %d, expected %d", f1.Size(), len(src1)) - } - s.Scan() // \n - s.Scan() // name - _, tok, _ := s.Scan() // = - if tok != token.ASSIGN { - t.Errorf("bad token: got %s, expected %s", tok, token.ASSIGN) - } - - // 2nd init - src2 := "[section]" - f2 := fset.AddFile("src2", fset.Base(), len(src2)) - s.Init(f2, []byte(src2), nil, 0) - if f2.Size() != len(src2) { - t.Errorf("bad file size: got %d, expected %d", f2.Size(), len(src2)) - } - _, tok, _ = s.Scan() // [ - if tok != token.LBRACK { - t.Errorf("bad token: got %s, expected %s", tok, token.LBRACK) - } - - if s.ErrorCount != 0 { - t.Errorf("found %d errors", s.ErrorCount) - } -} - -func TestStdErrorHandler(t *testing.T) { - const src = "@\n" + // illegal character, cause an error - "@ @\n" // two errors on the same line - - var list ErrorList - eh := func(pos token.Position, msg string) { list.Add(pos, msg) } - - var s Scanner - s.Init(fset.AddFile("File1", fset.Base(), len(src)), []byte(src), eh, 0) - for { - if _, tok, _ := s.Scan(); tok == token.EOF { - break - } - } - - if len(list) != s.ErrorCount { - t.Errorf("found %d errors, expected %d", len(list), s.ErrorCount) - } - - if len(list) != 3 { - t.Errorf("found %d raw errors, expected 3", len(list)) - PrintError(os.Stderr, list) - } - - list.Sort() - if len(list) != 3 { - t.Errorf("found %d sorted errors, expected 3", len(list)) - PrintError(os.Stderr, list) - } - - list.RemoveMultiples() - if len(list) != 2 { - t.Errorf("found %d one-per-line errors, expected 2", len(list)) - PrintError(os.Stderr, list) - } -} - -type errorCollector struct { - cnt int // number of errors encountered - msg string // last error message encountered - pos token.Position // last error position encountered -} - -func checkError(t *testing.T, src string, tok token.Token, pos int, err string) { - var s Scanner - var h errorCollector - eh := func(pos token.Position, msg string) { - h.cnt++ - h.msg = msg - h.pos = pos - } - s.Init(fset.AddFile("", fset.Base(), len(src)), []byte(src), eh, ScanComments) - if src[0] == '=' { - _, _, _ = s.Scan() - } - _, tok0, _ := s.Scan() - _, tok1, _ := s.Scan() - if tok0 != tok { - t.Errorf("%q: got %s, expected %s", src, tok0, tok) - } - if tok1 != token.EOF { - t.Errorf("%q: got %s, expected EOF", src, tok1) - } - cnt := 0 - if err != "" { - cnt = 1 - } - if h.cnt != cnt { - t.Errorf("%q: got cnt %d, expected %d", src, h.cnt, cnt) - } - if h.msg != err { - t.Errorf("%q: got msg %q, expected %q", src, h.msg, err) - } - if h.pos.Offset != pos { - t.Errorf("%q: got offset %d, expected %d", src, h.pos.Offset, pos) - } -} - -var errors = []struct { - src string - tok token.Token - pos int - err string -}{ - {"\a", token.ILLEGAL, 0, "illegal character U+0007"}, - {"/", token.ILLEGAL, 0, "illegal character U+002F '/'"}, - {"_", token.ILLEGAL, 0, "illegal character U+005F '_'"}, - {`…`, token.ILLEGAL, 0, "illegal character U+2026 '…'"}, - {`""`, token.STRING, 0, ""}, - {`"`, token.STRING, 0, "string not terminated"}, - {"\"\n", token.STRING, 0, "string not terminated"}, - {`="`, token.STRING, 1, "string not terminated"}, - {"=\"\n", token.STRING, 1, "string not terminated"}, - {"=\\", token.STRING, 1, "unquoted '\\' must be followed by new line"}, - {"=\\\r", token.STRING, 1, "unquoted '\\' must be followed by new line"}, - {`"\z"`, token.STRING, 2, "unknown escape sequence"}, - {`"\a"`, token.STRING, 2, "unknown escape sequence"}, - {`"\b"`, token.STRING, 2, "unknown escape sequence"}, - {`"\f"`, token.STRING, 2, "unknown escape sequence"}, - {`"\r"`, token.STRING, 2, "unknown escape sequence"}, - {`"\t"`, token.STRING, 2, "unknown escape sequence"}, - {`"\v"`, token.STRING, 2, "unknown escape sequence"}, - {`"\0"`, token.STRING, 2, "unknown escape sequence"}, -} - -func TestScanErrors(t *testing.T) { - for _, e := range errors { - checkError(t, e.src, e.tok, e.pos, e.err) - } -} - -func BenchmarkScan(b *testing.B) { - b.StopTimer() - fset := token.NewFileSet() - file := fset.AddFile("", fset.Base(), len(source)) - var s Scanner - b.StartTimer() - for i := b.N - 1; i >= 0; i-- { - s.Init(file, source, nil, ScanComments) - for { - _, tok, _ := s.Scan() - if tok == token.EOF { - break - } - } - } -} diff --git a/Godeps/_workspace/src/github.com/scalingdata/gcfg/testdata/gcfg_test.gcfg b/Godeps/_workspace/src/github.com/scalingdata/gcfg/testdata/gcfg_test.gcfg deleted file mode 100644 index cddff29ab..000000000 --- a/Godeps/_workspace/src/github.com/scalingdata/gcfg/testdata/gcfg_test.gcfg +++ /dev/null @@ -1,3 +0,0 @@ -; Comment line -[section] -name=value # comment diff --git a/Godeps/_workspace/src/github.com/scalingdata/gcfg/testdata/gcfg_unicode_test.gcfg b/Godeps/_workspace/src/github.com/scalingdata/gcfg/testdata/gcfg_unicode_test.gcfg deleted file mode 100644 index 3762a201e..000000000 --- a/Godeps/_workspace/src/github.com/scalingdata/gcfg/testdata/gcfg_unicode_test.gcfg +++ /dev/null @@ -1,3 +0,0 @@ -; Comment line -[甲] -乙=丙 # comment diff --git a/Godeps/_workspace/src/github.com/scalingdata/gcfg/token/position_test.go b/Godeps/_workspace/src/github.com/scalingdata/gcfg/token/position_test.go deleted file mode 100644 index 160107df4..000000000 --- a/Godeps/_workspace/src/github.com/scalingdata/gcfg/token/position_test.go +++ /dev/null @@ -1,181 +0,0 @@ -// 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 token - -import ( - "fmt" - "testing" -) - -func checkPos(t *testing.T, msg string, p, q Position) { - if p.Filename != q.Filename { - t.Errorf("%s: expected filename = %q; got %q", msg, q.Filename, p.Filename) - } - if p.Offset != q.Offset { - t.Errorf("%s: expected offset = %d; got %d", msg, q.Offset, p.Offset) - } - if p.Line != q.Line { - t.Errorf("%s: expected line = %d; got %d", msg, q.Line, p.Line) - } - if p.Column != q.Column { - t.Errorf("%s: expected column = %d; got %d", msg, q.Column, p.Column) - } -} - -func TestNoPos(t *testing.T) { - if NoPos.IsValid() { - t.Errorf("NoPos should not be valid") - } - var fset *FileSet - checkPos(t, "nil NoPos", fset.Position(NoPos), Position{}) - fset = NewFileSet() - checkPos(t, "fset NoPos", fset.Position(NoPos), Position{}) -} - -var tests = []struct { - filename string - source []byte // may be nil - size int - lines []int -}{ - {"a", []byte{}, 0, []int{}}, - {"b", []byte("01234"), 5, []int{0}}, - {"c", []byte("\n\n\n\n\n\n\n\n\n"), 9, []int{0, 1, 2, 3, 4, 5, 6, 7, 8}}, - {"d", nil, 100, []int{0, 5, 10, 20, 30, 70, 71, 72, 80, 85, 90, 99}}, - {"e", nil, 777, []int{0, 80, 100, 120, 130, 180, 267, 455, 500, 567, 620}}, - {"f", []byte("package p\n\nimport \"fmt\""), 23, []int{0, 10, 11}}, - {"g", []byte("package p\n\nimport \"fmt\"\n"), 24, []int{0, 10, 11}}, - {"h", []byte("package p\n\nimport \"fmt\"\n "), 25, []int{0, 10, 11, 24}}, -} - -func linecol(lines []int, offs int) (int, int) { - prevLineOffs := 0 - for line, lineOffs := range lines { - if offs < lineOffs { - return line, offs - prevLineOffs + 1 - } - prevLineOffs = lineOffs - } - return len(lines), offs - prevLineOffs + 1 -} - -func verifyPositions(t *testing.T, fset *FileSet, f *File, lines []int) { - for offs := 0; offs < f.Size(); offs++ { - p := f.Pos(offs) - offs2 := f.Offset(p) - if offs2 != offs { - t.Errorf("%s, Offset: expected offset %d; got %d", f.Name(), offs, offs2) - } - line, col := linecol(lines, offs) - msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p) - checkPos(t, msg, f.Position(f.Pos(offs)), Position{f.Name(), offs, line, col}) - checkPos(t, msg, fset.Position(p), Position{f.Name(), offs, line, col}) - } -} - -func makeTestSource(size int, lines []int) []byte { - src := make([]byte, size) - for _, offs := range lines { - if offs > 0 { - src[offs-1] = '\n' - } - } - return src -} - -func TestPositions(t *testing.T) { - const delta = 7 // a non-zero base offset increment - fset := NewFileSet() - for _, test := range tests { - // verify consistency of test case - if test.source != nil && len(test.source) != test.size { - t.Errorf("%s: inconsistent test case: expected file size %d; got %d", test.filename, test.size, len(test.source)) - } - - // add file and verify name and size - f := fset.AddFile(test.filename, fset.Base()+delta, test.size) - if f.Name() != test.filename { - t.Errorf("expected filename %q; got %q", test.filename, f.Name()) - } - if f.Size() != test.size { - t.Errorf("%s: expected file size %d; got %d", f.Name(), test.size, f.Size()) - } - if fset.File(f.Pos(0)) != f { - t.Errorf("%s: f.Pos(0) was not found in f", f.Name()) - } - - // add lines individually and verify all positions - for i, offset := range test.lines { - f.AddLine(offset) - if f.LineCount() != i+1 { - t.Errorf("%s, AddLine: expected line count %d; got %d", f.Name(), i+1, f.LineCount()) - } - // adding the same offset again should be ignored - f.AddLine(offset) - if f.LineCount() != i+1 { - t.Errorf("%s, AddLine: expected unchanged line count %d; got %d", f.Name(), i+1, f.LineCount()) - } - verifyPositions(t, fset, f, test.lines[0:i+1]) - } - - // add lines with SetLines and verify all positions - if ok := f.SetLines(test.lines); !ok { - t.Errorf("%s: SetLines failed", f.Name()) - } - if f.LineCount() != len(test.lines) { - t.Errorf("%s, SetLines: expected line count %d; got %d", f.Name(), len(test.lines), f.LineCount()) - } - verifyPositions(t, fset, f, test.lines) - - // add lines with SetLinesForContent and verify all positions - src := test.source - if src == nil { - // no test source available - create one from scratch - src = makeTestSource(test.size, test.lines) - } - f.SetLinesForContent(src) - if f.LineCount() != len(test.lines) { - t.Errorf("%s, SetLinesForContent: expected line count %d; got %d", f.Name(), len(test.lines), f.LineCount()) - } - verifyPositions(t, fset, f, test.lines) - } -} - -func TestLineInfo(t *testing.T) { - fset := NewFileSet() - f := fset.AddFile("foo", fset.Base(), 500) - lines := []int{0, 42, 77, 100, 210, 220, 277, 300, 333, 401} - // add lines individually and provide alternative line information - for _, offs := range lines { - f.AddLine(offs) - f.AddLineInfo(offs, "bar", 42) - } - // verify positions for all offsets - for offs := 0; offs <= f.Size(); offs++ { - p := f.Pos(offs) - _, col := linecol(lines, offs) - msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p) - checkPos(t, msg, f.Position(f.Pos(offs)), Position{"bar", offs, 42, col}) - checkPos(t, msg, fset.Position(p), Position{"bar", offs, 42, col}) - } -} - -func TestFiles(t *testing.T) { - fset := NewFileSet() - for i, test := range tests { - fset.AddFile(test.filename, fset.Base(), test.size) - j := 0 - fset.Iterate(func(f *File) bool { - if f.Name() != tests[j].filename { - t.Errorf("expected filename = %s; got %s", tests[j].filename, f.Name()) - } - j++ - return true - }) - if j != i+1 { - t.Errorf("expected %d files; got %d", i+1, j) - } - } -} diff --git a/Godeps/_workspace/src/github.com/scalingdata/gcfg/token/serialize_test.go b/Godeps/_workspace/src/github.com/scalingdata/gcfg/token/serialize_test.go deleted file mode 100644 index 4e925adb6..000000000 --- a/Godeps/_workspace/src/github.com/scalingdata/gcfg/token/serialize_test.go +++ /dev/null @@ -1,111 +0,0 @@ -// 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 - -import ( - "bytes" - "encoding/gob" - "fmt" - "testing" -) - -// equal returns nil if p and q describe the same file set; -// otherwise it returns an error describing the discrepancy. -func equal(p, q *FileSet) error { - if p == q { - // avoid deadlock if p == q - return nil - } - - // not strictly needed for the test - p.mutex.Lock() - q.mutex.Lock() - defer q.mutex.Unlock() - defer p.mutex.Unlock() - - if p.base != q.base { - return fmt.Errorf("different bases: %d != %d", p.base, q.base) - } - - if len(p.files) != len(q.files) { - return fmt.Errorf("different number of files: %d != %d", len(p.files), len(q.files)) - } - - for i, f := range p.files { - g := q.files[i] - if f.set != p { - return fmt.Errorf("wrong fileset for %q", f.name) - } - if g.set != q { - return fmt.Errorf("wrong fileset for %q", g.name) - } - if f.name != g.name { - return fmt.Errorf("different filenames: %q != %q", f.name, g.name) - } - if f.base != g.base { - return fmt.Errorf("different base for %q: %d != %d", f.name, f.base, g.base) - } - if f.size != g.size { - return fmt.Errorf("different size for %q: %d != %d", f.name, f.size, g.size) - } - for j, l := range f.lines { - m := g.lines[j] - if l != m { - return fmt.Errorf("different offsets for %q", f.name) - } - } - for j, l := range f.infos { - m := g.infos[j] - if l.Offset != m.Offset || l.Filename != m.Filename || l.Line != m.Line { - return fmt.Errorf("different infos for %q", f.name) - } - } - } - - // we don't care about .last - it's just a cache - return nil -} - -func checkSerialize(t *testing.T, p *FileSet) { - var buf bytes.Buffer - encode := func(x interface{}) error { - return gob.NewEncoder(&buf).Encode(x) - } - if err := p.Write(encode); err != nil { - t.Errorf("writing fileset failed: %s", err) - return - } - q := NewFileSet() - decode := func(x interface{}) error { - return gob.NewDecoder(&buf).Decode(x) - } - if err := q.Read(decode); err != nil { - t.Errorf("reading fileset failed: %s", err) - return - } - if err := equal(p, q); err != nil { - t.Errorf("filesets not identical: %s", err) - } -} - -func TestSerialization(t *testing.T) { - p := NewFileSet() - checkSerialize(t, p) - // add some files - for i := 0; i < 10; i++ { - f := p.AddFile(fmt.Sprintf("file%d", i), p.Base()+i, i*100) - checkSerialize(t, p) - // add some lines and alternative file infos - line := 1000 - for offs := 0; offs < f.Size(); offs += 40 + i { - f.AddLine(offs) - if offs%7 == 0 { - f.AddLineInfo(offs, fmt.Sprintf("file%d", offs), line) - line += 33 - } - } - checkSerialize(t, p) - } -} diff --git a/Godeps/_workspace/src/github.com/scalingdata/gcfg/types/enum_test.go b/Godeps/_workspace/src/github.com/scalingdata/gcfg/types/enum_test.go deleted file mode 100644 index 4bf135e67..000000000 --- a/Godeps/_workspace/src/github.com/scalingdata/gcfg/types/enum_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package types - -import ( - "testing" -) - -func TestEnumParserBool(t *testing.T) { - for _, tt := range []struct { - val string - res bool - ok bool - }{ - {val: "tRuE", res: true, ok: true}, - {val: "False", res: false, ok: true}, - {val: "t", ok: false}, - } { - b, err := ParseBool(tt.val) - switch { - case tt.ok && err != nil: - t.Errorf("%q: got error %v, want %v", tt.val, err, tt.res) - case !tt.ok && err == nil: - t.Errorf("%q: got %v, want error", tt.val, b) - case tt.ok && b != tt.res: - t.Errorf("%q: got %v, want %v", tt.val, b, tt.res) - default: - t.Logf("%q: got %v, %v", tt.val, b, err) - } - } -} diff --git a/Godeps/_workspace/src/github.com/scalingdata/gcfg/types/int_test.go b/Godeps/_workspace/src/github.com/scalingdata/gcfg/types/int_test.go deleted file mode 100644 index b63dbcbae..000000000 --- a/Godeps/_workspace/src/github.com/scalingdata/gcfg/types/int_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package types - -import ( - "reflect" - "testing" -) - -func elem(p interface{}) interface{} { - return reflect.ValueOf(p).Elem().Interface() -} - -func TestParseInt(t *testing.T) { - for _, tt := range []struct { - val string - mode IntMode - exp interface{} - ok bool - }{ - {"0", Dec, int(0), true}, - {"10", Dec, int(10), true}, - {"-10", Dec, int(-10), true}, - {"x", Dec, int(0), false}, - {"0xa", Hex, int(0xa), true}, - {"a", Hex, int(0xa), true}, - {"10", Hex, int(0x10), true}, - {"-0xa", Hex, int(-0xa), true}, - {"0x", Hex, int(0x0), true}, // Scanf doesn't require digit behind 0x - {"-0x", Hex, int(0x0), true}, // Scanf doesn't require digit behind 0x - {"-a", Hex, int(-0xa), true}, - {"-10", Hex, int(-0x10), true}, - {"x", Hex, int(0), false}, - {"10", Oct, int(010), true}, - {"010", Oct, int(010), true}, - {"-10", Oct, int(-010), true}, - {"-010", Oct, int(-010), true}, - {"10", Dec | Hex, int(10), true}, - {"010", Dec | Hex, int(10), true}, - {"0x10", Dec | Hex, int(0x10), true}, - {"10", Dec | Oct, int(10), true}, - {"010", Dec | Oct, int(010), true}, - {"0x10", Dec | Oct, int(0), false}, - {"10", Hex | Oct, int(0), false}, // need prefix to distinguish Hex/Oct - {"010", Hex | Oct, int(010), true}, - {"0x10", Hex | Oct, int(0x10), true}, - {"10", Dec | Hex | Oct, int(10), true}, - {"010", Dec | Hex | Oct, int(010), true}, - {"0x10", Dec | Hex | Oct, int(0x10), true}, - } { - typ := reflect.TypeOf(tt.exp) - res := reflect.New(typ).Interface() - err := ParseInt(res, tt.val, tt.mode) - switch { - case tt.ok && err != nil: - t.Errorf("ParseInt(%v, %#v, %v): fail; got error %v, want ok", - typ, tt.val, tt.mode, err) - case !tt.ok && err == nil: - t.Errorf("ParseInt(%v, %#v, %v): fail; got %v, want error", - typ, tt.val, tt.mode, elem(res)) - case tt.ok && !reflect.DeepEqual(elem(res), tt.exp): - t.Errorf("ParseInt(%v, %#v, %v): fail; got %v, want %v", - typ, tt.val, tt.mode, elem(res), tt.exp) - default: - t.Logf("ParseInt(%v, %#v, %s): pass; got %v, error %v", - typ, tt.val, tt.mode, elem(res), err) - } - } -} diff --git a/Godeps/_workspace/src/github.com/scalingdata/gcfg/types/scan_test.go b/Godeps/_workspace/src/github.com/scalingdata/gcfg/types/scan_test.go deleted file mode 100644 index a8083e04f..000000000 --- a/Godeps/_workspace/src/github.com/scalingdata/gcfg/types/scan_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package types - -import ( - "reflect" - "testing" -) - -func TestScanFully(t *testing.T) { - for _, tt := range []struct { - val string - verb byte - res interface{} - ok bool - }{ - {"a", 'v', int(0), false}, - {"0x", 'v', int(0), true}, - {"0x", 'd', int(0), false}, - } { - d := reflect.New(reflect.TypeOf(tt.res)).Interface() - err := ScanFully(d, tt.val, tt.verb) - switch { - case tt.ok && err != nil: - t.Errorf("ScanFully(%T, %q, '%c'): want ok, got error %v", - d, tt.val, tt.verb, err) - case !tt.ok && err == nil: - t.Errorf("ScanFully(%T, %q, '%c'): want error, got %v", - d, tt.val, tt.verb, elem(d)) - case tt.ok && err == nil && !reflect.DeepEqual(tt.res, elem(d)): - t.Errorf("ScanFully(%T, %q, '%c'): want %v, got %v", - d, tt.val, tt.verb, tt.res, elem(d)) - default: - t.Logf("ScanFully(%T, %q, '%c') = %v; *ptr==%v", - d, tt.val, tt.verb, err, elem(d)) - } - } -} diff --git a/Godeps/_workspace/src/github.com/shurcooL/sanitized_anchor_name/main_test.go b/Godeps/_workspace/src/github.com/shurcooL/sanitized_anchor_name/main_test.go deleted file mode 100644 index ad03201ca..000000000 --- a/Godeps/_workspace/src/github.com/shurcooL/sanitized_anchor_name/main_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package sanitized_anchor_name_test - -import ( - "fmt" - - "github.com/shurcooL/sanitized_anchor_name" -) - -func ExampleCreate() { - anchorName := sanitized_anchor_name.Create("This is a header") - - fmt.Println(anchorName) - - // Output: - // this-is-a-header -} - -func ExampleCreate2() { - fmt.Println(sanitized_anchor_name.Create("This is a header")) - fmt.Println(sanitized_anchor_name.Create("This is also a header")) - fmt.Println(sanitized_anchor_name.Create("main.go")) - fmt.Println(sanitized_anchor_name.Create("Article 123")) - fmt.Println(sanitized_anchor_name.Create("<- Let's try this, shall we?")) - fmt.Printf("%q\n", sanitized_anchor_name.Create(" ")) - fmt.Println(sanitized_anchor_name.Create("Hello, 世界")) - - // Output: - // this-is-a-header - // this-is-also-a-header - // main-go - // article-123 - // let-s-try-this-shall-we - // "" - // hello-世界 -} diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/bash_completions_test.go b/Godeps/_workspace/src/github.com/spf13/cobra/bash_completions_test.go deleted file mode 100644 index 53656c7fd..000000000 --- a/Godeps/_workspace/src/github.com/spf13/cobra/bash_completions_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package cobra - -import ( - "bytes" - "fmt" - "os" - "strings" - "testing" -) - -var _ = fmt.Println -var _ = os.Stderr - -func checkOmit(t *testing.T, found, unexpected string) { - if strings.Contains(found, unexpected) { - t.Errorf("Unexpected response.\nGot: %q\nBut should not have!\n", unexpected) - } -} - -func check(t *testing.T, found, expected string) { - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } -} - -// World worst custom function, just keep telling you to enter hello! -const ( - bash_completion_func = `__custom_func() { -COMPREPLY=( "hello" ) -} -` -) - -func TestBashCompletions(t *testing.T) { - c := initializeWithRootCmd() - cmdEcho.AddCommand(cmdTimes) - c.AddCommand(cmdEcho, cmdPrint, cmdDeprecated) - - // custom completion function - c.BashCompletionFunction = bash_completion_func - - // required flag - c.MarkFlagRequired("introot") - - // valid nouns - validArgs := []string{"pods", "nodes", "services", "replicationControllers"} - c.ValidArgs = validArgs - - // filename - var flagval string - c.Flags().StringVar(&flagval, "filename", "", "Enter a filename") - c.MarkFlagFilename("filename", "json", "yaml", "yml") - - // persistent filename - var flagvalPersistent string - c.PersistentFlags().StringVar(&flagvalPersistent, "persistent-filename", "", "Enter a filename") - c.MarkPersistentFlagFilename("persistent-filename") - c.MarkPersistentFlagRequired("persistent-filename") - - // filename extensions - var flagvalExt string - c.Flags().StringVar(&flagvalExt, "filename-ext", "", "Enter a filename (extension limited)") - c.MarkFlagFilename("filename-ext") - - // subdirectories in a given directory - var flagvalTheme string - c.Flags().StringVar(&flagvalTheme, "theme", "", "theme to use (located in /themes/THEMENAME/)") - c.Flags().SetAnnotation("theme", BashCompSubdirsInDir, []string{"themes"}) - - out := new(bytes.Buffer) - c.GenBashCompletion(out) - str := out.String() - - check(t, str, "_cobra-test") - check(t, str, "_cobra-test_echo") - check(t, str, "_cobra-test_echo_times") - check(t, str, "_cobra-test_print") - - // check for required flags - check(t, str, `must_have_one_flag+=("--introot=")`) - check(t, str, `must_have_one_flag+=("--persistent-filename=")`) - // check for custom completion function - check(t, str, `COMPREPLY=( "hello" )`) - // check for required nouns - check(t, str, `must_have_one_noun+=("pods")`) - // check for filename extension flags - check(t, str, `flags_completion+=("_filedir")`) - // check for filename extension flags - check(t, str, `flags_completion+=("__handle_filename_extension_flag json|yaml|yml")`) - // check for subdirs_in_dir flags - check(t, str, `flags_completion+=("__handle_subdirs_in_dir_flag themes")`) - - checkOmit(t, str, cmdDeprecated.Name()) -} diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/add.go b/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/add.go deleted file mode 100644 index b89d4c474..000000000 --- a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/add.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright © 2015 Steve Francia . -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "fmt" - "path/filepath" - "strings" - - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -func init() { - RootCmd.AddCommand(addCmd) -} - -var pName string - -// initialize Command -var addCmd = &cobra.Command{ - Use: "add [command name]", - Aliases: []string{"command"}, - Short: "Add a command to a Cobra Application", - Long: `Add (cobra add) will create a new command, with a license and -the appropriate structure for a Cobra-based CLI application, -and register it to its parent (default RootCmd). - -If you want your command to be public, pass in the command name -with an initial uppercase letter. - -Example: cobra add server -> resulting in a new cmd/server.go - `, - - Run: func(cmd *cobra.Command, args []string) { - if len(args) != 1 { - er("add needs a name for the command") - } - guessProjectPath() - createCmdFile(args[0]) - }, -} - -func init() { - addCmd.Flags().StringVarP(&pName, "parent", "p", "RootCmd", "name of parent command for this command") -} - -func parentName() string { - if !strings.HasSuffix(strings.ToLower(pName), "cmd") { - return pName + "Cmd" - } - - return pName -} - -func createCmdFile(cmdName string) { - lic := getLicense() - - template := `{{ comment .copyright }} -{{ comment .license }} - -package cmd - -import ( - "fmt" - - "github.com/spf13/cobra" -) - -// {{.cmdName}}Cmd represents the {{.cmdName}} command -var {{ .cmdName }}Cmd = &cobra.Command{ - Use: "{{ .cmdName }}", - Short: "A brief description of your command", - Long: ` + "`" + `A longer description that spans multiple lines and likely contains examples -and usage of using your command. For example: - -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.` + "`" + `, - Run: func(cmd *cobra.Command, args []string) { - // TODO: Work your own magic here - fmt.Println("{{ .cmdName }} called") - }, -} - -func init() { - {{ .parentName }}.AddCommand({{ .cmdName }}Cmd) - - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // {{.cmdName}}Cmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // {{.cmdName}}Cmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") - -} -` - - var data map[string]interface{} - data = make(map[string]interface{}) - - data["copyright"] = copyrightLine() - data["license"] = lic.Header - data["appName"] = projectName() - data["viper"] = viper.GetBool("useViper") - data["parentName"] = parentName() - data["cmdName"] = cmdName - - err := writeTemplateToFile(filepath.Join(ProjectPath(), guessCmdDir()), cmdName+".go", template, data) - if err != nil { - er(err) - } - fmt.Println(cmdName, "created at", filepath.Join(ProjectPath(), guessCmdDir(), cmdName+".go")) -} diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/helpers.go b/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/helpers.go deleted file mode 100644 index 3f6357f0e..000000000 --- a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/helpers.go +++ /dev/null @@ -1,347 +0,0 @@ -// Copyright © 2015 Steve Francia . -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "bytes" - "fmt" - "io" - "os" - "path/filepath" - "strings" - "text/template" - "time" - - "github.com/spf13/viper" -) - -// var BaseDir = "" -// var AppName = "" -// var CommandDir = "" - -var funcMap template.FuncMap -var projectPath = "" -var inputPath = "" -var projectBase = "" - -// for testing only -var testWd = "" - -var cmdDirs = []string{"cmd", "cmds", "command", "commands"} - -func init() { - funcMap = template.FuncMap{ - "comment": commentifyString, - } -} - -func er(msg interface{}) { - fmt.Println("Error:", msg) - os.Exit(-1) -} - -// Check if a file or directory exists. -func exists(path string) (bool, error) { - _, err := os.Stat(path) - if err == nil { - return true, nil - } - if os.IsNotExist(err) { - return false, nil - } - return false, err -} - -func ProjectPath() string { - if projectPath == "" { - guessProjectPath() - } - - return projectPath -} - -// wrapper of the os package so we can test better -func getWd() (string, error) { - if testWd == "" { - return os.Getwd() - } - return testWd, nil -} - -func guessCmdDir() string { - guessProjectPath() - if b, _ := isEmpty(projectPath); b { - return "cmd" - } - - files, _ := filepath.Glob(projectPath + string(os.PathSeparator) + "c*") - for _, f := range files { - for _, c := range cmdDirs { - if f == c { - return c - } - } - } - - return "cmd" -} - -func guessImportPath() string { - guessProjectPath() - - if !strings.HasPrefix(projectPath, getSrcPath()) { - er("Cobra only supports project within $GOPATH") - } - - return filepath.Clean(strings.TrimPrefix(projectPath, getSrcPath())) -} - -func getSrcPath() string { - return filepath.Join(os.Getenv("GOPATH"), "src") + string(os.PathSeparator) -} - -func projectName() string { - return filepath.Base(ProjectPath()) -} - -func guessProjectPath() { - // if no path is provided... assume CWD. - if inputPath == "" { - x, err := getWd() - if err != nil { - er(err) - } - - // inspect CWD - base := filepath.Base(x) - - // if we are in the cmd directory.. back up - for _, c := range cmdDirs { - if base == c { - projectPath = filepath.Dir(x) - return - } - } - - if projectPath == "" { - projectPath = filepath.Clean(x) - return - } - } - - srcPath := getSrcPath() - // if provided, inspect for logical locations - if strings.ContainsRune(inputPath, os.PathSeparator) { - if filepath.IsAbs(inputPath) { - // if Absolute, use it - projectPath = filepath.Clean(inputPath) - return - } - // If not absolute but contains slashes, - // assuming it means create it from $GOPATH - count := strings.Count(inputPath, string(os.PathSeparator)) - - switch count { - // If only one directory deep, assume "github.com" - case 1: - projectPath = filepath.Join(srcPath, "github.com", inputPath) - return - case 2: - projectPath = filepath.Join(srcPath, inputPath) - return - default: - er("Unknown directory") - } - } else { - // hardest case.. just a word. - if projectBase == "" { - x, err := getWd() - if err == nil { - projectPath = filepath.Join(x, inputPath) - return - } - er(err) - } else { - projectPath = filepath.Join(srcPath, projectBase, inputPath) - return - } - } -} - -// isEmpty checks if a given path is empty. -func isEmpty(path string) (bool, error) { - if b, _ := exists(path); !b { - return false, fmt.Errorf("%q path does not exist", path) - } - fi, err := os.Stat(path) - if err != nil { - return false, err - } - if fi.IsDir() { - f, err := os.Open(path) - // FIX: Resource leak - f.close() should be called here by defer or is missed - // if the err != nil branch is taken. - defer f.Close() - if err != nil { - return false, err - } - list, err := f.Readdir(-1) - // f.Close() - see bug fix above - return len(list) == 0, nil - } - return fi.Size() == 0, nil -} - -// isDir checks if a given path is a directory. -func isDir(path string) (bool, error) { - fi, err := os.Stat(path) - if err != nil { - return false, err - } - return fi.IsDir(), nil -} - -// dirExists checks if a path exists and is a directory. -func dirExists(path string) (bool, error) { - fi, err := os.Stat(path) - if err == nil && fi.IsDir() { - return true, nil - } - if os.IsNotExist(err) { - return false, nil - } - return false, err -} - -func writeTemplateToFile(path string, file string, template string, data interface{}) error { - filename := filepath.Join(path, file) - - r, err := templateToReader(template, data) - - if err != nil { - return err - } - - err = safeWriteToDisk(filename, r) - - if err != nil { - return err - } - return nil -} - -func writeStringToFile(path, file, text string) error { - filename := filepath.Join(path, file) - - r := strings.NewReader(text) - err := safeWriteToDisk(filename, r) - - if err != nil { - return err - } - return nil -} - -func templateToReader(tpl string, data interface{}) (io.Reader, error) { - tmpl := template.New("") - tmpl.Funcs(funcMap) - tmpl, err := tmpl.Parse(tpl) - - if err != nil { - return nil, err - } - buf := new(bytes.Buffer) - err = tmpl.Execute(buf, data) - - return buf, err -} - -// Same as WriteToDisk but checks to see if file/directory already exists. -func safeWriteToDisk(inpath string, r io.Reader) (err error) { - dir, _ := filepath.Split(inpath) - ospath := filepath.FromSlash(dir) - - if ospath != "" { - err = os.MkdirAll(ospath, 0777) // rwx, rw, r - if err != nil { - return - } - } - - ex, err := exists(inpath) - if err != nil { - return - } - if ex { - return fmt.Errorf("%v already exists", inpath) - } - - file, err := os.Create(inpath) - if err != nil { - return - } - defer file.Close() - - _, err = io.Copy(file, r) - return -} - -func getLicense() License { - l := whichLicense() - if l != "" { - if x, ok := Licenses[l]; ok { - return x - } - } - - return Licenses["apache"] -} - -func whichLicense() string { - // if explicitly flagged, use that - if userLicense != "" { - return matchLicense(userLicense) - } - - // if already present in the project, use that - // TODO: Inspect project for existing license - - // default to viper's setting - - return matchLicense(viper.GetString("license")) -} - -func copyrightLine() string { - author := viper.GetString("author") - year := time.Now().Format("2006") - - return "Copyright © " + year + " " + author -} - -func commentifyString(in string) string { - var newlines []string - lines := strings.Split(in, "\n") - for _, x := range lines { - if !strings.HasPrefix(x, "//") { - if x != "" { - newlines = append(newlines, "// "+x) - } else { - newlines = append(newlines, "//") - } - } else { - newlines = append(newlines, x) - } - } - return strings.Join(newlines, "\n") -} diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/helpers_test.go b/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/helpers_test.go deleted file mode 100644 index 564ddbe5f..000000000 --- a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/helpers_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - "testing" -) - -var _ = fmt.Println -var _ = os.Stderr - -func checkGuess(t *testing.T, wd, input, expected string) { - testWd = wd - inputPath = input - guessProjectPath() - - if projectPath != expected { - t.Errorf("Unexpected Project Path. \n Got: %q\nExpected: %q\n", projectPath, expected) - } - - reset() -} - -func reset() { - testWd = "" - inputPath = "" - projectPath = "" -} - -func TestProjectPath(t *testing.T) { - checkGuess(t, "", "github.com/spf13/hugo", getSrcPath()+"github.com/spf13/hugo") - checkGuess(t, "", "spf13/hugo", getSrcPath()+"github.com/spf13/hugo") - checkGuess(t, "", "/bar/foo", "/bar/foo") - checkGuess(t, "/bar/foo", "baz", "/bar/foo/baz") - checkGuess(t, "/bar/foo/cmd", "", "/bar/foo") - checkGuess(t, "/bar/foo/command", "", "/bar/foo") - checkGuess(t, "/bar/foo/commands", "", "/bar/foo") - checkGuess(t, "github.com/spf13/hugo/../hugo", "", "github.com/spf13/hugo") -} diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/init.go b/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/init.go deleted file mode 100644 index e3c0b45f0..000000000 --- a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/init.go +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright © 2015 Steve Francia . -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "fmt" - "os" - "strings" - - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -func init() { - RootCmd.AddCommand(initCmd) -} - -// initialize Command -var initCmd = &cobra.Command{ - Use: "init [name]", - Aliases: []string{"initialize", "initalise", "create"}, - Short: "Initalize a Cobra Application", - Long: `Initialize (cobra init) will create a new application, with a license -and the appropriate structure for a Cobra-based CLI application. - - * If a name is provided, it will be created in the current directory; - * If no name is provided, the current directory will be assumed; - * If a relative path is provided, it will be created inside $GOPATH - (e.g. github.com/spf13/hugo); - * If an absolute path is provided, it will be created; - * If the directory already exists but is empty, it will be used. - -Init will not use an exiting directory with contents.`, - - Run: func(cmd *cobra.Command, args []string) { - switch len(args) { - case 0: - inputPath = "" - - case 1: - inputPath = args[0] - - default: - er("init doesn't support more than 1 parameter") - } - guessProjectPath() - initalizePath(projectPath) - }, -} - -func initalizePath(path string) { - b, err := exists(path) - if err != nil { - er(err) - } - - if !b { // If path doesn't yet exist, create it - err := os.MkdirAll(path, os.ModePerm) - if err != nil { - er(err) - } - } else { // If path exists and is not empty don't use it - empty, err := exists(path) - if err != nil { - er(err) - } - if !empty { - er("Cobra will not create a new project in a non empty directory") - } - } - // We have a directory and it's empty.. Time to initialize it. - - createLicenseFile() - createMainFile() - createRootCmdFile() -} - -func createLicenseFile() { - lic := getLicense() - - template := lic.Text - - var data map[string]interface{} - data = make(map[string]interface{}) - - // Try to remove the email address, if any - data["copyright"] = strings.Split(copyrightLine(), " <")[0] - - err := writeTemplateToFile(ProjectPath(), "LICENSE", template, data) - _ = err - // if err != nil { - // er(err) - // } -} - -func createMainFile() { - lic := getLicense() - - template := `{{ comment .copyright }} -{{ comment .license }} - -package main - -import "{{ .importpath }}" - -func main() { - cmd.Execute() -} -` - var data map[string]interface{} - data = make(map[string]interface{}) - - data["copyright"] = copyrightLine() - data["license"] = lic.Header - data["importpath"] = guessImportPath() + "/" + guessCmdDir() - - err := writeTemplateToFile(ProjectPath(), "main.go", template, data) - _ = err - // if err != nil { - // er(err) - // } -} - -func createRootCmdFile() { - lic := getLicense() - - template := `{{ comment .copyright }} -{{ comment .license }} - -package cmd - -import ( - "fmt" - "os" - - "github.com/spf13/cobra" -{{ if .viper }} "github.com/spf13/viper" -{{ end }}) -{{if .viper}} -var cfgFile string -{{ end }} -// This represents the base command when called without any subcommands -var RootCmd = &cobra.Command{ - Use: "{{ .appName }}", - Short: "A brief description of your application", - Long: ` + "`" + `A longer description that spans multiple lines and likely contains -examples and usage of using your application. For example: - -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.` + "`" + `, -// Uncomment the following line if your bare application -// has an action associated with it: -// Run: func(cmd *cobra.Command, args []string) { }, -} - -// Execute adds all child commands to the root command sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - if err := RootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(-1) - } -} - -func init() { -{{ if .viper }} cobra.OnInitialize(initConfig) - -{{ end }} // Here you will define your flags and configuration settings. - // Cobra supports Persistent Flags, which, if defined here, - // will be global for your application. -{{ if .viper }} - RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.{{ .appName }}.yaml)") -{{ else }} - // RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.{{ .appName }}.yaml)") -{{ end }} // Cobra also supports local flags, which will only run - // when this action is called directly. - RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") -} -{{ if .viper }} -// initConfig reads in config file and ENV variables if set. -func initConfig() { - if cfgFile != "" { // enable ability to specify config file via flag - viper.SetConfigFile(cfgFile) - } - - viper.SetConfigName(".{{ .appName }}") // name of config file (without extension) - viper.AddConfigPath("$HOME") // adding home directory as first search path - viper.AutomaticEnv() // read in environment variables that match - - // If a config file is found, read it in. - if err := viper.ReadInConfig(); err == nil { - fmt.Println("Using config file:", viper.ConfigFileUsed()) - } -} -{{ end }}` - - var data map[string]interface{} - data = make(map[string]interface{}) - - data["copyright"] = copyrightLine() - data["license"] = lic.Header - data["appName"] = projectName() - data["viper"] = viper.GetBool("useViper") - - err := writeTemplateToFile(ProjectPath()+string(os.PathSeparator)+guessCmdDir(), "root.go", template, data) - if err != nil { - er(err) - } - - fmt.Println("Your Cobra application is ready at") - fmt.Println(ProjectPath()) - fmt.Println("Give it a try by going there and running `go run main.go`") - fmt.Println("Add commands to it by running `cobra add [cmdname]`") -} diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/licenses.go b/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/licenses.go deleted file mode 100644 index 5ad9c96ef..000000000 --- a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/licenses.go +++ /dev/null @@ -1,1133 +0,0 @@ -// Copyright © 2015 Steve Francia . -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Parts inspired by https://github.com/ryanuber/go-license - -package cmd - -import "strings" - -//Licenses contains all possible licenses a user can chose from -var Licenses map[string]License - -//License represents a software license agreement, containing the Name of -// the license, its possible matches (on the command line as given to cobra) -// the header to be used with each file on the file's creating, and the text -// of the license -type License struct { - Name string // The type of license in use - PossibleMatches []string // Similar names to guess - Text string // License text data - Header string // License header for source files -} - -// given a license name (in), try to match the license indicated -func matchLicense(in string) string { - for key, lic := range Licenses { - for _, match := range lic.PossibleMatches { - if strings.EqualFold(in, match) { - return key - } - } - } - return "" -} - -func init() { - Licenses = make(map[string]License) - - Licenses["apache"] = License{ - Name: "Apache 2.0", - PossibleMatches: []string{"apache", "apache20", "apache 2.0", "apache2.0", "apache-2.0"}, - Header: ` -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License.`, - Text: ` - 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. -`, - } - - Licenses["mit"] = License{ - Name: "Mit", - PossibleMatches: []string{"mit"}, - Header: ` -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.`, - Text: `The MIT License (MIT) - -{{ .copyright }} - -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. -`, - } - - Licenses["bsd"] = License{ - Name: "NewBSD", - PossibleMatches: []string{"bsd", "newbsd", "3 clause bsd"}, - Header: ` -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.`, - Text: `{{ .copyright }} -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. -`, - } - - Licenses["freebsd"] = License{ - Name: "Simplified BSD License", - PossibleMatches: []string{"freebsd", "simpbsd", "simple bsd", "2 clause bsd"}, - Header: ` -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. - -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.`, - Text: `{{ .copyright }} -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. - -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. -`, - } - - Licenses["gpl3"] = License{ - Name: "GNU General Public License 3.0", - PossibleMatches: []string{"gpl3", "gpl", "gnu gpl3", "gnu gpl"}, - Header: `{{ .copyright }} - - This file is part of {{ .appName }}. - - {{ .appName }} is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - {{ .appName }} is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with {{ .appName }}. If not, see . - `, - Text: ` GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type 'show c' for details. - -The hypothetical commands 'show w' and 'show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. -`, - } - - // Licenses["apache20"] = License{ - // Name: "Apache 2.0", - // PossibleMatches: []string{"apache", "apache20", ""}, - // Header: ` - // `, - // Text: ` - // `, - // } -} diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/root.go b/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/root.go deleted file mode 100644 index 610386ad7..000000000 --- a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/root.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright © 2015 Steve Francia . -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cmd - -import ( - "fmt" - "os" - - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -var cfgFile string -var userLicense string - -// This represents the base command when called without any subcommands -var RootCmd = &cobra.Command{ - Use: "cobra", - Short: "A generator for Cobra based Applications", - Long: `Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, -} - -//Execute adds all child commands to the root command sets flags appropriately. -func Execute() { - if err := RootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(-1) - } -} - -func init() { - cobra.OnInitialize(initConfig) - RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)") - RootCmd.PersistentFlags().StringVarP(&projectBase, "projectbase", "b", "", "base project directory, e.g. github.com/spf13/") - RootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "Author name for copyright attribution") - RootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "Name of license for the project (can provide `licensetext` in config)") - RootCmd.PersistentFlags().Bool("viper", true, "Use Viper for configuration") - viper.BindPFlag("author", RootCmd.PersistentFlags().Lookup("author")) - viper.BindPFlag("projectbase", RootCmd.PersistentFlags().Lookup("projectbase")) - viper.BindPFlag("useViper", RootCmd.PersistentFlags().Lookup("viper")) - viper.SetDefault("author", "NAME HERE ") - viper.SetDefault("license", "apache") - viper.SetDefault("licenseText", ` -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -`) -} - -// Read in config file and ENV variables if set. -func initConfig() { - if cfgFile != "" { // enable ability to specify config file via flag - viper.SetConfigFile(cfgFile) - } - - viper.SetConfigName(".cobra") // name of config file (without extension) - viper.AddConfigPath("$HOME") // adding home directory as first search path - viper.AutomaticEnv() // read in environment variables that match - - // If a config file is found, read it in. - if err := viper.ReadInConfig(); err == nil { - fmt.Println("Using config file:", viper.ConfigFileUsed()) - } -} diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/main.go b/Godeps/_workspace/src/github.com/spf13/cobra/cobra/main.go deleted file mode 100644 index c3a9d9cb0..000000000 --- a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/main.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright © 2015 Steve Francia . -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import "github.com/spf13/cobra/cobra/cmd" - -func main() { - cmd.Execute() -} diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/cobra_test.go b/Godeps/_workspace/src/github.com/spf13/cobra/cobra_test.go deleted file mode 100644 index e4138802e..000000000 --- a/Godeps/_workspace/src/github.com/spf13/cobra/cobra_test.go +++ /dev/null @@ -1,1163 +0,0 @@ -package cobra - -import ( - "bytes" - "fmt" - "os" - "reflect" - "runtime" - "strings" - "testing" - "text/template" - - "github.com/spf13/pflag" -) - -var _ = fmt.Println -var _ = os.Stderr - -var tp, te, tt, t1, tr []string -var rootPersPre, echoPre, echoPersPre, timesPersPre []string -var flagb1, flagb2, flagb3, flagbr, flagbp bool -var flags1, flags2a, flags2b, flags3, outs string -var flagi1, flagi2, flagi3, flagir int -var globalFlag1 bool -var flagEcho, rootcalled bool -var versionUsed int - -const strtwoParentHelp = "help message for parent flag strtwo" -const strtwoChildHelp = "help message for child flag strtwo" - -var cmdHidden = &Command{ - Use: "hide [secret string to print]", - Short: "Print anything to screen (if command is known)", - Long: `an absolutely utterly useless command for testing.`, - Run: func(cmd *Command, args []string) { - outs = "hidden" - }, - Hidden: true, -} - -var cmdPrint = &Command{ - Use: "print [string to print]", - Short: "Print anything to the screen", - Long: `an absolutely utterly useless command for testing.`, - Run: func(cmd *Command, args []string) { - tp = args - }, -} - -var cmdEcho = &Command{ - Use: "echo [string to echo]", - Aliases: []string{"say"}, - Short: "Echo anything to the screen", - Long: `an utterly useless command for testing.`, - Example: "Just run cobra-test echo", - PersistentPreRun: func(cmd *Command, args []string) { - echoPersPre = args - }, - PreRun: func(cmd *Command, args []string) { - echoPre = args - }, - Run: func(cmd *Command, args []string) { - te = args - }, -} - -var cmdEchoSub = &Command{ - Use: "echosub [string to print]", - Short: "second sub command for echo", - Long: `an absolutely utterly useless command for testing gendocs!.`, - Run: func(cmd *Command, args []string) { - }, -} - -var cmdDeprecated = &Command{ - Use: "deprecated [can't do anything here]", - Short: "A command which is deprecated", - Long: `an absolutely utterly useless command for testing deprecation!.`, - Deprecated: "Please use echo instead", - Run: func(cmd *Command, args []string) { - }, -} - -var cmdTimes = &Command{ - Use: "times [# times] [string to echo]", - SuggestFor: []string{"counts"}, - Short: "Echo anything to the screen more times", - Long: `a slightly useless command for testing.`, - PersistentPreRun: func(cmd *Command, args []string) { - timesPersPre = args - }, - Run: func(cmd *Command, args []string) { - tt = args - }, -} - -var cmdRootNoRun = &Command{ - Use: "cobra-test", - Short: "The root can run its own function", - Long: "The root description for help", - PersistentPreRun: func(cmd *Command, args []string) { - rootPersPre = args - }, -} - -var cmdRootSameName = &Command{ - Use: "print", - Short: "Root with the same name as a subcommand", - Long: "The root description for help", -} - -var cmdRootWithRun = &Command{ - Use: "cobra-test", - Short: "The root can run its own function", - Long: "The root description for help", - Run: func(cmd *Command, args []string) { - tr = args - rootcalled = true - }, -} - -var cmdSubNoRun = &Command{ - Use: "subnorun", - Short: "A subcommand without a Run function", - Long: "A long output about a subcommand without a Run function", -} - -var cmdVersion1 = &Command{ - Use: "version", - Short: "Print the version number", - Long: `First version of the version command`, - Run: func(cmd *Command, args []string) { - versionUsed = 1 - }, -} - -var cmdVersion2 = &Command{ - Use: "version", - Short: "Print the version number", - Long: `Second version of the version command`, - Run: func(cmd *Command, args []string) { - versionUsed = 2 - }, -} - -func flagInit() { - cmdEcho.ResetFlags() - cmdPrint.ResetFlags() - cmdTimes.ResetFlags() - cmdRootNoRun.ResetFlags() - cmdRootSameName.ResetFlags() - cmdRootWithRun.ResetFlags() - cmdSubNoRun.ResetFlags() - cmdRootNoRun.PersistentFlags().StringVarP(&flags2a, "strtwo", "t", "two", strtwoParentHelp) - cmdEcho.Flags().IntVarP(&flagi1, "intone", "i", 123, "help message for flag intone") - cmdTimes.Flags().IntVarP(&flagi2, "inttwo", "j", 234, "help message for flag inttwo") - cmdPrint.Flags().IntVarP(&flagi3, "intthree", "i", 345, "help message for flag intthree") - cmdEcho.PersistentFlags().StringVarP(&flags1, "strone", "s", "one", "help message for flag strone") - cmdEcho.PersistentFlags().BoolVarP(&flagbp, "persistentbool", "p", false, "help message for flag persistentbool") - cmdTimes.PersistentFlags().StringVarP(&flags2b, "strtwo", "t", "2", strtwoChildHelp) - cmdPrint.PersistentFlags().StringVarP(&flags3, "strthree", "s", "three", "help message for flag strthree") - cmdEcho.Flags().BoolVarP(&flagb1, "boolone", "b", true, "help message for flag boolone") - cmdTimes.Flags().BoolVarP(&flagb2, "booltwo", "c", false, "help message for flag booltwo") - cmdPrint.Flags().BoolVarP(&flagb3, "boolthree", "b", true, "help message for flag boolthree") - cmdVersion1.ResetFlags() - cmdVersion2.ResetFlags() -} - -func commandInit() { - cmdEcho.ResetCommands() - cmdPrint.ResetCommands() - cmdTimes.ResetCommands() - cmdRootNoRun.ResetCommands() - cmdRootSameName.ResetCommands() - cmdRootWithRun.ResetCommands() - cmdSubNoRun.ResetCommands() -} - -func initialize() *Command { - tt, tp, te = nil, nil, nil - rootPersPre, echoPre, echoPersPre, timesPersPre = nil, nil, nil, nil - - var c = cmdRootNoRun - flagInit() - commandInit() - return c -} - -func initializeWithSameName() *Command { - tt, tp, te = nil, nil, nil - rootPersPre, echoPre, echoPersPre, timesPersPre = nil, nil, nil, nil - var c = cmdRootSameName - flagInit() - commandInit() - return c -} - -func initializeWithRootCmd() *Command { - cmdRootWithRun.ResetCommands() - tt, tp, te, tr, rootcalled = nil, nil, nil, nil, false - flagInit() - cmdRootWithRun.Flags().BoolVarP(&flagbr, "boolroot", "b", false, "help message for flag boolroot") - cmdRootWithRun.Flags().IntVarP(&flagir, "introot", "i", 321, "help message for flag introot") - commandInit() - return cmdRootWithRun -} - -type resulter struct { - Error error - Output string - Command *Command -} - -func fullSetupTest(input string) resulter { - c := initializeWithRootCmd() - - return fullTester(c, input) -} - -func noRRSetupTestSilenced(input string) resulter { - c := initialize() - c.SilenceErrors = true - c.SilenceUsage = true - return fullTester(c, input) -} - -func noRRSetupTest(input string) resulter { - c := initialize() - - return fullTester(c, input) -} - -func rootOnlySetupTest(input string) resulter { - c := initializeWithRootCmd() - - return simpleTester(c, input) -} - -func simpleTester(c *Command, input string) resulter { - buf := new(bytes.Buffer) - // Testing flag with invalid input - c.SetOutput(buf) - c.SetArgs(strings.Split(input, " ")) - - err := c.Execute() - output := buf.String() - - return resulter{err, output, c} -} - -func simpleTesterC(c *Command, input string) resulter { - buf := new(bytes.Buffer) - // Testing flag with invalid input - c.SetOutput(buf) - c.SetArgs(strings.Split(input, " ")) - - cmd, err := c.ExecuteC() - output := buf.String() - - return resulter{err, output, cmd} -} - -func fullTester(c *Command, input string) resulter { - buf := new(bytes.Buffer) - // Testing flag with invalid input - c.SetOutput(buf) - cmdEcho.AddCommand(cmdTimes) - c.AddCommand(cmdPrint, cmdEcho, cmdSubNoRun, cmdDeprecated) - c.SetArgs(strings.Split(input, " ")) - - err := c.Execute() - output := buf.String() - - return resulter{err, output, c} -} - -func logErr(t *testing.T, found, expected string) { - out := new(bytes.Buffer) - - _, _, line, ok := runtime.Caller(2) - if ok { - fmt.Fprintf(out, "Line: %d ", line) - } - fmt.Fprintf(out, "Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - t.Errorf(out.String()) -} - -func checkStringContains(t *testing.T, found, expected string) { - if !strings.Contains(found, expected) { - logErr(t, found, expected) - } -} - -func checkResultContains(t *testing.T, x resulter, check string) { - checkStringContains(t, x.Output, check) -} - -func checkStringOmits(t *testing.T, found, expected string) { - if strings.Contains(found, expected) { - logErr(t, found, expected) - } -} - -func checkResultOmits(t *testing.T, x resulter, check string) { - checkStringOmits(t, x.Output, check) -} - -func checkOutputContains(t *testing.T, c *Command, check string) { - buf := new(bytes.Buffer) - c.SetOutput(buf) - c.Execute() - - if !strings.Contains(buf.String(), check) { - logErr(t, buf.String(), check) - } -} - -func TestSingleCommand(t *testing.T) { - noRRSetupTest("print one two") - - if te != nil || tt != nil { - t.Error("Wrong command called") - } - if tp == nil { - t.Error("Wrong command called") - } - if strings.Join(tp, " ") != "one two" { - t.Error("Command didn't parse correctly") - } -} - -func TestChildCommand(t *testing.T) { - noRRSetupTest("echo times one two") - - if te != nil || tp != nil { - t.Error("Wrong command called") - } - if tt == nil { - t.Error("Wrong command called") - } - if strings.Join(tt, " ") != "one two" { - t.Error("Command didn't parse correctly") - } -} - -func TestCommandAlias(t *testing.T) { - noRRSetupTest("say times one two") - - if te != nil || tp != nil { - t.Error("Wrong command called") - } - if tt == nil { - t.Error("Wrong command called") - } - if strings.Join(tt, " ") != "one two" { - t.Error("Command didn't parse correctly") - } -} - -func TestPrefixMatching(t *testing.T) { - EnablePrefixMatching = true - noRRSetupTest("ech times one two") - - if te != nil || tp != nil { - t.Error("Wrong command called") - } - if tt == nil { - t.Error("Wrong command called") - } - if strings.Join(tt, " ") != "one two" { - t.Error("Command didn't parse correctly") - } - - EnablePrefixMatching = false -} - -func TestNoPrefixMatching(t *testing.T) { - EnablePrefixMatching = false - - noRRSetupTest("ech times one two") - - if !(tt == nil && te == nil && tp == nil) { - t.Error("Wrong command called") - } -} - -func TestAliasPrefixMatching(t *testing.T) { - EnablePrefixMatching = true - noRRSetupTest("sa times one two") - - if te != nil || tp != nil { - t.Error("Wrong command called") - } - if tt == nil { - t.Error("Wrong command called") - } - if strings.Join(tt, " ") != "one two" { - t.Error("Command didn't parse correctly") - } - EnablePrefixMatching = false -} - -func TestChildSameName(t *testing.T) { - c := initializeWithSameName() - c.AddCommand(cmdPrint, cmdEcho) - c.SetArgs(strings.Split("print one two", " ")) - c.Execute() - - if te != nil || tt != nil { - t.Error("Wrong command called") - } - if tp == nil { - t.Error("Wrong command called") - } - if strings.Join(tp, " ") != "one two" { - t.Error("Command didn't parse correctly") - } -} - -func TestGrandChildSameName(t *testing.T) { - c := initializeWithSameName() - cmdTimes.AddCommand(cmdPrint) - c.AddCommand(cmdTimes) - c.SetArgs(strings.Split("times print one two", " ")) - c.Execute() - - if te != nil || tt != nil { - t.Error("Wrong command called") - } - if tp == nil { - t.Error("Wrong command called") - } - if strings.Join(tp, " ") != "one two" { - t.Error("Command didn't parse correctly") - } -} - -func TestFlagLong(t *testing.T) { - noRRSetupTest("echo --intone=13 something -- here") - - if cmdEcho.ArgsLenAtDash() != 1 { - t.Errorf("expected argsLenAtDash: %d but got %d", 1, cmdRootNoRun.ArgsLenAtDash()) - } - if strings.Join(te, " ") != "something here" { - t.Errorf("flags didn't leave proper args remaining..%s given", te) - } - if flagi1 != 13 { - t.Errorf("int flag didn't get correct value, had %d", flagi1) - } - if flagi2 != 234 { - t.Errorf("default flag value changed, 234 expected, %d given", flagi2) - } -} - -func TestFlagShort(t *testing.T) { - noRRSetupTest("echo -i13 -- something here") - - if cmdEcho.ArgsLenAtDash() != 0 { - t.Errorf("expected argsLenAtDash: %d but got %d", 0, cmdRootNoRun.ArgsLenAtDash()) - } - if strings.Join(te, " ") != "something here" { - t.Errorf("flags didn't leave proper args remaining..%s given", te) - } - if flagi1 != 13 { - t.Errorf("int flag didn't get correct value, had %d", flagi1) - } - if flagi2 != 234 { - t.Errorf("default flag value changed, 234 expected, %d given", flagi2) - } - - noRRSetupTest("echo -i 13 something here") - - if strings.Join(te, " ") != "something here" { - t.Errorf("flags didn't leave proper args remaining..%s given", te) - } - if flagi1 != 13 { - t.Errorf("int flag didn't get correct value, had %d", flagi1) - } - if flagi2 != 234 { - t.Errorf("default flag value changed, 234 expected, %d given", flagi2) - } - - noRRSetupTest("print -i99 one two") - - if strings.Join(tp, " ") != "one two" { - t.Errorf("flags didn't leave proper args remaining..%s given", tp) - } - if flagi3 != 99 { - t.Errorf("int flag didn't get correct value, had %d", flagi3) - } - if flagi1 != 123 { - t.Errorf("default flag value changed on different command with same shortname, 234 expected, %d given", flagi2) - } -} - -func TestChildCommandFlags(t *testing.T) { - noRRSetupTest("echo times -j 99 one two") - - if strings.Join(tt, " ") != "one two" { - t.Errorf("flags didn't leave proper args remaining..%s given", tt) - } - - // Testing with flag that shouldn't be persistent - r := noRRSetupTest("echo times -j 99 -i77 one two") - - if r.Error == nil { - t.Errorf("invalid flag should generate error") - } - - if !strings.Contains(r.Error.Error(), "unknown shorthand") { - t.Errorf("Wrong error message displayed, \n %s", r.Error) - } - - if flagi2 != 99 { - t.Errorf("flag value should be 99, %d given", flagi2) - } - - if flagi1 != 123 { - t.Errorf("unset flag should have default value, expecting 123, given %d", flagi1) - } - - // Testing with flag only existing on child - r = noRRSetupTest("echo -j 99 -i77 one two") - - if r.Error == nil { - t.Errorf("invalid flag should generate error") - } - if !strings.Contains(r.Error.Error(), "unknown shorthand flag") { - t.Errorf("Wrong error message displayed, \n %s", r.Error) - } - - // Testing with persistent flag overwritten by child - noRRSetupTest("echo times --strtwo=child one two") - - if flags2b != "child" { - t.Errorf("flag value should be child, %s given", flags2b) - } - - if flags2a != "two" { - t.Errorf("unset flag should have default value, expecting two, given %s", flags2a) - } - - // Testing flag with invalid input - r = noRRSetupTest("echo -i10E") - - if r.Error == nil { - t.Errorf("invalid input should generate error") - } - if !strings.Contains(r.Error.Error(), "invalid argument \"10E\" for i10E") { - t.Errorf("Wrong error message displayed, \n %s", r.Error) - } -} - -func TestTrailingCommandFlags(t *testing.T) { - x := fullSetupTest("echo two -x") - - if x.Error == nil { - t.Errorf("invalid flag should generate error") - } -} - -func TestInvalidSubcommandFlags(t *testing.T) { - cmd := initializeWithRootCmd() - cmd.AddCommand(cmdTimes) - - result := simpleTester(cmd, "times --inttwo=2 --badflag=bar") - // given that we are not checking here result.Error we check for - // stock usage message - checkResultContains(t, result, "cobra-test times [# times]") - if strings.Contains(result.Error.Error(), "unknown flag: --inttwo") { - t.Errorf("invalid --badflag flag shouldn't fail on 'unknown' --inttwo flag") - } - -} - -func TestSubcommandExecuteC(t *testing.T) { - cmd := initializeWithRootCmd() - double := &Command{ - Use: "double message", - Run: func(c *Command, args []string) { - msg := strings.Join(args, " ") - c.Println(msg, msg) - }, - } - - echo := &Command{ - Use: "echo message", - Run: func(c *Command, args []string) { - msg := strings.Join(args, " ") - c.Println(msg, msg) - }, - } - - cmd.AddCommand(double, echo) - - result := simpleTesterC(cmd, "double hello world") - checkResultContains(t, result, "hello world hello world") - - if result.Command.Name() != "double" { - t.Errorf("invalid cmd returned from ExecuteC: should be 'double' but got %s", result.Command.Name()) - } - - result = simpleTesterC(cmd, "echo msg to be echoed") - checkResultContains(t, result, "msg to be echoed") - - if result.Command.Name() != "echo" { - t.Errorf("invalid cmd returned from ExecuteC: should be 'echo' but got %s", result.Command.Name()) - } -} - -func TestSubcommandArgEvaluation(t *testing.T) { - cmd := initializeWithRootCmd() - - first := &Command{ - Use: "first", - Run: func(cmd *Command, args []string) { - }, - } - cmd.AddCommand(first) - - second := &Command{ - Use: "second", - Run: func(cmd *Command, args []string) { - fmt.Fprintf(cmd.Out(), "%v", args) - }, - } - first.AddCommand(second) - - result := simpleTester(cmd, "first second first third") - - expectedOutput := fmt.Sprintf("%v", []string{"first third"}) - if result.Output != expectedOutput { - t.Errorf("exptected %v, got %v", expectedOutput, result.Output) - } -} - -func TestPersistentFlags(t *testing.T) { - fullSetupTest("echo -s something -p more here") - - // persistentFlag should act like normal flag on its own command - if strings.Join(te, " ") != "more here" { - t.Errorf("flags didn't leave proper args remaining..%s given", te) - } - if flags1 != "something" { - t.Errorf("string flag didn't get correct value, had %v", flags1) - } - if !flagbp { - t.Errorf("persistent bool flag not parsed correctly. Expected true, had %v", flagbp) - } - - // persistentFlag should act like normal flag on its own command - fullSetupTest("echo times -s again -c -p test here") - - if strings.Join(tt, " ") != "test here" { - t.Errorf("flags didn't leave proper args remaining..%s given", tt) - } - - if flags1 != "again" { - t.Errorf("string flag didn't get correct value, had %v", flags1) - } - - if !flagb2 { - t.Errorf("local flag not parsed correctly. Expected true, had %v", flagb2) - } - if !flagbp { - t.Errorf("persistent bool flag not parsed correctly. Expected true, had %v", flagbp) - } -} - -func TestHelpCommand(t *testing.T) { - x := fullSetupTest("help") - checkResultContains(t, x, cmdRootWithRun.Long) - - x = fullSetupTest("help echo") - checkResultContains(t, x, cmdEcho.Long) - - x = fullSetupTest("help echo times") - checkResultContains(t, x, cmdTimes.Long) -} - -func TestChildCommandHelp(t *testing.T) { - c := noRRSetupTest("print --help") - checkResultContains(t, c, strtwoParentHelp) - r := noRRSetupTest("echo times --help") - checkResultContains(t, r, strtwoChildHelp) -} - -func TestNonRunChildHelp(t *testing.T) { - x := noRRSetupTest("subnorun") - checkResultContains(t, x, cmdSubNoRun.Long) -} - -func TestRunnableRootCommand(t *testing.T) { - x := fullSetupTest("") - - if rootcalled != true { - t.Errorf("Root Function was not called\n out:%v", x.Error) - } -} - -func TestVisitParents(t *testing.T) { - c := &Command{Use: "app"} - sub := &Command{Use: "sub"} - dsub := &Command{Use: "dsub"} - sub.AddCommand(dsub) - c.AddCommand(sub) - total := 0 - add := func(x *Command) { - total++ - } - sub.VisitParents(add) - if total != 1 { - t.Errorf("Should have visited 1 parent but visited %d", total) - } - - total = 0 - dsub.VisitParents(add) - if total != 2 { - t.Errorf("Should have visited 2 parent but visited %d", total) - } - - total = 0 - c.VisitParents(add) - if total != 0 { - t.Errorf("Should have not visited any parent but visited %d", total) - } -} - -func TestRunnableRootCommandNilInput(t *testing.T) { - empty_arg := make([]string, 0) - c := initializeWithRootCmd() - - buf := new(bytes.Buffer) - // Testing flag with invalid input - c.SetOutput(buf) - cmdEcho.AddCommand(cmdTimes) - c.AddCommand(cmdPrint, cmdEcho) - c.SetArgs(empty_arg) - - err := c.Execute() - if err != nil { - t.Errorf("Execute() failed with %v", err) - } - - if rootcalled != true { - t.Errorf("Root Function was not called") - } -} - -func TestRunnableRootCommandEmptyInput(t *testing.T) { - args := make([]string, 3) - args[0] = "" - args[1] = "--introot=12" - args[2] = "" - c := initializeWithRootCmd() - - buf := new(bytes.Buffer) - // Testing flag with invalid input - c.SetOutput(buf) - cmdEcho.AddCommand(cmdTimes) - c.AddCommand(cmdPrint, cmdEcho) - c.SetArgs(args) - - c.Execute() - - if rootcalled != true { - t.Errorf("Root Function was not called.\n\nOutput was:\n\n%s\n", buf) - } -} - -func TestInvalidSubcommandWhenArgsAllowed(t *testing.T) { - fullSetupTest("echo invalid-sub") - - if te[0] != "invalid-sub" { - t.Errorf("Subcommand didn't work...") - } -} - -func TestRootFlags(t *testing.T) { - fullSetupTest("-i 17 -b") - - if flagbr != true { - t.Errorf("flag value should be true, %v given", flagbr) - } - - if flagir != 17 { - t.Errorf("flag value should be 17, %d given", flagir) - } -} - -func TestRootHelp(t *testing.T) { - x := fullSetupTest("--help") - - checkResultContains(t, x, "Available Commands:") - checkResultContains(t, x, "for more information about a command") - - if strings.Contains(x.Output, "unknown flag: --help") { - t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) - } - - if strings.Contains(x.Output, cmdEcho.Use) { - t.Errorf("--help shouldn't display subcommand's usage, Got: \n %s", x.Output) - } - - x = fullSetupTest("echo --help") - - if strings.Contains(x.Output, cmdTimes.Use) { - t.Errorf("--help shouldn't display subsubcommand's usage, Got: \n %s", x.Output) - } - - checkResultContains(t, x, "Available Commands:") - checkResultContains(t, x, "for more information about a command") - - if strings.Contains(x.Output, "unknown flag: --help") { - t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) - } - -} - -func TestFlagAccess(t *testing.T) { - initialize() - - local := cmdTimes.LocalFlags() - inherited := cmdTimes.InheritedFlags() - - for _, f := range []string{"inttwo", "strtwo", "booltwo"} { - if local.Lookup(f) == nil { - t.Errorf("LocalFlags expected to contain %s, Got: nil", f) - } - } - if inherited.Lookup("strone") == nil { - t.Errorf("InheritedFlags expected to contain strone, Got: nil") - } - if inherited.Lookup("strtwo") != nil { - t.Errorf("InheritedFlags shouldn not contain overwritten flag strtwo") - - } -} - -func TestNoNRunnableRootCommandNilInput(t *testing.T) { - args := make([]string, 0) - c := initialize() - - buf := new(bytes.Buffer) - // Testing flag with invalid input - c.SetOutput(buf) - cmdEcho.AddCommand(cmdTimes) - c.AddCommand(cmdPrint, cmdEcho) - c.SetArgs(args) - - c.Execute() - - if !strings.Contains(buf.String(), cmdRootNoRun.Long) { - t.Errorf("Expected to get help output, Got: \n %s", buf) - } -} - -func TestRootNoCommandHelp(t *testing.T) { - x := rootOnlySetupTest("--help") - - checkResultOmits(t, x, "Available Commands:") - checkResultOmits(t, x, "for more information about a command") - - if strings.Contains(x.Output, "unknown flag: --help") { - t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) - } - - x = rootOnlySetupTest("echo --help") - - checkResultOmits(t, x, "Available Commands:") - checkResultOmits(t, x, "for more information about a command") - - if strings.Contains(x.Output, "unknown flag: --help") { - t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) - } -} - -func TestRootUnknownCommand(t *testing.T) { - r := noRRSetupTest("bogus") - s := "Error: unknown command \"bogus\" for \"cobra-test\"\nRun 'cobra-test --help' for usage.\n" - - if r.Output != s { - t.Errorf("Unexpected response.\nExpecting to be:\n %q\nGot:\n %q\n", s, r.Output) - } - - r = noRRSetupTest("--strtwo=a bogus") - if r.Output != s { - t.Errorf("Unexpected response.\nExpecting to be:\n %q\nGot:\n %q\n", s, r.Output) - } -} - -func TestRootUnknownCommandSilenced(t *testing.T) { - r := noRRSetupTestSilenced("bogus") - s := "Run 'cobra-test --help' for usage.\n" - - if r.Output != "" { - t.Errorf("Unexpected response.\nExpecting to be: \n\"\"\n Got:\n %q\n", s, r.Output) - } - - r = noRRSetupTestSilenced("--strtwo=a bogus") - if r.Output != "" { - t.Errorf("Unexpected response.\nExpecting to be:\n\"\"\nGot:\n %q\n", s, r.Output) - } -} - -func TestRootSuggestions(t *testing.T) { - outputWithSuggestions := "Error: unknown command \"%s\" for \"cobra-test\"\n\nDid you mean this?\n\t%s\n\nRun 'cobra-test --help' for usage.\n" - outputWithoutSuggestions := "Error: unknown command \"%s\" for \"cobra-test\"\nRun 'cobra-test --help' for usage.\n" - - cmd := initializeWithRootCmd() - cmd.AddCommand(cmdTimes) - - tests := map[string]string{ - "time": "times", - "tiems": "times", - "tims": "times", - "timeS": "times", - "rimes": "times", - "ti": "times", - "t": "times", - "timely": "times", - "ri": "", - "timezone": "", - "foo": "", - "counts": "times", - } - - for typo, suggestion := range tests { - for _, suggestionsDisabled := range []bool{false, true} { - cmd.DisableSuggestions = suggestionsDisabled - result := simpleTester(cmd, typo) - expected := "" - if len(suggestion) == 0 || suggestionsDisabled { - expected = fmt.Sprintf(outputWithoutSuggestions, typo) - } else { - expected = fmt.Sprintf(outputWithSuggestions, typo, suggestion) - } - if result.Output != expected { - t.Errorf("Unexpected response.\nExpecting to be:\n %q\nGot:\n %q\n", expected, result.Output) - } - } - } -} - -func TestFlagsBeforeCommand(t *testing.T) { - // short without space - x := fullSetupTest("-i10 echo") - if x.Error != nil { - t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error) - } - - // short (int) with equals - // It appears that pflags doesn't support this... - // Commenting out until support can be added - - //x = noRRSetupTest("echo -i=10") - //if x.Error != nil { - //t.Errorf("Valid Input shouldn't have errors, got:\n %s", x.Error) - //} - - // long with equals - x = noRRSetupTest("--intone=123 echo one two") - if x.Error != nil { - t.Errorf("Valid Input shouldn't have errors, got:\n %s", x.Error) - } - - // With parsing error properly reported - x = fullSetupTest("-i10E echo") - if !strings.Contains(x.Error.Error(), "invalid argument \"10E\" for i10E") { - t.Errorf("Wrong error message displayed, \n %s", x.Error) - } - - //With quotes - x = fullSetupTest("-s=\"walking\" echo") - if x.Error != nil { - t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error) - } - - //With quotes and space - x = fullSetupTest("-s=\"walking fast\" echo") - if x.Error != nil { - t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error) - } - - //With inner quote - x = fullSetupTest("-s=\"walking \\\"Inner Quote\\\" fast\" echo") - if x.Error != nil { - t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error) - } - - //With quotes and space - x = fullSetupTest("-s=\"walking \\\"Inner Quote\\\" fast\" echo") - if x.Error != nil { - t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error) - } - -} - -func TestRemoveCommand(t *testing.T) { - versionUsed = 0 - c := initializeWithRootCmd() - c.AddCommand(cmdVersion1) - c.RemoveCommand(cmdVersion1) - x := fullTester(c, "version") - if x.Error == nil { - t.Errorf("Removed command should not have been called\n") - return - } -} - -func TestCommandWithoutSubcommands(t *testing.T) { - c := initializeWithRootCmd() - - x := simpleTester(c, "") - if x.Error != nil { - t.Errorf("Calling command without subcommands should not have error: %v", x.Error) - return - } -} - -func TestCommandWithoutSubcommandsWithArg(t *testing.T) { - c := initializeWithRootCmd() - expectedArgs := []string{"arg"} - - x := simpleTester(c, "arg") - if x.Error != nil { - t.Errorf("Calling command without subcommands but with arg should not have error: %v", x.Error) - return - } - if !reflect.DeepEqual(expectedArgs, tr) { - t.Errorf("Calling command without subcommands but with arg has wrong args: expected: %v, actual: %v", expectedArgs, tr) - return - } -} - -func TestReplaceCommandWithRemove(t *testing.T) { - versionUsed = 0 - c := initializeWithRootCmd() - c.AddCommand(cmdVersion1) - c.RemoveCommand(cmdVersion1) - c.AddCommand(cmdVersion2) - x := fullTester(c, "version") - if x.Error != nil { - t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error) - return - } - if versionUsed == 1 { - t.Errorf("Removed command shouldn't be called\n") - } - if versionUsed != 2 { - t.Errorf("Replacing command should have been called but didn't\n") - } -} - -func TestDeprecatedSub(t *testing.T) { - c := fullSetupTest("deprecated") - - checkResultContains(t, c, cmdDeprecated.Deprecated) -} - -func TestPreRun(t *testing.T) { - noRRSetupTest("echo one two") - if echoPre == nil || echoPersPre == nil { - t.Error("PreRun or PersistentPreRun not called") - } - if rootPersPre != nil || timesPersPre != nil { - t.Error("Wrong *Pre functions called!") - } - - noRRSetupTest("echo times one two") - if timesPersPre == nil { - t.Error("PreRun or PersistentPreRun not called") - } - if echoPre != nil || echoPersPre != nil || rootPersPre != nil { - t.Error("Wrong *Pre functions called!") - } - - noRRSetupTest("print one two") - if rootPersPre == nil { - t.Error("Parent PersistentPreRun not called but should not have been") - } - if echoPre != nil || echoPersPre != nil || timesPersPre != nil { - t.Error("Wrong *Pre functions called!") - } -} - -// Check if cmdEchoSub gets PersistentPreRun from rootCmd even if is added last -func TestPeristentPreRunPropagation(t *testing.T) { - rootCmd := initialize() - - // First add the cmdEchoSub to cmdPrint - cmdPrint.AddCommand(cmdEchoSub) - // Now add cmdPrint to rootCmd - rootCmd.AddCommand(cmdPrint) - - rootCmd.SetArgs(strings.Split("print echosub lala", " ")) - rootCmd.Execute() - - if rootPersPre == nil || len(rootPersPre) == 0 || rootPersPre[0] != "lala" { - t.Error("RootCmd PersistentPreRun not called but should have been") - } -} - -func TestGlobalNormFuncPropagation(t *testing.T) { - normFunc := func(f *pflag.FlagSet, name string) pflag.NormalizedName { - return pflag.NormalizedName(name) - } - - rootCmd := initialize() - rootCmd.SetGlobalNormalizationFunc(normFunc) - if reflect.ValueOf(normFunc) != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()) { - t.Error("rootCmd seems to have a wrong normalization function") - } - - // First add the cmdEchoSub to cmdPrint - cmdPrint.AddCommand(cmdEchoSub) - if cmdPrint.GlobalNormalizationFunc() != nil && cmdEchoSub.GlobalNormalizationFunc() != nil { - t.Error("cmdPrint and cmdEchoSub should had no normalization functions") - } - - // Now add cmdPrint to rootCmd - rootCmd.AddCommand(cmdPrint) - if reflect.ValueOf(cmdPrint.GlobalNormalizationFunc()).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() || - reflect.ValueOf(cmdEchoSub.GlobalNormalizationFunc()).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() { - t.Error("cmdPrint and cmdEchoSub should had the normalization function of rootCmd") - } -} - -func TestFlagOnPflagCommandLine(t *testing.T) { - flagName := "flagOnCommandLine" - pflag.CommandLine.String(flagName, "", "about my flag") - r := fullSetupTest("--help") - - checkResultContains(t, r, flagName) -} - -func TestAddTemplateFunctions(t *testing.T) { - AddTemplateFunc("t", func() bool { return true }) - AddTemplateFuncs(template.FuncMap{ - "f": func() bool { return false }, - "h": func() string { return "Hello," }, - "w": func() string { return "world." }}) - - const usage = "Hello, world." - - c := &Command{} - c.SetUsageTemplate(`{{if t}}{{h}}{{end}}{{if f}}{{h}}{{end}} {{w}}`) - - if us := c.UsageString(); us != usage { - t.Errorf("c.UsageString() != \"%s\", is \"%s\"", usage, us) - } -} - -func TestUsageIsNotPrintedTwice(t *testing.T) { - var cmd = &Command{Use: "root"} - var sub = &Command{Use: "sub"} - cmd.AddCommand(sub) - - r := simpleTester(cmd, "") - if strings.Count(r.Output, "Usage:") != 1 { - t.Error("Usage output is not printed exactly once") - } -} diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/command_test.go b/Godeps/_workspace/src/github.com/spf13/cobra/command_test.go deleted file mode 100644 index 43ed7a34f..000000000 --- a/Godeps/_workspace/src/github.com/spf13/cobra/command_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package cobra - -import ( - "reflect" - "testing" -) - -// test to ensure hidden commands run as intended -func TestHiddenCommandExecutes(t *testing.T) { - - // ensure that outs does not already equal what the command will be setting it - // to, if it did this test would not actually be testing anything... - if outs == "hidden" { - t.Errorf("outs should NOT EQUAL hidden") - } - - cmdHidden.Execute() - - // upon running the command, the value of outs should now be 'hidden' - if outs != "hidden" { - t.Errorf("Hidden command failed to run!") - } -} - -// test to ensure hidden commands do not show up in usage/help text -func TestHiddenCommandIsHidden(t *testing.T) { - if cmdHidden.IsAvailableCommand() { - t.Errorf("Hidden command found!") - } -} - -func TestStripFlags(t *testing.T) { - tests := []struct { - input []string - output []string - }{ - { - []string{"foo", "bar"}, - []string{"foo", "bar"}, - }, - { - []string{"foo", "--bar", "-b"}, - []string{"foo"}, - }, - { - []string{"-b", "foo", "--bar", "bar"}, - []string{}, - }, - { - []string{"-i10", "echo"}, - []string{"echo"}, - }, - { - []string{"-i=10", "echo"}, - []string{"echo"}, - }, - { - []string{"--int=100", "echo"}, - []string{"echo"}, - }, - { - []string{"-ib", "echo", "-bfoo", "baz"}, - []string{"echo", "baz"}, - }, - { - []string{"-i=baz", "bar", "-i", "foo", "blah"}, - []string{"bar", "blah"}, - }, - { - []string{"--int=baz", "-bbar", "-i", "foo", "blah"}, - []string{"blah"}, - }, - { - []string{"--cat", "bar", "-i", "foo", "blah"}, - []string{"bar", "blah"}, - }, - { - []string{"-c", "bar", "-i", "foo", "blah"}, - []string{"bar", "blah"}, - }, - { - []string{"--persist", "bar"}, - []string{"bar"}, - }, - { - []string{"-p", "bar"}, - []string{"bar"}, - }, - } - - cmdPrint := &Command{ - Use: "print [string to print]", - Short: "Print anything to the screen", - Long: `an utterly useless command for testing.`, - Run: func(cmd *Command, args []string) { - tp = args - }, - } - - var flagi int - var flagstr string - var flagbool bool - cmdPrint.PersistentFlags().BoolVarP(&flagbool, "persist", "p", false, "help for persistent one") - cmdPrint.Flags().IntVarP(&flagi, "int", "i", 345, "help message for flag int") - cmdPrint.Flags().StringVarP(&flagstr, "bar", "b", "bar", "help message for flag string") - cmdPrint.Flags().BoolVarP(&flagbool, "cat", "c", false, "help message for flag bool") - - for _, test := range tests { - output := stripFlags(test.input, cmdPrint) - if !reflect.DeepEqual(test.output, output) { - t.Errorf("expected: %v, got: %v", test.output, output) - } - } -} diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/examples_test.go b/Godeps/_workspace/src/github.com/spf13/cobra/examples_test.go deleted file mode 100644 index 10da9850e..000000000 --- a/Godeps/_workspace/src/github.com/spf13/cobra/examples_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package cobra_test - -import ( - "bytes" - "fmt" - - "github.com/spf13/cobra" -) - -func ExampleCommand_GenManTree() { - cmd := &cobra.Command{ - Use: "test", - Short: "my test program", - } - header := &cobra.GenManHeader{ - Title: "MINE", - Section: "3", - } - cmd.GenManTree(header, "/tmp") -} - -func ExampleCommand_GenMan() { - cmd := &cobra.Command{ - Use: "test", - Short: "my test program", - } - header := &cobra.GenManHeader{ - Title: "MINE", - Section: "3", - } - out := new(bytes.Buffer) - cmd.GenMan(header, out) - fmt.Print(out.String()) -} diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/man_docs_test.go b/Godeps/_workspace/src/github.com/spf13/cobra/man_docs_test.go deleted file mode 100644 index ab4030c33..000000000 --- a/Godeps/_workspace/src/github.com/spf13/cobra/man_docs_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package cobra - -import ( - "bytes" - "fmt" - "os" - "strings" - "testing" -) - -var _ = fmt.Println -var _ = os.Stderr - -func translate(in string) string { - return strings.Replace(in, "-", "\\-", -1) -} - -func TestGenManDoc(t *testing.T) { - c := initializeWithRootCmd() - // Need two commands to run the command alphabetical sort - cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated) - c.AddCommand(cmdPrint, cmdEcho) - cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp) - - out := new(bytes.Buffer) - - header := &GenManHeader{ - Title: "Project", - Section: "2", - } - // We generate on a subcommand so we have both subcommands and parents - cmdEcho.GenMan(header, out) - found := out.String() - - // Make sure parent has - in CommandPath() in SEE ALSO: - parentPath := cmdEcho.Parent().CommandPath() - dashParentPath := strings.Replace(parentPath, " ", "-", -1) - expected := translate(dashParentPath) - expected = expected + "(" + header.Section + ")" - checkStringContains(t, found, expected) - - // Our description - expected = translate(cmdEcho.Name()) - checkStringContains(t, found, expected) - - // Better have our example - expected = translate(cmdEcho.Name()) - checkStringContains(t, found, expected) - - // A local flag - expected = "boolone" - checkStringContains(t, found, expected) - - // persistent flag on parent - expected = "rootflag" - checkStringContains(t, found, expected) - - // We better output info about our parent - expected = translate(cmdRootWithRun.Name()) - checkStringContains(t, found, expected) - - // And about subcommands - expected = translate(cmdEchoSub.Name()) - checkStringContains(t, found, expected) - - unexpected := translate(cmdDeprecated.Name()) - checkStringOmits(t, found, unexpected) - - // auto generated - expected = translate("Auto generated") - checkStringContains(t, found, expected) -} - -func TestGenManNoGenTag(t *testing.T) { - - c := initializeWithRootCmd() - // Need two commands to run the command alphabetical sort - cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated) - c.AddCommand(cmdPrint, cmdEcho) - cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp) - cmdEcho.DisableAutoGenTag = true - out := new(bytes.Buffer) - - header := &GenManHeader{ - Title: "Project", - Section: "2", - } - // We generate on a subcommand so we have both subcommands and parents - cmdEcho.GenMan(header, out) - found := out.String() - - unexpected := translate("#HISTORY") - checkStringOmits(t, found, unexpected) -} diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/md_docs_test.go b/Godeps/_workspace/src/github.com/spf13/cobra/md_docs_test.go deleted file mode 100644 index 82f5452ce..000000000 --- a/Godeps/_workspace/src/github.com/spf13/cobra/md_docs_test.go +++ /dev/null @@ -1,84 +0,0 @@ -package cobra - -import ( - "bytes" - "fmt" - "os" - "strings" - "testing" -) - -var _ = fmt.Println -var _ = os.Stderr - -func TestGenMdDoc(t *testing.T) { - c := initializeWithRootCmd() - // Need two commands to run the command alphabetical sort - cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated) - c.AddCommand(cmdPrint, cmdEcho) - cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp) - - out := new(bytes.Buffer) - - // We generate on s subcommand so we have both subcommands and parents - GenMarkdown(cmdEcho, out) - found := out.String() - - // Our description - expected := cmdEcho.Long - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - // Better have our example - expected = cmdEcho.Example - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - // A local flag - expected = "boolone" - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - // persistent flag on parent - expected = "rootflag" - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - // We better output info about our parent - expected = cmdRootWithRun.Short - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - // And about subcommands - expected = cmdEchoSub.Short - if !strings.Contains(found, expected) { - t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) - } - - unexpected := cmdDeprecated.Short - if strings.Contains(found, unexpected) { - t.Errorf("Unexpected response.\nFound: %v\nBut should not have!!\n", unexpected) - } -} - -func TestGenMdNoTag(t *testing.T) { - c := initializeWithRootCmd() - // Need two commands to run the command alphabetical sort - cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated) - c.AddCommand(cmdPrint, cmdEcho) - c.DisableAutoGenTag = true - cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp) - out := new(bytes.Buffer) - - GenMarkdown(c, out) - found := out.String() - - unexpected := "Auto generated" - checkStringOmits(t, found, unexpected) - -} diff --git a/Godeps/_workspace/src/github.com/spf13/pflag/bool_test.go b/Godeps/_workspace/src/github.com/spf13/pflag/bool_test.go deleted file mode 100644 index afd25ae2f..000000000 --- a/Godeps/_workspace/src/github.com/spf13/pflag/bool_test.go +++ /dev/null @@ -1,180 +0,0 @@ -// 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 pflag - -import ( - "bytes" - "fmt" - "strconv" - "testing" -) - -// This value can be a boolean ("true", "false") or "maybe" -type triStateValue int - -const ( - triStateFalse triStateValue = 0 - triStateTrue triStateValue = 1 - triStateMaybe triStateValue = 2 -) - -const strTriStateMaybe = "maybe" - -func (v *triStateValue) IsBoolFlag() bool { - return true -} - -func (v *triStateValue) Get() interface{} { - return triStateValue(*v) -} - -func (v *triStateValue) Set(s string) error { - if s == strTriStateMaybe { - *v = triStateMaybe - return nil - } - boolVal, err := strconv.ParseBool(s) - if boolVal { - *v = triStateTrue - } else { - *v = triStateFalse - } - return err -} - -func (v *triStateValue) String() string { - if *v == triStateMaybe { - return strTriStateMaybe - } - return fmt.Sprintf("%v", bool(*v == triStateTrue)) -} - -// The type of the flag as required by the pflag.Value interface -func (v *triStateValue) Type() string { - return "version" -} - -func setUpFlagSet(tristate *triStateValue) *FlagSet { - f := NewFlagSet("test", ContinueOnError) - *tristate = triStateFalse - flag := f.VarPF(tristate, "tristate", "t", "tristate value (true, maybe or false)") - flag.NoOptDefVal = "true" - return f -} - -func TestExplicitTrue(t *testing.T) { - var tristate triStateValue - f := setUpFlagSet(&tristate) - err := f.Parse([]string{"--tristate=true"}) - if err != nil { - t.Fatal("expected no error; got", err) - } - if tristate != triStateTrue { - t.Fatal("expected", triStateTrue, "(triStateTrue) but got", tristate, "instead") - } -} - -func TestImplicitTrue(t *testing.T) { - var tristate triStateValue - f := setUpFlagSet(&tristate) - err := f.Parse([]string{"--tristate"}) - if err != nil { - t.Fatal("expected no error; got", err) - } - if tristate != triStateTrue { - t.Fatal("expected", triStateTrue, "(triStateTrue) but got", tristate, "instead") - } -} - -func TestShortFlag(t *testing.T) { - var tristate triStateValue - f := setUpFlagSet(&tristate) - err := f.Parse([]string{"-t"}) - if err != nil { - t.Fatal("expected no error; got", err) - } - if tristate != triStateTrue { - t.Fatal("expected", triStateTrue, "(triStateTrue) but got", tristate, "instead") - } -} - -func TestShortFlagExtraArgument(t *testing.T) { - var tristate triStateValue - f := setUpFlagSet(&tristate) - // The"maybe"turns into an arg, since short boolean options will only do true/false - err := f.Parse([]string{"-t", "maybe"}) - if err != nil { - t.Fatal("expected no error; got", err) - } - if tristate != triStateTrue { - t.Fatal("expected", triStateTrue, "(triStateTrue) but got", tristate, "instead") - } - args := f.Args() - if len(args) != 1 || args[0] != "maybe" { - t.Fatal("expected an extra 'maybe' argument to stick around") - } -} - -func TestExplicitMaybe(t *testing.T) { - var tristate triStateValue - f := setUpFlagSet(&tristate) - err := f.Parse([]string{"--tristate=maybe"}) - if err != nil { - t.Fatal("expected no error; got", err) - } - if tristate != triStateMaybe { - t.Fatal("expected", triStateMaybe, "(triStateMaybe) but got", tristate, "instead") - } -} - -func TestExplicitFalse(t *testing.T) { - var tristate triStateValue - f := setUpFlagSet(&tristate) - err := f.Parse([]string{"--tristate=false"}) - if err != nil { - t.Fatal("expected no error; got", err) - } - if tristate != triStateFalse { - t.Fatal("expected", triStateFalse, "(triStateFalse) but got", tristate, "instead") - } -} - -func TestImplicitFalse(t *testing.T) { - var tristate triStateValue - f := setUpFlagSet(&tristate) - err := f.Parse([]string{}) - if err != nil { - t.Fatal("expected no error; got", err) - } - if tristate != triStateFalse { - t.Fatal("expected", triStateFalse, "(triStateFalse) but got", tristate, "instead") - } -} - -func TestInvalidValue(t *testing.T) { - var tristate triStateValue - f := setUpFlagSet(&tristate) - var buf bytes.Buffer - f.SetOutput(&buf) - err := f.Parse([]string{"--tristate=invalid"}) - if err == nil { - t.Fatal("expected an error but did not get any, tristate has value", tristate) - } -} - -func TestBoolP(t *testing.T) { - b := BoolP("bool", "b", false, "bool value in CommandLine") - c := BoolP("c", "c", false, "other bool value") - args := []string{"--bool"} - if err := CommandLine.Parse(args); err != nil { - t.Error("expected no error, got ", err) - } - if *b != true { - t.Errorf("expected b=true got b=%s", b) - } - if *c != false { - t.Errorf("expect c=false got c=%s", c) - } -} diff --git a/Godeps/_workspace/src/github.com/spf13/pflag/count_test.go b/Godeps/_workspace/src/github.com/spf13/pflag/count_test.go deleted file mode 100644 index 716765cba..000000000 --- a/Godeps/_workspace/src/github.com/spf13/pflag/count_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package pflag - -import ( - "fmt" - "os" - "testing" -) - -var _ = fmt.Printf - -func setUpCount(c *int) *FlagSet { - f := NewFlagSet("test", ContinueOnError) - f.CountVarP(c, "verbose", "v", "a counter") - return f -} - -func TestCount(t *testing.T) { - testCases := []struct { - input []string - success bool - expected int - }{ - {[]string{"-vvv"}, true, 3}, - {[]string{"-v", "-v", "-v"}, true, 3}, - {[]string{"-v", "--verbose", "-v"}, true, 3}, - {[]string{"-v=3", "-v"}, true, 4}, - {[]string{"-v=a"}, false, 0}, - } - - devnull, _ := os.Open(os.DevNull) - os.Stderr = devnull - for i := range testCases { - var count int - f := setUpCount(&count) - - tc := &testCases[i] - - err := f.Parse(tc.input) - if err != nil && tc.success == true { - t.Errorf("expected success, got %q", err) - continue - } else if err == nil && tc.success == false { - t.Errorf("expected failure, got success") - continue - } else if tc.success { - c, err := f.GetCount("verbose") - if err != nil { - t.Errorf("Got error trying to fetch the counter flag") - } - if c != tc.expected { - t.Errorf("expected %q, got %q", tc.expected, c) - } - } - } -} diff --git a/Godeps/_workspace/src/github.com/spf13/pflag/example_test.go b/Godeps/_workspace/src/github.com/spf13/pflag/example_test.go deleted file mode 100644 index 9be7a49f2..000000000 --- a/Godeps/_workspace/src/github.com/spf13/pflag/example_test.go +++ /dev/null @@ -1,77 +0,0 @@ -// 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. - -// These examples demonstrate more intricate uses of the flag package. -package pflag_test - -import ( - "errors" - "fmt" - "strings" - "time" - - flag "github.com/spf13/pflag" -) - -// Example 1: A single string flag called "species" with default value "gopher". -var species = flag.String("species", "gopher", "the species we are studying") - -// Example 2: A flag with a shorthand letter. -var gopherType = flag.StringP("gopher_type", "g", "pocket", "the variety of gopher") - -// Example 3: A user-defined flag type, a slice of durations. -type interval []time.Duration - -// String is the method to format the flag's value, part of the flag.Value interface. -// The String method's output will be used in diagnostics. -func (i *interval) String() string { - return fmt.Sprint(*i) -} - -func (i *interval) Type() string { - return "interval" -} - -// Set is the method to set the flag value, part of the flag.Value interface. -// Set's argument is a string to be parsed to set the flag. -// It's a comma-separated list, so we split it. -func (i *interval) Set(value string) error { - // If we wanted to allow the flag to be set multiple times, - // accumulating values, we would delete this if statement. - // That would permit usages such as - // -deltaT 10s -deltaT 15s - // and other combinations. - if len(*i) > 0 { - return errors.New("interval flag already set") - } - for _, dt := range strings.Split(value, ",") { - duration, err := time.ParseDuration(dt) - if err != nil { - return err - } - *i = append(*i, duration) - } - return nil -} - -// Define a flag to accumulate durations. Because it has a special type, -// we need to use the Var function and therefore create the flag during -// init. - -var intervalFlag interval - -func init() { - // Tie the command-line flag to the intervalFlag variable and - // set a usage message. - flag.Var(&intervalFlag, "deltaT", "comma-separated list of intervals to use between events") -} - -func Example() { - // All the interesting pieces are with the variables declared above, but - // to enable the flag package to see the flags defined there, one must - // execute, typically at the start of main (not init!): - // flag.Parse() - // We don't run it here because this is not a main function and - // the testing suite has already parsed the flags. -} diff --git a/Godeps/_workspace/src/github.com/spf13/pflag/export_test.go b/Godeps/_workspace/src/github.com/spf13/pflag/export_test.go deleted file mode 100644 index 9318fee00..000000000 --- a/Godeps/_workspace/src/github.com/spf13/pflag/export_test.go +++ /dev/null @@ -1,29 +0,0 @@ -// 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 pflag - -import ( - "io/ioutil" - "os" -) - -// Additional routines compiled into the package only during testing. - -// ResetForTesting clears all flag state and sets the usage function as directed. -// After calling ResetForTesting, parse errors in flag handling will not -// exit the program. -func ResetForTesting(usage func()) { - CommandLine = &FlagSet{ - name: os.Args[0], - errorHandling: ContinueOnError, - output: ioutil.Discard, - } - Usage = usage -} - -// GetCommandLine returns the default FlagSet. -func GetCommandLine() *FlagSet { - return CommandLine -} diff --git a/Godeps/_workspace/src/github.com/spf13/pflag/flag_test.go b/Godeps/_workspace/src/github.com/spf13/pflag/flag_test.go deleted file mode 100644 index e17b2aad8..000000000 --- a/Godeps/_workspace/src/github.com/spf13/pflag/flag_test.go +++ /dev/null @@ -1,874 +0,0 @@ -// 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 pflag - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "net" - "os" - "reflect" - "sort" - "strings" - "testing" - "time" -) - -var ( - testBool = Bool("test_bool", false, "bool value") - testInt = Int("test_int", 0, "int value") - testInt64 = Int64("test_int64", 0, "int64 value") - testUint = Uint("test_uint", 0, "uint value") - testUint64 = Uint64("test_uint64", 0, "uint64 value") - testString = String("test_string", "0", "string value") - testFloat = Float64("test_float64", 0, "float64 value") - testDuration = Duration("test_duration", 0, "time.Duration value") - testOptionalInt = Int("test_optional_int", 0, "optional int value") - normalizeFlagNameInvocations = 0 -) - -func boolString(s string) string { - if s == "0" { - return "false" - } - return "true" -} - -func TestEverything(t *testing.T) { - m := make(map[string]*Flag) - desired := "0" - visitor := func(f *Flag) { - if len(f.Name) > 5 && f.Name[0:5] == "test_" { - m[f.Name] = f - ok := false - switch { - case f.Value.String() == desired: - ok = true - case f.Name == "test_bool" && f.Value.String() == boolString(desired): - ok = true - case f.Name == "test_duration" && f.Value.String() == desired+"s": - ok = true - } - if !ok { - t.Error("Visit: bad value", f.Value.String(), "for", f.Name) - } - } - } - VisitAll(visitor) - if len(m) != 9 { - t.Error("VisitAll misses some flags") - for k, v := range m { - t.Log(k, *v) - } - } - m = make(map[string]*Flag) - Visit(visitor) - if len(m) != 0 { - t.Errorf("Visit sees unset flags") - for k, v := range m { - t.Log(k, *v) - } - } - // Now set all flags - Set("test_bool", "true") - Set("test_int", "1") - Set("test_int64", "1") - Set("test_uint", "1") - Set("test_uint64", "1") - Set("test_string", "1") - Set("test_float64", "1") - Set("test_duration", "1s") - Set("test_optional_int", "1") - desired = "1" - Visit(visitor) - if len(m) != 9 { - t.Error("Visit fails after set") - for k, v := range m { - t.Log(k, *v) - } - } - // Now test they're visited in sort order. - var flagNames []string - Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) }) - if !sort.StringsAreSorted(flagNames) { - t.Errorf("flag names not sorted: %v", flagNames) - } -} - -func TestUsage(t *testing.T) { - called := false - ResetForTesting(func() { called = true }) - if GetCommandLine().Parse([]string{"--x"}) == nil { - t.Error("parse did not fail for unknown flag") - } - if !called { - t.Error("did not call Usage for unknown flag") - } -} - -func TestAddFlagSet(t *testing.T) { - oldSet := NewFlagSet("old", ContinueOnError) - newSet := NewFlagSet("new", ContinueOnError) - - oldSet.String("flag1", "flag1", "flag1") - oldSet.String("flag2", "flag2", "flag2") - - newSet.String("flag2", "flag2", "flag2") - newSet.String("flag3", "flag3", "flag3") - - oldSet.AddFlagSet(newSet) - - if len(oldSet.formal) != 3 { - t.Errorf("Unexpected result adding a FlagSet to a FlagSet %v", oldSet) - } -} - -func TestAnnotation(t *testing.T) { - f := NewFlagSet("shorthand", ContinueOnError) - - if err := f.SetAnnotation("missing-flag", "key", nil); err == nil { - t.Errorf("Expected error setting annotation on non-existent flag") - } - - f.StringP("stringa", "a", "", "string value") - if err := f.SetAnnotation("stringa", "key", nil); err != nil { - t.Errorf("Unexpected error setting new nil annotation: %v", err) - } - if annotation := f.Lookup("stringa").Annotations["key"]; annotation != nil { - t.Errorf("Unexpected annotation: %v", annotation) - } - - f.StringP("stringb", "b", "", "string2 value") - if err := f.SetAnnotation("stringb", "key", []string{"value1"}); err != nil { - t.Errorf("Unexpected error setting new annotation: %v", err) - } - if annotation := f.Lookup("stringb").Annotations["key"]; !reflect.DeepEqual(annotation, []string{"value1"}) { - t.Errorf("Unexpected annotation: %v", annotation) - } - - if err := f.SetAnnotation("stringb", "key", []string{"value2"}); err != nil { - t.Errorf("Unexpected error updating annotation: %v", err) - } - if annotation := f.Lookup("stringb").Annotations["key"]; !reflect.DeepEqual(annotation, []string{"value2"}) { - t.Errorf("Unexpected annotation: %v", annotation) - } -} - -func testParse(f *FlagSet, t *testing.T) { - if f.Parsed() { - t.Error("f.Parse() = true before Parse") - } - boolFlag := f.Bool("bool", false, "bool value") - bool2Flag := f.Bool("bool2", false, "bool2 value") - bool3Flag := f.Bool("bool3", false, "bool3 value") - intFlag := f.Int("int", 0, "int value") - int8Flag := f.Int8("int8", 0, "int value") - int32Flag := f.Int32("int32", 0, "int value") - int64Flag := f.Int64("int64", 0, "int64 value") - uintFlag := f.Uint("uint", 0, "uint value") - uint8Flag := f.Uint8("uint8", 0, "uint value") - uint16Flag := f.Uint16("uint16", 0, "uint value") - uint32Flag := f.Uint32("uint32", 0, "uint value") - uint64Flag := f.Uint64("uint64", 0, "uint64 value") - stringFlag := f.String("string", "0", "string value") - float32Flag := f.Float32("float32", 0, "float32 value") - float64Flag := f.Float64("float64", 0, "float64 value") - ipFlag := f.IP("ip", net.ParseIP("127.0.0.1"), "ip value") - maskFlag := f.IPMask("mask", ParseIPv4Mask("0.0.0.0"), "mask value") - durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value") - optionalIntNoValueFlag := f.Int("optional-int-no-value", 0, "int value") - f.Lookup("optional-int-no-value").NoOptDefVal = "9" - optionalIntWithValueFlag := f.Int("optional-int-with-value", 0, "int value") - f.Lookup("optional-int-no-value").NoOptDefVal = "9" - extra := "one-extra-argument" - args := []string{ - "--bool", - "--bool2=true", - "--bool3=false", - "--int=22", - "--int8=-8", - "--int32=-32", - "--int64=0x23", - "--uint", "24", - "--uint8=8", - "--uint16=16", - "--uint32=32", - "--uint64=25", - "--string=hello", - "--float32=-172e12", - "--float64=2718e28", - "--ip=10.11.12.13", - "--mask=255.255.255.0", - "--duration=2m", - "--optional-int-no-value", - "--optional-int-with-value=42", - extra, - } - if err := f.Parse(args); err != nil { - t.Fatal(err) - } - if !f.Parsed() { - t.Error("f.Parse() = false after Parse") - } - if *boolFlag != true { - t.Error("bool flag should be true, is ", *boolFlag) - } - if v, err := f.GetBool("bool"); err != nil || v != *boolFlag { - t.Error("GetBool does not work.") - } - if *bool2Flag != true { - t.Error("bool2 flag should be true, is ", *bool2Flag) - } - if *bool3Flag != false { - t.Error("bool3 flag should be false, is ", *bool2Flag) - } - if *intFlag != 22 { - t.Error("int flag should be 22, is ", *intFlag) - } - if v, err := f.GetInt("int"); err != nil || v != *intFlag { - t.Error("GetInt does not work.") - } - if *int8Flag != -8 { - t.Error("int8 flag should be 0x23, is ", *int8Flag) - } - if v, err := f.GetInt8("int8"); err != nil || v != *int8Flag { - t.Error("GetInt8 does not work.") - } - if *int32Flag != -32 { - t.Error("int32 flag should be 0x23, is ", *int32Flag) - } - if v, err := f.GetInt32("int32"); err != nil || v != *int32Flag { - t.Error("GetInt32 does not work.") - } - if *int64Flag != 0x23 { - t.Error("int64 flag should be 0x23, is ", *int64Flag) - } - if v, err := f.GetInt64("int64"); err != nil || v != *int64Flag { - t.Error("GetInt64 does not work.") - } - if *uintFlag != 24 { - t.Error("uint flag should be 24, is ", *uintFlag) - } - if v, err := f.GetUint("uint"); err != nil || v != *uintFlag { - t.Error("GetUint does not work.") - } - if *uint8Flag != 8 { - t.Error("uint8 flag should be 8, is ", *uint8Flag) - } - if v, err := f.GetUint8("uint8"); err != nil || v != *uint8Flag { - t.Error("GetUint8 does not work.") - } - if *uint16Flag != 16 { - t.Error("uint16 flag should be 16, is ", *uint16Flag) - } - if v, err := f.GetUint16("uint16"); err != nil || v != *uint16Flag { - t.Error("GetUint16 does not work.") - } - if *uint32Flag != 32 { - t.Error("uint32 flag should be 32, is ", *uint32Flag) - } - if v, err := f.GetUint32("uint32"); err != nil || v != *uint32Flag { - t.Error("GetUint32 does not work.") - } - if *uint64Flag != 25 { - t.Error("uint64 flag should be 25, is ", *uint64Flag) - } - if v, err := f.GetUint64("uint64"); err != nil || v != *uint64Flag { - t.Error("GetUint64 does not work.") - } - if *stringFlag != "hello" { - t.Error("string flag should be `hello`, is ", *stringFlag) - } - if v, err := f.GetString("string"); err != nil || v != *stringFlag { - t.Error("GetString does not work.") - } - if *float32Flag != -172e12 { - t.Error("float32 flag should be -172e12, is ", *float32Flag) - } - if v, err := f.GetFloat32("float32"); err != nil || v != *float32Flag { - t.Errorf("GetFloat32 returned %v but float32Flag was %v", v, *float32Flag) - } - if *float64Flag != 2718e28 { - t.Error("float64 flag should be 2718e28, is ", *float64Flag) - } - if v, err := f.GetFloat64("float64"); err != nil || v != *float64Flag { - t.Errorf("GetFloat64 returned %v but float64Flag was %v", v, *float64Flag) - } - if !(*ipFlag).Equal(net.ParseIP("10.11.12.13")) { - t.Error("ip flag should be 10.11.12.13, is ", *ipFlag) - } - if v, err := f.GetIP("ip"); err != nil || !v.Equal(*ipFlag) { - t.Errorf("GetIP returned %v but ipFlag was %v", v, *ipFlag) - } - if (*maskFlag).String() != ParseIPv4Mask("255.255.255.0").String() { - t.Error("mask flag should be 255.255.255.0, is ", (*maskFlag).String()) - } - if v, err := f.GetIPv4Mask("mask"); err != nil || v.String() != (*maskFlag).String() { - t.Errorf("GetIP returned %v maskFlag was %v error was %v", v, *maskFlag, err) - } - if *durationFlag != 2*time.Minute { - t.Error("duration flag should be 2m, is ", *durationFlag) - } - if v, err := f.GetDuration("duration"); err != nil || v != *durationFlag { - t.Error("GetDuration does not work.") - } - if _, err := f.GetInt("duration"); err == nil { - t.Error("GetInt parsed a time.Duration?!?!") - } - if *optionalIntNoValueFlag != 9 { - t.Error("optional int flag should be the default value, is ", *optionalIntNoValueFlag) - } - if *optionalIntWithValueFlag != 42 { - t.Error("optional int flag should be 42, is ", *optionalIntWithValueFlag) - } - if len(f.Args()) != 1 { - t.Error("expected one argument, got", len(f.Args())) - } else if f.Args()[0] != extra { - t.Errorf("expected argument %q got %q", extra, f.Args()[0]) - } -} - -func TestShorthand(t *testing.T) { - f := NewFlagSet("shorthand", ContinueOnError) - if f.Parsed() { - t.Error("f.Parse() = true before Parse") - } - boolaFlag := f.BoolP("boola", "a", false, "bool value") - boolbFlag := f.BoolP("boolb", "b", false, "bool2 value") - boolcFlag := f.BoolP("boolc", "c", false, "bool3 value") - booldFlag := f.BoolP("boold", "d", false, "bool4 value") - stringaFlag := f.StringP("stringa", "s", "0", "string value") - stringzFlag := f.StringP("stringz", "z", "0", "string value") - extra := "interspersed-argument" - notaflag := "--i-look-like-a-flag" - args := []string{ - "-ab", - extra, - "-cs", - "hello", - "-z=something", - "-d=true", - "--", - notaflag, - } - f.SetOutput(ioutil.Discard) - if err := f.Parse(args); err != nil { - t.Error("expected no error, got ", err) - } - if !f.Parsed() { - t.Error("f.Parse() = false after Parse") - } - if *boolaFlag != true { - t.Error("boola flag should be true, is ", *boolaFlag) - } - if *boolbFlag != true { - t.Error("boolb flag should be true, is ", *boolbFlag) - } - if *boolcFlag != true { - t.Error("boolc flag should be true, is ", *boolcFlag) - } - if *booldFlag != true { - t.Error("boold flag should be true, is ", *booldFlag) - } - if *stringaFlag != "hello" { - t.Error("stringa flag should be `hello`, is ", *stringaFlag) - } - if *stringzFlag != "something" { - t.Error("stringz flag should be `something`, is ", *stringzFlag) - } - if len(f.Args()) != 2 { - t.Error("expected one argument, got", len(f.Args())) - } else if f.Args()[0] != extra { - t.Errorf("expected argument %q got %q", extra, f.Args()[0]) - } else if f.Args()[1] != notaflag { - t.Errorf("expected argument %q got %q", notaflag, f.Args()[1]) - } - if f.ArgsLenAtDash() != 1 { - t.Errorf("expected argsLenAtDash %d got %d", f.ArgsLenAtDash(), 1) - } -} - -func TestParse(t *testing.T) { - ResetForTesting(func() { t.Error("bad parse") }) - testParse(GetCommandLine(), t) -} - -func TestFlagSetParse(t *testing.T) { - testParse(NewFlagSet("test", ContinueOnError), t) -} - -func TestChangedHelper(t *testing.T) { - f := NewFlagSet("changedtest", ContinueOnError) - _ = f.Bool("changed", false, "changed bool") - _ = f.Bool("settrue", true, "true to true") - _ = f.Bool("setfalse", false, "false to false") - _ = f.Bool("unchanged", false, "unchanged bool") - - args := []string{"--changed", "--settrue", "--setfalse=false"} - if err := f.Parse(args); err != nil { - t.Error("f.Parse() = false after Parse") - } - if !f.Changed("changed") { - t.Errorf("--changed wasn't changed!") - } - if !f.Changed("settrue") { - t.Errorf("--settrue wasn't changed!") - } - if !f.Changed("setfalse") { - t.Errorf("--setfalse wasn't changed!") - } - if f.Changed("unchanged") { - t.Errorf("--unchanged was changed!") - } - if f.Changed("invalid") { - t.Errorf("--invalid was changed!") - } - if f.ArgsLenAtDash() != -1 { - t.Errorf("Expected argsLenAtDash: %d but got %d", -1, f.ArgsLenAtDash()) - } -} - -func replaceSeparators(name string, from []string, to string) string { - result := name - for _, sep := range from { - result = strings.Replace(result, sep, to, -1) - } - // Type convert to indicate normalization has been done. - return result -} - -func wordSepNormalizeFunc(f *FlagSet, name string) NormalizedName { - seps := []string{"-", "_"} - name = replaceSeparators(name, seps, ".") - normalizeFlagNameInvocations++ - - return NormalizedName(name) -} - -func testWordSepNormalizedNames(args []string, t *testing.T) { - f := NewFlagSet("normalized", ContinueOnError) - if f.Parsed() { - t.Error("f.Parse() = true before Parse") - } - withDashFlag := f.Bool("with-dash-flag", false, "bool value") - // Set this after some flags have been added and before others. - f.SetNormalizeFunc(wordSepNormalizeFunc) - withUnderFlag := f.Bool("with_under_flag", false, "bool value") - withBothFlag := f.Bool("with-both_flag", false, "bool value") - if err := f.Parse(args); err != nil { - t.Fatal(err) - } - if !f.Parsed() { - t.Error("f.Parse() = false after Parse") - } - if *withDashFlag != true { - t.Error("withDashFlag flag should be true, is ", *withDashFlag) - } - if *withUnderFlag != true { - t.Error("withUnderFlag flag should be true, is ", *withUnderFlag) - } - if *withBothFlag != true { - t.Error("withBothFlag flag should be true, is ", *withBothFlag) - } -} - -func TestWordSepNormalizedNames(t *testing.T) { - args := []string{ - "--with-dash-flag", - "--with-under-flag", - "--with-both-flag", - } - testWordSepNormalizedNames(args, t) - - args = []string{ - "--with_dash_flag", - "--with_under_flag", - "--with_both_flag", - } - testWordSepNormalizedNames(args, t) - - args = []string{ - "--with-dash_flag", - "--with-under_flag", - "--with-both_flag", - } - testWordSepNormalizedNames(args, t) -} - -func aliasAndWordSepFlagNames(f *FlagSet, name string) NormalizedName { - seps := []string{"-", "_"} - - oldName := replaceSeparators("old-valid_flag", seps, ".") - newName := replaceSeparators("valid-flag", seps, ".") - - name = replaceSeparators(name, seps, ".") - switch name { - case oldName: - name = newName - break - } - - return NormalizedName(name) -} - -func TestCustomNormalizedNames(t *testing.T) { - f := NewFlagSet("normalized", ContinueOnError) - if f.Parsed() { - t.Error("f.Parse() = true before Parse") - } - - validFlag := f.Bool("valid-flag", false, "bool value") - f.SetNormalizeFunc(aliasAndWordSepFlagNames) - someOtherFlag := f.Bool("some-other-flag", false, "bool value") - - args := []string{"--old_valid_flag", "--some-other_flag"} - if err := f.Parse(args); err != nil { - t.Fatal(err) - } - - if *validFlag != true { - t.Errorf("validFlag is %v even though we set the alias --old_valid_falg", *validFlag) - } - if *someOtherFlag != true { - t.Error("someOtherFlag should be true, is ", *someOtherFlag) - } -} - -// Every flag we add, the name (displayed also in usage) should normalized -func TestNormalizationFuncShouldChangeFlagName(t *testing.T) { - // Test normalization after addition - f := NewFlagSet("normalized", ContinueOnError) - - f.Bool("valid_flag", false, "bool value") - if f.Lookup("valid_flag").Name != "valid_flag" { - t.Error("The new flag should have the name 'valid_flag' instead of ", f.Lookup("valid_flag").Name) - } - - f.SetNormalizeFunc(wordSepNormalizeFunc) - if f.Lookup("valid_flag").Name != "valid.flag" { - t.Error("The new flag should have the name 'valid.flag' instead of ", f.Lookup("valid_flag").Name) - } - - // Test normalization before addition - f = NewFlagSet("normalized", ContinueOnError) - f.SetNormalizeFunc(wordSepNormalizeFunc) - - f.Bool("valid_flag", false, "bool value") - if f.Lookup("valid_flag").Name != "valid.flag" { - t.Error("The new flag should have the name 'valid.flag' instead of ", f.Lookup("valid_flag").Name) - } -} - -// Declare a user-defined flag type. -type flagVar []string - -func (f *flagVar) String() string { - return fmt.Sprint([]string(*f)) -} - -func (f *flagVar) Set(value string) error { - *f = append(*f, value) - return nil -} - -func (f *flagVar) Type() string { - return "flagVar" -} - -func TestUserDefined(t *testing.T) { - var flags FlagSet - flags.Init("test", ContinueOnError) - var v flagVar - flags.VarP(&v, "v", "v", "usage") - if err := flags.Parse([]string{"--v=1", "-v2", "-v", "3"}); err != nil { - t.Error(err) - } - if len(v) != 3 { - t.Fatal("expected 3 args; got ", len(v)) - } - expect := "[1 2 3]" - if v.String() != expect { - t.Errorf("expected value %q got %q", expect, v.String()) - } -} - -func TestSetOutput(t *testing.T) { - var flags FlagSet - var buf bytes.Buffer - flags.SetOutput(&buf) - flags.Init("test", ContinueOnError) - flags.Parse([]string{"--unknown"}) - if out := buf.String(); !strings.Contains(out, "--unknown") { - t.Logf("expected output mentioning unknown; got %q", out) - } -} - -// This tests that one can reset the flags. This still works but not well, and is -// superseded by FlagSet. -func TestChangingArgs(t *testing.T) { - ResetForTesting(func() { t.Fatal("bad parse") }) - oldArgs := os.Args - defer func() { os.Args = oldArgs }() - os.Args = []string{"cmd", "--before", "subcmd"} - before := Bool("before", false, "") - if err := GetCommandLine().Parse(os.Args[1:]); err != nil { - t.Fatal(err) - } - cmd := Arg(0) - os.Args = []string{"subcmd", "--after", "args"} - after := Bool("after", false, "") - Parse() - args := Args() - - if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" { - t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args) - } -} - -// Test that -help invokes the usage message and returns ErrHelp. -func TestHelp(t *testing.T) { - var helpCalled = false - fs := NewFlagSet("help test", ContinueOnError) - fs.Usage = func() { helpCalled = true } - var flag bool - fs.BoolVar(&flag, "flag", false, "regular flag") - // Regular flag invocation should work - err := fs.Parse([]string{"--flag=true"}) - if err != nil { - t.Fatal("expected no error; got ", err) - } - if !flag { - t.Error("flag was not set by --flag") - } - if helpCalled { - t.Error("help called for regular flag") - helpCalled = false // reset for next test - } - // Help flag should work as expected. - err = fs.Parse([]string{"--help"}) - if err == nil { - t.Fatal("error expected") - } - if err != ErrHelp { - t.Fatal("expected ErrHelp; got ", err) - } - if !helpCalled { - t.Fatal("help was not called") - } - // If we define a help flag, that should override. - var help bool - fs.BoolVar(&help, "help", false, "help flag") - helpCalled = false - err = fs.Parse([]string{"--help"}) - if err != nil { - t.Fatal("expected no error for defined --help; got ", err) - } - if helpCalled { - t.Fatal("help was called; should not have been for defined help flag") - } -} - -func TestNoInterspersed(t *testing.T) { - f := NewFlagSet("test", ContinueOnError) - f.SetInterspersed(false) - f.Bool("true", true, "always true") - f.Bool("false", false, "always false") - err := f.Parse([]string{"--true", "break", "--false"}) - if err != nil { - t.Fatal("expected no error; got ", err) - } - args := f.Args() - if len(args) != 2 || args[0] != "break" || args[1] != "--false" { - t.Fatal("expected interspersed options/non-options to fail") - } -} - -func TestTermination(t *testing.T) { - f := NewFlagSet("termination", ContinueOnError) - boolFlag := f.BoolP("bool", "l", false, "bool value") - if f.Parsed() { - t.Error("f.Parse() = true before Parse") - } - arg1 := "ls" - arg2 := "-l" - args := []string{ - "--", - arg1, - arg2, - } - f.SetOutput(ioutil.Discard) - if err := f.Parse(args); err != nil { - t.Fatal("expected no error; got ", err) - } - if !f.Parsed() { - t.Error("f.Parse() = false after Parse") - } - if *boolFlag { - t.Error("expected boolFlag=false, got true") - } - if len(f.Args()) != 2 { - t.Errorf("expected 2 arguments, got %d: %v", len(f.Args()), f.Args()) - } - if f.Args()[0] != arg1 { - t.Errorf("expected argument %q got %q", arg1, f.Args()[0]) - } - if f.Args()[1] != arg2 { - t.Errorf("expected argument %q got %q", arg2, f.Args()[1]) - } - if f.ArgsLenAtDash() != 0 { - t.Errorf("expected argsLenAtDash %d got %d", 0, f.ArgsLenAtDash()) - } -} - -func TestDeprecatedFlagInDocs(t *testing.T) { - f := NewFlagSet("bob", ContinueOnError) - f.Bool("badflag", true, "always true") - f.MarkDeprecated("badflag", "use --good-flag instead") - - out := new(bytes.Buffer) - f.SetOutput(out) - f.PrintDefaults() - - if strings.Contains(out.String(), "badflag") { - t.Errorf("found deprecated flag in usage!") - } -} - -func TestDeprecatedFlagShorthandInDocs(t *testing.T) { - f := NewFlagSet("bob", ContinueOnError) - name := "noshorthandflag" - f.BoolP(name, "n", true, "always true") - f.MarkShorthandDeprecated("noshorthandflag", fmt.Sprintf("use --%s instead", name)) - - out := new(bytes.Buffer) - f.SetOutput(out) - f.PrintDefaults() - - if strings.Contains(out.String(), "-n,") { - t.Errorf("found deprecated flag shorthand in usage!") - } -} - -func parseReturnStderr(t *testing.T, f *FlagSet, args []string) (string, error) { - oldStderr := os.Stderr - r, w, _ := os.Pipe() - os.Stderr = w - - err := f.Parse(args) - - outC := make(chan string) - // copy the output in a separate goroutine so printing can't block indefinitely - go func() { - var buf bytes.Buffer - io.Copy(&buf, r) - outC <- buf.String() - }() - - w.Close() - os.Stderr = oldStderr - out := <-outC - - return out, err -} - -func TestDeprecatedFlagUsage(t *testing.T) { - f := NewFlagSet("bob", ContinueOnError) - f.Bool("badflag", true, "always true") - usageMsg := "use --good-flag instead" - f.MarkDeprecated("badflag", usageMsg) - - args := []string{"--badflag"} - out, err := parseReturnStderr(t, f, args) - if err != nil { - t.Fatal("expected no error; got ", err) - } - - if !strings.Contains(out, usageMsg) { - t.Errorf("usageMsg not printed when using a deprecated flag!") - } -} - -func TestDeprecatedFlagShorthandUsage(t *testing.T) { - f := NewFlagSet("bob", ContinueOnError) - name := "noshorthandflag" - f.BoolP(name, "n", true, "always true") - usageMsg := fmt.Sprintf("use --%s instead", name) - f.MarkShorthandDeprecated(name, usageMsg) - - args := []string{"-n"} - out, err := parseReturnStderr(t, f, args) - if err != nil { - t.Fatal("expected no error; got ", err) - } - - if !strings.Contains(out, usageMsg) { - t.Errorf("usageMsg not printed when using a deprecated flag!") - } -} - -func TestDeprecatedFlagUsageNormalized(t *testing.T) { - f := NewFlagSet("bob", ContinueOnError) - f.Bool("bad-double_flag", true, "always true") - f.SetNormalizeFunc(wordSepNormalizeFunc) - usageMsg := "use --good-flag instead" - f.MarkDeprecated("bad_double-flag", usageMsg) - - args := []string{"--bad_double_flag"} - out, err := parseReturnStderr(t, f, args) - if err != nil { - t.Fatal("expected no error; got ", err) - } - - if !strings.Contains(out, usageMsg) { - t.Errorf("usageMsg not printed when using a deprecated flag!") - } -} - -// Name normalization function should be called only once on flag addition -func TestMultipleNormalizeFlagNameInvocations(t *testing.T) { - normalizeFlagNameInvocations = 0 - - f := NewFlagSet("normalized", ContinueOnError) - f.SetNormalizeFunc(wordSepNormalizeFunc) - f.Bool("with_under_flag", false, "bool value") - - if normalizeFlagNameInvocations != 1 { - t.Fatal("Expected normalizeFlagNameInvocations to be 1; got ", normalizeFlagNameInvocations) - } -} - -// -func TestHiddenFlagInUsage(t *testing.T) { - f := NewFlagSet("bob", ContinueOnError) - f.Bool("secretFlag", true, "shhh") - f.MarkHidden("secretFlag") - - out := new(bytes.Buffer) - f.SetOutput(out) - f.PrintDefaults() - - if strings.Contains(out.String(), "secretFlag") { - t.Errorf("found hidden flag in usage!") - } -} - -// -func TestHiddenFlagUsage(t *testing.T) { - f := NewFlagSet("bob", ContinueOnError) - f.Bool("secretFlag", true, "shhh") - f.MarkHidden("secretFlag") - - args := []string{"--secretFlag"} - out, err := parseReturnStderr(t, f, args) - if err != nil { - t.Fatal("expected no error; got ", err) - } - - if strings.Contains(out, "shhh") { - t.Errorf("usage message printed when using a hidden flag!") - } -} diff --git a/Godeps/_workspace/src/github.com/spf13/pflag/golangflag_test.go b/Godeps/_workspace/src/github.com/spf13/pflag/golangflag_test.go deleted file mode 100644 index 77e2d7d80..000000000 --- a/Godeps/_workspace/src/github.com/spf13/pflag/golangflag_test.go +++ /dev/null @@ -1,39 +0,0 @@ -// 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 pflag - -import ( - goflag "flag" - "testing" -) - -func TestGoflags(t *testing.T) { - goflag.String("stringFlag", "stringFlag", "stringFlag") - goflag.Bool("boolFlag", false, "boolFlag") - - f := NewFlagSet("test", ContinueOnError) - - f.AddGoFlagSet(goflag.CommandLine) - err := f.Parse([]string{"--stringFlag=bob", "--boolFlag"}) - if err != nil { - t.Fatal("expected no error; get", err) - } - - getString, err := f.GetString("stringFlag") - if err != nil { - t.Fatal("expected no error; get", err) - } - if getString != "bob" { - t.Fatalf("expected getString=bob but got getString=%s", getString) - } - - getBool, err := f.GetBool("boolFlag") - if err != nil { - t.Fatal("expected no error; get", err) - } - if getBool != true { - t.Fatalf("expected getBool=true but got getBool=%v", getBool) - } -} diff --git a/Godeps/_workspace/src/github.com/spf13/pflag/int_slice_test.go b/Godeps/_workspace/src/github.com/spf13/pflag/int_slice_test.go deleted file mode 100644 index 5f2eee66a..000000000 --- a/Godeps/_workspace/src/github.com/spf13/pflag/int_slice_test.go +++ /dev/null @@ -1,162 +0,0 @@ -// 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 pflag - -import ( - "fmt" - "strconv" - "strings" - "testing" -) - -func setUpISFlagSet(isp *[]int) *FlagSet { - f := NewFlagSet("test", ContinueOnError) - f.IntSliceVar(isp, "is", []int{}, "Command separated list!") - return f -} - -func setUpISFlagSetWithDefault(isp *[]int) *FlagSet { - f := NewFlagSet("test", ContinueOnError) - f.IntSliceVar(isp, "is", []int{0, 1}, "Command separated list!") - return f -} - -func TestEmptyIS(t *testing.T) { - var is []int - f := setUpISFlagSet(&is) - err := f.Parse([]string{}) - if err != nil { - t.Fatal("expected no error; got", err) - } - - getIS, err := f.GetIntSlice("is") - if err != nil { - t.Fatal("got an error from GetIntSlice():", err) - } - if len(getIS) != 0 { - t.Fatalf("got is %v with len=%d but expected length=0", getIS, len(getIS)) - } -} - -func TestIS(t *testing.T) { - var is []int - f := setUpISFlagSet(&is) - - vals := []string{"1", "2", "4", "3"} - arg := fmt.Sprintf("--is=%s", strings.Join(vals, ",")) - err := f.Parse([]string{arg}) - if err != nil { - t.Fatal("expected no error; got", err) - } - for i, v := range is { - d, err := strconv.Atoi(vals[i]) - if err != nil { - t.Fatalf("got error: %v", err) - } - if d != v { - t.Fatalf("expected is[%d] to be %s but got: %d", i, vals[i], v) - } - } - getIS, err := f.GetIntSlice("is") - for i, v := range getIS { - d, err := strconv.Atoi(vals[i]) - if err != nil { - t.Fatalf("got error: %v", err) - } - if d != v { - t.Fatalf("expected is[%d] to be %s but got: %d from GetIntSlice", i, vals[i], v) - } - } -} - -func TestISDefault(t *testing.T) { - var is []int - f := setUpISFlagSetWithDefault(&is) - - vals := []string{"0", "1"} - - err := f.Parse([]string{}) - if err != nil { - t.Fatal("expected no error; got", err) - } - for i, v := range is { - d, err := strconv.Atoi(vals[i]) - if err != nil { - t.Fatalf("got error: %v", err) - } - if d != v { - t.Fatalf("expected is[%d] to be %d but got: %d", i, d, v) - } - } - - getIS, err := f.GetIntSlice("is") - if err != nil { - t.Fatal("got an error from GetIntSlice():", err) - } - for i, v := range getIS { - d, err := strconv.Atoi(vals[i]) - if err != nil { - t.Fatal("got an error from GetIntSlice():", err) - } - if d != v { - t.Fatalf("expected is[%d] to be %d from GetIntSlice but got: %d", i, d, v) - } - } -} - -func TestISWithDefault(t *testing.T) { - var is []int - f := setUpISFlagSetWithDefault(&is) - - vals := []string{"1", "2"} - arg := fmt.Sprintf("--is=%s", strings.Join(vals, ",")) - err := f.Parse([]string{arg}) - if err != nil { - t.Fatal("expected no error; got", err) - } - for i, v := range is { - d, err := strconv.Atoi(vals[i]) - if err != nil { - t.Fatalf("got error: %v", err) - } - if d != v { - t.Fatalf("expected is[%d] to be %d but got: %d", i, d, v) - } - } - - getIS, err := f.GetIntSlice("is") - if err != nil { - t.Fatal("got an error from GetIntSlice():", err) - } - for i, v := range getIS { - d, err := strconv.Atoi(vals[i]) - if err != nil { - t.Fatalf("got error: %v", err) - } - if d != v { - t.Fatalf("expected is[%d] to be %d from GetIntSlice but got: %d", i, d, v) - } - } -} - -func TestISCalledTwice(t *testing.T) { - var is []int - f := setUpISFlagSet(&is) - - in := []string{"1,2", "3"} - expected := []int{1, 2, 3} - argfmt := "--is=%s" - arg1 := fmt.Sprintf(argfmt, in[0]) - arg2 := fmt.Sprintf(argfmt, in[1]) - err := f.Parse([]string{arg1, arg2}) - if err != nil { - t.Fatal("expected no error; got", err) - } - for i, v := range is { - if expected[i] != v { - t.Fatalf("expected is[%d] to be %d but got: %d", i, expected[i], v) - } - } -} diff --git a/Godeps/_workspace/src/github.com/spf13/pflag/ip_test.go b/Godeps/_workspace/src/github.com/spf13/pflag/ip_test.go deleted file mode 100644 index 1fec50e42..000000000 --- a/Godeps/_workspace/src/github.com/spf13/pflag/ip_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package pflag - -import ( - "fmt" - "net" - "os" - "testing" -) - -func setUpIP(ip *net.IP) *FlagSet { - f := NewFlagSet("test", ContinueOnError) - f.IPVar(ip, "address", net.ParseIP("0.0.0.0"), "IP Address") - return f -} - -func TestIP(t *testing.T) { - testCases := []struct { - input string - success bool - expected string - }{ - {"0.0.0.0", true, "0.0.0.0"}, - {" 0.0.0.0 ", true, "0.0.0.0"}, - {"1.2.3.4", true, "1.2.3.4"}, - {"127.0.0.1", true, "127.0.0.1"}, - {"255.255.255.255", true, "255.255.255.255"}, - {"", false, ""}, - {"0", false, ""}, - {"localhost", false, ""}, - {"0.0.0", false, ""}, - {"0.0.0.", false, ""}, - {"0.0.0.0.", false, ""}, - {"0.0.0.256", false, ""}, - {"0 . 0 . 0 . 0", false, ""}, - } - - devnull, _ := os.Open(os.DevNull) - os.Stderr = devnull - for i := range testCases { - var addr net.IP - f := setUpIP(&addr) - - tc := &testCases[i] - - arg := fmt.Sprintf("--address=%s", tc.input) - err := f.Parse([]string{arg}) - if err != nil && tc.success == true { - t.Errorf("expected success, got %q", err) - continue - } else if err == nil && tc.success == false { - t.Errorf("expected failure") - continue - } else if tc.success { - ip, err := f.GetIP("address") - if err != nil { - t.Errorf("Got error trying to fetch the IP flag: %v", err) - } - if ip.String() != tc.expected { - t.Errorf("expected %q, got %q", tc.expected, ip.String()) - } - } - } -} diff --git a/Godeps/_workspace/src/github.com/spf13/pflag/ipnet_test.go b/Godeps/_workspace/src/github.com/spf13/pflag/ipnet_test.go deleted file mode 100644 index 335b6fa15..000000000 --- a/Godeps/_workspace/src/github.com/spf13/pflag/ipnet_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package pflag - -import ( - "fmt" - "net" - "os" - "testing" -) - -func setUpIPNet(ip *net.IPNet) *FlagSet { - f := NewFlagSet("test", ContinueOnError) - _, def, _ := net.ParseCIDR("0.0.0.0/0") - f.IPNetVar(ip, "address", *def, "IP Address") - return f -} - -func TestIPNet(t *testing.T) { - testCases := []struct { - input string - success bool - expected string - }{ - {"0.0.0.0/0", true, "0.0.0.0/0"}, - {" 0.0.0.0/0 ", true, "0.0.0.0/0"}, - {"1.2.3.4/8", true, "1.0.0.0/8"}, - {"127.0.0.1/16", true, "127.0.0.0/16"}, - {"255.255.255.255/19", true, "255.255.224.0/19"}, - {"255.255.255.255/32", true, "255.255.255.255/32"}, - {"", false, ""}, - {"/0", false, ""}, - {"0", false, ""}, - {"0/0", false, ""}, - {"localhost/0", false, ""}, - {"0.0.0/4", false, ""}, - {"0.0.0./8", false, ""}, - {"0.0.0.0./12", false, ""}, - {"0.0.0.256/16", false, ""}, - {"0.0.0.0 /20", false, ""}, - {"0.0.0.0/ 24", false, ""}, - {"0 . 0 . 0 . 0 / 28", false, ""}, - {"0.0.0.0/33", false, ""}, - } - - devnull, _ := os.Open(os.DevNull) - os.Stderr = devnull - for i := range testCases { - var addr net.IPNet - f := setUpIPNet(&addr) - - tc := &testCases[i] - - arg := fmt.Sprintf("--address=%s", tc.input) - err := f.Parse([]string{arg}) - if err != nil && tc.success == true { - t.Errorf("expected success, got %q", err) - continue - } else if err == nil && tc.success == false { - t.Errorf("expected failure") - continue - } else if tc.success { - ip, err := f.GetIPNet("address") - if err != nil { - t.Errorf("Got error trying to fetch the IP flag: %v", err) - } - if ip.String() != tc.expected { - t.Errorf("expected %q, got %q", tc.expected, ip.String()) - } - } - } -} diff --git a/Godeps/_workspace/src/github.com/spf13/pflag/string_slice_test.go b/Godeps/_workspace/src/github.com/spf13/pflag/string_slice_test.go deleted file mode 100644 index c7fdc70b4..000000000 --- a/Godeps/_workspace/src/github.com/spf13/pflag/string_slice_test.go +++ /dev/null @@ -1,161 +0,0 @@ -// 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 pflag - -import ( - "fmt" - "strings" - "testing" -) - -func setUpSSFlagSet(ssp *[]string) *FlagSet { - f := NewFlagSet("test", ContinueOnError) - f.StringSliceVar(ssp, "ss", []string{}, "Command separated list!") - return f -} - -func setUpSSFlagSetWithDefault(ssp *[]string) *FlagSet { - f := NewFlagSet("test", ContinueOnError) - f.StringSliceVar(ssp, "ss", []string{"default", "values"}, "Command separated list!") - return f -} - -func TestEmptySS(t *testing.T) { - var ss []string - f := setUpSSFlagSet(&ss) - err := f.Parse([]string{}) - if err != nil { - t.Fatal("expected no error; got", err) - } - - getSS, err := f.GetStringSlice("ss") - if err != nil { - t.Fatal("got an error from GetStringSlice():", err) - } - if len(getSS) != 0 { - t.Fatalf("got ss %v with len=%d but expected length=0", getSS, len(getSS)) - } -} - -func TestSS(t *testing.T) { - var ss []string - f := setUpSSFlagSet(&ss) - - vals := []string{"one", "two", "4", "3"} - arg := fmt.Sprintf("--ss=%s", strings.Join(vals, ",")) - err := f.Parse([]string{arg}) - if err != nil { - t.Fatal("expected no error; got", err) - } - for i, v := range ss { - if vals[i] != v { - t.Fatalf("expected ss[%d] to be %s but got: %s", i, vals[i], v) - } - } - - getSS, err := f.GetStringSlice("ss") - if err != nil { - t.Fatal("got an error from GetStringSlice():", err) - } - for i, v := range getSS { - if vals[i] != v { - t.Fatalf("expected ss[%d] to be %s from GetStringSlice but got: %s", i, vals[i], v) - } - } -} - -func TestSSDefault(t *testing.T) { - var ss []string - f := setUpSSFlagSetWithDefault(&ss) - - vals := []string{"default", "values"} - - err := f.Parse([]string{}) - if err != nil { - t.Fatal("expected no error; got", err) - } - for i, v := range ss { - if vals[i] != v { - t.Fatalf("expected ss[%d] to be %s but got: %s", i, vals[i], v) - } - } - - getSS, err := f.GetStringSlice("ss") - if err != nil { - t.Fatal("got an error from GetStringSlice():", err) - } - for i, v := range getSS { - if vals[i] != v { - t.Fatalf("expected ss[%d] to be %s from GetStringSlice but got: %s", i, vals[i], v) - } - } -} - -func TestSSWithDefault(t *testing.T) { - var ss []string - f := setUpSSFlagSetWithDefault(&ss) - - vals := []string{"one", "two", "4", "3"} - arg := fmt.Sprintf("--ss=%s", strings.Join(vals, ",")) - err := f.Parse([]string{arg}) - if err != nil { - t.Fatal("expected no error; got", err) - } - for i, v := range ss { - if vals[i] != v { - t.Fatalf("expected ss[%d] to be %s but got: %s", i, vals[i], v) - } - } - - getSS, err := f.GetStringSlice("ss") - if err != nil { - t.Fatal("got an error from GetStringSlice():", err) - } - for i, v := range getSS { - if vals[i] != v { - t.Fatalf("expected ss[%d] to be %s from GetStringSlice but got: %s", i, vals[i], v) - } - } -} - -func TestSSCalledTwice(t *testing.T) { - var ss []string - f := setUpSSFlagSet(&ss) - - in := []string{"one,two", "three"} - expected := []string{"one", "two", "three"} - argfmt := "--ss=%s" - arg1 := fmt.Sprintf(argfmt, in[0]) - arg2 := fmt.Sprintf(argfmt, in[1]) - err := f.Parse([]string{arg1, arg2}) - if err != nil { - t.Fatal("expected no error; got", err) - } - for i, v := range ss { - if expected[i] != v { - t.Fatalf("expected ss[%d] to be %s but got: %s", i, expected[i], v) - } - } -} - -func TestSSWithComma(t *testing.T) { - var ss []string - f := setUpSSFlagSet(&ss) - - in := []string{`"one,two"`, `"three"`} - expected := []string{"one,two", "three"} - argfmt := "--ss=%s" - arg1 := fmt.Sprintf(argfmt, in[0]) - arg2 := fmt.Sprintf(argfmt, in[1]) - err := f.Parse([]string{arg1, arg2}) - if err != nil { - t.Fatal("expected no error; got", err) - } - for i, v := range ss { - if expected[i] != v { - t.Fatalf("expected ss[%d] to be %s but got: %s", i, expected[i], v) - } - } -} diff --git a/Godeps/_workspace/src/github.com/spf13/pflag/verify/all.sh b/Godeps/_workspace/src/github.com/spf13/pflag/verify/all.sh deleted file mode 100644 index 739f89c0b..000000000 --- a/Godeps/_workspace/src/github.com/spf13/pflag/verify/all.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -set -o errexit -set -o nounset -set -o pipefail - -ROOT=$(dirname "${BASH_SOURCE}")/.. - -# Some useful colors. -if [[ -z "${color_start-}" ]]; then - declare -r color_start="\033[" - declare -r color_red="${color_start}0;31m" - declare -r color_yellow="${color_start}0;33m" - declare -r color_green="${color_start}0;32m" - declare -r color_norm="${color_start}0m" -fi - -SILENT=true - -function is-excluded { - for e in $EXCLUDE; do - if [[ $1 -ef ${BASH_SOURCE} ]]; then - return - fi - if [[ $1 -ef "$ROOT/hack/$e" ]]; then - return - fi - done - return 1 -} - -while getopts ":v" opt; do - case $opt in - v) - SILENT=false - ;; - \?) - echo "Invalid flag: -$OPTARG" >&2 - exit 1 - ;; - esac -done - -if $SILENT ; then - echo "Running in the silent mode, run with -v if you want to see script logs." -fi - -EXCLUDE="all.sh" - -ret=0 -for t in `ls $ROOT/verify/*.sh` -do - if is-excluded $t ; then - echo "Skipping $t" - continue - fi - if $SILENT ; then - echo -e "Verifying $t" - if bash "$t" &> /dev/null; then - echo -e "${color_green}SUCCESS${color_norm}" - else - echo -e "${color_red}FAILED${color_norm}" - ret=1 - fi - else - bash "$t" || ret=1 - fi -done -exit $ret diff --git a/Godeps/_workspace/src/github.com/spf13/pflag/verify/gofmt.sh b/Godeps/_workspace/src/github.com/spf13/pflag/verify/gofmt.sh deleted file mode 100644 index f66acf803..000000000 --- a/Godeps/_workspace/src/github.com/spf13/pflag/verify/gofmt.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -set -o errexit -set -o nounset -set -o pipefail - -ROOT=$(dirname "${BASH_SOURCE}")/.. - -pushd "${ROOT}" > /dev/null - -GOFMT=${GOFMT:-"gofmt"} -bad_files=$(find . -name '*.go' | xargs $GOFMT -s -l) -if [[ -n "${bad_files}" ]]; then - echo "!!! '$GOFMT' needs to be run on the following files: " - echo "${bad_files}" - exit 1 -fi - -# ex: ts=2 sw=2 et filetype=sh diff --git a/Godeps/_workspace/src/github.com/spf13/pflag/verify/golint.sh b/Godeps/_workspace/src/github.com/spf13/pflag/verify/golint.sh deleted file mode 100644 index 685c1778e..000000000 --- a/Godeps/_workspace/src/github.com/spf13/pflag/verify/golint.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -ROOT=$(dirname "${BASH_SOURCE}")/.. -GOLINT=${GOLINT:-"golint"} - -pushd "${ROOT}" > /dev/null - bad_files=$($GOLINT -min_confidence=0.9 ./...) - if [[ -n "${bad_files}" ]]; then - echo "!!! '$GOLINT' problems: " - echo "${bad_files}" - exit 1 - fi -popd > /dev/null - -# ex: ts=2 sw=2 et filetype=sh diff --git a/Godeps/_workspace/src/github.com/ugorji/go/LICENSE b/Godeps/_workspace/src/github.com/ugorji/go/LICENSE new file mode 100644 index 000000000..95a0f0541 --- /dev/null +++ b/Godeps/_workspace/src/github.com/ugorji/go/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2012-2015 Ugorji Nwoke. +All rights reserved. + +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/Godeps/_workspace/src/github.com/ugorji/go/codec/0doc.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/0doc.go index caa7e0a3b..bd7361c87 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/0doc.go +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/0doc.go @@ -64,6 +64,7 @@ Rich Feature Set includes: - Never silently skip data when decoding. User decides whether to return an error or silently skip data when keys or indexes in the data stream do not map to fields in the struct. + - Detect and error when encoding a cyclic reference (instead of stack overflow shutdown) - Encode/Decode from/to chan types (for iterative streaming support) - Drop-in replacement for encoding/json. `json:` key in struct tag supported. - Provides a RPC Server and Client Codec for net/rpc communication protocol. @@ -171,6 +172,8 @@ package codec // TODO: // +// - optimization for codecgen: +// if len of entity is <= 3 words, then support a value receiver for encode. // - (En|De)coder should store an error when it occurs. // Until reset, subsequent calls return that error that was stored. // This means that free panics must go away. @@ -183,11 +186,14 @@ package codec // Name string // Ys []Y // Ys chan <- Y -// Ys func(interface{}) -> call this interface for each entry in there. +// Ys func(Y) -> call this function for each entry // } // - Consider adding a isZeroer interface { isZero() bool } // It is used within isEmpty, for omitEmpty support. // - Consider making Handle used AS-IS within the encoding/decoding session. // This means that we don't cache Handle information within the (En|De)coder, // except we really need it at Reset(...) -// - Handle recursive types during encoding/decoding? +// - Consider adding math/big support +// - Consider reducing the size of the generated functions: +// Maybe use one loop, and put the conditionals in the loop. +// for ... { if cLen > 0 { if j == cLen { break } } else if dd.CheckBreak() { break } } diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/binc.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/binc.go index c884d14dc..766d26cf6 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/binc.go +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/binc.go @@ -908,10 +908,14 @@ func (h *BincHandle) newDecDriver(d *Decoder) decDriver { func (e *bincEncDriver) reset() { e.w = e.e.w + e.s = 0 + e.m = nil } func (d *bincDecDriver) reset() { d.r = d.d.r + d.s = nil + d.bd, d.bdRead, d.vd, d.vs = 0, false, 0, 0 } var _ decDriver = (*bincDecDriver)(nil) diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/cbor.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/cbor.go index 0e5d32b2e..a224cd3a7 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/cbor.go +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/cbor.go @@ -508,7 +508,7 @@ func (d *cborDecDriver) DecodeNaked() { n.v = valueTypeExt n.u = d.decUint() n.l = nil - d.bdRead = false + // d.bdRead = false // d.d.decode(&re.Value) // handled by decode itself. // decodeFurther = true default: @@ -578,6 +578,7 @@ func (e *cborEncDriver) reset() { func (d *cborDecDriver) reset() { d.r = d.d.r + d.bd, d.bdRead = 0, false } var _ decDriver = (*cborDecDriver)(nil) diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/cbor_test.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/cbor_test.go deleted file mode 100644 index 205dffa7d..000000000 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/cbor_test.go +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved. -// Use of this source code is governed by a MIT license found in the LICENSE file. - -package codec - -import ( - "bufio" - "bytes" - "encoding/hex" - "math" - "os" - "regexp" - "strings" - "testing" -) - -func TestCborIndefiniteLength(t *testing.T) { - oldMapType := testCborH.MapType - defer func() { - testCborH.MapType = oldMapType - }() - testCborH.MapType = testMapStrIntfTyp - // var ( - // M1 map[string][]byte - // M2 map[uint64]bool - // L1 []interface{} - // S1 []string - // B1 []byte - // ) - var v, vv interface{} - // define it (v), encode it using indefinite lengths, decode it (vv), compare v to vv - v = map[string]interface{}{ - "one-byte-key": []byte{1, 2, 3, 4, 5, 6}, - "two-string-key": "two-value", - "three-list-key": []interface{}{true, false, uint64(1), int64(-1)}, - } - var buf bytes.Buffer - // buf.Reset() - e := NewEncoder(&buf, testCborH) - buf.WriteByte(cborBdIndefiniteMap) - //---- - buf.WriteByte(cborBdIndefiniteString) - e.MustEncode("one-") - e.MustEncode("byte-") - e.MustEncode("key") - buf.WriteByte(cborBdBreak) - - buf.WriteByte(cborBdIndefiniteBytes) - e.MustEncode([]byte{1, 2, 3}) - e.MustEncode([]byte{4, 5, 6}) - buf.WriteByte(cborBdBreak) - - //---- - buf.WriteByte(cborBdIndefiniteString) - e.MustEncode("two-") - e.MustEncode("string-") - e.MustEncode("key") - buf.WriteByte(cborBdBreak) - - buf.WriteByte(cborBdIndefiniteString) - e.MustEncode([]byte("two-")) // encode as bytes, to check robustness of code - e.MustEncode([]byte("value")) - buf.WriteByte(cborBdBreak) - - //---- - buf.WriteByte(cborBdIndefiniteString) - e.MustEncode("three-") - e.MustEncode("list-") - e.MustEncode("key") - buf.WriteByte(cborBdBreak) - - buf.WriteByte(cborBdIndefiniteArray) - e.MustEncode(true) - e.MustEncode(false) - e.MustEncode(uint64(1)) - e.MustEncode(int64(-1)) - buf.WriteByte(cborBdBreak) - - buf.WriteByte(cborBdBreak) // close map - - NewDecoderBytes(buf.Bytes(), testCborH).MustDecode(&vv) - if err := deepEqual(v, vv); err != nil { - logT(t, "-------- Before and After marshal do not match: Error: %v", err) - logT(t, " ....... GOLDEN: (%T) %#v", v, v) - logT(t, " ....... DECODED: (%T) %#v", vv, vv) - failT(t) - } -} - -type testCborGolden struct { - Base64 string `codec:"cbor"` - Hex string `codec:"hex"` - Roundtrip bool `codec:"roundtrip"` - Decoded interface{} `codec:"decoded"` - Diagnostic string `codec:"diagnostic"` - Skip bool `codec:"skip"` -} - -// Some tests are skipped because they include numbers outside the range of int64/uint64 -func doTestCborGoldens(t *testing.T) { - oldMapType := testCborH.MapType - defer func() { - testCborH.MapType = oldMapType - }() - testCborH.MapType = testMapStrIntfTyp - // decode test-cbor-goldens.json into a list of []*testCborGolden - // for each one, - // - decode hex into []byte bs - // - decode bs into interface{} v - // - compare both using deepequal - // - for any miss, record it - var gs []*testCborGolden - f, err := os.Open("test-cbor-goldens.json") - if err != nil { - logT(t, "error opening test-cbor-goldens.json: %v", err) - failT(t) - } - defer f.Close() - jh := new(JsonHandle) - jh.MapType = testMapStrIntfTyp - // d := NewDecoder(f, jh) - d := NewDecoder(bufio.NewReader(f), jh) - // err = d.Decode(&gs) - d.MustDecode(&gs) - if err != nil { - logT(t, "error json decoding test-cbor-goldens.json: %v", err) - failT(t) - } - - tagregex := regexp.MustCompile(`[\d]+\(.+?\)`) - hexregex := regexp.MustCompile(`h'([0-9a-fA-F]*)'`) - for i, g := range gs { - // fmt.Printf("%v, skip: %v, isTag: %v, %s\n", i, g.Skip, tagregex.MatchString(g.Diagnostic), g.Diagnostic) - // skip tags or simple or those with prefix, as we can't verify them. - if g.Skip || strings.HasPrefix(g.Diagnostic, "simple(") || tagregex.MatchString(g.Diagnostic) { - // fmt.Printf("%v: skipped\n", i) - logT(t, "[%v] skipping because skip=true OR unsupported simple value or Tag Value", i) - continue - } - // println("++++++++++++", i, "g.Diagnostic", g.Diagnostic) - if hexregex.MatchString(g.Diagnostic) { - // println(i, "g.Diagnostic matched hex") - if s2 := g.Diagnostic[2 : len(g.Diagnostic)-1]; s2 == "" { - g.Decoded = zeroByteSlice - } else if bs2, err2 := hex.DecodeString(s2); err2 == nil { - g.Decoded = bs2 - } - // fmt.Printf("%v: hex: %v\n", i, g.Decoded) - } - bs, err := hex.DecodeString(g.Hex) - if err != nil { - logT(t, "[%v] error hex decoding %s [%v]: %v", i, g.Hex, err) - failT(t) - } - var v interface{} - NewDecoderBytes(bs, testCborH).MustDecode(&v) - if _, ok := v.(RawExt); ok { - continue - } - // check the diagnostics to compare - switch g.Diagnostic { - case "Infinity": - b := math.IsInf(v.(float64), 1) - testCborError(t, i, math.Inf(1), v, nil, &b) - case "-Infinity": - b := math.IsInf(v.(float64), -1) - testCborError(t, i, math.Inf(-1), v, nil, &b) - case "NaN": - // println(i, "checking NaN") - b := math.IsNaN(v.(float64)) - testCborError(t, i, math.NaN(), v, nil, &b) - case "undefined": - b := v == nil - testCborError(t, i, nil, v, nil, &b) - default: - v0 := g.Decoded - // testCborCoerceJsonNumber(reflect.ValueOf(&v0)) - testCborError(t, i, v0, v, deepEqual(v0, v), nil) - } - } -} - -func testCborError(t *testing.T, i int, v0, v1 interface{}, err error, equal *bool) { - if err == nil && equal == nil { - // fmt.Printf("%v testCborError passed (err and equal nil)\n", i) - return - } - if err != nil { - logT(t, "[%v] deepEqual error: %v", i, err) - logT(t, " ....... GOLDEN: (%T) %#v", v0, v0) - logT(t, " ....... DECODED: (%T) %#v", v1, v1) - failT(t) - } - if equal != nil && !*equal { - logT(t, "[%v] values not equal", i) - logT(t, " ....... GOLDEN: (%T) %#v", v0, v0) - logT(t, " ....... DECODED: (%T) %#v", v1, v1) - failT(t) - } - // fmt.Printf("%v testCborError passed (checks passed)\n", i) -} - -func TestCborGoldens(t *testing.T) { - doTestCborGoldens(t) -} diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/codec_test.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/codec_test.go deleted file mode 100644 index ab14e2d01..000000000 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/codec_test.go +++ /dev/null @@ -1,1185 +0,0 @@ -// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved. -// Use of this source code is governed by a MIT license found in the LICENSE file. - -package codec - -// Test works by using a slice of interfaces. -// It can test for encoding/decoding into/from a nil interface{} -// or passing the object to encode/decode into. -// -// There are basically 2 main tests here. -// First test internally encodes and decodes things and verifies that -// the artifact was as expected. -// Second test will use python msgpack to create a bunch of golden files, -// read those files, and compare them to what it should be. It then -// writes those files back out and compares the byte streams. -// -// Taken together, the tests are pretty extensive. -// -// The following manual tests must be done: -// - TestCodecUnderlyingType -// - Set fastpathEnabled to false and run tests (to ensure that regular reflection works). -// We don't want to use a variable there so that code is ellided. - -import ( - "bytes" - "encoding/gob" - "flag" - "fmt" - "io/ioutil" - "math" - "math/rand" - "net" - "net/rpc" - "os" - "os/exec" - "path/filepath" - "reflect" - "runtime" - "strconv" - "sync/atomic" - "testing" - "time" -) - -func init() { - testInitFlags() - testPreInitFns = append(testPreInitFns, testInit) -} - -type testVerifyArg int - -const ( - testVerifyMapTypeSame testVerifyArg = iota - testVerifyMapTypeStrIntf - testVerifyMapTypeIntfIntf - // testVerifySliceIntf - testVerifyForPython -) - -const testSkipRPCTests = false - -var ( - testVerbose bool - testInitDebug bool - testUseIoEncDec bool - testStructToArray bool - testCanonical bool - testUseReset bool - testWriteNoSymbols bool - testSkipIntf bool - testInternStr bool - testUseMust bool - - skipVerifyVal interface{} = &(struct{}{}) - - testMapStrIntfTyp = reflect.TypeOf(map[string]interface{}(nil)) - - // For Go Time, do not use a descriptive timezone. - // It's unnecessary, and makes it harder to do a reflect.DeepEqual. - // The Offset already tells what the offset should be, if not on UTC and unknown zone name. - timeLoc = time.FixedZone("", -8*60*60) // UTC-08:00 //time.UTC-8 - timeToCompare1 = time.Date(2012, 2, 2, 2, 2, 2, 2000, timeLoc).UTC() - timeToCompare2 = time.Date(1900, 2, 2, 2, 2, 2, 2000, timeLoc).UTC() - timeToCompare3 = time.Unix(0, 270).UTC() // use value that must be encoded as uint64 for nanoseconds (for cbor/msgpack comparison) - //timeToCompare4 = time.Time{}.UTC() // does not work well with simple cbor time encoding (overflow) - timeToCompare4 = time.Unix(-2013855848, 4223).UTC() - - table []interface{} // main items we encode - tableVerify []interface{} // we verify encoded things against this after decode - tableTestNilVerify []interface{} // for nil interface, use this to verify (rules are different) - tablePythonVerify []interface{} // for verifying for python, since Python sometimes - // will encode a float32 as float64, or large int as uint - testRpcInt = new(TestRpcInt) -) - -func testInitFlags() { - // delete(testDecOpts.ExtFuncs, timeTyp) - flag.BoolVar(&testVerbose, "tv", false, "Test Verbose") - flag.BoolVar(&testInitDebug, "tg", false, "Test Init Debug") - flag.BoolVar(&testUseIoEncDec, "ti", false, "Use IO Reader/Writer for Marshal/Unmarshal") - flag.BoolVar(&testStructToArray, "ts", false, "Set StructToArray option") - flag.BoolVar(&testWriteNoSymbols, "tn", false, "Set NoSymbols option") - flag.BoolVar(&testCanonical, "tc", false, "Set Canonical option") - flag.BoolVar(&testInternStr, "te", false, "Set InternStr option") - flag.BoolVar(&testSkipIntf, "tf", false, "Skip Interfaces") - flag.BoolVar(&testUseReset, "tr", false, "Use Reset") - flag.BoolVar(&testUseMust, "tm", true, "Use Must(En|De)code") -} - -func testByteBuf(in []byte) *bytes.Buffer { - return bytes.NewBuffer(in) -} - -type TestABC struct { - A, B, C string -} - -type TestRpcInt struct { - i int -} - -func (r *TestRpcInt) Update(n int, res *int) error { r.i = n; *res = r.i; return nil } -func (r *TestRpcInt) Square(ignore int, res *int) error { *res = r.i * r.i; return nil } -func (r *TestRpcInt) Mult(n int, res *int) error { *res = r.i * n; return nil } -func (r *TestRpcInt) EchoStruct(arg TestABC, res *string) error { - *res = fmt.Sprintf("%#v", arg) - return nil -} -func (r *TestRpcInt) Echo123(args []string, res *string) error { - *res = fmt.Sprintf("%#v", args) - return nil -} - -type testUnixNanoTimeExt struct { - // keep timestamp here, so that do not incur interface-conversion costs - ts int64 -} - -// func (x *testUnixNanoTimeExt) WriteExt(interface{}) []byte { panic("unsupported") } -// func (x *testUnixNanoTimeExt) ReadExt(interface{}, []byte) { panic("unsupported") } -func (x *testUnixNanoTimeExt) ConvertExt(v interface{}) interface{} { - switch v2 := v.(type) { - case time.Time: - x.ts = v2.UTC().UnixNano() - case *time.Time: - x.ts = v2.UTC().UnixNano() - default: - panic(fmt.Sprintf("unsupported format for time conversion: expecting time.Time; got %T", v)) - } - return &x.ts -} -func (x *testUnixNanoTimeExt) UpdateExt(dest interface{}, v interface{}) { - // fmt.Printf("testUnixNanoTimeExt.UpdateExt: v: %v\n", v) - tt := dest.(*time.Time) - switch v2 := v.(type) { - case int64: - *tt = time.Unix(0, v2).UTC() - case *int64: - *tt = time.Unix(0, *v2).UTC() - case uint64: - *tt = time.Unix(0, int64(v2)).UTC() - case *uint64: - *tt = time.Unix(0, int64(*v2)).UTC() - //case float64: - //case string: - default: - panic(fmt.Sprintf("unsupported format for time conversion: expecting int64/uint64; got %T", v)) - } - // fmt.Printf("testUnixNanoTimeExt.UpdateExt: v: %v, tt: %#v\n", v, tt) -} - -func testVerifyVal(v interface{}, arg testVerifyArg) (v2 interface{}) { - //for python msgpack, - // - all positive integers are unsigned 64-bit ints - // - all floats are float64 - switch iv := v.(type) { - case int8: - if iv >= 0 { - v2 = uint64(iv) - } else { - v2 = int64(iv) - } - case int16: - if iv >= 0 { - v2 = uint64(iv) - } else { - v2 = int64(iv) - } - case int32: - if iv >= 0 { - v2 = uint64(iv) - } else { - v2 = int64(iv) - } - case int64: - if iv >= 0 { - v2 = uint64(iv) - } else { - v2 = int64(iv) - } - case uint8: - v2 = uint64(iv) - case uint16: - v2 = uint64(iv) - case uint32: - v2 = uint64(iv) - case uint64: - v2 = uint64(iv) - case float32: - v2 = float64(iv) - case float64: - v2 = float64(iv) - case []interface{}: - m2 := make([]interface{}, len(iv)) - for j, vj := range iv { - m2[j] = testVerifyVal(vj, arg) - } - v2 = m2 - case map[string]bool: - switch arg { - case testVerifyMapTypeSame: - m2 := make(map[string]bool) - for kj, kv := range iv { - m2[kj] = kv - } - v2 = m2 - case testVerifyMapTypeStrIntf, testVerifyForPython: - m2 := make(map[string]interface{}) - for kj, kv := range iv { - m2[kj] = kv - } - v2 = m2 - case testVerifyMapTypeIntfIntf: - m2 := make(map[interface{}]interface{}) - for kj, kv := range iv { - m2[kj] = kv - } - v2 = m2 - } - case map[string]interface{}: - switch arg { - case testVerifyMapTypeSame: - m2 := make(map[string]interface{}) - for kj, kv := range iv { - m2[kj] = testVerifyVal(kv, arg) - } - v2 = m2 - case testVerifyMapTypeStrIntf, testVerifyForPython: - m2 := make(map[string]interface{}) - for kj, kv := range iv { - m2[kj] = testVerifyVal(kv, arg) - } - v2 = m2 - case testVerifyMapTypeIntfIntf: - m2 := make(map[interface{}]interface{}) - for kj, kv := range iv { - m2[kj] = testVerifyVal(kv, arg) - } - v2 = m2 - } - case map[interface{}]interface{}: - m2 := make(map[interface{}]interface{}) - for kj, kv := range iv { - m2[testVerifyVal(kj, arg)] = testVerifyVal(kv, arg) - } - v2 = m2 - case time.Time: - switch arg { - case testVerifyForPython: - if iv2 := iv.UnixNano(); iv2 >= 0 { - v2 = uint64(iv2) - } else { - v2 = int64(iv2) - } - default: - v2 = v - } - default: - v2 = v - } - return -} - -func testInit() { - gob.Register(new(TestStruc)) - if testInitDebug { - ts0 := newTestStruc(2, false, !testSkipIntf, false) - fmt.Printf("====> depth: %v, ts: %#v\n", 2, ts0) - } - - for _, v := range testHandles { - bh := v.getBasicHandle() - bh.InternString = testInternStr - bh.Canonical = testCanonical - bh.StructToArray = testStructToArray - // mostly doing this for binc - if testWriteNoSymbols { - bh.AsSymbols = AsSymbolNone - } else { - bh.AsSymbols = AsSymbolAll - } - } - - testMsgpackH.RawToString = true - - // testMsgpackH.AddExt(byteSliceTyp, 0, testMsgpackH.BinaryEncodeExt, testMsgpackH.BinaryDecodeExt) - // testMsgpackH.AddExt(timeTyp, 1, testMsgpackH.TimeEncodeExt, testMsgpackH.TimeDecodeExt) - timeEncExt := func(rv reflect.Value) (bs []byte, err error) { - defer panicToErr(&err) - bs = timeExt{}.WriteExt(rv.Interface()) - return - } - timeDecExt := func(rv reflect.Value, bs []byte) (err error) { - defer panicToErr(&err) - timeExt{}.ReadExt(rv.Interface(), bs) - return - } - - // add extensions for msgpack, simple for time.Time, so we can encode/decode same way. - // use different flavors of XXXExt calls, including deprecated ones. - testSimpleH.AddExt(timeTyp, 1, timeEncExt, timeDecExt) - testMsgpackH.SetBytesExt(timeTyp, 1, timeExt{}) - testCborH.SetInterfaceExt(timeTyp, 1, &testUnixNanoTimeExt{}) - testJsonH.SetInterfaceExt(timeTyp, 1, &testUnixNanoTimeExt{}) - - primitives := []interface{}{ - int8(-8), - int16(-1616), - int32(-32323232), - int64(-6464646464646464), - uint8(192), - uint16(1616), - uint32(32323232), - uint64(6464646464646464), - byte(192), - float32(-3232.0), - float64(-6464646464.0), - float32(3232.0), - float64(6464646464.0), - false, - true, - nil, - "someday", - "", - "bytestring", - timeToCompare1, - timeToCompare2, - timeToCompare3, - timeToCompare4, - } - mapsAndStrucs := []interface{}{ - map[string]bool{ - "true": true, - "false": false, - }, - map[string]interface{}{ - "true": "True", - "false": false, - "uint16(1616)": uint16(1616), - }, - //add a complex combo map in here. (map has list which has map) - //note that after the first thing, everything else should be generic. - map[string]interface{}{ - "list": []interface{}{ - int16(1616), - int32(32323232), - true, - float32(-3232.0), - map[string]interface{}{ - "TRUE": true, - "FALSE": false, - }, - []interface{}{true, false}, - }, - "int32": int32(32323232), - "bool": true, - "LONG STRING": "123456789012345678901234567890123456789012345678901234567890", - "SHORT STRING": "1234567890", - }, - map[interface{}]interface{}{ - true: "true", - uint8(138): false, - "false": uint8(200), - }, - newTestStruc(0, false, !testSkipIntf, false), - } - - table = []interface{}{} - table = append(table, primitives...) //0-19 are primitives - table = append(table, primitives) //20 is a list of primitives - table = append(table, mapsAndStrucs...) //21-24 are maps. 25 is a *struct - - tableVerify = make([]interface{}, len(table)) - tableTestNilVerify = make([]interface{}, len(table)) - tablePythonVerify = make([]interface{}, len(table)) - - lp := len(primitives) - av := tableVerify - for i, v := range table { - if i == lp+3 { - av[i] = skipVerifyVal - continue - } - //av[i] = testVerifyVal(v, testVerifyMapTypeSame) - switch v.(type) { - case []interface{}: - av[i] = testVerifyVal(v, testVerifyMapTypeSame) - case map[string]interface{}: - av[i] = testVerifyVal(v, testVerifyMapTypeSame) - case map[interface{}]interface{}: - av[i] = testVerifyVal(v, testVerifyMapTypeSame) - default: - av[i] = v - } - } - - av = tableTestNilVerify - for i, v := range table { - if i > lp+3 { - av[i] = skipVerifyVal - continue - } - av[i] = testVerifyVal(v, testVerifyMapTypeStrIntf) - } - - av = tablePythonVerify - for i, v := range table { - if i > lp+3 { - av[i] = skipVerifyVal - continue - } - av[i] = testVerifyVal(v, testVerifyForPython) - } - - tablePythonVerify = tablePythonVerify[:24] -} - -func testUnmarshal(v interface{}, data []byte, h Handle) (err error) { - return testCodecDecode(data, v, h) -} - -func testMarshal(v interface{}, h Handle) (bs []byte, err error) { - return testCodecEncode(v, nil, testByteBuf, h) -} - -func testMarshalErr(v interface{}, h Handle, t *testing.T, name string) (bs []byte, err error) { - if bs, err = testMarshal(v, h); err != nil { - logT(t, "Error encoding %s: %v, Err: %v", name, v, err) - t.FailNow() - } - return -} - -func testUnmarshalErr(v interface{}, data []byte, h Handle, t *testing.T, name string) (err error) { - if err = testUnmarshal(v, data, h); err != nil { - logT(t, "Error Decoding into %s: %v, Err: %v", name, v, err) - t.FailNow() - } - return -} - -// doTestCodecTableOne allows us test for different variations based on arguments passed. -func doTestCodecTableOne(t *testing.T, testNil bool, h Handle, - vs []interface{}, vsVerify []interface{}) { - //if testNil, then just test for when a pointer to a nil interface{} is passed. It should work. - //Current setup allows us test (at least manually) the nil interface or typed interface. - logT(t, "================ TestNil: %v ================\n", testNil) - for i, v0 := range vs { - logT(t, "..............................................") - logT(t, " Testing: #%d:, %T, %#v\n", i, v0, v0) - b0, err := testMarshalErr(v0, h, t, "v0") - if err != nil { - continue - } - if h.isBinary() { - logT(t, " Encoded bytes: len: %v, %v\n", len(b0), b0) - } else { - logT(t, " Encoded string: len: %v, %v\n", len(string(b0)), string(b0)) - // println("########### encoded string: " + string(b0)) - } - var v1 interface{} - - if testNil { - err = testUnmarshal(&v1, b0, h) - } else { - if v0 != nil { - v0rt := reflect.TypeOf(v0) // ptr - rv1 := reflect.New(v0rt) - err = testUnmarshal(rv1.Interface(), b0, h) - v1 = rv1.Elem().Interface() - // v1 = reflect.Indirect(reflect.ValueOf(v1)).Interface() - } - } - - logT(t, " v1 returned: %T, %#v", v1, v1) - // if v1 != nil { - // logT(t, " v1 returned: %T, %#v", v1, v1) - // //we always indirect, because ptr to typed value may be passed (if not testNil) - // v1 = reflect.Indirect(reflect.ValueOf(v1)).Interface() - // } - if err != nil { - logT(t, "-------- Error: %v. Partial return: %v", err, v1) - failT(t) - continue - } - v0check := vsVerify[i] - if v0check == skipVerifyVal { - logT(t, " Nil Check skipped: Decoded: %T, %#v\n", v1, v1) - continue - } - - if err = deepEqual(v0check, v1); err == nil { - logT(t, "++++++++ Before and After marshal matched\n") - } else { - // logT(t, "-------- Before and After marshal do not match: Error: %v"+ - // " ====> GOLDEN: (%T) %#v, DECODED: (%T) %#v\n", err, v0check, v0check, v1, v1) - logT(t, "-------- Before and After marshal do not match: Error: %v", err) - logT(t, " ....... GOLDEN: (%T) %#v", v0check, v0check) - logT(t, " ....... DECODED: (%T) %#v", v1, v1) - failT(t) - } - } -} - -func testCodecTableOne(t *testing.T, h Handle) { - testOnce.Do(testInitAll) - // func TestMsgpackAllExperimental(t *testing.T) { - // dopts := testDecOpts(nil, nil, false, true, true), - - idxTime, numPrim, numMap := 19, 23, 4 - //println("#################") - switch v := h.(type) { - case *MsgpackHandle: - var oldWriteExt, oldRawToString bool - oldWriteExt, v.WriteExt = v.WriteExt, true - oldRawToString, v.RawToString = v.RawToString, true - doTestCodecTableOne(t, false, h, table, tableVerify) - v.WriteExt, v.RawToString = oldWriteExt, oldRawToString - case *JsonHandle: - //skip []interface{} containing time.Time, as it encodes as a number, but cannot decode back to time.Time. - //As there is no real support for extension tags in json, this must be skipped. - doTestCodecTableOne(t, false, h, table[:numPrim], tableVerify[:numPrim]) - doTestCodecTableOne(t, false, h, table[numPrim+1:], tableVerify[numPrim+1:]) - default: - doTestCodecTableOne(t, false, h, table, tableVerify) - } - // func TestMsgpackAll(t *testing.T) { - - // //skip []interface{} containing time.Time - // doTestCodecTableOne(t, false, h, table[:numPrim], tableVerify[:numPrim]) - // doTestCodecTableOne(t, false, h, table[numPrim+1:], tableVerify[numPrim+1:]) - // func TestMsgpackNilStringMap(t *testing.T) { - var oldMapType reflect.Type - v := h.getBasicHandle() - - oldMapType, v.MapType = v.MapType, testMapStrIntfTyp - - //skip time.Time, []interface{} containing time.Time, last map, and newStruc - doTestCodecTableOne(t, true, h, table[:idxTime], tableTestNilVerify[:idxTime]) - doTestCodecTableOne(t, true, h, table[numPrim+1:numPrim+numMap], tableTestNilVerify[numPrim+1:numPrim+numMap]) - - v.MapType = oldMapType - - // func TestMsgpackNilIntf(t *testing.T) { - - //do newTestStruc and last element of map - doTestCodecTableOne(t, true, h, table[numPrim+numMap:], tableTestNilVerify[numPrim+numMap:]) - //TODO? What is this one? - //doTestCodecTableOne(t, true, h, table[17:18], tableTestNilVerify[17:18]) -} - -func testCodecMiscOne(t *testing.T, h Handle) { - testOnce.Do(testInitAll) - b, err := testMarshalErr(32, h, t, "32") - // Cannot do this nil one, because faster type assertion decoding will panic - // var i *int32 - // if err = testUnmarshal(b, i, nil); err == nil { - // logT(t, "------- Expecting error because we cannot unmarshal to int32 nil ptr") - // t.FailNow() - // } - var i2 int32 = 0 - err = testUnmarshalErr(&i2, b, h, t, "int32-ptr") - if i2 != int32(32) { - logT(t, "------- didn't unmarshal to 32: Received: %d", i2) - t.FailNow() - } - - // func TestMsgpackDecodePtr(t *testing.T) { - ts := newTestStruc(0, false, !testSkipIntf, false) - b, err = testMarshalErr(ts, h, t, "pointer-to-struct") - if len(b) < 40 { - logT(t, "------- Size must be > 40. Size: %d", len(b)) - t.FailNow() - } - if h.isBinary() { - logT(t, "------- b: %v", b) - } else { - logT(t, "------- b: %s", b) - } - ts2 := new(TestStruc) - err = testUnmarshalErr(ts2, b, h, t, "pointer-to-struct") - if ts2.I64 != math.MaxInt64*2/3 { - logT(t, "------- Unmarshal wrong. Expect I64 = 64. Got: %v", ts2.I64) - t.FailNow() - } - - // func TestMsgpackIntfDecode(t *testing.T) { - m := map[string]int{"A": 2, "B": 3} - p := []interface{}{m} - bs, err := testMarshalErr(p, h, t, "p") - - m2 := map[string]int{} - p2 := []interface{}{m2} - err = testUnmarshalErr(&p2, bs, h, t, "&p2") - - if m2["A"] != 2 || m2["B"] != 3 { - logT(t, "m2 not as expected: expecting: %v, got: %v", m, m2) - t.FailNow() - } - // log("m: %v, m2: %v, p: %v, p2: %v", m, m2, p, p2) - checkEqualT(t, p, p2, "p=p2") - checkEqualT(t, m, m2, "m=m2") - if err = deepEqual(p, p2); err == nil { - logT(t, "p and p2 match") - } else { - logT(t, "Not Equal: %v. p: %v, p2: %v", err, p, p2) - t.FailNow() - } - if err = deepEqual(m, m2); err == nil { - logT(t, "m and m2 match") - } else { - logT(t, "Not Equal: %v. m: %v, m2: %v", err, m, m2) - t.FailNow() - } - - // func TestMsgpackDecodeStructSubset(t *testing.T) { - // test that we can decode a subset of the stream - mm := map[string]interface{}{"A": 5, "B": 99, "C": 333} - bs, err = testMarshalErr(mm, h, t, "mm") - type ttt struct { - A uint8 - C int32 - } - var t2 ttt - testUnmarshalErr(&t2, bs, h, t, "t2") - t3 := ttt{5, 333} - checkEqualT(t, t2, t3, "t2=t3") - - // println(">>>>>") - // test simple arrays, non-addressable arrays, slices - type tarr struct { - A int64 - B [3]int64 - C []byte - D [3]byte - } - var tarr0 = tarr{1, [3]int64{2, 3, 4}, []byte{4, 5, 6}, [3]byte{7, 8, 9}} - // test both pointer and non-pointer (value) - for _, tarr1 := range []interface{}{tarr0, &tarr0} { - bs, err = testMarshalErr(tarr1, h, t, "tarr1") - if err != nil { - logT(t, "Error marshalling: %v", err) - t.FailNow() - } - if _, ok := h.(*JsonHandle); ok { - logT(t, "Marshal as: %s", bs) - } - var tarr2 tarr - testUnmarshalErr(&tarr2, bs, h, t, "tarr2") - checkEqualT(t, tarr0, tarr2, "tarr0=tarr2") - // fmt.Printf(">>>> err: %v. tarr1: %v, tarr2: %v\n", err, tarr0, tarr2) - } - - // test byte array, even if empty (msgpack only) - if h == testMsgpackH { - type ystruct struct { - Anarray []byte - } - var ya = ystruct{} - testUnmarshalErr(&ya, []byte{0x91, 0x90}, h, t, "ya") - } -} - -func testCodecEmbeddedPointer(t *testing.T, h Handle) { - testOnce.Do(testInitAll) - type Z int - type A struct { - AnInt int - } - type B struct { - *Z - *A - MoreInt int - } - var z Z = 4 - x1 := &B{&z, &A{5}, 6} - bs, err := testMarshalErr(x1, h, t, "x1") - // fmt.Printf("buf: len(%v): %x\n", buf.Len(), buf.Bytes()) - var x2 = new(B) - err = testUnmarshalErr(x2, bs, h, t, "x2") - err = checkEqualT(t, x1, x2, "x1=x2") - _ = err -} - -func testCodecUnderlyingType(t *testing.T, h Handle) { - testOnce.Do(testInitAll) - // Manual Test. - // Run by hand, with accompanying print statements in fast-path.go - // to ensure that the fast functions are called. - type T1 map[string]string - v := T1{"1": "1s", "2": "2s"} - var bs []byte - var err error - NewEncoderBytes(&bs, h).MustEncode(v) - if err != nil { - logT(t, "Error during encode: %v", err) - failT(t) - } - var v2 T1 - NewDecoderBytes(bs, h).MustDecode(&v2) - if err != nil { - logT(t, "Error during decode: %v", err) - failT(t) - } -} - -func testCodecChan(t *testing.T, h Handle) { - // - send a slice []*int64 (sl1) into an chan (ch1) with cap > len(s1) - // - encode ch1 as a stream array - // - decode a chan (ch2), with cap > len(s1) from the stream array - // - receive from ch2 into slice sl2 - // - compare sl1 and sl2 - // - do this for codecs: json, cbor (covers all types) - sl1 := make([]*int64, 4) - for i := range sl1 { - var j int64 = int64(i) - sl1[i] = &j - } - ch1 := make(chan *int64, 4) - for _, j := range sl1 { - ch1 <- j - } - var bs []byte - NewEncoderBytes(&bs, h).MustEncode(ch1) - // if !h.isBinary() { - // fmt.Printf("before: len(ch1): %v, bs: %s\n", len(ch1), bs) - // } - // var ch2 chan *int64 // this will block if json, etc. - ch2 := make(chan *int64, 8) - NewDecoderBytes(bs, h).MustDecode(&ch2) - // logT(t, "Len(ch2): %v", len(ch2)) - // fmt.Printf("after: len(ch2): %v, ch2: %v\n", len(ch2), ch2) - close(ch2) - var sl2 []*int64 - for j := range ch2 { - sl2 = append(sl2, j) - } - if err := deepEqual(sl1, sl2); err != nil { - logT(t, "Not Match: %v; len: %v, %v", err, len(sl1), len(sl2)) - failT(t) - } -} - -func testCodecRpcOne(t *testing.T, rr Rpc, h Handle, doRequest bool, exitSleepMs time.Duration, -) (port int) { - testOnce.Do(testInitAll) - if testSkipRPCTests { - return - } - // rpc needs EOF, which is sent via a panic, and so must be recovered. - if !recoverPanicToErr { - logT(t, "EXPECTED. set recoverPanicToErr=true, since rpc needs EOF") - t.FailNow() - } - srv := rpc.NewServer() - srv.Register(testRpcInt) - ln, err := net.Listen("tcp", "127.0.0.1:0") - // log("listener: %v", ln.Addr()) - checkErrT(t, err) - port = (ln.Addr().(*net.TCPAddr)).Port - // var opts *DecoderOptions - // opts := testDecOpts - // opts.MapType = mapStrIntfTyp - // opts.RawToString = false - serverExitChan := make(chan bool, 1) - var serverExitFlag uint64 = 0 - serverFn := func() { - for { - conn1, err1 := ln.Accept() - // if err1 != nil { - // //fmt.Printf("accept err1: %v\n", err1) - // continue - // } - if atomic.LoadUint64(&serverExitFlag) == 1 { - serverExitChan <- true - conn1.Close() - return // exit serverFn goroutine - } - if err1 == nil { - var sc rpc.ServerCodec = rr.ServerCodec(conn1, h) - srv.ServeCodec(sc) - } - } - } - - clientFn := func(cc rpc.ClientCodec) { - cl := rpc.NewClientWithCodec(cc) - defer cl.Close() - // defer func() { println("##### client closing"); cl.Close() }() - var up, sq, mult int - var rstr string - // log("Calling client") - checkErrT(t, cl.Call("TestRpcInt.Update", 5, &up)) - // log("Called TestRpcInt.Update") - checkEqualT(t, testRpcInt.i, 5, "testRpcInt.i=5") - checkEqualT(t, up, 5, "up=5") - checkErrT(t, cl.Call("TestRpcInt.Square", 1, &sq)) - checkEqualT(t, sq, 25, "sq=25") - checkErrT(t, cl.Call("TestRpcInt.Mult", 20, &mult)) - checkEqualT(t, mult, 100, "mult=100") - checkErrT(t, cl.Call("TestRpcInt.EchoStruct", TestABC{"Aa", "Bb", "Cc"}, &rstr)) - checkEqualT(t, rstr, fmt.Sprintf("%#v", TestABC{"Aa", "Bb", "Cc"}), "rstr=") - checkErrT(t, cl.Call("TestRpcInt.Echo123", []string{"A1", "B2", "C3"}, &rstr)) - checkEqualT(t, rstr, fmt.Sprintf("%#v", []string{"A1", "B2", "C3"}), "rstr=") - } - - connFn := func() (bs net.Conn) { - // log("calling f1") - bs, err2 := net.Dial(ln.Addr().Network(), ln.Addr().String()) - //fmt.Printf("f1. bs: %v, err2: %v\n", bs, err2) - checkErrT(t, err2) - return - } - - exitFn := func() { - atomic.StoreUint64(&serverExitFlag, 1) - bs := connFn() - <-serverExitChan - bs.Close() - // serverExitChan <- true - } - - go serverFn() - runtime.Gosched() - //time.Sleep(100 * time.Millisecond) - if exitSleepMs == 0 { - defer ln.Close() - defer exitFn() - } - if doRequest { - bs := connFn() - cc := rr.ClientCodec(bs, h) - clientFn(cc) - } - if exitSleepMs != 0 { - go func() { - defer ln.Close() - time.Sleep(exitSleepMs) - exitFn() - }() - } - return -} - -func doTestMapEncodeForCanonical(t *testing.T, name string, h Handle) { - v1 := map[string]interface{}{ - "a": 1, - "b": "hello", - "c": map[string]interface{}{ - "c/a": 1, - "c/b": "world", - "c/c": []int{1, 2, 3, 4}, - "c/d": map[string]interface{}{ - "c/d/a": "fdisajfoidsajfopdjsaopfjdsapofda", - "c/d/b": "fdsafjdposakfodpsakfopdsakfpodsakfpodksaopfkdsopafkdopsa", - "c/d/c": "poir02 ir30qif4p03qir0pogjfpoaerfgjp ofke[padfk[ewapf kdp[afep[aw", - "c/d/d": "fdsopafkd[sa f-32qor-=4qeof -afo-erfo r-eafo 4e- o r4-qwo ag", - "c/d/e": "kfep[a sfkr0[paf[a foe-[wq ewpfao-q ro3-q ro-4qof4-qor 3-e orfkropzjbvoisdb", - "c/d/f": "", - }, - "c/e": map[int]string{ - 1: "1", - 22: "22", - 333: "333", - 4444: "4444", - 55555: "55555", - }, - "c/f": map[string]int{ - "1": 1, - "22": 22, - "333": 333, - "4444": 4444, - "55555": 55555, - }, - }, - } - var v2 map[string]interface{} - var b1, b2 []byte - - // encode v1 into b1, decode b1 into v2, encode v2 into b2, compare b1 and b2 - - bh := h.getBasicHandle() - if !bh.Canonical { - bh.Canonical = true - defer func() { bh.Canonical = false }() - } - - e1 := NewEncoderBytes(&b1, h) - e1.MustEncode(v1) - d1 := NewDecoderBytes(b1, h) - d1.MustDecode(&v2) - e2 := NewEncoderBytes(&b2, h) - e2.MustEncode(v2) - if !bytes.Equal(b1, b2) { - logT(t, "Unequal bytes: %v VS %v", b1, b2) - t.FailNow() - } -} - -// Comprehensive testing that generates data encoded from python handle (cbor, msgpack), -// and validates that our code can read and write it out accordingly. -// We keep this unexported here, and put actual test in ext_dep_test.go. -// This way, it can be excluded by excluding file completely. -func doTestPythonGenStreams(t *testing.T, name string, h Handle) { - logT(t, "TestPythonGenStreams-%v", name) - tmpdir, err := ioutil.TempDir("", "golang-"+name+"-test") - if err != nil { - logT(t, "-------- Unable to create temp directory\n") - t.FailNow() - } - defer os.RemoveAll(tmpdir) - logT(t, "tmpdir: %v", tmpdir) - cmd := exec.Command("python", "test.py", "testdata", tmpdir) - //cmd.Stdin = strings.NewReader("some input") - //cmd.Stdout = &out - var cmdout []byte - if cmdout, err = cmd.CombinedOutput(); err != nil { - logT(t, "-------- Error running test.py testdata. Err: %v", err) - logT(t, " %v", string(cmdout)) - t.FailNow() - } - - bh := h.getBasicHandle() - - oldMapType := bh.MapType - for i, v := range tablePythonVerify { - // if v == uint64(0) && h == testMsgpackH { - // v = int64(0) - // } - bh.MapType = oldMapType - //load up the golden file based on number - //decode it - //compare to in-mem object - //encode it again - //compare to output stream - logT(t, "..............................................") - logT(t, " Testing: #%d: %T, %#v\n", i, v, v) - var bss []byte - bss, err = ioutil.ReadFile(filepath.Join(tmpdir, strconv.Itoa(i)+"."+name+".golden")) - if err != nil { - logT(t, "-------- Error reading golden file: %d. Err: %v", i, err) - failT(t) - continue - } - bh.MapType = testMapStrIntfTyp - - var v1 interface{} - if err = testUnmarshal(&v1, bss, h); err != nil { - logT(t, "-------- Error decoding stream: %d: Err: %v", i, err) - failT(t) - continue - } - if v == skipVerifyVal { - continue - } - //no need to indirect, because we pass a nil ptr, so we already have the value - //if v1 != nil { v1 = reflect.Indirect(reflect.ValueOf(v1)).Interface() } - if err = deepEqual(v, v1); err == nil { - logT(t, "++++++++ Objects match: %T, %v", v, v) - } else { - logT(t, "-------- Objects do not match: %v. Source: %T. Decoded: %T", err, v, v1) - logT(t, "-------- GOLDEN: %#v", v) - // logT(t, "-------- DECODED: %#v <====> %#v", v1, reflect.Indirect(reflect.ValueOf(v1)).Interface()) - logT(t, "-------- DECODED: %#v <====> %#v", v1, reflect.Indirect(reflect.ValueOf(v1)).Interface()) - failT(t) - } - bsb, err := testMarshal(v1, h) - if err != nil { - logT(t, "Error encoding to stream: %d: Err: %v", i, err) - failT(t) - continue - } - if err = deepEqual(bsb, bss); err == nil { - logT(t, "++++++++ Bytes match") - } else { - logT(t, "???????? Bytes do not match. %v.", err) - xs := "--------" - if reflect.ValueOf(v).Kind() == reflect.Map { - xs = " " - logT(t, "%s It's a map. Ok that they don't match (dependent on ordering).", xs) - } else { - logT(t, "%s It's not a map. They should match.", xs) - failT(t) - } - logT(t, "%s FROM_FILE: %4d] %v", xs, len(bss), bss) - logT(t, "%s ENCODED: %4d] %v", xs, len(bsb), bsb) - } - } - bh.MapType = oldMapType -} - -// To test MsgpackSpecRpc, we test 3 scenarios: -// - Go Client to Go RPC Service (contained within TestMsgpackRpcSpec) -// - Go client to Python RPC Service (contained within doTestMsgpackRpcSpecGoClientToPythonSvc) -// - Python Client to Go RPC Service (contained within doTestMsgpackRpcSpecPythonClientToGoSvc) -// -// This allows us test the different calling conventions -// - Go Service requires only one argument -// - Python Service allows multiple arguments - -func doTestMsgpackRpcSpecGoClientToPythonSvc(t *testing.T) { - if testSkipRPCTests { - return - } - // openPorts are between 6700 and 6800 - r := rand.New(rand.NewSource(time.Now().UnixNano())) - openPort := strconv.FormatInt(6700+r.Int63n(99), 10) - // openPort := "6792" - cmd := exec.Command("python", "test.py", "rpc-server", openPort, "4") - checkErrT(t, cmd.Start()) - bs, err2 := net.Dial("tcp", ":"+openPort) - for i := 0; i < 10 && err2 != nil; i++ { - time.Sleep(50 * time.Millisecond) // time for python rpc server to start - bs, err2 = net.Dial("tcp", ":"+openPort) - } - checkErrT(t, err2) - cc := MsgpackSpecRpc.ClientCodec(bs, testMsgpackH) - cl := rpc.NewClientWithCodec(cc) - defer cl.Close() - var rstr string - checkErrT(t, cl.Call("EchoStruct", TestABC{"Aa", "Bb", "Cc"}, &rstr)) - //checkEqualT(t, rstr, "{'A': 'Aa', 'B': 'Bb', 'C': 'Cc'}") - var mArgs MsgpackSpecRpcMultiArgs = []interface{}{"A1", "B2", "C3"} - checkErrT(t, cl.Call("Echo123", mArgs, &rstr)) - checkEqualT(t, rstr, "1:A1 2:B2 3:C3", "rstr=") - cmd.Process.Kill() -} - -func doTestMsgpackRpcSpecPythonClientToGoSvc(t *testing.T) { - if testSkipRPCTests { - return - } - port := testCodecRpcOne(t, MsgpackSpecRpc, testMsgpackH, false, 1*time.Second) - //time.Sleep(1000 * time.Millisecond) - cmd := exec.Command("python", "test.py", "rpc-client-go-service", strconv.Itoa(port)) - var cmdout []byte - var err error - if cmdout, err = cmd.CombinedOutput(); err != nil { - logT(t, "-------- Error running test.py rpc-client-go-service. Err: %v", err) - logT(t, " %v", string(cmdout)) - t.FailNow() - } - checkEqualT(t, string(cmdout), - fmt.Sprintf("%#v\n%#v\n", []string{"A1", "B2", "C3"}, TestABC{"Aa", "Bb", "Cc"}), "cmdout=") -} - -func TestBincCodecsTable(t *testing.T) { - testCodecTableOne(t, testBincH) -} - -func TestBincCodecsMisc(t *testing.T) { - testCodecMiscOne(t, testBincH) -} - -func TestBincCodecsEmbeddedPointer(t *testing.T) { - testCodecEmbeddedPointer(t, testBincH) -} - -func TestSimpleCodecsTable(t *testing.T) { - testCodecTableOne(t, testSimpleH) -} - -func TestSimpleCodecsMisc(t *testing.T) { - testCodecMiscOne(t, testSimpleH) -} - -func TestSimpleCodecsEmbeddedPointer(t *testing.T) { - testCodecEmbeddedPointer(t, testSimpleH) -} - -func TestMsgpackCodecsTable(t *testing.T) { - testCodecTableOne(t, testMsgpackH) -} - -func TestMsgpackCodecsMisc(t *testing.T) { - testCodecMiscOne(t, testMsgpackH) -} - -func TestMsgpackCodecsEmbeddedPointer(t *testing.T) { - testCodecEmbeddedPointer(t, testMsgpackH) -} - -func TestCborCodecsTable(t *testing.T) { - testCodecTableOne(t, testCborH) -} - -func TestCborCodecsMisc(t *testing.T) { - testCodecMiscOne(t, testCborH) -} - -func TestCborCodecsEmbeddedPointer(t *testing.T) { - testCodecEmbeddedPointer(t, testCborH) -} - -func TestCborMapEncodeForCanonical(t *testing.T) { - doTestMapEncodeForCanonical(t, "cbor", testCborH) -} - -func TestJsonCodecsTable(t *testing.T) { - testCodecTableOne(t, testJsonH) -} - -func TestJsonCodecsMisc(t *testing.T) { - testCodecMiscOne(t, testJsonH) -} - -func TestJsonCodecsEmbeddedPointer(t *testing.T) { - testCodecEmbeddedPointer(t, testJsonH) -} - -func TestJsonCodecChan(t *testing.T) { - testCodecChan(t, testJsonH) -} - -func TestCborCodecChan(t *testing.T) { - testCodecChan(t, testCborH) -} - -// ----- RPC ----- - -func TestBincRpcGo(t *testing.T) { - testCodecRpcOne(t, GoRpc, testBincH, true, 0) -} - -func TestSimpleRpcGo(t *testing.T) { - testCodecRpcOne(t, GoRpc, testSimpleH, true, 0) -} - -func TestMsgpackRpcGo(t *testing.T) { - testCodecRpcOne(t, GoRpc, testMsgpackH, true, 0) -} - -func TestCborRpcGo(t *testing.T) { - testCodecRpcOne(t, GoRpc, testCborH, true, 0) -} - -func TestJsonRpcGo(t *testing.T) { - testCodecRpcOne(t, GoRpc, testJsonH, true, 0) -} - -func TestMsgpackRpcSpec(t *testing.T) { - testCodecRpcOne(t, MsgpackSpecRpc, testMsgpackH, true, 0) -} - -func TestBincUnderlyingType(t *testing.T) { - testCodecUnderlyingType(t, testBincH) -} - -// TODO: -// Add Tests for: -// - decoding empty list/map in stream into a nil slice/map -// - binary(M|Unm)arsher support for time.Time (e.g. cbor encoding) -// - text(M|Unm)arshaler support for time.Time (e.g. json encoding) -// - non fast-path scenarios e.g. map[string]uint16, []customStruct. -// Expand cbor to include indefinite length stuff for this non-fast-path types. -// This may not be necessary, since we have the manual tests (fastpathEnabled=false) to test/validate with. -// - CodecSelfer -// Ensure it is called when (en|de)coding interface{} or reflect.Value (2 different codepaths). -// - interfaces: textMarshaler, binaryMarshaler, codecSelfer -// - struct tags: -// on anonymous fields, _struct (all fields), etc -// - codecgen of struct containing channels. -// -// Cleanup tests: -// - The are brittle in their handling of validation and skipping diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/README.md b/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/README.md deleted file mode 100644 index 3ae8a056f..000000000 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# codecgen tool - -Generate is given a list of *.go files to parse, and an output file (fout), -codecgen will create an output file __file.go__ which -contains `codec.Selfer` implementations for the named types found -in the files parsed. - -Using codecgen is very straightforward. - -**Download and install the tool** - -`go get -u github.com/ugorji/go/codec/codecgen` - -**Run the tool on your files** - -The command line format is: - -`codecgen [options] (-o outfile) (infile ...)` - -```sh -% codecgen -? -Usage of codecgen: - -c="github.com/ugorji/go/codec": codec path - -o="": out file - -r=".*": regex for type name to match - -rt="": tags for go run - -t="": build tag to put in file - -u=false: Use unsafe, e.g. to avoid unnecessary allocation on []byte->string - -x=false: keep temp file - -% codecgen -o values_codecgen.go values.go values2.go moretypedefs.go -``` - -Please see the [blog article](http://ugorji.net/blog/go-codecgen) -for more information on how to use the tool. - diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/gen.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/gen.go deleted file mode 100644 index f370b4c79..000000000 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/gen.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved. -// Use of this source code is governed by a MIT license found in the LICENSE file. - -// codecgen generates codec.Selfer implementations for a set of types. -package main - -import ( - "bufio" - "bytes" - "errors" - "flag" - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/token" - "math/rand" - "os" - "os/exec" - "path/filepath" - "regexp" - "strconv" - "text/template" - "time" -) - -const genCodecPkg = "codec1978" // keep this in sync with codec.genCodecPkg - -const genFrunMainTmpl = `//+build ignore - -package main -{{ if .Types }}import "{{ .ImportPath }}"{{ end }} -func main() { - {{ $.PackageName }}.CodecGenTempWrite{{ .RandString }}() -} -` - -// const genFrunPkgTmpl = `//+build codecgen -const genFrunPkgTmpl = ` -package {{ $.PackageName }} - -import ( - {{ if not .CodecPkgFiles }}{{ .CodecPkgName }} "{{ .CodecImportPath }}"{{ end }} - "os" - "reflect" - "bytes" - "strings" - "go/format" -) - -func CodecGenTempWrite{{ .RandString }}() { - fout, err := os.Create("{{ .OutFile }}") - if err != nil { - panic(err) - } - defer fout.Close() - var out bytes.Buffer - - var typs []reflect.Type -{{ range $index, $element := .Types }} - var t{{ $index }} {{ . }} - typs = append(typs, reflect.TypeOf(t{{ $index }})) -{{ end }} - {{ if not .CodecPkgFiles }}{{ .CodecPkgName }}.{{ end }}Gen(&out, "{{ .BuildTag }}", "{{ .PackageName }}", "{{ .RandString }}", {{ .UseUnsafe }}, {{ if not .CodecPkgFiles }}{{ .CodecPkgName }}.{{ end }}NewTypeInfos(strings.Split("{{ .StructTags }}", ",")), typs...) - bout, err := format.Source(out.Bytes()) - if err != nil { - fout.Write(out.Bytes()) - panic(err) - } - fout.Write(bout) -} - -` - -// Generate is given a list of *.go files to parse, and an output file (fout). -// -// It finds all types T in the files, and it creates 2 tmp files (frun). -// - main package file passed to 'go run' -// - package level file which calls *genRunner.Selfer to write Selfer impls for each T. -// We use a package level file so that it can reference unexported types in the package being worked on. -// Tool then executes: "go run __frun__" which creates fout. -// fout contains Codec(En|De)codeSelf implementations for every type T. -// -func Generate(outfile, buildTag, codecPkgPath string, uid int64, useUnsafe bool, goRunTag string, - st string, regexName *regexp.Regexp, deleteTempFile bool, infiles ...string) (err error) { - // For each file, grab AST, find each type, and write a call to it. - if len(infiles) == 0 { - return - } - if outfile == "" || codecPkgPath == "" { - err = errors.New("outfile and codec package path cannot be blank") - return - } - if uid < 0 { - uid = -uid - } - if uid == 0 { - rr := rand.New(rand.NewSource(time.Now().UnixNano())) - uid = 101 + rr.Int63n(9777) - } - // We have to parse dir for package, before opening the temp file for writing (else ImportDir fails). - // Also, ImportDir(...) must take an absolute path. - lastdir := filepath.Dir(outfile) - absdir, err := filepath.Abs(lastdir) - if err != nil { - return - } - pkg, err := build.Default.ImportDir(absdir, build.AllowBinary) - if err != nil { - return - } - type tmplT struct { - CodecPkgName string - CodecImportPath string - ImportPath string - OutFile string - PackageName string - RandString string - BuildTag string - StructTags string - Types []string - CodecPkgFiles bool - UseUnsafe bool - } - tv := tmplT{ - CodecPkgName: genCodecPkg, - OutFile: outfile, - CodecImportPath: codecPkgPath, - BuildTag: buildTag, - UseUnsafe: useUnsafe, - RandString: strconv.FormatInt(uid, 10), - StructTags: st, - } - tv.ImportPath = pkg.ImportPath - if tv.ImportPath == tv.CodecImportPath { - tv.CodecPkgFiles = true - tv.CodecPkgName = "codec" - } - astfiles := make([]*ast.File, len(infiles)) - for i, infile := range infiles { - if filepath.Dir(infile) != lastdir { - err = errors.New("in files must all be in same directory as outfile") - return - } - fset := token.NewFileSet() - astfiles[i], err = parser.ParseFile(fset, infile, nil, 0) - if err != nil { - return - } - if i == 0 { - tv.PackageName = astfiles[i].Name.Name - if tv.PackageName == "main" { - // codecgen cannot be run on types in the 'main' package. - // A temporary 'main' package must be created, and should reference the fully built - // package containing the types. - // Also, the temporary main package will conflict with the main package which already has a main method. - err = errors.New("codecgen cannot be run on types in the 'main' package") - return - } - } - } - - for _, f := range astfiles { - for _, d := range f.Decls { - if gd, ok := d.(*ast.GenDecl); ok { - for _, dd := range gd.Specs { - if td, ok := dd.(*ast.TypeSpec); ok { - // if len(td.Name.Name) == 0 || td.Name.Name[0] > 'Z' || td.Name.Name[0] < 'A' { - if len(td.Name.Name) == 0 { - continue - } - - // only generate for: - // struct: StructType - // primitives (numbers, bool, string): Ident - // map: MapType - // slice, array: ArrayType - // chan: ChanType - // do not generate: - // FuncType, InterfaceType, StarExpr (ptr), etc - switch td.Type.(type) { - case *ast.StructType, *ast.Ident, *ast.MapType, *ast.ArrayType, *ast.ChanType: - if regexName.FindStringIndex(td.Name.Name) != nil { - tv.Types = append(tv.Types, td.Name.Name) - } - } - } - } - } - } - } - - if len(tv.Types) == 0 { - return - } - - // we cannot use ioutil.TempFile, because we cannot guarantee the file suffix (.go). - // Also, we cannot create file in temp directory, - // because go run will not work (as it needs to see the types here). - // Consequently, create the temp file in the current directory, and remove when done. - - // frun, err = ioutil.TempFile("", "codecgen-") - // frunName := filepath.Join(os.TempDir(), "codecgen-"+strconv.FormatInt(time.Now().UnixNano(), 10)+".go") - - frunMainName := "codecgen-main-" + tv.RandString + ".generated.go" - frunPkgName := "codecgen-pkg-" + tv.RandString + ".generated.go" - if deleteTempFile { - defer os.Remove(frunMainName) - defer os.Remove(frunPkgName) - } - // var frunMain, frunPkg *os.File - if _, err = gen1(frunMainName, genFrunMainTmpl, &tv); err != nil { - return - } - if _, err = gen1(frunPkgName, genFrunPkgTmpl, &tv); err != nil { - return - } - - // remove outfile, so "go run ..." will not think that types in outfile already exist. - os.Remove(outfile) - - // execute go run frun - cmd := exec.Command("go", "run", "-tags="+goRunTag, frunMainName) //, frunPkg.Name()) - var buf bytes.Buffer - cmd.Stdout = &buf - cmd.Stderr = &buf - if err = cmd.Run(); err != nil { - err = fmt.Errorf("error running 'go run %s': %v, console: %s", - frunMainName, err, buf.Bytes()) - return - } - os.Stdout.Write(buf.Bytes()) - return -} - -func gen1(frunName, tmplStr string, tv interface{}) (frun *os.File, err error) { - os.Remove(frunName) - if frun, err = os.Create(frunName); err != nil { - return - } - defer frun.Close() - - t := template.New("") - if t, err = t.Parse(tmplStr); err != nil { - return - } - bw := bufio.NewWriter(frun) - if err = t.Execute(bw, tv); err != nil { - return - } - if err = bw.Flush(); err != nil { - return - } - return -} - -func main() { - o := flag.String("o", "", "out file") - c := flag.String("c", genCodecPath, "codec path") - t := flag.String("t", "", "build tag to put in file") - r := flag.String("r", ".*", "regex for type name to match") - rt := flag.String("rt", "", "tags for go run") - st := flag.String("st", "codec,json", "struct tag keys to introspect") - x := flag.Bool("x", false, "keep temp file") - u := flag.Bool("u", false, "Use unsafe, e.g. to avoid unnecessary allocation on []byte->string") - d := flag.Int64("d", 0, "random identifier for use in generated code") - flag.Parse() - if err := Generate(*o, *t, *c, *d, *u, *rt, *st, - regexp.MustCompile(*r), !*x, flag.Args()...); err != nil { - fmt.Fprintf(os.Stderr, "codecgen error: %v\n", err) - os.Exit(1) - } -} diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/z.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/z.go deleted file mode 100644 index e120a4eb9..000000000 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/z.go +++ /dev/null @@ -1,3 +0,0 @@ -package main - -const genCodecPath = "github.com/ugorji/go/codec" diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen_test.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen_test.go deleted file mode 100644 index a73497e91..000000000 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen_test.go +++ /dev/null @@ -1,24 +0,0 @@ -//+build x,codecgen - -package codec - -import ( - "fmt" - "testing" -) - -func _TestCodecgenJson1(t *testing.T) { - // This is just a simplistic test for codecgen. - // It is typically disabled. We only enable it for debugging purposes. - const callCodecgenDirect bool = true - v := newTestStruc(2, false, !testSkipIntf, false) - var bs []byte - e := NewEncoderBytes(&bs, testJsonH) - if callCodecgenDirect { - v.CodecEncodeSelf(e) - e.w.atEndOfEncode() - } else { - e.MustEncode(v) - } - fmt.Printf("%s\n", bs) -} diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/decode.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/decode.go index b3b99f036..7e56f1eca 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/decode.go +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/decode.go @@ -583,14 +583,16 @@ func (f *decFnInfo) kInterfaceNaked() (rvn reflect.Value) { if d.mtid == 0 || d.mtid == mapIntfIntfTypId { l := len(n.ms) n.ms = append(n.ms, nil) - d.decode(&n.ms[l]) - rvn = reflect.ValueOf(&n.ms[l]).Elem() + var v2 interface{} = &n.ms[l] + d.decode(v2) + rvn = reflect.ValueOf(v2).Elem() n.ms = n.ms[:l] } else if d.mtid == mapStrIntfTypId { // for json performance l := len(n.ns) n.ns = append(n.ns, nil) - d.decode(&n.ns[l]) - rvn = reflect.ValueOf(&n.ns[l]).Elem() + var v2 interface{} = &n.ns[l] + d.decode(v2) + rvn = reflect.ValueOf(v2).Elem() n.ns = n.ns[:l] } else { rvn = reflect.New(d.h.MapType).Elem() @@ -601,8 +603,9 @@ func (f *decFnInfo) kInterfaceNaked() (rvn reflect.Value) { if d.stid == 0 || d.stid == intfSliceTypId { l := len(n.ss) n.ss = append(n.ss, nil) - d.decode(&n.ss[l]) - rvn = reflect.ValueOf(&n.ss[l]).Elem() + var v2 interface{} = &n.ss[l] + d.decode(v2) + rvn = reflect.ValueOf(v2).Elem() n.ss = n.ss[:l] } else { rvn = reflect.New(d.h.SliceType).Elem() @@ -615,9 +618,9 @@ func (f *decFnInfo) kInterfaceNaked() (rvn reflect.Value) { l := len(n.is) n.is = append(n.is, nil) v2 := &n.is[l] - n.is = n.is[:l] d.decode(v2) v = *v2 + n.is = n.is[:l] } bfn := d.h.getExtForTag(tag) if bfn == nil { @@ -1453,8 +1456,8 @@ func (d *Decoder) swallow() { l := len(n.is) n.is = append(n.is, nil) v2 := &n.is[l] - n.is = n.is[:l] d.decode(v2) + n.is = n.is[:l] } } } @@ -1862,6 +1865,7 @@ func (d *Decoder) intern(s string) { } } +// nextValueBytes returns the next value in the stream as a set of bytes. func (d *Decoder) nextValueBytes() []byte { d.d.uncacheRead() d.r.track() diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/encode.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/encode.go index 99af6fa55..a874c744b 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/encode.go +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/encode.go @@ -110,6 +110,15 @@ type EncodeOptions struct { // Canonical bool + // CheckCircularRef controls whether we check for circular references + // and error fast during an encode. + // + // If enabled, an error is received if a pointer to a struct + // references itself either directly or through one of its fields (iteratively). + // + // This is opt-in, as there may be a performance hit to checking circular references. + CheckCircularRef bool + // AsSymbols defines what should be encoded as symbols. // // Encoding as symbols can reduce the encoded size significantly. @@ -464,7 +473,7 @@ func (f *encFnInfo) kSlice(rv reflect.Value) { for j := 0; j < l; j++ { if cr != nil { if ti.mbs { - if l%2 == 0 { + if j%2 == 0 { cr.sendContainerState(containerMapKey) } else { cr.sendContainerState(containerMapValue) @@ -503,7 +512,7 @@ func (f *encFnInfo) kStruct(rv reflect.Value) { newlen := len(fti.sfi) // Use sync.Pool to reduce allocating slices unnecessarily. - // The cost of the occasional locking is less than the cost of new allocation. + // The cost of sync.Pool is less than the cost of new allocation. pool, poolv, fkvs := encStructPoolGet(newlen) // if toMap, use the sorted array. If toArray, use unsorted array (to match sequence in struct) @@ -514,11 +523,6 @@ func (f *encFnInfo) kStruct(rv reflect.Value) { var kv stringRv for _, si := range tisfi { kv.r = si.field(rv, false) - // if si.i != -1 { - // rvals[newlen] = rv.Field(int(si.i)) - // } else { - // rvals[newlen] = rv.FieldByIndex(si.is) - // } if toMap { if si.omitEmpty && isEmptyValue(kv.r) { continue @@ -596,13 +600,15 @@ func (f *encFnInfo) kStruct(rv reflect.Value) { // f.e.encodeValue(rv.Elem()) // } -func (f *encFnInfo) kInterface(rv reflect.Value) { - if rv.IsNil() { - f.e.e.EncodeNil() - return - } - f.e.encodeValue(rv.Elem(), nil) -} +// func (f *encFnInfo) kInterface(rv reflect.Value) { +// println("kInterface called") +// debug.PrintStack() +// if rv.IsNil() { +// f.e.e.EncodeNil() +// return +// } +// f.e.encodeValue(rv.Elem(), nil) +// } func (f *encFnInfo) kMap(rv reflect.Value) { ee := f.e.e @@ -877,6 +883,7 @@ type Encoder struct { // as the handler MAY need to do some coordination. w encWriter s []encRtidFn + ci set be bool // is binary encoding js bool // is json handle @@ -1133,20 +1140,23 @@ func (e *Encoder) encode(iv interface{}) { } } -func (e *Encoder) encodeI(iv interface{}, checkFastpath, checkCodecSelfer bool) { - if rv, proceed := e.preEncodeValue(reflect.ValueOf(iv)); proceed { - rt := rv.Type() - rtid := reflect.ValueOf(rt).Pointer() - fn := e.getEncFn(rtid, rt, checkFastpath, checkCodecSelfer) - fn.f(&fn.i, rv) - } -} - -func (e *Encoder) preEncodeValue(rv reflect.Value) (rv2 reflect.Value, proceed bool) { +func (e *Encoder) preEncodeValue(rv reflect.Value) (rv2 reflect.Value, sptr uintptr, proceed bool) { // use a goto statement instead of a recursive function for ptr/interface. TOP: switch rv.Kind() { - case reflect.Ptr, reflect.Interface: + case reflect.Ptr: + if rv.IsNil() { + e.e.EncodeNil() + return + } + rv = rv.Elem() + if e.h.CheckCircularRef && rv.Kind() == reflect.Struct { + // TODO: Movable pointers will be an issue here. Future problem. + sptr = rv.UnsafeAddr() + break TOP + } + goto TOP + case reflect.Interface: if rv.IsNil() { e.e.EncodeNil() return @@ -1163,18 +1173,40 @@ TOP: return } - return rv, true + proceed = true + rv2 = rv + return +} + +func (e *Encoder) doEncodeValue(rv reflect.Value, fn *encFn, sptr uintptr, + checkFastpath, checkCodecSelfer bool) { + if sptr != 0 { + if (&e.ci).add(sptr) { + e.errorf("circular reference found: # %d", sptr) + } + } + if fn == nil { + rt := rv.Type() + rtid := reflect.ValueOf(rt).Pointer() + // fn = e.getEncFn(rtid, rt, true, true) + fn = e.getEncFn(rtid, rt, checkFastpath, checkCodecSelfer) + } + fn.f(&fn.i, rv) + if sptr != 0 { + (&e.ci).remove(sptr) + } +} + +func (e *Encoder) encodeI(iv interface{}, checkFastpath, checkCodecSelfer bool) { + if rv, sptr, proceed := e.preEncodeValue(reflect.ValueOf(iv)); proceed { + e.doEncodeValue(rv, nil, sptr, checkFastpath, checkCodecSelfer) + } } func (e *Encoder) encodeValue(rv reflect.Value, fn *encFn) { // if a valid fn is passed, it MUST BE for the dereferenced type of rv - if rv, proceed := e.preEncodeValue(rv); proceed { - if fn == nil { - rt := rv.Type() - rtid := reflect.ValueOf(rt).Pointer() - fn = e.getEncFn(rtid, rt, true, true) - } - fn.f(&fn.i, rv) + if rv, sptr, proceed := e.preEncodeValue(rv); proceed { + e.doEncodeValue(rv, fn, sptr, true, true) } } @@ -1234,7 +1266,7 @@ func (e *Encoder) getEncFn(rtid uintptr, rt reflect.Type, checkFastpath, checkCo } else { rk := rt.Kind() if fastpathEnabled && checkFastpath && (rk == reflect.Map || rk == reflect.Slice) { - if rt.PkgPath() == "" { + if rt.PkgPath() == "" { // un-named slice or map if idx := fastpathAV.index(rtid); idx != -1 { fn.f = fastpathAV[idx].encfn } @@ -1284,10 +1316,11 @@ func (e *Encoder) getEncFn(rtid uintptr, rt reflect.Type, checkFastpath, checkCo fn.f = (*encFnInfo).kSlice case reflect.Struct: fn.f = (*encFnInfo).kStruct + // reflect.Ptr and reflect.Interface are handled already by preEncodeValue // case reflect.Ptr: // fn.f = (*encFnInfo).kPtr - case reflect.Interface: - fn.f = (*encFnInfo).kInterface + // case reflect.Interface: + // fn.f = (*encFnInfo).kInterface case reflect.Map: fn.f = (*encFnInfo).kMap default: @@ -1353,25 +1386,6 @@ func encStructPoolGet(newlen int) (p *sync.Pool, v interface{}, s []stringRv) { // panic(errors.New("encStructPoolLen must be equal to 4")) // defensive, in case it is changed // } // idxpool := newlen / 8 - - // if pool == nil { - // fkvs = make([]stringRv, newlen) - // } else { - // poolv = pool.Get() - // switch vv := poolv.(type) { - // case *[8]stringRv: - // fkvs = vv[:newlen] - // case *[16]stringRv: - // fkvs = vv[:newlen] - // case *[32]stringRv: - // fkvs = vv[:newlen] - // case *[64]stringRv: - // fkvs = vv[:newlen] - // case *[128]stringRv: - // fkvs = vv[:newlen] - // } - // } - if newlen <= 8 { p = &encStructPool[0] v = p.Get() diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/fast-path.generated.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/fast-path.generated.go index d968a500f..cf6e00df2 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/fast-path.generated.go +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/fast-path.generated.go @@ -3124,7 +3124,11 @@ func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool { // -- -- fast path functions func (f *encFnInfo) fastpathEncSliceIntfR(rv reflect.Value) { - fastpathTV.EncSliceIntfV(rv.Interface().([]interface{}), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.EncAsMapSliceIntfV(rv.Interface().([]interface{}), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.EncSliceIntfV(rv.Interface().([]interface{}), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) EncSliceIntfV(v []interface{}, checkNil bool, e *Encoder) { ee := e.e @@ -3145,8 +3149,39 @@ func (_ fastpathT) EncSliceIntfV(v []interface{}, checkNil bool, e *Encoder) { } } +func (_ fastpathT) EncAsMapSliceIntfV(v []interface{}, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + e.encode(v2) + } + if cr != nil { + cr.sendContainerState(containerMapEnd) + } +} + func (f *encFnInfo) fastpathEncSliceStringR(rv reflect.Value) { - fastpathTV.EncSliceStringV(rv.Interface().([]string), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.EncAsMapSliceStringV(rv.Interface().([]string), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.EncSliceStringV(rv.Interface().([]string), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) EncSliceStringV(v []string, checkNil bool, e *Encoder) { ee := e.e @@ -3167,8 +3202,39 @@ func (_ fastpathT) EncSliceStringV(v []string, checkNil bool, e *Encoder) { } } +func (_ fastpathT) EncAsMapSliceStringV(v []string, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + ee.EncodeString(c_UTF8, v2) + } + if cr != nil { + cr.sendContainerState(containerMapEnd) + } +} + func (f *encFnInfo) fastpathEncSliceFloat32R(rv reflect.Value) { - fastpathTV.EncSliceFloat32V(rv.Interface().([]float32), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.EncAsMapSliceFloat32V(rv.Interface().([]float32), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.EncSliceFloat32V(rv.Interface().([]float32), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) EncSliceFloat32V(v []float32, checkNil bool, e *Encoder) { ee := e.e @@ -3189,8 +3255,39 @@ func (_ fastpathT) EncSliceFloat32V(v []float32, checkNil bool, e *Encoder) { } } +func (_ fastpathT) EncAsMapSliceFloat32V(v []float32, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + ee.EncodeFloat32(v2) + } + if cr != nil { + cr.sendContainerState(containerMapEnd) + } +} + func (f *encFnInfo) fastpathEncSliceFloat64R(rv reflect.Value) { - fastpathTV.EncSliceFloat64V(rv.Interface().([]float64), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.EncAsMapSliceFloat64V(rv.Interface().([]float64), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.EncSliceFloat64V(rv.Interface().([]float64), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) EncSliceFloat64V(v []float64, checkNil bool, e *Encoder) { ee := e.e @@ -3211,8 +3308,39 @@ func (_ fastpathT) EncSliceFloat64V(v []float64, checkNil bool, e *Encoder) { } } +func (_ fastpathT) EncAsMapSliceFloat64V(v []float64, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + ee.EncodeFloat64(v2) + } + if cr != nil { + cr.sendContainerState(containerMapEnd) + } +} + func (f *encFnInfo) fastpathEncSliceUintR(rv reflect.Value) { - fastpathTV.EncSliceUintV(rv.Interface().([]uint), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.EncAsMapSliceUintV(rv.Interface().([]uint), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.EncSliceUintV(rv.Interface().([]uint), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) EncSliceUintV(v []uint, checkNil bool, e *Encoder) { ee := e.e @@ -3233,8 +3361,39 @@ func (_ fastpathT) EncSliceUintV(v []uint, checkNil bool, e *Encoder) { } } +func (_ fastpathT) EncAsMapSliceUintV(v []uint, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + ee.EncodeUint(uint64(v2)) + } + if cr != nil { + cr.sendContainerState(containerMapEnd) + } +} + func (f *encFnInfo) fastpathEncSliceUint16R(rv reflect.Value) { - fastpathTV.EncSliceUint16V(rv.Interface().([]uint16), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.EncAsMapSliceUint16V(rv.Interface().([]uint16), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.EncSliceUint16V(rv.Interface().([]uint16), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) EncSliceUint16V(v []uint16, checkNil bool, e *Encoder) { ee := e.e @@ -3255,8 +3414,39 @@ func (_ fastpathT) EncSliceUint16V(v []uint16, checkNil bool, e *Encoder) { } } +func (_ fastpathT) EncAsMapSliceUint16V(v []uint16, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + ee.EncodeUint(uint64(v2)) + } + if cr != nil { + cr.sendContainerState(containerMapEnd) + } +} + func (f *encFnInfo) fastpathEncSliceUint32R(rv reflect.Value) { - fastpathTV.EncSliceUint32V(rv.Interface().([]uint32), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.EncAsMapSliceUint32V(rv.Interface().([]uint32), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.EncSliceUint32V(rv.Interface().([]uint32), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) EncSliceUint32V(v []uint32, checkNil bool, e *Encoder) { ee := e.e @@ -3277,8 +3467,39 @@ func (_ fastpathT) EncSliceUint32V(v []uint32, checkNil bool, e *Encoder) { } } +func (_ fastpathT) EncAsMapSliceUint32V(v []uint32, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + ee.EncodeUint(uint64(v2)) + } + if cr != nil { + cr.sendContainerState(containerMapEnd) + } +} + func (f *encFnInfo) fastpathEncSliceUint64R(rv reflect.Value) { - fastpathTV.EncSliceUint64V(rv.Interface().([]uint64), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.EncAsMapSliceUint64V(rv.Interface().([]uint64), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.EncSliceUint64V(rv.Interface().([]uint64), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) EncSliceUint64V(v []uint64, checkNil bool, e *Encoder) { ee := e.e @@ -3299,8 +3520,39 @@ func (_ fastpathT) EncSliceUint64V(v []uint64, checkNil bool, e *Encoder) { } } +func (_ fastpathT) EncAsMapSliceUint64V(v []uint64, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + ee.EncodeUint(uint64(v2)) + } + if cr != nil { + cr.sendContainerState(containerMapEnd) + } +} + func (f *encFnInfo) fastpathEncSliceUintptrR(rv reflect.Value) { - fastpathTV.EncSliceUintptrV(rv.Interface().([]uintptr), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.EncAsMapSliceUintptrV(rv.Interface().([]uintptr), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.EncSliceUintptrV(rv.Interface().([]uintptr), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) EncSliceUintptrV(v []uintptr, checkNil bool, e *Encoder) { ee := e.e @@ -3321,8 +3573,39 @@ func (_ fastpathT) EncSliceUintptrV(v []uintptr, checkNil bool, e *Encoder) { } } +func (_ fastpathT) EncAsMapSliceUintptrV(v []uintptr, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + e.encode(v2) + } + if cr != nil { + cr.sendContainerState(containerMapEnd) + } +} + func (f *encFnInfo) fastpathEncSliceIntR(rv reflect.Value) { - fastpathTV.EncSliceIntV(rv.Interface().([]int), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.EncAsMapSliceIntV(rv.Interface().([]int), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.EncSliceIntV(rv.Interface().([]int), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) EncSliceIntV(v []int, checkNil bool, e *Encoder) { ee := e.e @@ -3343,8 +3626,39 @@ func (_ fastpathT) EncSliceIntV(v []int, checkNil bool, e *Encoder) { } } +func (_ fastpathT) EncAsMapSliceIntV(v []int, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + ee.EncodeInt(int64(v2)) + } + if cr != nil { + cr.sendContainerState(containerMapEnd) + } +} + func (f *encFnInfo) fastpathEncSliceInt8R(rv reflect.Value) { - fastpathTV.EncSliceInt8V(rv.Interface().([]int8), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.EncAsMapSliceInt8V(rv.Interface().([]int8), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.EncSliceInt8V(rv.Interface().([]int8), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) EncSliceInt8V(v []int8, checkNil bool, e *Encoder) { ee := e.e @@ -3365,8 +3679,39 @@ func (_ fastpathT) EncSliceInt8V(v []int8, checkNil bool, e *Encoder) { } } +func (_ fastpathT) EncAsMapSliceInt8V(v []int8, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + ee.EncodeInt(int64(v2)) + } + if cr != nil { + cr.sendContainerState(containerMapEnd) + } +} + func (f *encFnInfo) fastpathEncSliceInt16R(rv reflect.Value) { - fastpathTV.EncSliceInt16V(rv.Interface().([]int16), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.EncAsMapSliceInt16V(rv.Interface().([]int16), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.EncSliceInt16V(rv.Interface().([]int16), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) EncSliceInt16V(v []int16, checkNil bool, e *Encoder) { ee := e.e @@ -3387,8 +3732,39 @@ func (_ fastpathT) EncSliceInt16V(v []int16, checkNil bool, e *Encoder) { } } +func (_ fastpathT) EncAsMapSliceInt16V(v []int16, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + ee.EncodeInt(int64(v2)) + } + if cr != nil { + cr.sendContainerState(containerMapEnd) + } +} + func (f *encFnInfo) fastpathEncSliceInt32R(rv reflect.Value) { - fastpathTV.EncSliceInt32V(rv.Interface().([]int32), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.EncAsMapSliceInt32V(rv.Interface().([]int32), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.EncSliceInt32V(rv.Interface().([]int32), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) EncSliceInt32V(v []int32, checkNil bool, e *Encoder) { ee := e.e @@ -3409,8 +3785,39 @@ func (_ fastpathT) EncSliceInt32V(v []int32, checkNil bool, e *Encoder) { } } +func (_ fastpathT) EncAsMapSliceInt32V(v []int32, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + ee.EncodeInt(int64(v2)) + } + if cr != nil { + cr.sendContainerState(containerMapEnd) + } +} + func (f *encFnInfo) fastpathEncSliceInt64R(rv reflect.Value) { - fastpathTV.EncSliceInt64V(rv.Interface().([]int64), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.EncAsMapSliceInt64V(rv.Interface().([]int64), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.EncSliceInt64V(rv.Interface().([]int64), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) EncSliceInt64V(v []int64, checkNil bool, e *Encoder) { ee := e.e @@ -3431,8 +3838,39 @@ func (_ fastpathT) EncSliceInt64V(v []int64, checkNil bool, e *Encoder) { } } +func (_ fastpathT) EncAsMapSliceInt64V(v []int64, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + ee.EncodeInt(int64(v2)) + } + if cr != nil { + cr.sendContainerState(containerMapEnd) + } +} + func (f *encFnInfo) fastpathEncSliceBoolR(rv reflect.Value) { - fastpathTV.EncSliceBoolV(rv.Interface().([]bool), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.EncAsMapSliceBoolV(rv.Interface().([]bool), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.EncSliceBoolV(rv.Interface().([]bool), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) EncSliceBoolV(v []bool, checkNil bool, e *Encoder) { ee := e.e @@ -3453,6 +3891,33 @@ func (_ fastpathT) EncSliceBoolV(v []bool, checkNil bool, e *Encoder) { } } +func (_ fastpathT) EncAsMapSliceBoolV(v []bool, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + ee.EncodeBool(v2) + } + if cr != nil { + cr.sendContainerState(containerMapEnd) + } +} + func (f *encFnInfo) fastpathEncMapIntfIntfR(rv reflect.Value) { fastpathTV.EncMapIntfIntfV(rv.Interface().(map[interface{}]interface{}), fastpathCheckNilFalse, f.e) } @@ -17712,7 +18177,7 @@ func (_ fastpathT) DecSliceIntfV(v []interface{}, checkNil bool, canChange bool, changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -17771,7 +18236,7 @@ func (_ fastpathT) DecSliceIntfV(v []interface{}, checkNil bool, canChange bool, changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]interface{}, 1, 4) @@ -17846,7 +18311,7 @@ func (_ fastpathT) DecSliceStringV(v []string, checkNil bool, canChange bool, d changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -17905,7 +18370,7 @@ func (_ fastpathT) DecSliceStringV(v []string, checkNil bool, canChange bool, d changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]string, 1, 4) @@ -17979,7 +18444,7 @@ func (_ fastpathT) DecSliceFloat32V(v []float32, checkNil bool, canChange bool, changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -18038,7 +18503,7 @@ func (_ fastpathT) DecSliceFloat32V(v []float32, checkNil bool, canChange bool, changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]float32, 1, 4) @@ -18112,7 +18577,7 @@ func (_ fastpathT) DecSliceFloat64V(v []float64, checkNil bool, canChange bool, changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -18171,7 +18636,7 @@ func (_ fastpathT) DecSliceFloat64V(v []float64, checkNil bool, canChange bool, changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]float64, 1, 4) @@ -18245,7 +18710,7 @@ func (_ fastpathT) DecSliceUintV(v []uint, checkNil bool, canChange bool, d *Dec changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -18304,7 +18769,7 @@ func (_ fastpathT) DecSliceUintV(v []uint, checkNil bool, canChange bool, d *Dec changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]uint, 1, 4) @@ -18378,7 +18843,7 @@ func (_ fastpathT) DecSliceUint16V(v []uint16, checkNil bool, canChange bool, d changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -18437,7 +18902,7 @@ func (_ fastpathT) DecSliceUint16V(v []uint16, checkNil bool, canChange bool, d changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]uint16, 1, 4) @@ -18511,7 +18976,7 @@ func (_ fastpathT) DecSliceUint32V(v []uint32, checkNil bool, canChange bool, d changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -18570,7 +19035,7 @@ func (_ fastpathT) DecSliceUint32V(v []uint32, checkNil bool, canChange bool, d changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]uint32, 1, 4) @@ -18644,7 +19109,7 @@ func (_ fastpathT) DecSliceUint64V(v []uint64, checkNil bool, canChange bool, d changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -18703,7 +19168,7 @@ func (_ fastpathT) DecSliceUint64V(v []uint64, checkNil bool, canChange bool, d changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]uint64, 1, 4) @@ -18777,7 +19242,7 @@ func (_ fastpathT) DecSliceUintptrV(v []uintptr, checkNil bool, canChange bool, changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -18836,7 +19301,7 @@ func (_ fastpathT) DecSliceUintptrV(v []uintptr, checkNil bool, canChange bool, changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]uintptr, 1, 4) @@ -18910,7 +19375,7 @@ func (_ fastpathT) DecSliceIntV(v []int, checkNil bool, canChange bool, d *Decod changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -18969,7 +19434,7 @@ func (_ fastpathT) DecSliceIntV(v []int, checkNil bool, canChange bool, d *Decod changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]int, 1, 4) @@ -19043,7 +19508,7 @@ func (_ fastpathT) DecSliceInt8V(v []int8, checkNil bool, canChange bool, d *Dec changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -19102,7 +19567,7 @@ func (_ fastpathT) DecSliceInt8V(v []int8, checkNil bool, canChange bool, d *Dec changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]int8, 1, 4) @@ -19176,7 +19641,7 @@ func (_ fastpathT) DecSliceInt16V(v []int16, checkNil bool, canChange bool, d *D changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -19235,7 +19700,7 @@ func (_ fastpathT) DecSliceInt16V(v []int16, checkNil bool, canChange bool, d *D changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]int16, 1, 4) @@ -19309,7 +19774,7 @@ func (_ fastpathT) DecSliceInt32V(v []int32, checkNil bool, canChange bool, d *D changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -19368,7 +19833,7 @@ func (_ fastpathT) DecSliceInt32V(v []int32, checkNil bool, canChange bool, d *D changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]int32, 1, 4) @@ -19442,7 +19907,7 @@ func (_ fastpathT) DecSliceInt64V(v []int64, checkNil bool, canChange bool, d *D changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -19501,7 +19966,7 @@ func (_ fastpathT) DecSliceInt64V(v []int64, checkNil bool, canChange bool, d *D changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]int64, 1, 4) @@ -19575,7 +20040,7 @@ func (_ fastpathT) DecSliceBoolV(v []bool, checkNil bool, canChange bool, d *Dec changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -19634,7 +20099,7 @@ func (_ fastpathT) DecSliceBoolV(v []bool, checkNil bool, canChange bool, d *Dec changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]bool, 1, 4) diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/fast-path.go.tmpl b/Godeps/_workspace/src/github.com/ugorji/go/codec/fast-path.go.tmpl index 58cc6df4c..04c173fba 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/fast-path.go.tmpl +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/fast-path.go.tmpl @@ -165,7 +165,11 @@ func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool { {{range .Values}}{{if not .Primitive}}{{if not .MapKey }} func (f *encFnInfo) {{ .MethodNamePfx "fastpathEnc" false }}R(rv reflect.Value) { - fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv.Interface().([]{{ .Elem }}), fastpathCheckNilFalse, f.e) + if f.ti.mbs { + fastpathTV.{{ .MethodNamePfx "EncAsMap" false }}V(rv.Interface().([]{{ .Elem }}), fastpathCheckNilFalse, f.e) + } else { + fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv.Interface().([]{{ .Elem }}), fastpathCheckNilFalse, f.e) + } } func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v []{{ .Elem }}, checkNil bool, e *Encoder) { ee := e.e @@ -182,6 +186,31 @@ func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v []{{ .Elem }}, checkNil b if cr != nil { cr.sendContainerState(containerArrayEnd) }{{/* ee.EncodeEnd() */}} } +func (_ fastpathT) {{ .MethodNamePfx "EncAsMap" false }}V(v []{{ .Elem }}, checkNil bool, e *Encoder) { + ee := e.e + cr := e.cr + if checkNil && v == nil { + ee.EncodeNil() + return + } + if len(v)%2 == 1 { + e.errorf("mapBySlice requires even slice length, but got %v", len(v)) + return + } + ee.EncodeMapStart(len(v) / 2) + for j, v2 := range v { + if cr != nil { + if j%2 == 0 { + cr.sendContainerState(containerMapKey) + } else { + cr.sendContainerState(containerMapValue) + } + } + {{ encmd .Elem "v2"}} + } + if cr != nil { cr.sendContainerState(containerMapEnd) } +} + {{end}}{{end}}{{end}} {{range .Values}}{{if not .Primitive}}{{if .MapKey }} @@ -328,7 +357,7 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, checkNil b changed = true } slh.End() - return + return v, changed } if containerLenS > 0 { @@ -391,7 +420,7 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, checkNil b changed = true } slh.End() - return + return v, changed } if cap(v) == 0 { v = make([]{{ .Elem }}, 1, 4) diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-dec-array.go.tmpl b/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-dec-array.go.tmpl index 2caae5bfd..32df54144 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-dec-array.go.tmpl +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-dec-array.go.tmpl @@ -1,6 +1,7 @@ {{var "v"}} := {{if not isArray}}*{{end}}{{ .Varname }} -{{var "h"}}, {{var "l"}} := z.DecSliceHelperStart() {{/* // helper, containerLenS */}} +{{var "h"}}, {{var "l"}} := z.DecSliceHelperStart() {{/* // helper, containerLenS */}}{{if not isArray}} var {{var "c"}} bool {{/* // changed */}} +_ = {{var "c"}}{{end}} if {{var "l"}} == 0 { {{if isSlice }}if {{var "v"}} == nil { {{var "v"}} = []{{ .Typ }}{} @@ -26,6 +27,8 @@ if {{var "l"}} == 0 { } {{ else }} var {{var "rr"}}, {{var "rl"}} int {{/* // num2read, length of slice/array/chan */}} var {{var "rt"}} bool {{/* truncated */}} + _, _ = {{var "rl"}}, {{var "rt"}} + {{var "rr"}} = {{var "l"}} // len({{var "v"}}) if {{var "l"}} > cap({{var "v"}}) { {{if isArray }}z.DecArrayCannotExpand(len({{var "v"}}), {{var "l"}}) {{ else }}{{if not .Immutable }} diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/gen.generated.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/gen.generated.go index fb6f4b809..2ace97b78 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/gen.generated.go +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/gen.generated.go @@ -68,8 +68,9 @@ z.DecSendContainerState(codecSelfer_containerMapEnd{{ .Sfx }}) const genDecListTmpl = ` {{var "v"}} := {{if not isArray}}*{{end}}{{ .Varname }} -{{var "h"}}, {{var "l"}} := z.DecSliceHelperStart() {{/* // helper, containerLenS */}} +{{var "h"}}, {{var "l"}} := z.DecSliceHelperStart() {{/* // helper, containerLenS */}}{{if not isArray}} var {{var "c"}} bool {{/* // changed */}} +_ = {{var "c"}}{{end}} if {{var "l"}} == 0 { {{if isSlice }}if {{var "v"}} == nil { {{var "v"}} = []{{ .Typ }}{} @@ -95,6 +96,8 @@ if {{var "l"}} == 0 { } {{ else }} var {{var "rr"}}, {{var "rl"}} int {{/* // num2read, length of slice/array/chan */}} var {{var "rt"}} bool {{/* truncated */}} + _, _ = {{var "rl"}}, {{var "rt"}} + {{var "rr"}} = {{var "l"}} // len({{var "v"}}) if {{var "l"}} > cap({{var "v"}}) { {{if isArray }}z.DecArrayCannotExpand(len({{var "v"}}), {{var "l"}}) {{ else }}{{if not .Immutable }} diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/gen.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/gen.go index a075e7c0d..ffc5aecf6 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/gen.go +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/gen.go @@ -21,6 +21,8 @@ import ( "sync" "text/template" "time" + "unicode" + "unicode/utf8" ) // --------------------------------------------------- @@ -266,6 +268,7 @@ func Gen(w io.Writer, buildTags, pkgName, uid string, useUnsafe bool, ti *TypeIn x.line("type " + x.hn + " struct{}") x.line("") + x.varsfxreset() x.line("func init() {") x.linef("if %sGenVersion != %v {", x.cpfx, GenVersion) x.line("_, file, _, _ := runtime.Caller(0)") @@ -309,6 +312,7 @@ func Gen(w io.Writer, buildTags, pkgName, uid string, useUnsafe bool, ti *TypeIn for _, t := range x.ts { rtid := reflect.ValueOf(t).Pointer() // generate enc functions for all these slice/map types. + x.varsfxreset() x.linef("func (x %s) enc%s(v %s%s, e *%sEncoder) {", x.hn, x.genMethodNameT(t), x.arr2str(t, "*"), x.genTypeName(t), x.cpfx) x.genRequiredMethodVars(true) switch t.Kind() { @@ -323,6 +327,7 @@ func Gen(w io.Writer, buildTags, pkgName, uid string, useUnsafe bool, ti *TypeIn x.line("") // generate dec functions for all these slice/map types. + x.varsfxreset() x.linef("func (x %s) dec%s(v *%s, d *%sDecoder) {", x.hn, x.genMethodNameT(t), x.genTypeName(t), x.cpfx) x.genRequiredMethodVars(false) switch t.Kind() { @@ -377,7 +382,7 @@ func (x *genRunner) genRefPkgs(t reflect.Type) { x.imn[tpkg] = tpkg } else { x.imc++ - x.imn[tpkg] = "pkg" + strconv.FormatUint(x.imc, 10) + "_" + tpkg[idx+1:] + x.imn[tpkg] = "pkg" + strconv.FormatUint(x.imc, 10) + "_" + genGoIdentifier(tpkg[idx+1:], false) } } } @@ -408,6 +413,10 @@ func (x *genRunner) varsfx() string { return strconv.FormatUint(x.c, 10) } +func (x *genRunner) varsfxreset() { + x.c = 0 +} + func (x *genRunner) out(s string) { if _, err := io.WriteString(x.w, s); err != nil { panic(err) @@ -494,6 +503,7 @@ func (x *genRunner) selfer(encode bool) { // always make decode use a pointer receiver, // and structs always use a ptr receiver (encode|decode) isptr := !encode || t.Kind() == reflect.Struct + x.varsfxreset() fnSigPfx := "func (x " if isptr { fnSigPfx += "*" @@ -566,9 +576,28 @@ func (x *genRunner) xtraSM(varname string, encode bool, t reflect.Type) { } else { x.linef("h.dec%s((*%s)(%s), d)", x.genMethodNameT(t), x.genTypeName(t), varname) } - if _, ok := x.tm[t]; !ok { - x.tm[t] = struct{}{} - x.ts = append(x.ts, t) + x.registerXtraT(t) +} + +func (x *genRunner) registerXtraT(t reflect.Type) { + // recursively register the types + if _, ok := x.tm[t]; ok { + return + } + var tkey reflect.Type + switch t.Kind() { + case reflect.Chan, reflect.Slice, reflect.Array: + case reflect.Map: + tkey = t.Key() + default: + return + } + x.tm[t] = struct{}{} + x.ts = append(x.ts, t) + // check if this refers to any xtra types eg. a slice of array: add the array + x.registerXtraT(t.Elem()) + if tkey != nil { + x.registerXtraT(tkey) } } @@ -608,22 +637,33 @@ func (x *genRunner) encVar(varname string, t reflect.Type) { } -// enc will encode a variable (varname) of type T, -// except t is of kind reflect.Struct or reflect.Array, wherein varname is of type *T (to prevent copying) +// enc will encode a variable (varname) of type t, +// except t is of kind reflect.Struct or reflect.Array, wherein varname is of type ptrTo(T) (to prevent copying) func (x *genRunner) enc(varname string, t reflect.Type) { - // varName here must be to a pointer to a struct/array, or to a value directly. rtid := reflect.ValueOf(t).Pointer() // We call CodecEncodeSelf if one of the following are honored: // - the type already implements Selfer, call that // - the type has a Selfer implementation just created, use that // - the type is in the list of the ones we will generate for, but it is not currently being generated + mi := x.varsfx() tptr := reflect.PtrTo(t) tk := t.Kind() if x.checkForSelfer(t, varname) { - if t.Implements(selferTyp) || (tptr.Implements(selferTyp) && (tk == reflect.Array || tk == reflect.Struct)) { - x.line(varname + ".CodecEncodeSelf(e)") - return + if tk == reflect.Array || tk == reflect.Struct { // varname is of type *T + if tptr.Implements(selferTyp) || t.Implements(selferTyp) { + x.line(varname + ".CodecEncodeSelf(e)") + return + } + } else { // varname is of type T + if t.Implements(selferTyp) { + x.line(varname + ".CodecEncodeSelf(e)") + return + } else if tptr.Implements(selferTyp) { + x.linef("%ssf%s := &%s", genTempVarPfx, mi, varname) + x.linef("%ssf%s.CodecEncodeSelf(e)", genTempVarPfx, mi) + return + } } if _, ok := x.te[rtid]; ok { @@ -653,7 +693,6 @@ func (x *genRunner) enc(varname string, t reflect.Type) { // check if // - type is RawExt // - the type implements (Text|JSON|Binary)(Unm|M)arshal - mi := x.varsfx() x.linef("%sm%s := z.EncBinary()", genTempVarPfx, mi) x.linef("_ = %sm%s", genTempVarPfx, mi) x.line("if false {") //start if block @@ -676,15 +715,31 @@ func (x *genRunner) enc(varname string, t reflect.Type) { // first check if extensions are configued, before doing the interface conversion x.linef("} else if z.HasExtensions() && z.EncExt(%s) {", varname) } - if t.Implements(binaryMarshalerTyp) || tptr.Implements(binaryMarshalerTyp) { - x.linef("} else if %sm%s { z.EncBinaryMarshal(%v) ", genTempVarPfx, mi, varname) + if tk == reflect.Array || tk == reflect.Struct { // varname is of type *T + if t.Implements(binaryMarshalerTyp) || tptr.Implements(binaryMarshalerTyp) { + x.linef("} else if %sm%s { z.EncBinaryMarshal(%v) ", genTempVarPfx, mi, varname) + } + if t.Implements(jsonMarshalerTyp) || tptr.Implements(jsonMarshalerTyp) { + x.linef("} else if !%sm%s && z.IsJSONHandle() { z.EncJSONMarshal(%v) ", genTempVarPfx, mi, varname) + } else if t.Implements(textMarshalerTyp) || tptr.Implements(textMarshalerTyp) { + x.linef("} else if !%sm%s { z.EncTextMarshal(%v) ", genTempVarPfx, mi, varname) + } + } else { // varname is of type T + if t.Implements(binaryMarshalerTyp) { + x.linef("} else if %sm%s { z.EncBinaryMarshal(%v) ", genTempVarPfx, mi, varname) + } else if tptr.Implements(binaryMarshalerTyp) { + x.linef("} else if %sm%s { z.EncBinaryMarshal(&%v) ", genTempVarPfx, mi, varname) + } + if t.Implements(jsonMarshalerTyp) { + x.linef("} else if !%sm%s && z.IsJSONHandle() { z.EncJSONMarshal(%v) ", genTempVarPfx, mi, varname) + } else if tptr.Implements(jsonMarshalerTyp) { + x.linef("} else if !%sm%s && z.IsJSONHandle() { z.EncJSONMarshal(&%v) ", genTempVarPfx, mi, varname) + } else if t.Implements(textMarshalerTyp) { + x.linef("} else if !%sm%s { z.EncTextMarshal(%v) ", genTempVarPfx, mi, varname) + } else if tptr.Implements(textMarshalerTyp) { + x.linef("} else if !%sm%s { z.EncTextMarshal(&%v) ", genTempVarPfx, mi, varname) + } } - if t.Implements(jsonMarshalerTyp) || tptr.Implements(jsonMarshalerTyp) { - x.linef("} else if !%sm%s && z.IsJSONHandle() { z.EncJSONMarshal(%v) ", genTempVarPfx, mi, varname) - } else if t.Implements(textMarshalerTyp) || tptr.Implements(textMarshalerTyp) { - x.linef("} else if !%sm%s { z.EncTextMarshal(%v) ", genTempVarPfx, mi, varname) - } - x.line("} else {") switch t.Kind() { @@ -1020,6 +1075,8 @@ func (x *genRunner) decVar(varname string, t reflect.Type, canBeNil bool) { } } +// dec will decode a variable (varname) of type ptrTo(t). +// t is always a basetype (i.e. not of kind reflect.Ptr). func (x *genRunner) dec(varname string, t reflect.Type) { // assumptions: // - the varname is to a pointer already. No need to take address of it @@ -1592,6 +1649,26 @@ func genImportPath(t reflect.Type) (s string) { return } +// A go identifier is (letter|_)[letter|number|_]* +func genGoIdentifier(s string, checkFirstChar bool) string { + b := make([]byte, 0, len(s)) + t := make([]byte, 4) + var n int + for i, r := range s { + if checkFirstChar && i == 0 && !unicode.IsLetter(r) { + b = append(b, '_') + } + // r must be unicode_letter, unicode_digit or _ + if unicode.IsLetter(r) || unicode.IsDigit(r) { + n = utf8.EncodeRune(t, r) + b = append(b, t[:n]...) + } else { + b = append(b, '_') + } + } + return string(b) +} + func genNonPtr(t reflect.Type) reflect.Type { for t.Kind() == reflect.Ptr { t = t.Elem() diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/helper.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/helper.go index 560014ae3..40065a01c 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/helper.go +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/helper.go @@ -155,8 +155,10 @@ const ( resetSliceElemToZeroValue bool = false ) -var oneByteArr = [1]byte{0} -var zeroByteSlice = oneByteArr[:0:0] +var ( + oneByteArr = [1]byte{0} + zeroByteSlice = oneByteArr[:0:0] +) type charEncoding uint8 @@ -215,6 +217,24 @@ const ( containerArrayEnd ) +type rgetPoolT struct { + encNames [8]string + fNames [8]string + etypes [8]uintptr + sfis [8]*structFieldInfo +} + +var rgetPool = sync.Pool{ + New: func() interface{} { return new(rgetPoolT) }, +} + +type rgetT struct { + fNames []string + encNames []string + etypes []uintptr + sfis []*structFieldInfo +} + type containerStateRecv interface { sendContainerState(containerState) } @@ -833,14 +853,17 @@ func (x *TypeInfos) get(rtid uintptr, rt reflect.Type) (pti *typeInfo) { siInfo = parseStructFieldInfo(structInfoFieldName, x.structTag(f.Tag)) ti.toArray = siInfo.toArray } - sfip := make([]*structFieldInfo, 0, rt.NumField()) - x.rget(rt, nil, make(map[string]bool, 16), &sfip, siInfo) - - ti.sfip = make([]*structFieldInfo, len(sfip)) - ti.sfi = make([]*structFieldInfo, len(sfip)) - copy(ti.sfip, sfip) - sort.Sort(sfiSortedByEncName(sfip)) - copy(ti.sfi, sfip) + pi := rgetPool.Get() + pv := pi.(*rgetPoolT) + pv.etypes[0] = ti.baseId + vv := rgetT{pv.fNames[:0], pv.encNames[:0], pv.etypes[:1], pv.sfis[:0]} + x.rget(rt, rtid, nil, &vv, siInfo) + ti.sfip = make([]*structFieldInfo, len(vv.sfis)) + ti.sfi = make([]*structFieldInfo, len(vv.sfis)) + copy(ti.sfip, vv.sfis) + sort.Sort(sfiSortedByEncName(vv.sfis)) + copy(ti.sfi, vv.sfis) + rgetPool.Put(pi) } // sfi = sfip @@ -853,16 +876,37 @@ func (x *TypeInfos) get(rtid uintptr, rt reflect.Type) (pti *typeInfo) { return } -func (x *TypeInfos) rget(rt reflect.Type, indexstack []int, fnameToHastag map[string]bool, - sfi *[]*structFieldInfo, siInfo *structFieldInfo, +func (x *TypeInfos) rget(rt reflect.Type, rtid uintptr, + indexstack []int, pv *rgetT, siInfo *structFieldInfo, ) { - for j := 0; j < rt.NumField(); j++ { + // This will read up the fields and store how to access the value. + // It uses the go language's rules for embedding, as below: + // - if a field has been seen while traversing, skip it + // - if an encName has been seen while traversing, skip it + // - if an embedded type has been seen, skip it + // + // Also, per Go's rules, embedded fields must be analyzed AFTER all top-level fields. + // + // Note: we consciously use slices, not a map, to simulate a set. + // Typically, types have < 16 fields, and iteration using equals is faster than maps there + + type anonField struct { + ft reflect.Type + idx int + } + + var anonFields []anonField + +LOOP: + for j, jlen := 0, rt.NumField(); j < jlen; j++ { f := rt.Field(j) fkind := f.Type.Kind() // skip if a func type, or is unexported, or structTag value == "-" - if fkind == reflect.Func { - continue + switch fkind { + case reflect.Func, reflect.Complex64, reflect.Complex128, reflect.UnsafePointer: + continue LOOP } + // if r1, _ := utf8.DecodeRuneInString(f.Name); r1 == utf8.RuneError || !unicode.IsUpper(r1) { if f.PkgPath != "" && !f.Anonymous { // unexported, not embedded continue @@ -886,11 +930,8 @@ func (x *TypeInfos) rget(rt reflect.Type, indexstack []int, fnameToHastag map[st ft = ft.Elem() } if ft.Kind() == reflect.Struct { - indexstack2 := make([]int, len(indexstack)+1, len(indexstack)+4) - copy(indexstack2, indexstack) - indexstack2[len(indexstack)] = j - // indexstack2 := append(append(make([]int, 0, len(indexstack)+4), indexstack...), j) - x.rget(ft, indexstack2, fnameToHastag, sfi, siInfo) + // handle anonymous fields after handling all the non-anon fields + anonFields = append(anonFields, anonField{ft, j}) continue } } @@ -901,26 +942,39 @@ func (x *TypeInfos) rget(rt reflect.Type, indexstack []int, fnameToHastag map[st continue } - // do not let fields with same name in embedded structs override field at higher level. - // this must be done after anonymous check, to allow anonymous field - // still include their child fields - if _, ok := fnameToHastag[f.Name]; ok { - continue - } if f.Name == "" { panic(noFieldNameToStructFieldInfoErr) } + + for _, k := range pv.fNames { + if k == f.Name { + continue LOOP + } + } + pv.fNames = append(pv.fNames, f.Name) + if si == nil { si = parseStructFieldInfo(f.Name, stag) } else if si.encName == "" { si.encName = f.Name } + + for _, k := range pv.encNames { + if k == si.encName { + continue LOOP + } + } + pv.encNames = append(pv.encNames, si.encName) + // si.ikind = int(f.Type.Kind()) if len(indexstack) == 0 { si.i = int16(j) } else { si.i = -1 - si.is = append(append(make([]int, 0, len(indexstack)+4), indexstack...), j) + si.is = make([]int, len(indexstack)+1) + copy(si.is, indexstack) + si.is[len(indexstack)] = j + // si.is = append(append(make([]int, 0, len(indexstack)+4), indexstack...), j) } if siInfo != nil { @@ -928,8 +982,26 @@ func (x *TypeInfos) rget(rt reflect.Type, indexstack []int, fnameToHastag map[st si.omitEmpty = true } } - *sfi = append(*sfi, si) - fnameToHastag[f.Name] = stag != "" + pv.sfis = append(pv.sfis, si) + } + + // now handle anonymous fields +LOOP2: + for _, af := range anonFields { + // if etypes contains this, then do not call rget again (as the fields are already seen here) + ftid := reflect.ValueOf(af.ft).Pointer() + for _, k := range pv.etypes { + if k == ftid { + continue LOOP2 + } + } + pv.etypes = append(pv.etypes, ftid) + + indexstack2 := make([]int, len(indexstack)+1) + copy(indexstack2, indexstack) + indexstack2[len(indexstack)] = af.idx + // indexstack2 := append(append(make([]int, 0, len(indexstack)+4), indexstack...), j) + x.rget(af.ft, ftid, indexstack2, pv, siInfo) } } @@ -1127,3 +1199,73 @@ type bytesISlice []bytesI func (p bytesISlice) Len() int { return len(p) } func (p bytesISlice) Less(i, j int) bool { return bytes.Compare(p[i].v, p[j].v) == -1 } func (p bytesISlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + +// ----------------- + +type set []uintptr + +func (s *set) add(v uintptr) (exists bool) { + // e.ci is always nil, or len >= 1 + // defer func() { fmt.Printf("$$$$$$$$$$$ cirRef Add: %v, exists: %v\n", v, exists) }() + x := *s + if x == nil { + x = make([]uintptr, 1, 8) + x[0] = v + *s = x + return + } + // typically, length will be 1. make this perform. + if len(x) == 1 { + if j := x[0]; j == 0 { + x[0] = v + } else if j == v { + exists = true + } else { + x = append(x, v) + *s = x + } + return + } + // check if it exists + for _, j := range x { + if j == v { + exists = true + return + } + } + // try to replace a "deleted" slot + for i, j := range x { + if j == 0 { + x[i] = v + return + } + } + // if unable to replace deleted slot, just append it. + x = append(x, v) + *s = x + return +} + +func (s *set) remove(v uintptr) (exists bool) { + // defer func() { fmt.Printf("$$$$$$$$$$$ cirRef Rm: %v, exists: %v\n", v, exists) }() + x := *s + if len(x) == 0 { + return + } + if len(x) == 1 { + if x[0] == v { + x[0] = 0 + } + return + } + for i, j := range x { + if j == v { + exists = true + x[i] = 0 // set it to 0, as way to delete it. + // copy(x[i:], x[i+1:]) + // x = x[:len(x)-1] + return + } + } + return +} diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/helper_test.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/helper_test.go deleted file mode 100644 index e1dea52f4..000000000 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/helper_test.go +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved. -// Use of this source code is governed by a MIT license found in the LICENSE file. - -package codec - -// All non-std package dependencies related to testing live in this file, -// so porting to different environment is easy (just update functions). -// -// This file sets up the variables used, including testInitFns. -// Each file should add initialization that should be performed -// after flags are parsed. -// -// init is a multi-step process: -// - setup vars (handled by init functions in each file) -// - parse flags -// - setup derived vars (handled by pre-init registered functions - registered in init function) -// - post init (handled by post-init registered functions - registered in init function) -// This way, no one has to manage carefully control the initialization -// using file names, etc. -// -// Tests which require external dependencies need the -tag=x parameter. -// They should be run as: -// go test -tags=x -run=. -// Benchmarks should also take this parameter, to include the sereal, xdr, etc. -// To run against codecgen, etc, make sure you pass extra parameters. -// Example usage: -// go test "-tags=x codecgen unsafe" -bench=. -// -// To fully test everything: -// go test -tags=x -benchtime=100ms -tv -bg -bi -brw -bu -v -run=. -bench=. - -// Handling flags -// codec_test.go will define a set of global flags for testing, including: -// - Use Reset -// - Use IO reader/writer (vs direct bytes) -// - Set Canonical -// - Set InternStrings -// - Use Symbols -// -// This way, we can test them all by running same set of tests with a different -// set of flags. -// -// Following this, all the benchmarks will utilize flags set by codec_test.go -// and will not redefine these "global" flags. - -import ( - "bytes" - "errors" - "flag" - "fmt" - "reflect" - "sync" - "testing" -) - -type testHED struct { - H Handle - E *Encoder - D *Decoder -} - -var ( - testNoopH = NoopHandle(8) - testMsgpackH = &MsgpackHandle{} - testBincH = &BincHandle{} - testSimpleH = &SimpleHandle{} - testCborH = &CborHandle{} - testJsonH = &JsonHandle{} - - testHandles []Handle - testPreInitFns []func() - testPostInitFns []func() - - testOnce sync.Once - - testHEDs []testHED -) - -func init() { - testHEDs = make([]testHED, 0, 32) - testHandles = append(testHandles, - testNoopH, testMsgpackH, testBincH, testSimpleH, - testCborH, testJsonH) -} - -func testHEDGet(h Handle) *testHED { - for i := range testHEDs { - v := &testHEDs[i] - if v.H == h { - return v - } - } - testHEDs = append(testHEDs, testHED{h, NewEncoder(nil, h), NewDecoder(nil, h)}) - return &testHEDs[len(testHEDs)-1] -} - -func testInitAll() { - flag.Parse() - for _, f := range testPreInitFns { - f() - } - for _, f := range testPostInitFns { - f() - } -} - -func testCodecEncode(ts interface{}, bsIn []byte, - fn func([]byte) *bytes.Buffer, h Handle) (bs []byte, err error) { - // bs = make([]byte, 0, approxSize) - var e *Encoder - var buf *bytes.Buffer - if testUseReset { - e = testHEDGet(h).E - } else { - e = NewEncoder(nil, h) - } - if testUseIoEncDec { - buf = fn(bsIn) - e.Reset(buf) - } else { - bs = bsIn - e.ResetBytes(&bs) - } - if testUseMust { - e.MustEncode(ts) - } else { - err = e.Encode(ts) - } - if testUseIoEncDec { - bs = buf.Bytes() - } - return -} - -func testCodecDecode(bs []byte, ts interface{}, h Handle) (err error) { - var d *Decoder - var buf *bytes.Reader - if testUseReset { - d = testHEDGet(h).D - } else { - d = NewDecoder(nil, h) - } - if testUseIoEncDec { - buf = bytes.NewReader(bs) - d.Reset(buf) - } else { - d.ResetBytes(bs) - } - if testUseMust { - d.MustDecode(ts) - } else { - err = d.Decode(ts) - } - return -} - -// ----- functions below are used only by tests (not benchmarks) - -const ( - testLogToT = true - failNowOnFail = true -) - -func checkErrT(t *testing.T, err error) { - if err != nil { - logT(t, err.Error()) - failT(t) - } -} - -func checkEqualT(t *testing.T, v1 interface{}, v2 interface{}, desc string) (err error) { - if err = deepEqual(v1, v2); err != nil { - logT(t, "Not Equal: %s: %v. v1: %v, v2: %v", desc, err, v1, v2) - failT(t) - } - return -} - -func failT(t *testing.T) { - if failNowOnFail { - t.FailNow() - } else { - t.Fail() - } -} - -// --- these functions are used by both benchmarks and tests - -func deepEqual(v1, v2 interface{}) (err error) { - if !reflect.DeepEqual(v1, v2) { - err = errors.New("Not Match") - } - return -} - -func logT(x interface{}, format string, args ...interface{}) { - if t, ok := x.(*testing.T); ok && t != nil && testLogToT { - if testVerbose { - t.Logf(format, args...) - } - } else if b, ok := x.(*testing.B); ok && b != nil && testLogToT { - b.Logf(format, args...) - } else { - if len(format) == 0 || format[len(format)-1] != '\n' { - format = format + "\n" - } - fmt.Printf(format, args...) - } -} - -func approxDataSize(rv reflect.Value) (sum int) { - switch rk := rv.Kind(); rk { - case reflect.Invalid: - case reflect.Ptr, reflect.Interface: - sum += int(rv.Type().Size()) - sum += approxDataSize(rv.Elem()) - case reflect.Slice: - sum += int(rv.Type().Size()) - for j := 0; j < rv.Len(); j++ { - sum += approxDataSize(rv.Index(j)) - } - case reflect.String: - sum += int(rv.Type().Size()) - sum += rv.Len() - case reflect.Map: - sum += int(rv.Type().Size()) - for _, mk := range rv.MapKeys() { - sum += approxDataSize(mk) - sum += approxDataSize(rv.MapIndex(mk)) - } - case reflect.Struct: - //struct size already includes the full data size. - //sum += int(rv.Type().Size()) - for j := 0; j < rv.NumField(); j++ { - sum += approxDataSize(rv.Field(j)) - } - default: - //pure value types - sum += int(rv.Type().Size()) - } - return -} diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/json.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/json.go index a18a5f706..a04dfcb9d 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/json.go +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/json.go @@ -43,18 +43,23 @@ import ( //-------------------------------- -var jsonLiterals = [...]byte{'t', 'r', 'u', 'e', 'f', 'a', 'l', 's', 'e', 'n', 'u', 'l', 'l'} +var ( + jsonLiterals = [...]byte{'t', 'r', 'u', 'e', 'f', 'a', 'l', 's', 'e', 'n', 'u', 'l', 'l'} -var jsonFloat64Pow10 = [...]float64{ - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, - 1e20, 1e21, 1e22, -} + jsonFloat64Pow10 = [...]float64{ + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22, + } -var jsonUint64Pow10 = [...]uint64{ - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, -} + jsonUint64Pow10 = [...]uint64{ + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + } + + // jsonTabs and jsonSpaces are used as caches for indents + jsonTabs, jsonSpaces string +) const ( // jsonUnreadAfterDecNum controls whether we unread after decoding a number. @@ -85,8 +90,23 @@ const ( jsonNumUintMaxVal = 1< 1<<53 || v < -(1<<53)) { + e.w.writen1('"') + e.w.writeb(strconv.AppendInt(e.b[:0], v, 10)) + e.w.writen1('"') + return + } e.w.writeb(strconv.AppendInt(e.b[:0], v, 10)) } func (e *jsonEncDriver) EncodeUint(v uint64) { + if x := e.h.IntegerAsString; x == 'A' || x == 'L' && v > 1<<53 { + e.w.writen1('"') + e.w.writeb(strconv.AppendUint(e.b[:0], v, 10)) + e.w.writen1('"') + return + } e.w.writeb(strconv.AppendUint(e.b[:0], v, 10)) } @@ -165,11 +243,17 @@ func (e *jsonEncDriver) EncodeRawExt(re *RawExt, en *Encoder) { } func (e *jsonEncDriver) EncodeArrayStart(length int) { + if e.d { + e.dl++ + } e.w.writen1('[') e.c = containerArrayStart } func (e *jsonEncDriver) EncodeMapStart(length int) { + if e.d { + e.dl++ + } e.w.writen1('{') e.c = containerMapStart } @@ -564,6 +648,11 @@ func (d *jsonDecDriver) decNum(storeBytes bool) { d.tok = b } b := d.tok + var str bool + if b == '"' { + str = true + b = d.r.readn1() + } if !(b == '+' || b == '-' || b == '.' || (b >= '0' && b <= '9')) { d.d.errorf("json: decNum: got first char '%c'", b) return @@ -578,6 +667,10 @@ func (d *jsonDecDriver) decNum(storeBytes bool) { n.reset() d.bs = d.bs[:0] + if str && storeBytes { + d.bs = append(d.bs, '"') + } + // The format of a number is as below: // parsing: sign? digit* dot? digit* e? sign? digit* // states: 0 1* 2 3* 4 5* 6 7 @@ -668,6 +761,14 @@ LOOP: default: break LOOP } + case '"': + if str { + if storeBytes { + d.bs = append(d.bs, '"') + } + b, eof = r.readn1eof() + } + break LOOP default: break LOOP } @@ -1033,6 +1134,24 @@ type JsonHandle struct { // RawBytesExt, if configured, is used to encode and decode raw bytes in a custom way. // If not configured, raw bytes are encoded to/from base64 text. RawBytesExt InterfaceExt + + // Indent indicates how a value is encoded. + // - If positive, indent by that number of spaces. + // - If negative, indent by that number of tabs. + Indent int8 + + // IntegerAsString controls how integers (signed and unsigned) are encoded. + // + // Per the JSON Spec, JSON numbers are 64-bit floating point numbers. + // Consequently, integers > 2^53 cannot be represented as a JSON number without losing precision. + // This can be mitigated by configuring how to encode integers. + // + // IntegerAsString interpretes the following values: + // - if 'L', then encode integers > 2^53 as a json string. + // - if 'A', then encode all integers as a json string + // containing the exact integer representation as a decimal. + // - else encode all integers as a json number (default) + IntegerAsString uint8 } func (h *JsonHandle) SetInterfaceExt(rt reflect.Type, tag uint64, ext InterfaceExt) (err error) { @@ -1040,26 +1159,48 @@ func (h *JsonHandle) SetInterfaceExt(rt reflect.Type, tag uint64, ext InterfaceE } func (h *JsonHandle) newEncDriver(e *Encoder) encDriver { - hd := jsonEncDriver{e: e, w: e.w, h: h} + hd := jsonEncDriver{e: e, h: h} hd.bs = hd.b[:0] - hd.se.i = h.RawBytesExt + + hd.reset() + return &hd } func (h *JsonHandle) newDecDriver(d *Decoder) decDriver { // d := jsonDecDriver{r: r.(*bytesDecReader), h: h} - hd := jsonDecDriver{d: d, r: d.r, h: h} + hd := jsonDecDriver{d: d, h: h} hd.bs = hd.b[:0] - hd.se.i = h.RawBytesExt + hd.reset() return &hd } func (e *jsonEncDriver) reset() { e.w = e.e.w + e.se.i = e.h.RawBytesExt + if e.bs != nil { + e.bs = e.bs[:0] + } + e.d, e.dt, e.dl, e.ds = false, false, 0, "" + e.c = 0 + if e.h.Indent > 0 { + e.d = true + e.ds = jsonSpaces[:e.h.Indent] + } else if e.h.Indent < 0 { + e.d = true + e.dt = true + e.ds = jsonTabs[:-(e.h.Indent)] + } } func (d *jsonDecDriver) reset() { d.r = d.d.r + d.se.i = d.h.RawBytesExt + if d.bs != nil { + d.bs = d.bs[:0] + } + d.c, d.tok = 0, 0 + d.n.reset() } var jsonEncodeTerminate = []byte{' '} diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/msgpack.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/msgpack.go index 5eb4c9636..f9f872362 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/msgpack.go +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/msgpack.go @@ -374,7 +374,7 @@ func (d *msgpackDecDriver) DecodeNaked() { } if n.v == valueTypeUint && d.h.SignedInteger { n.v = valueTypeInt - n.i = int64(n.v) + n.i = int64(n.u) } return } @@ -729,6 +729,7 @@ func (e *msgpackEncDriver) reset() { func (d *msgpackDecDriver) reset() { d.r = d.d.r + d.bd, d.bdRead = 0, false } //-------------------------------------------------- diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/prebuild.sh b/Godeps/_workspace/src/github.com/ugorji/go/codec/prebuild.sh index 98f442487..909f4bb0f 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/prebuild.sh +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/prebuild.sh @@ -171,7 +171,7 @@ do 'xf') zforce=1;; 'xb') zbak=1;; 'xx') zexternal=1;; - *) echo "prebuild.sh accepts [-fb] only"; return 1;; + *) echo "prebuild.sh accepts [-fbx] only"; return 1;; esac done shift $((OPTIND-1)) diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/py_test.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/py_test.go deleted file mode 100644 index bedd7b0dc..000000000 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/py_test.go +++ /dev/null @@ -1,30 +0,0 @@ -//+build x - -// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved. -// Use of this source code is governed by a MIT license found in the LICENSE file. - -package codec - -// These tests are used to verify msgpack and cbor implementations against their python libraries. -// If you have the library installed, you can enable the tests back by running: go test -tags=x . -// Look at test.py for how to setup your environment. - -import ( - "testing" -) - -func TestMsgpackPythonGenStreams(t *testing.T) { - doTestPythonGenStreams(t, "msgpack", testMsgpackH) -} - -func TestCborPythonGenStreams(t *testing.T) { - doTestPythonGenStreams(t, "cbor", testCborH) -} - -func TestMsgpackRpcSpecGoClientToPythonSvc(t *testing.T) { - doTestMsgpackRpcSpecGoClientToPythonSvc(t) -} - -func TestMsgpackRpcSpecPythonClientToGoSvc(t *testing.T) { - doTestMsgpackRpcSpecPythonClientToGoSvc(t) -} diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/simple.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/simple.go index c15049650..7c0ba7aff 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/simple.go +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/simple.go @@ -512,6 +512,7 @@ func (e *simpleEncDriver) reset() { func (d *simpleDecDriver) reset() { d.r = d.d.r + d.bd, d.bdRead = 0, false } var _ decDriver = (*simpleDecDriver)(nil) diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/test.py b/Godeps/_workspace/src/github.com/ugorji/go/codec/test.py index dfe3b0c9a..c0ad20b34 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/test.py +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/test.py @@ -9,6 +9,8 @@ # sudo apt-get install python-pip # pip install --user msgpack-python msgpack-rpc-python cbor +# Ensure all "string" keys are utf strings (else encoded as bytes) + import cbor, msgpack, msgpackrpc, sys, os, threading def get_test_data_list(): @@ -26,35 +28,39 @@ def get_test_data_list(): -3232.0, -6464646464.0, 3232.0, + 6464.0, 6464646464.0, False, True, + u"null", None, u"someday", - u"", - u"bytestring", 1328176922000002000, + u"", -2206187877999998000, + u"bytestring", 270, + u"none", -2013855847999995777, #-6795364578871345152, ] l1 = [ { "true": True, "false": False }, - { "true": "True", + { "true": u"True", "false": False, "uint16(1616)": 1616 }, { "list": [1616, 32323232, True, -3232.0, {"TRUE":True, "FALSE":False}, [True, False] ], "int32":32323232, "bool": True, - "LONG STRING": "123456789012345678901234567890123456789012345678901234567890", - "SHORT STRING": "1234567890" }, - { True: "true", 8: False, "false": 0 } + "LONG STRING": u"123456789012345678901234567890123456789012345678901234567890", + "SHORT STRING": u"1234567890" }, + { True: "true", 138: False, "false": 200 } ] l = [] l.extend(l0) l.append(l0) + l.append(1) l.extend(l1) return l diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/tests.sh b/Godeps/_workspace/src/github.com/ugorji/go/codec/tests.sh index b1602ea7e..00857b620 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/tests.sh +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/tests.sh @@ -6,6 +6,7 @@ _run() { # 1. VARIATIONS: regular (t), canonical (c), IO R/W (i), # binc-nosymbols (n), struct2array (s), intern string (e), + # json-indent (d), circular (l) # 2. MODE: reflection (r), external (x), codecgen (g), unsafe (u), notfastpath (f) # 3. OPTIONS: verbose (v), reset (z), must (m), # @@ -16,7 +17,7 @@ _run() { zargs="" local OPTIND OPTIND=1 - while getopts "xurtcinsvgzmef" flag + while getopts "_xurtcinsvgzmefdl" flag do case "x$flag" in 'xr') ;; @@ -27,6 +28,7 @@ _run() { 'xv') zargs="$zargs -tv" ;; 'xz') zargs="$zargs -tr" ;; 'xm') zargs="$zargs -tm" ;; + 'xl') zargs="$zargs -tl" ;; *) ;; esac done @@ -35,15 +37,19 @@ _run() { # echo ">>>>>>> TAGS: $ztags" OPTIND=1 - while getopts "xurtcinsvgzmef" flag + while getopts "_xurtcinsvgzmefdl" flag do case "x$flag" in 'xt') printf ">>>>>>> REGULAR : "; go test "-tags=$ztags" $zargs ; sleep 2 ;; 'xc') printf ">>>>>>> CANONICAL : "; go test "-tags=$ztags" $zargs -tc; sleep 2 ;; 'xi') printf ">>>>>>> I/O : "; go test "-tags=$ztags" $zargs -ti; sleep 2 ;; - 'xn') printf ">>>>>>> NO_SYMBOLS : "; go test "-tags=$ztags" $zargs -tn; sleep 2 ;; + 'xn') printf ">>>>>>> NO_SYMBOLS : "; go test "-tags=$ztags" -run=Binc $zargs -tn; sleep 2 ;; 'xs') printf ">>>>>>> TO_ARRAY : "; go test "-tags=$ztags" $zargs -ts; sleep 2 ;; 'xe') printf ">>>>>>> INTERN : "; go test "-tags=$ztags" $zargs -te; sleep 2 ;; + 'xd') printf ">>>>>>> INDENT : "; + go test "-tags=$ztags" -run=JsonCodecsTable -td=-1 $zargs; + go test "-tags=$ztags" -run=JsonCodecsTable -td=8 $zargs; + sleep 2 ;; *) ;; esac done @@ -55,20 +61,20 @@ _run() { # echo ">>>>>>> RUNNING VARIATIONS OF TESTS" if [[ "x$@" = "x" ]]; then # All: r, x, g, gu - _run "-rtcinsm" # regular - _run "-rtcinsmz" # regular with reset - _run "-rtcinsmf" # regular with no fastpath (notfastpath) - _run "-xtcinsm" # external - _run "-gxtcinsm" # codecgen: requires external - _run "-gxutcinsm" # codecgen + unsafe + _run "-_tcinsed_ml" # regular + _run "-_tcinsed_ml_z" # regular with reset + _run "-_tcinsed_ml_f" # regular with no fastpath (notfastpath) + _run "-x_tcinsed_ml" # external + _run "-gx_tcinsed_ml" # codecgen: requires external + _run "-gxu_tcinsed_ml" # codecgen + unsafe elif [[ "x$@" = "x-Z" ]]; then # Regular - _run "-rtcinsm" # regular - _run "-rtcinsmz" # regular with reset + _run "-_tcinsed_ml" # regular + _run "-_tcinsed_ml_z" # regular with reset elif [[ "x$@" = "x-F" ]]; then # regular with notfastpath - _run "-rtcinsmf" # regular - _run "-rtcinsmzf" # regular with reset + _run "-_tcinsed_ml_f" # regular + _run "-_tcinsed_ml_zf" # regular with reset else _run "$@" fi diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/time.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/time.go index fc4c63e1d..718b731ec 100644 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/time.go +++ b/Godeps/_workspace/src/github.com/ugorji/go/codec/time.go @@ -5,11 +5,22 @@ package codec import ( "fmt" + "reflect" "time" ) var ( - timeDigits = [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'} + timeDigits = [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'} + timeExtEncFn = func(rv reflect.Value) (bs []byte, err error) { + defer panicToErr(&err) + bs = timeExt{}.WriteExt(rv.Interface()) + return + } + timeExtDecFn = func(rv reflect.Value, bs []byte) (err error) { + defer panicToErr(&err) + timeExt{}.ReadExt(rv.Interface(), bs) + return + } ) type timeExt struct{} diff --git a/Godeps/_workspace/src/github.com/ugorji/go/codec/values_test.go b/Godeps/_workspace/src/github.com/ugorji/go/codec/values_test.go deleted file mode 100644 index 4ec28e131..000000000 --- a/Godeps/_workspace/src/github.com/ugorji/go/codec/values_test.go +++ /dev/null @@ -1,203 +0,0 @@ -// // +build testing - -// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved. -// Use of this source code is governed by a MIT license found in the LICENSE file. - -package codec - -// This file contains values used by tests and benchmarks. -// JSON/BSON do not like maps with keys that are not strings, -// so we only use maps with string keys here. - -import ( - "math" - "time" -) - -var testStrucTime = time.Date(2012, 2, 2, 2, 2, 2, 2000, time.UTC).UTC() - -type AnonInTestStruc struct { - AS string - AI64 int64 - AI16 int16 - AUi64 uint64 - ASslice []string - AI64slice []int64 - AF64slice []float64 - // AMI32U32 map[int32]uint32 - // AMU32F64 map[uint32]float64 // json/bson do not like it - AMSU16 map[string]uint16 -} - -type AnonInTestStrucIntf struct { - Islice []interface{} - Ms map[string]interface{} - Nintf interface{} //don't set this, so we can test for nil - T time.Time -} - -type TestStruc struct { - _struct struct{} `codec:",omitempty"` //set omitempty for every field - - S string - I64 int64 - I16 int16 - Ui64 uint64 - Ui8 uint8 - B bool - By uint8 // byte: msgp doesn't like byte - - Sslice []string - I64slice []int64 - I16slice []int16 - Ui64slice []uint64 - Ui8slice []uint8 - Bslice []bool - Byslice []byte - - Iptrslice []*int64 - - // TODO: test these separately, specifically for reflection and codecgen. - // Unfortunately, ffjson doesn't support these. Its compilation even fails. - // Ui64array [4]uint64 - // Ui64slicearray [][4]uint64 - - AnonInTestStruc - - //M map[interface{}]interface{} `json:"-",bson:"-"` - Msi64 map[string]int64 - - // make this a ptr, so that it could be set or not. - // for comparison (e.g. with msgp), give it a struct tag (so it is not inlined), - // make this one omitempty (so it is included if nil). - *AnonInTestStrucIntf `codec:",omitempty"` - - Nmap map[string]bool //don't set this, so we can test for nil - Nslice []byte //don't set this, so we can test for nil - Nint64 *int64 //don't set this, so we can test for nil - Mtsptr map[string]*TestStruc - Mts map[string]TestStruc - Its []*TestStruc - Nteststruc *TestStruc -} - -// small struct for testing that codecgen works for unexported types -type tLowerFirstLetter struct { - I int - u uint64 - S string - b []byte -} - -func newTestStruc(depth int, bench bool, useInterface, useStringKeyOnly bool) (ts *TestStruc) { - var i64a, i64b, i64c, i64d int64 = 64, 6464, 646464, 64646464 - - ts = &TestStruc{ - S: "some string", - I64: math.MaxInt64 * 2 / 3, // 64, - I16: 1616, - Ui64: uint64(int64(math.MaxInt64 * 2 / 3)), // 64, //don't use MaxUint64, as bson can't write it - Ui8: 160, - B: true, - By: 5, - - Sslice: []string{"one", "two", "three"}, - I64slice: []int64{1111, 2222, 3333}, - I16slice: []int16{44, 55, 66}, - Ui64slice: []uint64{12121212, 34343434, 56565656}, - Ui8slice: []uint8{210, 211, 212}, - Bslice: []bool{true, false, true, false}, - Byslice: []byte{13, 14, 15}, - - Msi64: map[string]int64{ - "one": 1, - "two": 2, - }, - AnonInTestStruc: AnonInTestStruc{ - // There's more leeway in altering this. - AS: "A-String", - AI64: -64646464, - AI16: 1616, - AUi64: 64646464, - // (U+1D11E)G-clef character may be represented in json as "\uD834\uDD1E". - // single reverse solidus character may be represented in json as "\u005C". - // include these in ASslice below. - ASslice: []string{"Aone", "Atwo", "Athree", - "Afour.reverse_solidus.\u005c", "Afive.Gclef.\U0001d11E"}, - AI64slice: []int64{1, -22, 333, -4444, 55555, -666666}, - AMSU16: map[string]uint16{"1": 1, "22": 2, "333": 3, "4444": 4}, - AF64slice: []float64{11.11e-11, 22.22E+22, 33.33E-33, 44.44e+44, 555.55E-6, 666.66E6}, - }, - } - if useInterface { - ts.AnonInTestStrucIntf = &AnonInTestStrucIntf{ - Islice: []interface{}{"true", true, "no", false, uint64(288), float64(0.4)}, - Ms: map[string]interface{}{ - "true": "true", - "int64(9)": false, - }, - T: testStrucTime, - } - } - - //For benchmarks, some things will not work. - if !bench { - //json and bson require string keys in maps - //ts.M = map[interface{}]interface{}{ - // true: "true", - // int8(9): false, - //} - //gob cannot encode nil in element in array (encodeArray: nil element) - ts.Iptrslice = []*int64{nil, &i64a, nil, &i64b, nil, &i64c, nil, &i64d, nil} - // ts.Iptrslice = nil - } - if !useStringKeyOnly { - // ts.AnonInTestStruc.AMU32F64 = map[uint32]float64{1: 1, 2: 2, 3: 3} // Json/Bson barf - } - if depth > 0 { - depth-- - if ts.Mtsptr == nil { - ts.Mtsptr = make(map[string]*TestStruc) - } - if ts.Mts == nil { - ts.Mts = make(map[string]TestStruc) - } - ts.Mtsptr["0"] = newTestStruc(depth, bench, useInterface, useStringKeyOnly) - ts.Mts["0"] = *(ts.Mtsptr["0"]) - ts.Its = append(ts.Its, ts.Mtsptr["0"]) - } - return -} - -// Some other types - -type Sstring string -type Bbool bool -type Sstructsmall struct { - A int -} - -type Sstructbig struct { - A int - B bool - c string - // Sval Sstruct - Ssmallptr *Sstructsmall - Ssmall *Sstructsmall - Sptr *Sstructbig -} - -type SstructbigMapBySlice struct { - _struct struct{} `codec:",toarray"` - A int - B bool - c string - // Sval Sstruct - Ssmallptr *Sstructsmall - Ssmall *Sstructsmall - Sptr *Sstructbig -} - -type Sinterface interface { - Noop() -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/LICENSE b/Godeps/_workspace/src/golang.org/x/net/LICENSE similarity index 83% rename from Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/LICENSE rename to Godeps/_workspace/src/golang.org/x/net/LICENSE index 0e5fb8728..6a66aea5e 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/LICENSE +++ b/Godeps/_workspace/src/golang.org/x/net/LICENSE @@ -1,16 +1,16 @@ -Copyright (c) 2012 Rodrigo Moraes. All rights reserved. +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 + * 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 + * 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 + * 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. diff --git a/Godeps/_workspace/src/golang.org/x/net/PATENTS b/Godeps/_workspace/src/golang.org/x/net/PATENTS new file mode 100644 index 000000000..733099041 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google 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, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/Godeps/_workspace/src/golang.org/x/net/context/context_test.go b/Godeps/_workspace/src/golang.org/x/net/context/context_test.go deleted file mode 100644 index e64afa64c..000000000 --- a/Godeps/_workspace/src/golang.org/x/net/context/context_test.go +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright 2014 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 context - -import ( - "fmt" - "math/rand" - "runtime" - "strings" - "sync" - "testing" - "time" -) - -// otherContext is a Context that's not one of the types defined in context.go. -// This lets us test code paths that differ based on the underlying type of the -// Context. -type otherContext struct { - Context -} - -func TestBackground(t *testing.T) { - c := Background() - if c == nil { - t.Fatalf("Background returned nil") - } - select { - case x := <-c.Done(): - t.Errorf("<-c.Done() == %v want nothing (it should block)", x) - default: - } - if got, want := fmt.Sprint(c), "context.Background"; got != want { - t.Errorf("Background().String() = %q want %q", got, want) - } -} - -func TestTODO(t *testing.T) { - c := TODO() - if c == nil { - t.Fatalf("TODO returned nil") - } - select { - case x := <-c.Done(): - t.Errorf("<-c.Done() == %v want nothing (it should block)", x) - default: - } - if got, want := fmt.Sprint(c), "context.TODO"; got != want { - t.Errorf("TODO().String() = %q want %q", got, want) - } -} - -func TestWithCancel(t *testing.T) { - c1, cancel := WithCancel(Background()) - - if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want { - t.Errorf("c1.String() = %q want %q", got, want) - } - - o := otherContext{c1} - c2, _ := WithCancel(o) - contexts := []Context{c1, o, c2} - - for i, c := range contexts { - if d := c.Done(); d == nil { - t.Errorf("c[%d].Done() == %v want non-nil", i, d) - } - if e := c.Err(); e != nil { - t.Errorf("c[%d].Err() == %v want nil", i, e) - } - - select { - case x := <-c.Done(): - t.Errorf("<-c.Done() == %v want nothing (it should block)", x) - default: - } - } - - cancel() - time.Sleep(100 * time.Millisecond) // let cancelation propagate - - for i, c := range contexts { - select { - case <-c.Done(): - default: - t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i) - } - if e := c.Err(); e != Canceled { - t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled) - } - } -} - -func TestParentFinishesChild(t *testing.T) { - // Context tree: - // parent -> cancelChild - // parent -> valueChild -> timerChild - parent, cancel := WithCancel(Background()) - cancelChild, stop := WithCancel(parent) - defer stop() - valueChild := WithValue(parent, "key", "value") - timerChild, stop := WithTimeout(valueChild, 10000*time.Hour) - defer stop() - - select { - case x := <-parent.Done(): - t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) - case x := <-cancelChild.Done(): - t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x) - case x := <-timerChild.Done(): - t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x) - case x := <-valueChild.Done(): - t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x) - default: - } - - // The parent's children should contain the two cancelable children. - pc := parent.(*cancelCtx) - cc := cancelChild.(*cancelCtx) - tc := timerChild.(*timerCtx) - pc.mu.Lock() - if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] { - t.Errorf("bad linkage: pc.children = %v, want %v and %v", - pc.children, cc, tc) - } - pc.mu.Unlock() - - if p, ok := parentCancelCtx(cc.Context); !ok || p != pc { - t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc) - } - if p, ok := parentCancelCtx(tc.Context); !ok || p != pc { - t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc) - } - - cancel() - - pc.mu.Lock() - if len(pc.children) != 0 { - t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children) - } - pc.mu.Unlock() - - // parent and children should all be finished. - check := func(ctx Context, name string) { - select { - case <-ctx.Done(): - default: - t.Errorf("<-%s.Done() blocked, but shouldn't have", name) - } - if e := ctx.Err(); e != Canceled { - t.Errorf("%s.Err() == %v want %v", name, e, Canceled) - } - } - check(parent, "parent") - check(cancelChild, "cancelChild") - check(valueChild, "valueChild") - check(timerChild, "timerChild") - - // WithCancel should return a canceled context on a canceled parent. - precanceledChild := WithValue(parent, "key", "value") - select { - case <-precanceledChild.Done(): - default: - t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have") - } - if e := precanceledChild.Err(); e != Canceled { - t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled) - } -} - -func TestChildFinishesFirst(t *testing.T) { - cancelable, stop := WithCancel(Background()) - defer stop() - for _, parent := range []Context{Background(), cancelable} { - child, cancel := WithCancel(parent) - - select { - case x := <-parent.Done(): - t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) - case x := <-child.Done(): - t.Errorf("<-child.Done() == %v want nothing (it should block)", x) - default: - } - - cc := child.(*cancelCtx) - pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background() - if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) { - t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok) - } - - if pcok { - pc.mu.Lock() - if len(pc.children) != 1 || !pc.children[cc] { - t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc) - } - pc.mu.Unlock() - } - - cancel() - - if pcok { - pc.mu.Lock() - if len(pc.children) != 0 { - t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children) - } - pc.mu.Unlock() - } - - // child should be finished. - select { - case <-child.Done(): - default: - t.Errorf("<-child.Done() blocked, but shouldn't have") - } - if e := child.Err(); e != Canceled { - t.Errorf("child.Err() == %v want %v", e, Canceled) - } - - // parent should not be finished. - select { - case x := <-parent.Done(): - t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) - default: - } - if e := parent.Err(); e != nil { - t.Errorf("parent.Err() == %v want nil", e) - } - } -} - -func testDeadline(c Context, wait time.Duration, t *testing.T) { - select { - case <-time.After(wait): - t.Fatalf("context should have timed out") - case <-c.Done(): - } - if e := c.Err(); e != DeadlineExceeded { - t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded) - } -} - -func TestDeadline(t *testing.T) { - c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) - if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { - t.Errorf("c.String() = %q want prefix %q", got, prefix) - } - testDeadline(c, 200*time.Millisecond, t) - - c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) - o := otherContext{c} - testDeadline(o, 200*time.Millisecond, t) - - c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) - o = otherContext{c} - c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond)) - testDeadline(c, 200*time.Millisecond, t) -} - -func TestTimeout(t *testing.T) { - c, _ := WithTimeout(Background(), 100*time.Millisecond) - if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { - t.Errorf("c.String() = %q want prefix %q", got, prefix) - } - testDeadline(c, 200*time.Millisecond, t) - - c, _ = WithTimeout(Background(), 100*time.Millisecond) - o := otherContext{c} - testDeadline(o, 200*time.Millisecond, t) - - c, _ = WithTimeout(Background(), 100*time.Millisecond) - o = otherContext{c} - c, _ = WithTimeout(o, 300*time.Millisecond) - testDeadline(c, 200*time.Millisecond, t) -} - -func TestCanceledTimeout(t *testing.T) { - c, _ := WithTimeout(Background(), 200*time.Millisecond) - o := otherContext{c} - c, cancel := WithTimeout(o, 400*time.Millisecond) - cancel() - time.Sleep(100 * time.Millisecond) // let cancelation propagate - select { - case <-c.Done(): - default: - t.Errorf("<-c.Done() blocked, but shouldn't have") - } - if e := c.Err(); e != Canceled { - t.Errorf("c.Err() == %v want %v", e, Canceled) - } -} - -type key1 int -type key2 int - -var k1 = key1(1) -var k2 = key2(1) // same int as k1, different type -var k3 = key2(3) // same type as k2, different int - -func TestValues(t *testing.T) { - check := func(c Context, nm, v1, v2, v3 string) { - if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 { - t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0) - } - if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 { - t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0) - } - if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 { - t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0) - } - } - - c0 := Background() - check(c0, "c0", "", "", "") - - c1 := WithValue(Background(), k1, "c1k1") - check(c1, "c1", "c1k1", "", "") - - if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want { - t.Errorf("c.String() = %q want %q", got, want) - } - - c2 := WithValue(c1, k2, "c2k2") - check(c2, "c2", "c1k1", "c2k2", "") - - c3 := WithValue(c2, k3, "c3k3") - check(c3, "c2", "c1k1", "c2k2", "c3k3") - - c4 := WithValue(c3, k1, nil) - check(c4, "c4", "", "c2k2", "c3k3") - - o0 := otherContext{Background()} - check(o0, "o0", "", "", "") - - o1 := otherContext{WithValue(Background(), k1, "c1k1")} - check(o1, "o1", "c1k1", "", "") - - o2 := WithValue(o1, k2, "o2k2") - check(o2, "o2", "c1k1", "o2k2", "") - - o3 := otherContext{c4} - check(o3, "o3", "", "c2k2", "c3k3") - - o4 := WithValue(o3, k3, nil) - check(o4, "o4", "", "c2k2", "") -} - -func TestAllocs(t *testing.T) { - bg := Background() - for _, test := range []struct { - desc string - f func() - limit float64 - gccgoLimit float64 - }{ - { - desc: "Background()", - f: func() { Background() }, - limit: 0, - gccgoLimit: 0, - }, - { - desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1), - f: func() { - c := WithValue(bg, k1, nil) - c.Value(k1) - }, - limit: 3, - gccgoLimit: 3, - }, - { - desc: "WithTimeout(bg, 15*time.Millisecond)", - f: func() { - c, _ := WithTimeout(bg, 15*time.Millisecond) - <-c.Done() - }, - limit: 8, - gccgoLimit: 15, - }, - { - desc: "WithCancel(bg)", - f: func() { - c, cancel := WithCancel(bg) - cancel() - <-c.Done() - }, - limit: 5, - gccgoLimit: 8, - }, - { - desc: "WithTimeout(bg, 100*time.Millisecond)", - f: func() { - c, cancel := WithTimeout(bg, 100*time.Millisecond) - cancel() - <-c.Done() - }, - limit: 8, - gccgoLimit: 25, - }, - } { - limit := test.limit - if runtime.Compiler == "gccgo" { - // gccgo does not yet do escape analysis. - // TOOD(iant): Remove this when gccgo does do escape analysis. - limit = test.gccgoLimit - } - if n := testing.AllocsPerRun(100, test.f); n > limit { - t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit)) - } - } -} - -func TestSimultaneousCancels(t *testing.T) { - root, cancel := WithCancel(Background()) - m := map[Context]CancelFunc{root: cancel} - q := []Context{root} - // Create a tree of contexts. - for len(q) != 0 && len(m) < 100 { - parent := q[0] - q = q[1:] - for i := 0; i < 4; i++ { - ctx, cancel := WithCancel(parent) - m[ctx] = cancel - q = append(q, ctx) - } - } - // Start all the cancels in a random order. - var wg sync.WaitGroup - wg.Add(len(m)) - for _, cancel := range m { - go func(cancel CancelFunc) { - cancel() - wg.Done() - }(cancel) - } - // Wait on all the contexts in a random order. - for ctx := range m { - select { - case <-ctx.Done(): - case <-time.After(1 * time.Second): - buf := make([]byte, 10<<10) - n := runtime.Stack(buf, true) - t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n]) - } - } - // Wait for all the cancel functions to return. - done := make(chan struct{}) - go func() { - wg.Wait() - close(done) - }() - select { - case <-done: - case <-time.After(1 * time.Second): - buf := make([]byte, 10<<10) - n := runtime.Stack(buf, true) - t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n]) - } -} - -func TestInterlockedCancels(t *testing.T) { - parent, cancelParent := WithCancel(Background()) - child, cancelChild := WithCancel(parent) - go func() { - parent.Done() - cancelChild() - }() - cancelParent() - select { - case <-child.Done(): - case <-time.After(1 * time.Second): - buf := make([]byte, 10<<10) - n := runtime.Stack(buf, true) - t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n]) - } -} - -func TestLayersCancel(t *testing.T) { - testLayers(t, time.Now().UnixNano(), false) -} - -func TestLayersTimeout(t *testing.T) { - testLayers(t, time.Now().UnixNano(), true) -} - -func testLayers(t *testing.T, seed int64, testTimeout bool) { - rand.Seed(seed) - errorf := func(format string, a ...interface{}) { - t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...) - } - const ( - timeout = 200 * time.Millisecond - minLayers = 30 - ) - type value int - var ( - vals []*value - cancels []CancelFunc - numTimers int - ctx = Background() - ) - for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ { - switch rand.Intn(3) { - case 0: - v := new(value) - ctx = WithValue(ctx, v, v) - vals = append(vals, v) - case 1: - var cancel CancelFunc - ctx, cancel = WithCancel(ctx) - cancels = append(cancels, cancel) - case 2: - var cancel CancelFunc - ctx, cancel = WithTimeout(ctx, timeout) - cancels = append(cancels, cancel) - numTimers++ - } - } - checkValues := func(when string) { - for _, key := range vals { - if val := ctx.Value(key).(*value); key != val { - errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key) - } - } - } - select { - case <-ctx.Done(): - errorf("ctx should not be canceled yet") - default: - } - if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) { - t.Errorf("ctx.String() = %q want prefix %q", s, prefix) - } - t.Log(ctx) - checkValues("before cancel") - if testTimeout { - select { - case <-ctx.Done(): - case <-time.After(timeout + timeout/10): - errorf("ctx should have timed out") - } - checkValues("after timeout") - } else { - cancel := cancels[rand.Intn(len(cancels))] - cancel() - select { - case <-ctx.Done(): - default: - errorf("ctx should be canceled") - } - checkValues("after cancel") - } -} - -func TestCancelRemoves(t *testing.T) { - checkChildren := func(when string, ctx Context, want int) { - if got := len(ctx.(*cancelCtx).children); got != want { - t.Errorf("%s: context has %d children, want %d", when, got, want) - } - } - - ctx, _ := WithCancel(Background()) - checkChildren("after creation", ctx, 0) - _, cancel := WithCancel(ctx) - checkChildren("with WithCancel child ", ctx, 1) - cancel() - checkChildren("after cancelling WithCancel child", ctx, 0) - - ctx, _ = WithCancel(Background()) - checkChildren("after creation", ctx, 0) - _, cancel = WithTimeout(ctx, 60*time.Minute) - checkChildren("with WithTimeout child ", ctx, 1) - cancel() - checkChildren("after cancelling WithTimeout child", ctx, 0) -} diff --git a/Godeps/_workspace/src/golang.org/x/net/context/ctxhttp/ctxhttp_test.go b/Godeps/_workspace/src/golang.org/x/net/context/ctxhttp/ctxhttp_test.go deleted file mode 100644 index 47b53d7f1..000000000 --- a/Godeps/_workspace/src/golang.org/x/net/context/ctxhttp/ctxhttp_test.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2015 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 ctxhttp - -import ( - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" - "time" - - "golang.org/x/net/context" -) - -const ( - requestDuration = 100 * time.Millisecond - requestBody = "ok" -) - -func TestNoTimeout(t *testing.T) { - ctx := context.Background() - resp, err := doRequest(ctx) - - if resp == nil || err != nil { - t.Fatalf("error received from client: %v %v", err, resp) - } -} -func TestCancel(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - go func() { - time.Sleep(requestDuration / 2) - cancel() - }() - - resp, err := doRequest(ctx) - - if resp != nil || err == nil { - t.Fatalf("expected error, didn't get one. resp: %v", resp) - } - if err != ctx.Err() { - t.Fatalf("expected error from context but got: %v", err) - } -} - -func TestCancelAfterRequest(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - - resp, err := doRequest(ctx) - - // Cancel before reading the body. - // Request.Body should still be readable after the context is canceled. - cancel() - - b, err := ioutil.ReadAll(resp.Body) - if err != nil || string(b) != requestBody { - t.Fatalf("could not read body: %q %v", b, err) - } -} - -func doRequest(ctx context.Context) (*http.Response, error) { - var okHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - time.Sleep(requestDuration) - w.Write([]byte(requestBody)) - }) - - serv := httptest.NewServer(okHandler) - defer serv.Close() - - return Get(ctx, nil, serv.URL) -} diff --git a/Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go b/Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go deleted file mode 100644 index a6754dc36..000000000 --- a/Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2014 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 context_test - -import ( - "fmt" - "time" - - "golang.org/x/net/context" -) - -func ExampleWithTimeout() { - // Pass a context with a timeout to tell a blocking function that it - // should abandon its work after the timeout elapses. - ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond) - select { - case <-time.After(200 * time.Millisecond): - fmt.Println("overslept") - case <-ctx.Done(): - fmt.Println(ctx.Err()) // prints "context deadline exceeded" - } - // Output: - // context deadline exceeded -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/clientcredentials/clientcredentials.go b/Godeps/_workspace/src/golang.org/x/oauth2/clientcredentials/clientcredentials.go deleted file mode 100644 index 452fb8c12..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/clientcredentials/clientcredentials.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2014 The oauth2 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 clientcredentials implements the OAuth2.0 "client credentials" token flow, -// also known as the "two-legged OAuth 2.0". -// -// This should be used when the client is acting on its own behalf or when the client -// is the resource owner. It may also be used when requesting access to protected -// resources based on an authorization previously arranged with the authorization -// server. -// -// See http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.4 -package clientcredentials - -import ( - "net/http" - "net/url" - "strings" - - "golang.org/x/net/context" - "golang.org/x/oauth2" - "golang.org/x/oauth2/internal" -) - -// tokenFromInternal maps an *internal.Token struct into -// an *oauth2.Token struct. -func tokenFromInternal(t *internal.Token) *oauth2.Token { - if t == nil { - return nil - } - tk := &oauth2.Token{ - AccessToken: t.AccessToken, - TokenType: t.TokenType, - RefreshToken: t.RefreshToken, - Expiry: t.Expiry, - } - return tk.WithExtra(t.Raw) -} - -// retrieveToken takes a *Config and uses that to retrieve an *internal.Token. -// This token is then mapped from *internal.Token into an *oauth2.Token which is -// returned along with an error. -func retrieveToken(ctx context.Context, c *Config, v url.Values) (*oauth2.Token, error) { - tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.TokenURL, v) - if err != nil { - return nil, err - } - return tokenFromInternal(tk), nil -} - -// Client Credentials Config describes a 2-legged OAuth2 flow, with both the -// client application information and the server's endpoint URLs. -type Config struct { - // ClientID is the application's ID. - ClientID string - - // ClientSecret is the application's secret. - ClientSecret string - - // TokenURL is the resource server's token endpoint - // URL. This is a constant specific to each server. - TokenURL string - - // Scope specifies optional requested permissions. - Scopes []string -} - -// Token uses client credentials to retreive a token. -// The HTTP client to use is derived from the context. -// If nil, http.DefaultClient is used. -func (c *Config) Token(ctx context.Context) (*oauth2.Token, error) { - return retrieveToken(ctx, c, url.Values{ - "grant_type": {"client_credentials"}, - "scope": internal.CondVal(strings.Join(c.Scopes, " ")), - }) -} - -// Client returns an HTTP client using the provided token. -// The token will auto-refresh as necessary. The underlying -// HTTP transport will be obtained using the provided context. -// The returned client and its Transport should not be modified. -func (c *Config) Client(ctx context.Context) *http.Client { - return oauth2.NewClient(ctx, c.TokenSource(ctx)) -} - -// TokenSource returns a TokenSource that returns t until t expires, -// automatically refreshing it as necessary using the provided context and the -// client ID and client secret. -// -// Most users will use Config.Client instead. -func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource { - source := &tokenSource{ - ctx: ctx, - conf: c, - } - return oauth2.ReuseTokenSource(nil, source) -} - -type tokenSource struct { - ctx context.Context - conf *Config -} - -// Token refreshes the token by using a new client credentials request. -// tokens received this way do not include a refresh token -func (c *tokenSource) Token() (*oauth2.Token, error) { - return retrieveToken(c.ctx, c.conf, url.Values{ - "grant_type": {"client_credentials"}, - "scope": internal.CondVal(strings.Join(c.conf.Scopes, " ")), - }) -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/clientcredentials/clientcredentials_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/clientcredentials/clientcredentials_test.go deleted file mode 100644 index ab319e082..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/clientcredentials/clientcredentials_test.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2014 The oauth2 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 clientcredentials - -import ( - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" - - "golang.org/x/oauth2" -) - -func newConf(url string) *Config { - return &Config{ - ClientID: "CLIENT_ID", - ClientSecret: "CLIENT_SECRET", - Scopes: []string{"scope1", "scope2"}, - TokenURL: url + "/token", - } -} - -type mockTransport struct { - rt func(req *http.Request) (resp *http.Response, err error) -} - -func (t *mockTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) { - return t.rt(req) -} - -func TestTokenRequest(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.String() != "/token" { - t.Errorf("authenticate client request URL = %q; want %q", r.URL, "/token") - } - headerAuth := r.Header.Get("Authorization") - if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" { - t.Errorf("Unexpected authorization header, %v is found.", headerAuth) - } - if got, want := r.Header.Get("Content-Type"), "application/x-www-form-urlencoded"; got != want { - t.Errorf("Content-Type header = %q; want %q", got, want) - } - body, err := ioutil.ReadAll(r.Body) - if err != nil { - r.Body.Close() - } - if err != nil { - t.Errorf("failed reading request body: %s.", err) - } - if string(body) != "client_id=CLIENT_ID&grant_type=client_credentials&scope=scope1+scope2" { - t.Errorf("payload = %q; want %q", string(body), "client_id=CLIENT_ID&grant_type=client_credentials&scope=scope1+scope2") - } - w.Header().Set("Content-Type", "application/x-www-form-urlencoded") - w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&token_type=bearer")) - })) - defer ts.Close() - conf := newConf(ts.URL) - tok, err := conf.Token(oauth2.NoContext) - if err != nil { - t.Error(err) - } - if !tok.Valid() { - t.Fatalf("token invalid. got: %#v", tok) - } - if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" { - t.Errorf("Access token = %q; want %q", tok.AccessToken, "90d64460d14870c08c81352a05dedd3465940a7c") - } - if tok.TokenType != "bearer" { - t.Errorf("token type = %q; want %q", tok.TokenType, "bearer") - } -} - -func TestTokenRefreshRequest(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.String() == "/somethingelse" { - return - } - if r.URL.String() != "/token" { - t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL) - } - headerContentType := r.Header.Get("Content-Type") - if headerContentType != "application/x-www-form-urlencoded" { - t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType) - } - body, _ := ioutil.ReadAll(r.Body) - if string(body) != "client_id=CLIENT_ID&grant_type=client_credentials&scope=scope1+scope2" { - t.Errorf("Unexpected refresh token payload, %v is found.", string(body)) - } - })) - defer ts.Close() - conf := newConf(ts.URL) - c := conf.Client(oauth2.NoContext) - c.Get(ts.URL + "/somethingelse") -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/example_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/example_test.go deleted file mode 100644 index 8be278855..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/example_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2014 The oauth2 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 oauth2_test - -import ( - "fmt" - "log" - - "golang.org/x/oauth2" -) - -func ExampleConfig() { - conf := &oauth2.Config{ - ClientID: "YOUR_CLIENT_ID", - ClientSecret: "YOUR_CLIENT_SECRET", - Scopes: []string{"SCOPE1", "SCOPE2"}, - Endpoint: oauth2.Endpoint{ - AuthURL: "https://provider.com/o/oauth2/auth", - TokenURL: "https://provider.com/o/oauth2/token", - }, - } - - // Redirect user to consent page to ask for permission - // for the scopes specified above. - url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline) - fmt.Printf("Visit the URL for the auth dialog: %v", url) - - // Use the authorization code that is pushed to the redirect URL. - // NewTransportWithCode will do the handshake to retrieve - // an access token and initiate a Transport that is - // authorized and authenticated by the retrieved token. - var code string - if _, err := fmt.Scan(&code); err != nil { - log.Fatal(err) - } - tok, err := conf.Exchange(oauth2.NoContext, code) - if err != nil { - log.Fatal(err) - } - - client := conf.Client(oauth2.NoContext, tok) - client.Get("...") -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/facebook/facebook.go b/Godeps/_workspace/src/golang.org/x/oauth2/facebook/facebook.go deleted file mode 100644 index 9c816ff80..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/facebook/facebook.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2015 The oauth2 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 facebook provides constants for using OAuth2 to access Facebook. -package facebook - -import ( - "golang.org/x/oauth2" -) - -// Endpoint is Facebook's OAuth 2.0 endpoint. -var Endpoint = oauth2.Endpoint{ - AuthURL: "https://www.facebook.com/dialog/oauth", - TokenURL: "https://graph.facebook.com/oauth/access_token", -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/github/github.go b/Godeps/_workspace/src/golang.org/x/oauth2/github/github.go deleted file mode 100644 index 82ca623dd..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/github/github.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2014 The oauth2 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 github provides constants for using OAuth2 to access Github. -package github - -import ( - "golang.org/x/oauth2" -) - -// Endpoint is Github's OAuth 2.0 endpoint. -var Endpoint = oauth2.Endpoint{ - AuthURL: "https://github.com/login/oauth/authorize", - TokenURL: "https://github.com/login/oauth/access_token", -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/example_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/example_test.go deleted file mode 100644 index 17262802a..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/google/example_test.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2014 The oauth2 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 appenginevm !appengine - -package google_test - -import ( - "fmt" - "io/ioutil" - "log" - "net/http" - - "golang.org/x/oauth2" - "golang.org/x/oauth2/google" - "golang.org/x/oauth2/jwt" - "google.golang.org/appengine" - "google.golang.org/appengine/urlfetch" -) - -func ExampleDefaultClient() { - client, err := google.DefaultClient(oauth2.NoContext, - "https://www.googleapis.com/auth/devstorage.full_control") - if err != nil { - log.Fatal(err) - } - client.Get("...") -} - -func Example_webServer() { - // Your credentials should be obtained from the Google - // Developer Console (https://console.developers.google.com). - conf := &oauth2.Config{ - ClientID: "YOUR_CLIENT_ID", - ClientSecret: "YOUR_CLIENT_SECRET", - RedirectURL: "YOUR_REDIRECT_URL", - Scopes: []string{ - "https://www.googleapis.com/auth/bigquery", - "https://www.googleapis.com/auth/blogger", - }, - Endpoint: google.Endpoint, - } - // Redirect user to Google's consent page to ask for permission - // for the scopes specified above. - url := conf.AuthCodeURL("state") - fmt.Printf("Visit the URL for the auth dialog: %v", url) - - // Handle the exchange code to initiate a transport. - tok, err := conf.Exchange(oauth2.NoContext, "authorization-code") - if err != nil { - log.Fatal(err) - } - client := conf.Client(oauth2.NoContext, tok) - client.Get("...") -} - -func ExampleJWTConfigFromJSON() { - // Your credentials should be obtained from the Google - // Developer Console (https://console.developers.google.com). - // Navigate to your project, then see the "Credentials" page - // under "APIs & Auth". - // To create a service account client, click "Create new Client ID", - // select "Service Account", and click "Create Client ID". A JSON - // key file will then be downloaded to your computer. - data, err := ioutil.ReadFile("/path/to/your-project-key.json") - if err != nil { - log.Fatal(err) - } - conf, err := google.JWTConfigFromJSON(data, "https://www.googleapis.com/auth/bigquery") - if err != nil { - log.Fatal(err) - } - // Initiate an http.Client. The following GET request will be - // authorized and authenticated on the behalf of - // your service account. - client := conf.Client(oauth2.NoContext) - client.Get("...") -} - -func ExampleSDKConfig() { - // The credentials will be obtained from the first account that - // has been authorized with `gcloud auth login`. - conf, err := google.NewSDKConfig("") - if err != nil { - log.Fatal(err) - } - // Initiate an http.Client. The following GET request will be - // authorized and authenticated on the behalf of the SDK user. - client := conf.Client(oauth2.NoContext) - client.Get("...") -} - -func Example_serviceAccount() { - // Your credentials should be obtained from the Google - // Developer Console (https://console.developers.google.com). - conf := &jwt.Config{ - Email: "xxx@developer.gserviceaccount.com", - // The contents of your RSA private key or your PEM file - // that contains a private key. - // If you have a p12 file instead, you - // can use `openssl` to export the private key into a pem file. - // - // $ openssl pkcs12 -in key.p12 -passin pass:notasecret -out key.pem -nodes - // - // The field only supports PEM containers with no passphrase. - // The openssl command will convert p12 keys to passphrase-less PEM containers. - PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."), - Scopes: []string{ - "https://www.googleapis.com/auth/bigquery", - "https://www.googleapis.com/auth/blogger", - }, - TokenURL: google.JWTTokenURL, - // If you would like to impersonate a user, you can - // create a transport with a subject. The following GET - // request will be made on the behalf of user@example.com. - // Optional. - Subject: "user@example.com", - } - // Initiate an http.Client, the following GET request will be - // authorized and authenticated on the behalf of user@example.com. - client := conf.Client(oauth2.NoContext) - client.Get("...") -} - -func ExampleAppEngineTokenSource() { - var req *http.Request // from the ServeHTTP handler - ctx := appengine.NewContext(req) - client := &http.Client{ - Transport: &oauth2.Transport{ - Source: google.AppEngineTokenSource(ctx, "https://www.googleapis.com/auth/bigquery"), - Base: &urlfetch.Transport{ - Context: ctx, - }, - }, - } - client.Get("...") -} - -func ExampleComputeTokenSource() { - client := &http.Client{ - Transport: &oauth2.Transport{ - // Fetch from Google Compute Engine's metadata server to retrieve - // an access token for the provided account. - // If no account is specified, "default" is used. - Source: google.ComputeTokenSource(""), - }, - } - client.Get("...") -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/google_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/google_test.go deleted file mode 100644 index 4cc01884b..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/google/google_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2015 The oauth2 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 google - -import ( - "strings" - "testing" -) - -var webJSONKey = []byte(` -{ - "web": { - "auth_uri": "https://google.com/o/oauth2/auth", - "client_secret": "3Oknc4jS_wA2r9i", - "token_uri": "https://google.com/o/oauth2/token", - "client_email": "222-nprqovg5k43uum874cs9osjt2koe97g8@developer.gserviceaccount.com", - "redirect_uris": ["https://www.example.com/oauth2callback"], - "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/222-nprqovg5k43uum874cs9osjt2koe97g8@developer.gserviceaccount.com", - "client_id": "222-nprqovg5k43uum874cs9osjt2koe97g8.apps.googleusercontent.com", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "javascript_origins": ["https://www.example.com"] - } -}`) - -var installedJSONKey = []byte(`{ - "installed": { - "client_id": "222-installed.apps.googleusercontent.com", - "redirect_uris": ["https://www.example.com/oauth2callback"] - } -}`) - -func TestConfigFromJSON(t *testing.T) { - conf, err := ConfigFromJSON(webJSONKey, "scope1", "scope2") - if err != nil { - t.Error(err) - } - if got, want := conf.ClientID, "222-nprqovg5k43uum874cs9osjt2koe97g8.apps.googleusercontent.com"; got != want { - t.Errorf("ClientID = %q; want %q", got, want) - } - if got, want := conf.ClientSecret, "3Oknc4jS_wA2r9i"; got != want { - t.Errorf("ClientSecret = %q; want %q", got, want) - } - if got, want := conf.RedirectURL, "https://www.example.com/oauth2callback"; got != want { - t.Errorf("RedictURL = %q; want %q", got, want) - } - if got, want := strings.Join(conf.Scopes, ","), "scope1,scope2"; got != want { - t.Errorf("Scopes = %q; want %q", got, want) - } - if got, want := conf.Endpoint.AuthURL, "https://google.com/o/oauth2/auth"; got != want { - t.Errorf("AuthURL = %q; want %q", got, want) - } - if got, want := conf.Endpoint.TokenURL, "https://google.com/o/oauth2/token"; got != want { - t.Errorf("TokenURL = %q; want %q", got, want) - } -} - -func TestConfigFromJSON_Installed(t *testing.T) { - conf, err := ConfigFromJSON(installedJSONKey) - if err != nil { - t.Error(err) - } - if got, want := conf.ClientID, "222-installed.apps.googleusercontent.com"; got != want { - t.Errorf("ClientID = %q; want %q", got, want) - } -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk_test.go deleted file mode 100644 index 79df88964..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2015 The oauth2 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 google - -import "testing" - -func TestSDKConfig(t *testing.T) { - sdkConfigPath = func() (string, error) { - return "testdata/gcloud", nil - } - - tests := []struct { - account string - accessToken string - err bool - }{ - {"", "bar_access_token", false}, - {"foo@example.com", "foo_access_token", false}, - {"bar@example.com", "bar_access_token", false}, - {"baz@serviceaccount.example.com", "", true}, - } - for _, tt := range tests { - c, err := NewSDKConfig(tt.account) - if got, want := err != nil, tt.err; got != want { - if !tt.err { - t.Errorf("expected no error, got error: %v", tt.err, err) - } else { - t.Errorf("expected error, got none") - } - continue - } - if err != nil { - continue - } - tok := c.initialToken - if tok == nil { - t.Errorf("expected token %q, got: nil", tt.accessToken) - continue - } - if tok.AccessToken != tt.accessToken { - t.Errorf("expected token %q, got: %q", tt.accessToken, tok.AccessToken) - } - } -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/credentials b/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/credentials deleted file mode 100644 index ff5eefbd0..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/credentials +++ /dev/null @@ -1,122 +0,0 @@ -{ - "data": [ - { - "credential": { - "_class": "OAuth2Credentials", - "_module": "oauth2client.client", - "access_token": "foo_access_token", - "client_id": "foo_client_id", - "client_secret": "foo_client_secret", - "id_token": { - "at_hash": "foo_at_hash", - "aud": "foo_aud", - "azp": "foo_azp", - "cid": "foo_cid", - "email": "foo@example.com", - "email_verified": true, - "exp": 1420573614, - "iat": 1420569714, - "id": "1337", - "iss": "accounts.google.com", - "sub": "1337", - "token_hash": "foo_token_hash", - "verified_email": true - }, - "invalid": false, - "refresh_token": "foo_refresh_token", - "revoke_uri": "https://accounts.google.com/o/oauth2/revoke", - "token_expiry": "2015-01-09T00:51:51Z", - "token_response": { - "access_token": "foo_access_token", - "expires_in": 3600, - "id_token": "foo_id_token", - "token_type": "Bearer" - }, - "token_uri": "https://accounts.google.com/o/oauth2/token", - "user_agent": "Cloud SDK Command Line Tool" - }, - "key": { - "account": "foo@example.com", - "clientId": "foo_client_id", - "scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting", - "type": "google-cloud-sdk" - } - }, - { - "credential": { - "_class": "OAuth2Credentials", - "_module": "oauth2client.client", - "access_token": "bar_access_token", - "client_id": "bar_client_id", - "client_secret": "bar_client_secret", - "id_token": { - "at_hash": "bar_at_hash", - "aud": "bar_aud", - "azp": "bar_azp", - "cid": "bar_cid", - "email": "bar@example.com", - "email_verified": true, - "exp": 1420573614, - "iat": 1420569714, - "id": "1337", - "iss": "accounts.google.com", - "sub": "1337", - "token_hash": "bar_token_hash", - "verified_email": true - }, - "invalid": false, - "refresh_token": "bar_refresh_token", - "revoke_uri": "https://accounts.google.com/o/oauth2/revoke", - "token_expiry": "2015-01-09T00:51:51Z", - "token_response": { - "access_token": "bar_access_token", - "expires_in": 3600, - "id_token": "bar_id_token", - "token_type": "Bearer" - }, - "token_uri": "https://accounts.google.com/o/oauth2/token", - "user_agent": "Cloud SDK Command Line Tool" - }, - "key": { - "account": "bar@example.com", - "clientId": "bar_client_id", - "scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting", - "type": "google-cloud-sdk" - } - }, - { - "credential": { - "_class": "ServiceAccountCredentials", - "_kwargs": {}, - "_module": "oauth2client.client", - "_private_key_id": "00000000000000000000000000000000", - "_private_key_pkcs8_text": "-----BEGIN RSA PRIVATE KEY-----\nMIICWwIBAAKBgQCt3fpiynPSaUhWSIKMGV331zudwJ6GkGmvQtwsoK2S2LbvnSwU\nNxgj4fp08kIDR5p26wF4+t/HrKydMwzftXBfZ9UmLVJgRdSswmS5SmChCrfDS5OE\nvFFcN5+6w1w8/Nu657PF/dse8T0bV95YrqyoR0Osy8WHrUOMSIIbC3hRuwIDAQAB\nAoGAJrGE/KFjn0sQ7yrZ6sXmdLawrM3mObo/2uI9T60+k7SpGbBX0/Pi6nFrJMWZ\nTVONG7P3Mu5aCPzzuVRYJB0j8aldSfzABTY3HKoWCczqw1OztJiEseXGiYz4QOyr\nYU3qDyEpdhS6q6wcoLKGH+hqRmz6pcSEsc8XzOOu7s4xW8kCQQDkc75HjhbarCnd\nJJGMe3U76+6UGmdK67ltZj6k6xoB5WbTNChY9TAyI2JC+ppYV89zv3ssj4L+02u3\nHIHFGxsHAkEAwtU1qYb1tScpchPobnYUFiVKJ7KA8EZaHVaJJODW/cghTCV7BxcJ\nbgVvlmk4lFKn3lPKAgWw7PdQsBTVBUcCrQJATPwoIirizrv3u5soJUQxZIkENAqV\nxmybZx9uetrzP7JTrVbFRf0SScMcyN90hdLJiQL8+i4+gaszgFht7sNMnwJAAbfj\nq0UXcauQwALQ7/h2oONfTg5S+MuGC/AxcXPSMZbMRGGoPh3D5YaCv27aIuS/ukQ+\n6dmm/9AGlCb64fsIWQJAPaokbjIifo+LwC5gyK73Mc4t8nAOSZDenzd/2f6TCq76\nS1dcnKiPxaED7W/y6LJiuBT2rbZiQ2L93NJpFZD/UA==\n-----END RSA PRIVATE KEY-----\n", - "_revoke_uri": "https://accounts.google.com/o/oauth2/revoke", - "_scopes": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting", - "_service_account_email": "baz@serviceaccount.example.com", - "_service_account_id": "baz.serviceaccount.example.com", - "_token_uri": "https://accounts.google.com/o/oauth2/token", - "_user_agent": "Cloud SDK Command Line Tool", - "access_token": null, - "assertion_type": null, - "client_id": null, - "client_secret": null, - "id_token": null, - "invalid": false, - "refresh_token": null, - "revoke_uri": "https://accounts.google.com/o/oauth2/revoke", - "service_account_name": "baz@serviceaccount.example.com", - "token_expiry": null, - "token_response": null, - "user_agent": "Cloud SDK Command Line Tool" - }, - "key": { - "account": "baz@serviceaccount.example.com", - "clientId": "baz_client_id", - "scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting", - "type": "google-cloud-sdk" - } - } - ], - "file_version": 1 -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/properties b/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/properties deleted file mode 100644 index 025de886c..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/properties +++ /dev/null @@ -1,2 +0,0 @@ -[core] -account = bar@example.com \ No newline at end of file diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2_test.go deleted file mode 100644 index 014a351e0..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2014 The oauth2 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 internal contains support packages for oauth2 package. -package internal - -import ( - "reflect" - "strings" - "testing" -) - -func TestParseINI(t *testing.T) { - tests := []struct { - ini string - want map[string]map[string]string - }{ - { - `root = toor -[foo] -bar = hop -ini = nin -`, - map[string]map[string]string{ - "": map[string]string{"root": "toor"}, - "foo": map[string]string{"bar": "hop", "ini": "nin"}, - }, - }, - { - `[empty] -[section] -empty= -`, - map[string]map[string]string{ - "": map[string]string{}, - "empty": map[string]string{}, - "section": map[string]string{"empty": ""}, - }, - }, - { - `ignore -[invalid -=stuff -;comment=true -`, - map[string]map[string]string{ - "": map[string]string{}, - }, - }, - } - for _, tt := range tests { - result, err := ParseINI(strings.NewReader(tt.ini)) - if err != nil { - t.Errorf("ParseINI(%q) error %v, want: no error", tt.ini, err) - continue - } - if !reflect.DeepEqual(result, tt.want) { - t.Errorf("ParseINI(%q) = %#v, want: %#v", tt.ini, result, tt.want) - } - } -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/internal/token_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/internal/token_test.go deleted file mode 100644 index 864f6fa07..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/internal/token_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2014 The oauth2 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 internal contains support packages for oauth2 package. -package internal - -import ( - "fmt" - "testing" -) - -func Test_providerAuthHeaderWorks(t *testing.T) { - for _, p := range brokenAuthHeaderProviders { - if providerAuthHeaderWorks(p) { - t.Errorf("URL: %s not found in list", p) - } - p := fmt.Sprintf("%ssomesuffix", p) - if providerAuthHeaderWorks(p) { - t.Errorf("URL: %s not found in list", p) - } - } - p := "https://api.not-in-the-list-example.com/" - if !providerAuthHeaderWorks(p) { - t.Errorf("URL: %s found in list", p) - } - -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/jwt/example_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/jwt/example_test.go deleted file mode 100644 index 6d618836e..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/jwt/example_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2014 The oauth2 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 jwt_test - -import ( - "golang.org/x/oauth2" - "golang.org/x/oauth2/jwt" -) - -func ExampleJWTConfig() { - conf := &jwt.Config{ - Email: "xxx@developer.com", - // The contents of your RSA private key or your PEM file - // that contains a private key. - // If you have a p12 file instead, you - // can use `openssl` to export the private key into a pem file. - // - // $ openssl pkcs12 -in key.p12 -out key.pem -nodes - // - // It only supports PEM containers with no passphrase. - PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."), - Subject: "user@example.com", - TokenURL: "https://provider.com/o/oauth2/token", - } - // Initiate an http.Client, the following GET request will be - // authorized and authenticated on the behalf of user@example.com. - client := conf.Client(oauth2.NoContext) - client.Get("...") -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt_test.go deleted file mode 100644 index da922c3d0..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt_test.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2014 The oauth2 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 jwt - -import ( - "net/http" - "net/http/httptest" - "testing" - - "golang.org/x/oauth2" -) - -var dummyPrivateKey = []byte(`-----BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAx4fm7dngEmOULNmAs1IGZ9Apfzh+BkaQ1dzkmbUgpcoghucE -DZRnAGd2aPyB6skGMXUytWQvNYav0WTR00wFtX1ohWTfv68HGXJ8QXCpyoSKSSFY -fuP9X36wBSkSX9J5DVgiuzD5VBdzUISSmapjKm+DcbRALjz6OUIPEWi1Tjl6p5RK -1w41qdbmt7E5/kGhKLDuT7+M83g4VWhgIvaAXtnhklDAggilPPa8ZJ1IFe31lNlr -k4DRk38nc6sEutdf3RL7QoH7FBusI7uXV03DC6dwN1kP4GE7bjJhcRb/7jYt7CQ9 -/E9Exz3c0yAp0yrTg0Fwh+qxfH9dKwN52S7SBwIDAQABAoIBAQCaCs26K07WY5Jt -3a2Cw3y2gPrIgTCqX6hJs7O5ByEhXZ8nBwsWANBUe4vrGaajQHdLj5OKfsIDrOvn -2NI1MqflqeAbu/kR32q3tq8/Rl+PPiwUsW3E6Pcf1orGMSNCXxeducF2iySySzh3 -nSIhCG5uwJDWI7a4+9KiieFgK1pt/Iv30q1SQS8IEntTfXYwANQrfKUVMmVF9aIK -6/WZE2yd5+q3wVVIJ6jsmTzoDCX6QQkkJICIYwCkglmVy5AeTckOVwcXL0jqw5Kf -5/soZJQwLEyBoQq7Kbpa26QHq+CJONetPP8Ssy8MJJXBT+u/bSseMb3Zsr5cr43e -DJOhwsThAoGBAPY6rPKl2NT/K7XfRCGm1sbWjUQyDShscwuWJ5+kD0yudnT/ZEJ1 -M3+KS/iOOAoHDdEDi9crRvMl0UfNa8MAcDKHflzxg2jg/QI+fTBjPP5GOX0lkZ9g -z6VePoVoQw2gpPFVNPPTxKfk27tEzbaffvOLGBEih0Kb7HTINkW8rIlzAoGBAM9y -1yr+jvfS1cGFtNU+Gotoihw2eMKtIqR03Yn3n0PK1nVCDKqwdUqCypz4+ml6cxRK -J8+Pfdh7D+ZJd4LEG6Y4QRDLuv5OA700tUoSHxMSNn3q9As4+T3MUyYxWKvTeu3U -f2NWP9ePU0lV8ttk7YlpVRaPQmc1qwooBA/z/8AdAoGAW9x0HWqmRICWTBnpjyxx -QGlW9rQ9mHEtUotIaRSJ6K/F3cxSGUEkX1a3FRnp6kPLcckC6NlqdNgNBd6rb2rA -cPl/uSkZP42Als+9YMoFPU/xrrDPbUhu72EDrj3Bllnyb168jKLa4VBOccUvggxr -Dm08I1hgYgdN5huzs7y6GeUCgYEAj+AZJSOJ6o1aXS6rfV3mMRve9bQ9yt8jcKXw -5HhOCEmMtaSKfnOF1Ziih34Sxsb7O2428DiX0mV/YHtBnPsAJidL0SdLWIapBzeg -KHArByIRkwE6IvJvwpGMdaex1PIGhx5i/3VZL9qiq/ElT05PhIb+UXgoWMabCp84 -OgxDK20CgYAeaFo8BdQ7FmVX2+EEejF+8xSge6WVLtkaon8bqcn6P0O8lLypoOhd -mJAYH8WU+UAy9pecUnDZj14LAGNVmYcse8HFX71MoshnvCTFEPVo4rZxIAGwMpeJ -5jgQ3slYLpqrGlcbLgUXBUgzEO684Wk/UV9DFPlHALVqCfXQ9dpJPg== ------END RSA PRIVATE KEY-----`) - -func TestJWTFetch_JSONResponse(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{ - "access_token": "90d64460d14870c08c81352a05dedd3465940a7c", - "scope": "user", - "token_type": "bearer", - "expires_in": 3600 - }`)) - })) - defer ts.Close() - - conf := &Config{ - Email: "aaa@xxx.com", - PrivateKey: dummyPrivateKey, - TokenURL: ts.URL, - } - tok, err := conf.TokenSource(oauth2.NoContext).Token() - if err != nil { - t.Fatal(err) - } - if !tok.Valid() { - t.Errorf("Token invalid") - } - if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" { - t.Errorf("Unexpected access token, %#v", tok.AccessToken) - } - if tok.TokenType != "bearer" { - t.Errorf("Unexpected token type, %#v", tok.TokenType) - } - if tok.Expiry.IsZero() { - t.Errorf("Unexpected token expiry, %#v", tok.Expiry) - } - scope := tok.Extra("scope") - if scope != "user" { - t.Errorf("Unexpected value for scope: %v", scope) - } -} - -func TestJWTFetch_BadResponse(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{"scope": "user", "token_type": "bearer"}`)) - })) - defer ts.Close() - - conf := &Config{ - Email: "aaa@xxx.com", - PrivateKey: dummyPrivateKey, - TokenURL: ts.URL, - } - tok, err := conf.TokenSource(oauth2.NoContext).Token() - if err != nil { - t.Fatal(err) - } - if tok == nil { - t.Fatalf("token is nil") - } - if tok.Valid() { - t.Errorf("token is valid. want invalid.") - } - if tok.AccessToken != "" { - t.Errorf("Unexpected non-empty access token %q.", tok.AccessToken) - } - if want := "bearer"; tok.TokenType != want { - t.Errorf("TokenType = %q; want %q", tok.TokenType, want) - } - scope := tok.Extra("scope") - if want := "user"; scope != want { - t.Errorf("token scope = %q; want %q", scope, want) - } -} - -func TestJWTFetch_BadResponseType(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{"access_token":123, "scope": "user", "token_type": "bearer"}`)) - })) - defer ts.Close() - conf := &Config{ - Email: "aaa@xxx.com", - PrivateKey: dummyPrivateKey, - TokenURL: ts.URL, - } - tok, err := conf.TokenSource(oauth2.NoContext).Token() - if err == nil { - t.Error("got a token; expected error") - if tok.AccessToken != "" { - t.Errorf("Unexpected access token, %#v.", tok.AccessToken) - } - } -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/linkedin/linkedin.go b/Godeps/_workspace/src/golang.org/x/oauth2/linkedin/linkedin.go deleted file mode 100644 index d93fded6a..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/linkedin/linkedin.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2015 The oauth2 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 linkedin provides constants for using OAuth2 to access LinkedIn. -package linkedin - -import ( - "golang.org/x/oauth2" -) - -// Endpoint is LinkedIn's OAuth 2.0 endpoint. -var Endpoint = oauth2.Endpoint{ - AuthURL: "https://www.linkedin.com/uas/oauth2/authorization", - TokenURL: "https://www.linkedin.com/uas/oauth2/accessToken", -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/oauth2_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/oauth2_test.go deleted file mode 100644 index 2f7d731c1..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/oauth2_test.go +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright 2014 The oauth2 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 oauth2 - -import ( - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - "reflect" - "strconv" - "testing" - "time" - - "golang.org/x/net/context" -) - -type mockTransport struct { - rt func(req *http.Request) (resp *http.Response, err error) -} - -func (t *mockTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) { - return t.rt(req) -} - -type mockCache struct { - token *Token - readErr error -} - -func (c *mockCache) ReadToken() (*Token, error) { - return c.token, c.readErr -} - -func (c *mockCache) WriteToken(*Token) { - // do nothing -} - -func newConf(url string) *Config { - return &Config{ - ClientID: "CLIENT_ID", - ClientSecret: "CLIENT_SECRET", - RedirectURL: "REDIRECT_URL", - Scopes: []string{"scope1", "scope2"}, - Endpoint: Endpoint{ - AuthURL: url + "/auth", - TokenURL: url + "/token", - }, - } -} - -func TestAuthCodeURL(t *testing.T) { - conf := newConf("server") - url := conf.AuthCodeURL("foo", AccessTypeOffline, ApprovalForce) - if url != "server/auth?access_type=offline&approval_prompt=force&client_id=CLIENT_ID&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=foo" { - t.Errorf("Auth code URL doesn't match the expected, found: %v", url) - } -} - -func TestAuthCodeURL_CustomParam(t *testing.T) { - conf := newConf("server") - param := SetAuthURLParam("foo", "bar") - url := conf.AuthCodeURL("baz", param) - if url != "server/auth?client_id=CLIENT_ID&foo=bar&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=baz" { - t.Errorf("Auth code URL doesn't match the expected, found: %v", url) - } -} - -func TestAuthCodeURL_Optional(t *testing.T) { - conf := &Config{ - ClientID: "CLIENT_ID", - Endpoint: Endpoint{ - AuthURL: "/auth-url", - TokenURL: "/token-url", - }, - } - url := conf.AuthCodeURL("") - if url != "/auth-url?client_id=CLIENT_ID&response_type=code" { - t.Fatalf("Auth code URL doesn't match the expected, found: %v", url) - } -} - -func TestExchangeRequest(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.String() != "/token" { - t.Errorf("Unexpected exchange request URL, %v is found.", r.URL) - } - headerAuth := r.Header.Get("Authorization") - if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" { - t.Errorf("Unexpected authorization header, %v is found.", headerAuth) - } - headerContentType := r.Header.Get("Content-Type") - if headerContentType != "application/x-www-form-urlencoded" { - t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType) - } - body, err := ioutil.ReadAll(r.Body) - if err != nil { - t.Errorf("Failed reading request body: %s.", err) - } - if string(body) != "client_id=CLIENT_ID&code=exchange-code&grant_type=authorization_code&redirect_uri=REDIRECT_URL&scope=scope1+scope2" { - t.Errorf("Unexpected exchange payload, %v is found.", string(body)) - } - w.Header().Set("Content-Type", "application/x-www-form-urlencoded") - w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer")) - })) - defer ts.Close() - conf := newConf(ts.URL) - tok, err := conf.Exchange(NoContext, "exchange-code") - if err != nil { - t.Error(err) - } - if !tok.Valid() { - t.Fatalf("Token invalid. Got: %#v", tok) - } - if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" { - t.Errorf("Unexpected access token, %#v.", tok.AccessToken) - } - if tok.TokenType != "bearer" { - t.Errorf("Unexpected token type, %#v.", tok.TokenType) - } - scope := tok.Extra("scope") - if scope != "user" { - t.Errorf("Unexpected value for scope: %v", scope) - } -} - -func TestExchangeRequest_JSONResponse(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.String() != "/token" { - t.Errorf("Unexpected exchange request URL, %v is found.", r.URL) - } - headerAuth := r.Header.Get("Authorization") - if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" { - t.Errorf("Unexpected authorization header, %v is found.", headerAuth) - } - headerContentType := r.Header.Get("Content-Type") - if headerContentType != "application/x-www-form-urlencoded" { - t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType) - } - body, err := ioutil.ReadAll(r.Body) - if err != nil { - t.Errorf("Failed reading request body: %s.", err) - } - if string(body) != "client_id=CLIENT_ID&code=exchange-code&grant_type=authorization_code&redirect_uri=REDIRECT_URL&scope=scope1+scope2" { - t.Errorf("Unexpected exchange payload, %v is found.", string(body)) - } - w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{"access_token": "90d64460d14870c08c81352a05dedd3465940a7c", "scope": "user", "token_type": "bearer", "expires_in": 86400}`)) - })) - defer ts.Close() - conf := newConf(ts.URL) - tok, err := conf.Exchange(NoContext, "exchange-code") - if err != nil { - t.Error(err) - } - if !tok.Valid() { - t.Fatalf("Token invalid. Got: %#v", tok) - } - if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" { - t.Errorf("Unexpected access token, %#v.", tok.AccessToken) - } - if tok.TokenType != "bearer" { - t.Errorf("Unexpected token type, %#v.", tok.TokenType) - } - scope := tok.Extra("scope") - if scope != "user" { - t.Errorf("Unexpected value for scope: %v", scope) - } -} - -const day = 24 * time.Hour - -func TestExchangeRequest_JSONResponse_Expiry(t *testing.T) { - seconds := int32(day.Seconds()) - jsonNumberType := reflect.TypeOf(json.Number("0")) - for _, c := range []struct { - expires string - expect error - }{ - {fmt.Sprintf(`"expires_in": %d`, seconds), nil}, - {fmt.Sprintf(`"expires_in": "%d"`, seconds), nil}, // PayPal case - {fmt.Sprintf(`"expires": %d`, seconds), nil}, // Facebook case - {`"expires": false`, &json.UnmarshalTypeError{Value: "bool", Type: jsonNumberType}}, // wrong type - {`"expires": {}`, &json.UnmarshalTypeError{Value: "object", Type: jsonNumberType}}, // wrong type - {`"expires": "zzz"`, &strconv.NumError{Func: "ParseInt", Num: "zzz", Err: strconv.ErrSyntax}}, // wrong value - } { - testExchangeRequest_JSONResponse_expiry(t, c.expires, c.expect) - } -} - -func testExchangeRequest_JSONResponse_expiry(t *testing.T, exp string, expect error) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte(fmt.Sprintf(`{"access_token": "90d", "scope": "user", "token_type": "bearer", %s}`, exp))) - })) - defer ts.Close() - conf := newConf(ts.URL) - t1 := time.Now().Add(day) - tok, err := conf.Exchange(NoContext, "exchange-code") - t2 := time.Now().Add(day) - // Do a fmt.Sprint comparison so either side can be - // nil. fmt.Sprint just stringifies them to "", and no - // non-nil expected error ever stringifies as "", so this - // isn't terribly disgusting. We do this because Go 1.4 and - // Go 1.5 return a different deep value for - // json.UnmarshalTypeError. In Go 1.5, the - // json.UnmarshalTypeError contains a new field with a new - // non-zero value. Rather than ignore it here with reflect or - // add new files and +build tags, just look at the strings. - if fmt.Sprint(err) != fmt.Sprint(expect) { - t.Errorf("Error = %v; want %v", err, expect) - } - if err != nil { - return - } - if !tok.Valid() { - t.Fatalf("Token invalid. Got: %#v", tok) - } - expiry := tok.Expiry - if expiry.Before(t1) || expiry.After(t2) { - t.Errorf("Unexpected value for Expiry: %v (shold be between %v and %v)", expiry, t1, t2) - } -} - -func TestExchangeRequest_BadResponse(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{"scope": "user", "token_type": "bearer"}`)) - })) - defer ts.Close() - conf := newConf(ts.URL) - tok, err := conf.Exchange(NoContext, "code") - if err != nil { - t.Fatal(err) - } - if tok.AccessToken != "" { - t.Errorf("Unexpected access token, %#v.", tok.AccessToken) - } -} - -func TestExchangeRequest_BadResponseType(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{"access_token":123, "scope": "user", "token_type": "bearer"}`)) - })) - defer ts.Close() - conf := newConf(ts.URL) - _, err := conf.Exchange(NoContext, "exchange-code") - if err == nil { - t.Error("expected error from invalid access_token type") - } -} - -func TestExchangeRequest_NonBasicAuth(t *testing.T) { - tr := &mockTransport{ - rt: func(r *http.Request) (w *http.Response, err error) { - headerAuth := r.Header.Get("Authorization") - if headerAuth != "" { - t.Errorf("Unexpected authorization header, %v is found.", headerAuth) - } - return nil, errors.New("no response") - }, - } - c := &http.Client{Transport: tr} - conf := &Config{ - ClientID: "CLIENT_ID", - Endpoint: Endpoint{ - AuthURL: "https://accounts.google.com/auth", - TokenURL: "https://accounts.google.com/token", - }, - } - - ctx := context.WithValue(context.Background(), HTTPClient, c) - conf.Exchange(ctx, "code") -} - -func TestPasswordCredentialsTokenRequest(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - expected := "/token" - if r.URL.String() != expected { - t.Errorf("URL = %q; want %q", r.URL, expected) - } - headerAuth := r.Header.Get("Authorization") - expected = "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" - if headerAuth != expected { - t.Errorf("Authorization header = %q; want %q", headerAuth, expected) - } - headerContentType := r.Header.Get("Content-Type") - expected = "application/x-www-form-urlencoded" - if headerContentType != expected { - t.Errorf("Content-Type header = %q; want %q", headerContentType, expected) - } - body, err := ioutil.ReadAll(r.Body) - if err != nil { - t.Errorf("Failed reading request body: %s.", err) - } - expected = "client_id=CLIENT_ID&grant_type=password&password=password1&scope=scope1+scope2&username=user1" - if string(body) != expected { - t.Errorf("res.Body = %q; want %q", string(body), expected) - } - w.Header().Set("Content-Type", "application/x-www-form-urlencoded") - w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer")) - })) - defer ts.Close() - conf := newConf(ts.URL) - tok, err := conf.PasswordCredentialsToken(NoContext, "user1", "password1") - if err != nil { - t.Error(err) - } - if !tok.Valid() { - t.Fatalf("Token invalid. Got: %#v", tok) - } - expected := "90d64460d14870c08c81352a05dedd3465940a7c" - if tok.AccessToken != expected { - t.Errorf("AccessToken = %q; want %q", tok.AccessToken, expected) - } - expected = "bearer" - if tok.TokenType != expected { - t.Errorf("TokenType = %q; want %q", tok.TokenType, expected) - } -} - -func TestTokenRefreshRequest(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.String() == "/somethingelse" { - return - } - if r.URL.String() != "/token" { - t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL) - } - headerContentType := r.Header.Get("Content-Type") - if headerContentType != "application/x-www-form-urlencoded" { - t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType) - } - body, _ := ioutil.ReadAll(r.Body) - if string(body) != "client_id=CLIENT_ID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN" { - t.Errorf("Unexpected refresh token payload, %v is found.", string(body)) - } - })) - defer ts.Close() - conf := newConf(ts.URL) - c := conf.Client(NoContext, &Token{RefreshToken: "REFRESH_TOKEN"}) - c.Get(ts.URL + "/somethingelse") -} - -func TestFetchWithNoRefreshToken(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.String() == "/somethingelse" { - return - } - if r.URL.String() != "/token" { - t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL) - } - headerContentType := r.Header.Get("Content-Type") - if headerContentType != "application/x-www-form-urlencoded" { - t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType) - } - body, _ := ioutil.ReadAll(r.Body) - if string(body) != "client_id=CLIENT_ID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN" { - t.Errorf("Unexpected refresh token payload, %v is found.", string(body)) - } - })) - defer ts.Close() - conf := newConf(ts.URL) - c := conf.Client(NoContext, nil) - _, err := c.Get(ts.URL + "/somethingelse") - if err == nil { - t.Errorf("Fetch should return an error if no refresh token is set") - } -} - -func TestRefreshToken_RefreshTokenReplacement(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{"access_token":"ACCESS TOKEN", "scope": "user", "token_type": "bearer", "refresh_token": "NEW REFRESH TOKEN"}`)) - return - })) - defer ts.Close() - conf := newConf(ts.URL) - tkr := tokenRefresher{ - conf: conf, - ctx: NoContext, - refreshToken: "OLD REFRESH TOKEN", - } - tk, err := tkr.Token() - if err != nil { - t.Errorf("Unexpected refreshToken error returned: %v", err) - return - } - if tk.RefreshToken != tkr.refreshToken { - t.Errorf("tokenRefresher.refresh_token = %s; want %s", tkr.refreshToken, tk.RefreshToken) - } -} - -func TestConfigClientWithToken(t *testing.T) { - tok := &Token{ - AccessToken: "abc123", - } - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if got, want := r.Header.Get("Authorization"), fmt.Sprintf("Bearer %s", tok.AccessToken); got != want { - t.Errorf("Authorization header = %q; want %q", got, want) - } - return - })) - defer ts.Close() - conf := newConf(ts.URL) - - c := conf.Client(NoContext, tok) - req, err := http.NewRequest("GET", ts.URL, nil) - if err != nil { - t.Error(err) - } - _, err = c.Do(req) - if err != nil { - t.Error(err) - } -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go b/Godeps/_workspace/src/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go deleted file mode 100644 index f0b66f97d..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2015 The oauth2 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 odnoklassniki provides constants for using OAuth2 to access Odnoklassniki. -package odnoklassniki - -import ( - "golang.org/x/oauth2" -) - -// Endpoint is Odnoklassniki's OAuth 2.0 endpoint. -var Endpoint = oauth2.Endpoint{ - AuthURL: "https://www.odnoklassniki.ru/oauth/authorize", - TokenURL: "https://api.odnoklassniki.ru/oauth/token.do", -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/paypal/paypal.go b/Godeps/_workspace/src/golang.org/x/oauth2/paypal/paypal.go deleted file mode 100644 index a99366b6e..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/paypal/paypal.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2015 The oauth2 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 paypal provides constants for using OAuth2 to access PayPal. -package paypal - -import ( - "golang.org/x/oauth2" -) - -// Endpoint is PayPal's OAuth 2.0 endpoint in live (production) environment. -var Endpoint = oauth2.Endpoint{ - AuthURL: "https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize", - TokenURL: "https://api.paypal.com/v1/identity/openidconnect/tokenservice", -} - -// SandboxEndpoint is PayPal's OAuth 2.0 endpoint in sandbox (testing) environment. -var SandboxEndpoint = oauth2.Endpoint{ - AuthURL: "https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize", - TokenURL: "https://api.sandbox.paypal.com/v1/identity/openidconnect/tokenservice", -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/token_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/token_test.go deleted file mode 100644 index 739eeb2a2..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/token_test.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2014 The oauth2 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 oauth2 - -import ( - "testing" - "time" -) - -func TestTokenExtra(t *testing.T) { - type testCase struct { - key string - val interface{} - want interface{} - } - const key = "extra-key" - cases := []testCase{ - {key: key, val: "abc", want: "abc"}, - {key: key, val: 123, want: 123}, - {key: key, val: "", want: ""}, - {key: "other-key", val: "def", want: nil}, - } - for _, tc := range cases { - extra := make(map[string]interface{}) - extra[tc.key] = tc.val - tok := &Token{raw: extra} - if got, want := tok.Extra(key), tc.want; got != want { - t.Errorf("Extra(%q) = %q; want %q", key, got, want) - } - } -} - -func TestTokenExpiry(t *testing.T) { - now := time.Now() - cases := []struct { - name string - tok *Token - want bool - }{ - {name: "12 seconds", tok: &Token{Expiry: now.Add(12 * time.Second)}, want: false}, - {name: "10 seconds", tok: &Token{Expiry: now.Add(expiryDelta)}, want: true}, - } - for _, tc := range cases { - if got, want := tc.tok.expired(), tc.want; got != want { - t.Errorf("expired (%q) = %v; want %v", tc.name, got, want) - } - } -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/transport_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/transport_test.go deleted file mode 100644 index 35cb25ed5..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/transport_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package oauth2 - -import ( - "net/http" - "net/http/httptest" - "testing" - "time" -) - -type tokenSource struct{ token *Token } - -func (t *tokenSource) Token() (*Token, error) { - return t.token, nil -} - -func TestTransportTokenSource(t *testing.T) { - ts := &tokenSource{ - token: &Token{ - AccessToken: "abc", - }, - } - tr := &Transport{ - Source: ts, - } - server := newMockServer(func(w http.ResponseWriter, r *http.Request) { - if r.Header.Get("Authorization") != "Bearer abc" { - t.Errorf("Transport doesn't set the Authorization header from the fetched token") - } - }) - defer server.Close() - client := http.Client{Transport: tr} - client.Get(server.URL) -} - -// Test for case-sensitive token types, per https://github.com/golang/oauth2/issues/113 -func TestTransportTokenSourceTypes(t *testing.T) { - const val = "abc" - tests := []struct { - key string - val string - want string - }{ - {key: "bearer", val: val, want: "Bearer abc"}, - {key: "mac", val: val, want: "MAC abc"}, - {key: "basic", val: val, want: "Basic abc"}, - } - for _, tc := range tests { - ts := &tokenSource{ - token: &Token{ - AccessToken: tc.val, - TokenType: tc.key, - }, - } - tr := &Transport{ - Source: ts, - } - server := newMockServer(func(w http.ResponseWriter, r *http.Request) { - if got, want := r.Header.Get("Authorization"), tc.want; got != want { - t.Errorf("Authorization header (%q) = %q; want %q", val, got, want) - } - }) - defer server.Close() - client := http.Client{Transport: tr} - client.Get(server.URL) - } -} - -func TestTokenValidNoAccessToken(t *testing.T) { - token := &Token{} - if token.Valid() { - t.Errorf("Token should not be valid with no access token") - } -} - -func TestExpiredWithExpiry(t *testing.T) { - token := &Token{ - Expiry: time.Now().Add(-5 * time.Hour), - } - if token.Valid() { - t.Errorf("Token should not be valid if it expired in the past") - } -} - -func newMockServer(handler func(w http.ResponseWriter, r *http.Request)) *httptest.Server { - return httptest.NewServer(http.HandlerFunc(handler)) -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/vk/vk.go b/Godeps/_workspace/src/golang.org/x/oauth2/vk/vk.go deleted file mode 100644 index 00e929357..000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/vk/vk.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2015 The oauth2 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 vk provides constants for using OAuth2 to access VK.com. -package vk - -import ( - "golang.org/x/oauth2" -) - -// Endpoint is VK's OAuth 2.0 endpoint. -var Endpoint = oauth2.Endpoint{ - AuthURL: "https://oauth.vk.com/authorize", - TokenURL: "https://oauth.vk.com/access_token", -} diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/LICENSE b/Godeps/_workspace/src/google.golang.org/api/LICENSE similarity index 83% rename from Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/LICENSE rename to Godeps/_workspace/src/google.golang.org/api/LICENSE index 0e5fb8728..263aa7a0c 100644 --- a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/context/LICENSE +++ b/Godeps/_workspace/src/google.golang.org/api/LICENSE @@ -1,16 +1,16 @@ -Copyright (c) 2012 Rodrigo Moraes. All rights reserved. +Copyright (c) 2011 Google Inc. 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 + * 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 + * 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 + * 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. diff --git a/Godeps/_workspace/src/google.golang.org/api/gensupport/json_test.go b/Godeps/_workspace/src/google.golang.org/api/gensupport/json_test.go deleted file mode 100644 index b2f921913..000000000 --- a/Godeps/_workspace/src/google.golang.org/api/gensupport/json_test.go +++ /dev/null @@ -1,367 +0,0 @@ -// Copyright 2015 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package gensupport - -import ( - "encoding/json" - "reflect" - "testing" - - "google.golang.org/api/googleapi" -) - -type schema struct { - // Basic types - B bool `json:"b,omitempty"` - F float64 `json:"f,omitempty"` - I int64 `json:"i,omitempty"` - Istr int64 `json:"istr,omitempty,string"` - Str string `json:"str,omitempty"` - - // Pointers to basic types - PB *bool `json:"pb,omitempty"` - PF *float64 `json:"pf,omitempty"` - PI *int64 `json:"pi,omitempty"` - PIStr *int64 `json:"pistr,omitempty,string"` - PStr *string `json:"pstr,omitempty"` - - // Other types - Int64s googleapi.Int64s `json:"i64s,omitempty"` - S []int `json:"s,omitempty"` - M map[string]string `json:"m,omitempty"` - Any interface{} `json:"any,omitempty"` - Child *child `json:"child,omitempty"` - - ForceSendFields []string `json:"-"` -} - -type child struct { - B bool `json:"childbool,omitempty"` -} - -type testCase struct { - s schema - want string -} - -func TestBasics(t *testing.T) { - for _, tc := range []testCase{ - { - s: schema{}, - want: `{}`, - }, - { - s: schema{ - ForceSendFields: []string{"B", "F", "I", "Istr", "Str", "PB", "PF", "PI", "PIStr", "PStr"}, - }, - want: `{"b":false,"f":0.0,"i":0,"istr":"0","str":""}`, - }, - { - s: schema{ - B: true, - F: 1.2, - I: 1, - Istr: 2, - Str: "a", - PB: googleapi.Bool(true), - PF: googleapi.Float64(1.2), - PI: googleapi.Int64(int64(1)), - PIStr: googleapi.Int64(int64(2)), - PStr: googleapi.String("a"), - }, - want: `{"b":true,"f":1.2,"i":1,"istr":"2","str":"a","pb":true,"pf":1.2,"pi":1,"pistr":"2","pstr":"a"}`, - }, - { - s: schema{ - B: false, - F: 0.0, - I: 0, - Istr: 0, - Str: "", - PB: googleapi.Bool(false), - PF: googleapi.Float64(0.0), - PI: googleapi.Int64(int64(0)), - PIStr: googleapi.Int64(int64(0)), - PStr: googleapi.String(""), - }, - want: `{"pb":false,"pf":0.0,"pi":0,"pistr":"0","pstr":""}`, - }, - { - s: schema{ - B: false, - F: 0.0, - I: 0, - Istr: 0, - Str: "", - PB: googleapi.Bool(false), - PF: googleapi.Float64(0.0), - PI: googleapi.Int64(int64(0)), - PIStr: googleapi.Int64(int64(0)), - PStr: googleapi.String(""), - ForceSendFields: []string{"B", "F", "I", "Istr", "Str", "PB", "PF", "PI", "PIStr", "PStr"}, - }, - want: `{"b":false,"f":0.0,"i":0,"istr":"0","str":"","pb":false,"pf":0.0,"pi":0,"pistr":"0","pstr":""}`, - }, - } { - checkMarshalJSON(t, tc) - } -} - -func TestSliceFields(t *testing.T) { - for _, tc := range []testCase{ - { - s: schema{}, - want: `{}`, - }, - { - s: schema{S: []int{}, Int64s: googleapi.Int64s{}}, - want: `{}`, - }, - { - s: schema{S: []int{1}, Int64s: googleapi.Int64s{1}}, - want: `{"s":[1],"i64s":["1"]}`, - }, - { - s: schema{ - ForceSendFields: []string{"S", "Int64s"}, - }, - want: `{"s":[],"i64s":[]}`, - }, - { - s: schema{ - S: []int{}, - Int64s: googleapi.Int64s{}, - ForceSendFields: []string{"S", "Int64s"}, - }, - want: `{"s":[],"i64s":[]}`, - }, - { - s: schema{ - S: []int{1}, - Int64s: googleapi.Int64s{1}, - ForceSendFields: []string{"S", "Int64s"}, - }, - want: `{"s":[1],"i64s":["1"]}`, - }, - } { - checkMarshalJSON(t, tc) - } -} - -func TestMapField(t *testing.T) { - for _, tc := range []testCase{ - { - s: schema{}, - want: `{}`, - }, - { - s: schema{M: make(map[string]string)}, - want: `{}`, - }, - { - s: schema{M: map[string]string{"a": "b"}}, - want: `{"m":{"a":"b"}}`, - }, - { - s: schema{ - ForceSendFields: []string{"M"}, - }, - want: `{"m":{}}`, - }, - { - s: schema{ - M: make(map[string]string), - ForceSendFields: []string{"M"}, - }, - want: `{"m":{}}`, - }, - { - s: schema{ - M: map[string]string{"a": "b"}, - ForceSendFields: []string{"M"}, - }, - want: `{"m":{"a":"b"}}`, - }, - } { - checkMarshalJSON(t, tc) - } -} - -type anyType struct { - Field int -} - -func (a anyType) MarshalJSON() ([]byte, error) { - return []byte(`"anyType value"`), nil -} - -func TestAnyField(t *testing.T) { - // ForceSendFields has no effect on nil interfaces and interfaces that contain nil pointers. - var nilAny *anyType - for _, tc := range []testCase{ - { - s: schema{}, - want: `{}`, - }, - { - s: schema{Any: nilAny}, - want: `{"any": null}`, - }, - { - s: schema{Any: &anyType{}}, - want: `{"any":"anyType value"}`, - }, - { - s: schema{Any: anyType{}}, - want: `{"any":"anyType value"}`, - }, - { - s: schema{ - ForceSendFields: []string{"Any"}, - }, - want: `{}`, - }, - { - s: schema{ - Any: nilAny, - ForceSendFields: []string{"Any"}, - }, - want: `{"any": null}`, - }, - { - s: schema{ - Any: &anyType{}, - ForceSendFields: []string{"Any"}, - }, - want: `{"any":"anyType value"}`, - }, - { - s: schema{ - Any: anyType{}, - ForceSendFields: []string{"Any"}, - }, - want: `{"any":"anyType value"}`, - }, - } { - checkMarshalJSON(t, tc) - } -} - -func TestSubschema(t *testing.T) { - // Subschemas are always stored as pointers, so ForceSendFields has no effect on them. - for _, tc := range []testCase{ - { - s: schema{}, - want: `{}`, - }, - { - s: schema{ - ForceSendFields: []string{"Child"}, - }, - want: `{}`, - }, - { - s: schema{Child: &child{}}, - want: `{"child":{}}`, - }, - { - s: schema{ - Child: &child{}, - ForceSendFields: []string{"Child"}, - }, - want: `{"child":{}}`, - }, - { - s: schema{Child: &child{B: true}}, - want: `{"child":{"childbool":true}}`, - }, - - { - s: schema{ - Child: &child{B: true}, - ForceSendFields: []string{"Child"}, - }, - want: `{"child":{"childbool":true}}`, - }, - } { - checkMarshalJSON(t, tc) - } -} - -// checkMarshalJSON verifies that calling schemaToMap on tc.s yields a result which is equivalent to tc.want. -func checkMarshalJSON(t *testing.T, tc testCase) { - doCheckMarshalJSON(t, tc.s, tc.s.ForceSendFields, tc.want) - if len(tc.s.ForceSendFields) == 0 { - // verify that the code path used when ForceSendFields - // is non-empty produces the same output as the fast - // path that is used when it is empty. - doCheckMarshalJSON(t, tc.s, []string{"dummy"}, tc.want) - } -} - -func doCheckMarshalJSON(t *testing.T, s schema, forceSendFields []string, wantJSON string) { - encoded, err := MarshalJSON(s, forceSendFields) - if err != nil { - t.Fatalf("encoding json:\n got err: %v", err) - } - - // The expected and obtained JSON can differ in field ordering, so unmarshal before comparing. - var got interface{} - var want interface{} - err = json.Unmarshal(encoded, &got) - if err != nil { - t.Fatalf("decoding json:\n got err: %v", err) - } - err = json.Unmarshal([]byte(wantJSON), &want) - if err != nil { - t.Fatalf("decoding json:\n got err: %v", err) - } - if !reflect.DeepEqual(got, want) { - t.Errorf("schemaToMap:\ngot :%s\nwant:%s", got, want) - } -} - -func TestParseJSONTag(t *testing.T) { - for _, tc := range []struct { - tag string - want jsonTag - }{ - { - tag: "-", - want: jsonTag{ignore: true}, - }, { - tag: "name,omitempty", - want: jsonTag{apiName: "name"}, - }, { - tag: "name,omitempty,string", - want: jsonTag{apiName: "name", stringFormat: true}, - }, - } { - got, err := parseJSONTag(tc.tag) - if err != nil { - t.Fatalf("parsing json:\n got err: %v\ntag: %q", err, tc.tag) - } - if !reflect.DeepEqual(got, tc.want) { - t.Errorf("parseJSONTage:\ngot :%s\nwant:%s", got, tc.want) - } - } -} -func TestParseMalformedJSONTag(t *testing.T) { - for _, tag := range []string{ - "", - "name", - "name,", - "name,blah", - "name,blah,string", - ",omitempty", - ",omitempty,string", - "name,omitempty,string,blah", - } { - _, err := parseJSONTag(tag) - if err == nil { - t.Fatalf("parsing json: expected err, got nil for tag: %v", tag) - } - } -} diff --git a/Godeps/_workspace/src/google.golang.org/api/gensupport/media_test.go b/Godeps/_workspace/src/google.golang.org/api/gensupport/media_test.go deleted file mode 100644 index 72edb453e..000000000 --- a/Godeps/_workspace/src/google.golang.org/api/gensupport/media_test.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2015 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package gensupport - -import ( - "bytes" - "errors" - "io" - "io/ioutil" - "reflect" - "testing" -) - -// errReader reads out of a buffer until it is empty, then returns the specified error. -type errReader struct { - buf []byte - err error -} - -var errBang error = errors.New("bang") - -func (er *errReader) Read(p []byte) (int, error) { - if len(er.buf) == 0 { - if er.err == nil { - return 0, io.EOF - } - return 0, er.err - } - n := copy(p, er.buf) - er.buf = er.buf[n:] - return n, nil -} - -func TestAll(t *testing.T) { - type testCase struct { - data []byte // the data to read from the Reader - finalErr error // error to return after data has been read - - wantContentType string - wantContentTypeResult bool - } - - for _, tc := range []testCase{ - { - data: []byte{0, 0, 0, 0}, - finalErr: nil, - wantContentType: "application/octet-stream", - wantContentTypeResult: true, - }, - { - data: []byte(""), - finalErr: nil, - wantContentType: "text/plain; charset=utf-8", - wantContentTypeResult: true, - }, - { - data: []byte(""), - finalErr: errBang, - wantContentType: "text/plain; charset=utf-8", - wantContentTypeResult: false, - }, - { - data: []byte("abc"), - finalErr: nil, - wantContentType: "text/plain; charset=utf-8", - wantContentTypeResult: true, - }, - { - data: []byte("abc"), - finalErr: errBang, - wantContentType: "text/plain; charset=utf-8", - wantContentTypeResult: false, - }, - // The following examples contain more bytes than are buffered for sniffing. - { - data: bytes.Repeat([]byte("a"), 513), - finalErr: nil, - wantContentType: "text/plain; charset=utf-8", - wantContentTypeResult: true, - }, - { - data: bytes.Repeat([]byte("a"), 513), - finalErr: errBang, - wantContentType: "text/plain; charset=utf-8", - wantContentTypeResult: true, // true because error is after first 512 bytes. - }, - } { - er := &errReader{buf: tc.data, err: tc.finalErr} - - sct := NewContentSniffer(er) - - // Even if was an error during the first 512 bytes, we should still be able to read those bytes. - buf, err := ioutil.ReadAll(sct) - - if !reflect.DeepEqual(buf, tc.data) { - t.Fatalf("Failed reading buffer: got: %q; want:%q", buf, tc.data) - } - - if err != tc.finalErr { - t.Fatalf("Reading buffer error: got: %v; want: %v", err, tc.finalErr) - } - - ct, ok := sct.ContentType() - if ok != tc.wantContentTypeResult { - t.Fatalf("Content type result got: %v; want: %v", ok, tc.wantContentTypeResult) - } - if ok && ct != tc.wantContentType { - t.Fatalf("Content type got: %q; want: %q", ct, tc.wantContentType) - } - } -} diff --git a/Godeps/_workspace/src/google.golang.org/api/googleapi/googleapi_test.go b/Godeps/_workspace/src/google.golang.org/api/googleapi/googleapi_test.go deleted file mode 100644 index 75d33ea09..000000000 --- a/Godeps/_workspace/src/google.golang.org/api/googleapi/googleapi_test.go +++ /dev/null @@ -1,599 +0,0 @@ -// Copyright 2011 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package googleapi - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "os" - "reflect" - "regexp" - "strconv" - "strings" - "testing" - "time" - - "golang.org/x/net/context" -) - -type SetOpaqueTest struct { - in *url.URL - wantRequestURI string -} - -var setOpaqueTests = []SetOpaqueTest{ - // no path - { - &url.URL{ - Scheme: "http", - Host: "www.golang.org", - }, - "http://www.golang.org", - }, - // path - { - &url.URL{ - Scheme: "http", - Host: "www.golang.org", - Path: "/", - }, - "http://www.golang.org/", - }, - // file with hex escaping - { - &url.URL{ - Scheme: "https", - Host: "www.golang.org", - Path: "/file%20one&two", - }, - "https://www.golang.org/file%20one&two", - }, - // query - { - &url.URL{ - Scheme: "http", - Host: "www.golang.org", - Path: "/", - RawQuery: "q=go+language", - }, - "http://www.golang.org/?q=go+language", - }, - // file with hex escaping in path plus query - { - &url.URL{ - Scheme: "https", - Host: "www.golang.org", - Path: "/file%20one&two", - RawQuery: "q=go+language", - }, - "https://www.golang.org/file%20one&two?q=go+language", - }, - // query with hex escaping - { - &url.URL{ - Scheme: "http", - Host: "www.golang.org", - Path: "/", - RawQuery: "q=go%20language", - }, - "http://www.golang.org/?q=go%20language", - }, -} - -// prefixTmpl is a template for the expected prefix of the output of writing -// an HTTP request. -const prefixTmpl = "GET %v HTTP/1.1\r\nHost: %v\r\n" - -func TestSetOpaque(t *testing.T) { - for _, test := range setOpaqueTests { - u := *test.in - SetOpaque(&u) - - w := &bytes.Buffer{} - r := &http.Request{URL: &u} - if err := r.Write(w); err != nil { - t.Errorf("write request: %v", err) - continue - } - - prefix := fmt.Sprintf(prefixTmpl, test.wantRequestURI, test.in.Host) - if got := string(w.Bytes()); !strings.HasPrefix(got, prefix) { - t.Errorf("got %q expected prefix %q", got, prefix) - } - } -} - -type ExpandTest struct { - in string - expansions map[string]string - want string -} - -var expandTests = []ExpandTest{ - // no expansions - { - "http://www.golang.org/", - map[string]string{}, - "http://www.golang.org/", - }, - // one expansion, no escaping - { - "http://www.golang.org/{bucket}/delete", - map[string]string{ - "bucket": "red", - }, - "http://www.golang.org/red/delete", - }, - // one expansion, with hex escapes - { - "http://www.golang.org/{bucket}/delete", - map[string]string{ - "bucket": "red/blue", - }, - "http://www.golang.org/red%2Fblue/delete", - }, - // one expansion, with space - { - "http://www.golang.org/{bucket}/delete", - map[string]string{ - "bucket": "red or blue", - }, - "http://www.golang.org/red%20or%20blue/delete", - }, - // expansion not found - { - "http://www.golang.org/{object}/delete", - map[string]string{ - "bucket": "red or blue", - }, - "http://www.golang.org//delete", - }, - // multiple expansions - { - "http://www.golang.org/{one}/{two}/{three}/get", - map[string]string{ - "one": "ONE", - "two": "TWO", - "three": "THREE", - }, - "http://www.golang.org/ONE/TWO/THREE/get", - }, - // utf-8 characters - { - "http://www.golang.org/{bucket}/get", - map[string]string{ - "bucket": "£100", - }, - "http://www.golang.org/%C2%A3100/get", - }, - // punctuations - { - "http://www.golang.org/{bucket}/get", - map[string]string{ - "bucket": `/\@:,.`, - }, - "http://www.golang.org/%2F%5C%40%3A%2C./get", - }, - // mis-matched brackets - { - "http://www.golang.org/{bucket/get", - map[string]string{ - "bucket": "red", - }, - "http://www.golang.org/{bucket/get", - }, - // "+" prefix for suppressing escape - // See also: http://tools.ietf.org/html/rfc6570#section-3.2.3 - { - "http://www.golang.org/{+topic}", - map[string]string{ - "topic": "/topics/myproject/mytopic", - }, - // The double slashes here look weird, but it's intentional - "http://www.golang.org//topics/myproject/mytopic", - }, -} - -func TestExpand(t *testing.T) { - for i, test := range expandTests { - u := url.URL{ - Path: test.in, - } - Expand(&u, test.expansions) - got := u.Path - if got != test.want { - t.Errorf("got %q expected %q in test %d", got, test.want, i+1) - } - } -} - -type CheckResponseTest struct { - in *http.Response - bodyText string - want error - errText string -} - -var checkResponseTests = []CheckResponseTest{ - { - &http.Response{ - StatusCode: http.StatusOK, - }, - "", - nil, - "", - }, - { - &http.Response{ - StatusCode: http.StatusInternalServerError, - }, - `{"error":{}}`, - &Error{ - Code: http.StatusInternalServerError, - Body: `{"error":{}}`, - }, - `googleapi: got HTTP response code 500 with body: {"error":{}}`, - }, - { - &http.Response{ - StatusCode: http.StatusNotFound, - }, - `{"error":{"message":"Error message for StatusNotFound."}}`, - &Error{ - Code: http.StatusNotFound, - Message: "Error message for StatusNotFound.", - Body: `{"error":{"message":"Error message for StatusNotFound."}}`, - }, - "googleapi: Error 404: Error message for StatusNotFound.", - }, - { - &http.Response{ - StatusCode: http.StatusBadRequest, - }, - `{"error":"invalid_token","error_description":"Invalid Value"}`, - &Error{ - Code: http.StatusBadRequest, - Body: `{"error":"invalid_token","error_description":"Invalid Value"}`, - }, - `googleapi: got HTTP response code 400 with body: {"error":"invalid_token","error_description":"Invalid Value"}`, - }, - { - &http.Response{ - StatusCode: http.StatusBadRequest, - }, - `{"error":{"errors":[{"domain":"usageLimits","reason":"keyInvalid","message":"Bad Request"}],"code":400,"message":"Bad Request"}}`, - &Error{ - Code: http.StatusBadRequest, - Errors: []ErrorItem{ - { - Reason: "keyInvalid", - Message: "Bad Request", - }, - }, - Body: `{"error":{"errors":[{"domain":"usageLimits","reason":"keyInvalid","message":"Bad Request"}],"code":400,"message":"Bad Request"}}`, - Message: "Bad Request", - }, - "googleapi: Error 400: Bad Request, keyInvalid", - }, -} - -func TestCheckResponse(t *testing.T) { - for _, test := range checkResponseTests { - res := test.in - if test.bodyText != "" { - res.Body = ioutil.NopCloser(strings.NewReader(test.bodyText)) - } - g := CheckResponse(res) - if !reflect.DeepEqual(g, test.want) { - t.Errorf("CheckResponse: got %v, want %v", g, test.want) - gotJson, err := json.Marshal(g) - if err != nil { - t.Error(err) - } - wantJson, err := json.Marshal(test.want) - if err != nil { - t.Error(err) - } - t.Errorf("json(got): %q\njson(want): %q", string(gotJson), string(wantJson)) - } - if g != nil && g.Error() != test.errText { - t.Errorf("CheckResponse: unexpected error message.\nGot: %q\nwant: %q", g, test.errText) - } - } -} - -type VariantPoint struct { - Type string - Coordinates []float64 -} - -type VariantTest struct { - in map[string]interface{} - result bool - want VariantPoint -} - -var coords = []interface{}{1.0, 2.0} - -var variantTests = []VariantTest{ - { - in: map[string]interface{}{ - "type": "Point", - "coordinates": coords, - }, - result: true, - want: VariantPoint{ - Type: "Point", - Coordinates: []float64{1.0, 2.0}, - }, - }, - { - in: map[string]interface{}{ - "type": "Point", - "bogus": coords, - }, - result: true, - want: VariantPoint{ - Type: "Point", - }, - }, -} - -func TestVariantType(t *testing.T) { - for _, test := range variantTests { - if g := VariantType(test.in); g != test.want.Type { - t.Errorf("VariantType(%v): got %v, want %v", test.in, g, test.want.Type) - } - } -} - -func TestConvertVariant(t *testing.T) { - for _, test := range variantTests { - g := VariantPoint{} - r := ConvertVariant(test.in, &g) - if r != test.result { - t.Errorf("ConvertVariant(%v): got %v, want %v", test.in, r, test.result) - } - if !reflect.DeepEqual(g, test.want) { - t.Errorf("ConvertVariant(%v): got %v, want %v", test.in, g, test.want) - } - } -} - -type unexpectedReader struct{} - -func (unexpectedReader) Read([]byte) (int, error) { - return 0, fmt.Errorf("unexpected read in test.") -} - -var contentRangeRE = regexp.MustCompile(`^bytes (\d+)\-(\d+)/(\d+)$`) - -func (t *testTransport) RoundTrip(req *http.Request) (*http.Response, error) { - t.req = req - if rng := req.Header.Get("Content-Range"); rng != "" && !strings.HasPrefix(rng, "bytes */") { // Read the data - m := contentRangeRE.FindStringSubmatch(rng) - if len(m) != 4 { - return nil, fmt.Errorf("unable to parse content range: %v", rng) - } - start, err := strconv.ParseInt(m[1], 10, 64) - if err != nil { - return nil, fmt.Errorf("unable to parse content range: %v", rng) - } - end, err := strconv.ParseInt(m[2], 10, 64) - if err != nil { - return nil, fmt.Errorf("unable to parse content range: %v", rng) - } - totalSize, err := strconv.ParseInt(m[3], 10, 64) - if err != nil { - return nil, fmt.Errorf("unable to parse content range: %v", rng) - } - partialSize := end - start + 1 - t.buf, err = ioutil.ReadAll(req.Body) - if err != nil || int64(len(t.buf)) != partialSize { - return nil, fmt.Errorf("unable to read %v bytes from request data, n=%v: %v", partialSize, len(t.buf), err) - } - if totalSize == end+1 { - t.statusCode = 200 // signify completion of transfer - } - } - f := ioutil.NopCloser(unexpectedReader{}) - res := &http.Response{ - Body: f, - StatusCode: t.statusCode, - Header: http.Header{}, - } - if t.rangeVal != "" { - res.Header.Set("Range", t.rangeVal) - } - return res, nil -} - -type testTransport struct { - req *http.Request - statusCode int - rangeVal string - want int64 - buf []byte -} - -var statusTests = []*testTransport{ - &testTransport{statusCode: 308, want: 0}, - &testTransport{statusCode: 308, rangeVal: "bytes=0-0", want: 1}, - &testTransport{statusCode: 308, rangeVal: "bytes=0-42", want: 43}, -} - -func TestTransferStatus(t *testing.T) { - ctx := context.Background() - for _, tr := range statusTests { - rx := &ResumableUpload{ - Client: &http.Client{Transport: tr}, - } - g, _, err := rx.transferStatus(ctx) - if err != nil { - t.Error(err) - } - if g != tr.want { - t.Errorf("transferStatus got %v, want %v", g, tr.want) - } - } -} - -func (t *interruptedTransport) RoundTrip(req *http.Request) (*http.Response, error) { - t.req = req - if rng := req.Header.Get("Content-Range"); rng != "" && !strings.HasPrefix(rng, "bytes */") { - t.interruptCount += 1 - if t.interruptCount%7 == 0 { // Respond with a "service unavailable" error - res := &http.Response{ - StatusCode: http.StatusServiceUnavailable, - Header: http.Header{}, - } - t.rangeVal = fmt.Sprintf("bytes=0-%v", len(t.buf)-1) // Set the response for next time - return res, nil - } - m := contentRangeRE.FindStringSubmatch(rng) - if len(m) != 4 { - return nil, fmt.Errorf("unable to parse content range: %v", rng) - } - start, err := strconv.ParseInt(m[1], 10, 64) - if err != nil { - return nil, fmt.Errorf("unable to parse content range: %v", rng) - } - end, err := strconv.ParseInt(m[2], 10, 64) - if err != nil { - return nil, fmt.Errorf("unable to parse content range: %v", rng) - } - totalSize, err := strconv.ParseInt(m[3], 10, 64) - if err != nil { - return nil, fmt.Errorf("unable to parse content range: %v", rng) - } - partialSize := end - start + 1 - buf, err := ioutil.ReadAll(req.Body) - if err != nil || int64(len(buf)) != partialSize { - return nil, fmt.Errorf("unable to read %v bytes from request data, n=%v: %v", partialSize, len(buf), err) - } - t.buf = append(t.buf, buf...) - if totalSize == end+1 { - t.statusCode = 200 // signify completion of transfer - } - } - f := ioutil.NopCloser(unexpectedReader{}) - res := &http.Response{ - Body: f, - StatusCode: t.statusCode, - Header: http.Header{}, - } - if t.rangeVal != "" { - res.Header.Set("Range", t.rangeVal) - } - return res, nil -} - -type interruptedTransport struct { - req *http.Request - statusCode int - rangeVal string - interruptCount int - buf []byte - progressUpdates []int64 -} - -func (tr *interruptedTransport) ProgressUpdate(current int64) { - tr.progressUpdates = append(tr.progressUpdates, current) -} - -func TestInterruptedTransferChunks(t *testing.T) { - f, err := os.Open("googleapi.go") - if err != nil { - t.Fatalf("unable to open googleapi.go: %v", err) - } - defer f.Close() - slurp, err := ioutil.ReadAll(f) - if err != nil { - t.Fatalf("unable to slurp file: %v", err) - } - st, err := f.Stat() - if err != nil { - t.Fatalf("unable to stat googleapi.go: %v", err) - } - tr := &interruptedTransport{ - statusCode: 308, - buf: make([]byte, 0, st.Size()), - } - oldChunkSize := chunkSize - defer func() { chunkSize = oldChunkSize }() - chunkSize = 100 // override to process small chunks for test. - - sleep = func(time.Duration) {} // override time.Sleep - rx := &ResumableUpload{ - Client: &http.Client{Transport: tr}, - Media: f, - MediaType: "text/plain", - ContentLength: st.Size(), - Callback: tr.ProgressUpdate, - } - res, err := rx.Upload(context.Background()) - if err != nil || res == nil || res.StatusCode != http.StatusOK { - if res == nil { - t.Errorf("transferChunks not successful, res=nil: %v", err) - } else { - t.Errorf("transferChunks not successful, statusCode=%v: %v", res.StatusCode, err) - } - } - if len(tr.buf) != len(slurp) || bytes.Compare(tr.buf, slurp) != 0 { - t.Errorf("transferred file corrupted:\ngot %s\nwant %s", tr.buf, slurp) - } - want := []int64{} - for i := chunkSize; i <= st.Size(); i += chunkSize { - want = append(want, i) - } - if st.Size()%chunkSize != 0 { - want = append(want, st.Size()) - } - if !reflect.DeepEqual(tr.progressUpdates, want) { - t.Errorf("progress update error, got %v, want %v", tr.progressUpdates, want) - } -} - -func TestCancelUpload(t *testing.T) { - f, err := os.Open("googleapi.go") - if err != nil { - t.Fatalf("unable to open googleapi.go: %v", err) - } - defer f.Close() - st, err := f.Stat() - if err != nil { - t.Fatalf("unable to stat googleapi.go: %v", err) - } - tr := &interruptedTransport{ - statusCode: 308, - buf: make([]byte, 0, st.Size()), - } - oldChunkSize := chunkSize - defer func() { chunkSize = oldChunkSize }() - chunkSize = 100 // override to process small chunks for test. - - sleep = func(time.Duration) {} // override time.Sleep - rx := &ResumableUpload{ - Client: &http.Client{Transport: tr}, - Media: f, - MediaType: "text/plain", - ContentLength: st.Size(), - Callback: tr.ProgressUpdate, - } - ctx, cancelFunc := context.WithCancel(context.Background()) - cancelFunc() // stop the upload that hasn't started yet - res, err := rx.Upload(ctx) - if err == nil || res == nil || res.StatusCode != http.StatusRequestTimeout { - if res == nil { - t.Errorf("transferChunks not successful, got res=nil, err=%v, want StatusRequestTimeout", err) - } else { - t.Errorf("transferChunks not successful, got statusCode=%v, err=%v, want StatusRequestTimeout", res.StatusCode, err) - } - } -} diff --git a/Godeps/_workspace/src/google.golang.org/api/googleapi/transport/apikey.go b/Godeps/_workspace/src/google.golang.org/api/googleapi/transport/apikey.go deleted file mode 100644 index eca1ea250..000000000 --- a/Godeps/_workspace/src/google.golang.org/api/googleapi/transport/apikey.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2012 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package transport contains HTTP transports used to make -// authenticated API requests. -package transport - -import ( - "errors" - "net/http" -) - -// APIKey is an HTTP Transport which wraps an underlying transport and -// appends an API Key "key" parameter to the URL of outgoing requests. -type APIKey struct { - // Key is the API Key to set on requests. - Key string - - // Transport is the underlying HTTP transport. - // If nil, http.DefaultTransport is used. - Transport http.RoundTripper -} - -func (t *APIKey) RoundTrip(req *http.Request) (*http.Response, error) { - rt := t.Transport - if rt == nil { - rt = http.DefaultTransport - if rt == nil { - return nil, errors.New("googleapi/transport: no Transport specified or available") - } - } - newReq := *req - args := newReq.URL.Query() - args.Set("key", t.Key) - newReq.URL.RawQuery = args.Encode() - return rt.RoundTrip(&newReq) -} diff --git a/Godeps/_workspace/src/google.golang.org/api/googleapi/types_test.go b/Godeps/_workspace/src/google.golang.org/api/googleapi/types_test.go deleted file mode 100644 index a6b204515..000000000 --- a/Godeps/_workspace/src/google.golang.org/api/googleapi/types_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package googleapi - -import ( - "encoding/json" - "reflect" - "testing" -) - -func TestTypes(t *testing.T) { - type T struct { - I32 Int32s - I64 Int64s - U32 Uint32s - U64 Uint64s - F64 Float64s - } - v := &T{ - I32: Int32s{-1, 2, 3}, - I64: Int64s{-1, 2, 1 << 33}, - U32: Uint32s{1, 2}, - U64: Uint64s{1, 2, 1 << 33}, - F64: Float64s{1.5, 3.33}, - } - got, err := json.Marshal(v) - if err != nil { - t.Fatal(err) - } - want := `{"I32":["-1","2","3"],"I64":["-1","2","8589934592"],"U32":["1","2"],"U64":["1","2","8589934592"],"F64":["1.5","3.33"]}` - if string(got) != want { - t.Fatalf("Marshal mismatch.\n got: %s\nwant: %s\n", got, want) - } - - v2 := new(T) - if err := json.Unmarshal(got, v2); err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if !reflect.DeepEqual(v, v2) { - t.Fatalf("Unmarshal didn't produce same results.\n got: %#v\nwant: %#v\n", v, v2) - } -} diff --git a/Godeps/_workspace/src/google.golang.org/cloud/LICENSE b/Godeps/_workspace/src/google.golang.org/cloud/LICENSE new file mode 100644 index 000000000..a4c5efd82 --- /dev/null +++ b/Godeps/_workspace/src/google.golang.org/cloud/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 2014 Google 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/Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.pb.go b/Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.pb.go deleted file mode 100644 index be903e5ce..000000000 --- a/Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.pb.go +++ /dev/null @@ -1,1633 +0,0 @@ -// Code generated by protoc-gen-go. -// source: datastore_v1.proto -// DO NOT EDIT! - -/* -Package pb is a generated protocol buffer package. - -It is generated from these files: - datastore_v1.proto - -It has these top-level messages: - PartitionId - Key - Value - Property - Entity - EntityResult - Query - KindExpression - PropertyReference - PropertyExpression - PropertyOrder - Filter - CompositeFilter - PropertyFilter - GqlQuery - GqlQueryArg - QueryResultBatch - Mutation - MutationResult - ReadOptions - LookupRequest - LookupResponse - RunQueryRequest - RunQueryResponse - BeginTransactionRequest - BeginTransactionResponse - RollbackRequest - RollbackResponse - CommitRequest - CommitResponse - AllocateIdsRequest - AllocateIdsResponse -*/ -package pb - -import proto "github.com/golang/protobuf/proto" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = math.Inf - -// Specifies what data the 'entity' field contains. -// A ResultType is either implied (for example, in LookupResponse.found it -// is always FULL) or specified by context (for example, in message -// QueryResultBatch, field 'entity_result_type' specifies a ResultType -// for all the values in field 'entity_result'). -type EntityResult_ResultType int32 - -const ( - EntityResult_FULL EntityResult_ResultType = 1 - EntityResult_PROJECTION EntityResult_ResultType = 2 - // The entity may have no key. - // A property value may have meaning 18. - EntityResult_KEY_ONLY EntityResult_ResultType = 3 -) - -var EntityResult_ResultType_name = map[int32]string{ - 1: "FULL", - 2: "PROJECTION", - 3: "KEY_ONLY", -} -var EntityResult_ResultType_value = map[string]int32{ - "FULL": 1, - "PROJECTION": 2, - "KEY_ONLY": 3, -} - -func (x EntityResult_ResultType) Enum() *EntityResult_ResultType { - p := new(EntityResult_ResultType) - *p = x - return p -} -func (x EntityResult_ResultType) String() string { - return proto.EnumName(EntityResult_ResultType_name, int32(x)) -} -func (x *EntityResult_ResultType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(EntityResult_ResultType_value, data, "EntityResult_ResultType") - if err != nil { - return err - } - *x = EntityResult_ResultType(value) - return nil -} - -type PropertyExpression_AggregationFunction int32 - -const ( - PropertyExpression_FIRST PropertyExpression_AggregationFunction = 1 -) - -var PropertyExpression_AggregationFunction_name = map[int32]string{ - 1: "FIRST", -} -var PropertyExpression_AggregationFunction_value = map[string]int32{ - "FIRST": 1, -} - -func (x PropertyExpression_AggregationFunction) Enum() *PropertyExpression_AggregationFunction { - p := new(PropertyExpression_AggregationFunction) - *p = x - return p -} -func (x PropertyExpression_AggregationFunction) String() string { - return proto.EnumName(PropertyExpression_AggregationFunction_name, int32(x)) -} -func (x *PropertyExpression_AggregationFunction) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(PropertyExpression_AggregationFunction_value, data, "PropertyExpression_AggregationFunction") - if err != nil { - return err - } - *x = PropertyExpression_AggregationFunction(value) - return nil -} - -type PropertyOrder_Direction int32 - -const ( - PropertyOrder_ASCENDING PropertyOrder_Direction = 1 - PropertyOrder_DESCENDING PropertyOrder_Direction = 2 -) - -var PropertyOrder_Direction_name = map[int32]string{ - 1: "ASCENDING", - 2: "DESCENDING", -} -var PropertyOrder_Direction_value = map[string]int32{ - "ASCENDING": 1, - "DESCENDING": 2, -} - -func (x PropertyOrder_Direction) Enum() *PropertyOrder_Direction { - p := new(PropertyOrder_Direction) - *p = x - return p -} -func (x PropertyOrder_Direction) String() string { - return proto.EnumName(PropertyOrder_Direction_name, int32(x)) -} -func (x *PropertyOrder_Direction) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(PropertyOrder_Direction_value, data, "PropertyOrder_Direction") - if err != nil { - return err - } - *x = PropertyOrder_Direction(value) - return nil -} - -type CompositeFilter_Operator int32 - -const ( - CompositeFilter_AND CompositeFilter_Operator = 1 -) - -var CompositeFilter_Operator_name = map[int32]string{ - 1: "AND", -} -var CompositeFilter_Operator_value = map[string]int32{ - "AND": 1, -} - -func (x CompositeFilter_Operator) Enum() *CompositeFilter_Operator { - p := new(CompositeFilter_Operator) - *p = x - return p -} -func (x CompositeFilter_Operator) String() string { - return proto.EnumName(CompositeFilter_Operator_name, int32(x)) -} -func (x *CompositeFilter_Operator) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(CompositeFilter_Operator_value, data, "CompositeFilter_Operator") - if err != nil { - return err - } - *x = CompositeFilter_Operator(value) - return nil -} - -type PropertyFilter_Operator int32 - -const ( - PropertyFilter_LESS_THAN PropertyFilter_Operator = 1 - PropertyFilter_LESS_THAN_OR_EQUAL PropertyFilter_Operator = 2 - PropertyFilter_GREATER_THAN PropertyFilter_Operator = 3 - PropertyFilter_GREATER_THAN_OR_EQUAL PropertyFilter_Operator = 4 - PropertyFilter_EQUAL PropertyFilter_Operator = 5 - PropertyFilter_HAS_ANCESTOR PropertyFilter_Operator = 11 -) - -var PropertyFilter_Operator_name = map[int32]string{ - 1: "LESS_THAN", - 2: "LESS_THAN_OR_EQUAL", - 3: "GREATER_THAN", - 4: "GREATER_THAN_OR_EQUAL", - 5: "EQUAL", - 11: "HAS_ANCESTOR", -} -var PropertyFilter_Operator_value = map[string]int32{ - "LESS_THAN": 1, - "LESS_THAN_OR_EQUAL": 2, - "GREATER_THAN": 3, - "GREATER_THAN_OR_EQUAL": 4, - "EQUAL": 5, - "HAS_ANCESTOR": 11, -} - -func (x PropertyFilter_Operator) Enum() *PropertyFilter_Operator { - p := new(PropertyFilter_Operator) - *p = x - return p -} -func (x PropertyFilter_Operator) String() string { - return proto.EnumName(PropertyFilter_Operator_name, int32(x)) -} -func (x *PropertyFilter_Operator) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(PropertyFilter_Operator_value, data, "PropertyFilter_Operator") - if err != nil { - return err - } - *x = PropertyFilter_Operator(value) - return nil -} - -// The possible values for the 'more_results' field. -type QueryResultBatch_MoreResultsType int32 - -const ( - QueryResultBatch_NOT_FINISHED QueryResultBatch_MoreResultsType = 1 - QueryResultBatch_MORE_RESULTS_AFTER_LIMIT QueryResultBatch_MoreResultsType = 2 - // results after the limit. - QueryResultBatch_NO_MORE_RESULTS QueryResultBatch_MoreResultsType = 3 -) - -var QueryResultBatch_MoreResultsType_name = map[int32]string{ - 1: "NOT_FINISHED", - 2: "MORE_RESULTS_AFTER_LIMIT", - 3: "NO_MORE_RESULTS", -} -var QueryResultBatch_MoreResultsType_value = map[string]int32{ - "NOT_FINISHED": 1, - "MORE_RESULTS_AFTER_LIMIT": 2, - "NO_MORE_RESULTS": 3, -} - -func (x QueryResultBatch_MoreResultsType) Enum() *QueryResultBatch_MoreResultsType { - p := new(QueryResultBatch_MoreResultsType) - *p = x - return p -} -func (x QueryResultBatch_MoreResultsType) String() string { - return proto.EnumName(QueryResultBatch_MoreResultsType_name, int32(x)) -} -func (x *QueryResultBatch_MoreResultsType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(QueryResultBatch_MoreResultsType_value, data, "QueryResultBatch_MoreResultsType") - if err != nil { - return err - } - *x = QueryResultBatch_MoreResultsType(value) - return nil -} - -type ReadOptions_ReadConsistency int32 - -const ( - ReadOptions_DEFAULT ReadOptions_ReadConsistency = 0 - ReadOptions_STRONG ReadOptions_ReadConsistency = 1 - ReadOptions_EVENTUAL ReadOptions_ReadConsistency = 2 -) - -var ReadOptions_ReadConsistency_name = map[int32]string{ - 0: "DEFAULT", - 1: "STRONG", - 2: "EVENTUAL", -} -var ReadOptions_ReadConsistency_value = map[string]int32{ - "DEFAULT": 0, - "STRONG": 1, - "EVENTUAL": 2, -} - -func (x ReadOptions_ReadConsistency) Enum() *ReadOptions_ReadConsistency { - p := new(ReadOptions_ReadConsistency) - *p = x - return p -} -func (x ReadOptions_ReadConsistency) String() string { - return proto.EnumName(ReadOptions_ReadConsistency_name, int32(x)) -} -func (x *ReadOptions_ReadConsistency) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(ReadOptions_ReadConsistency_value, data, "ReadOptions_ReadConsistency") - if err != nil { - return err - } - *x = ReadOptions_ReadConsistency(value) - return nil -} - -type BeginTransactionRequest_IsolationLevel int32 - -const ( - BeginTransactionRequest_SNAPSHOT BeginTransactionRequest_IsolationLevel = 0 - // conflict if their mutations conflict. For example: - // Read(A),Write(B) may not conflict with Read(B),Write(A), - // but Read(B),Write(B) does conflict with Read(B),Write(B). - BeginTransactionRequest_SERIALIZABLE BeginTransactionRequest_IsolationLevel = 1 -) - -var BeginTransactionRequest_IsolationLevel_name = map[int32]string{ - 0: "SNAPSHOT", - 1: "SERIALIZABLE", -} -var BeginTransactionRequest_IsolationLevel_value = map[string]int32{ - "SNAPSHOT": 0, - "SERIALIZABLE": 1, -} - -func (x BeginTransactionRequest_IsolationLevel) Enum() *BeginTransactionRequest_IsolationLevel { - p := new(BeginTransactionRequest_IsolationLevel) - *p = x - return p -} -func (x BeginTransactionRequest_IsolationLevel) String() string { - return proto.EnumName(BeginTransactionRequest_IsolationLevel_name, int32(x)) -} -func (x *BeginTransactionRequest_IsolationLevel) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(BeginTransactionRequest_IsolationLevel_value, data, "BeginTransactionRequest_IsolationLevel") - if err != nil { - return err - } - *x = BeginTransactionRequest_IsolationLevel(value) - return nil -} - -type CommitRequest_Mode int32 - -const ( - CommitRequest_TRANSACTIONAL CommitRequest_Mode = 1 - CommitRequest_NON_TRANSACTIONAL CommitRequest_Mode = 2 -) - -var CommitRequest_Mode_name = map[int32]string{ - 1: "TRANSACTIONAL", - 2: "NON_TRANSACTIONAL", -} -var CommitRequest_Mode_value = map[string]int32{ - "TRANSACTIONAL": 1, - "NON_TRANSACTIONAL": 2, -} - -func (x CommitRequest_Mode) Enum() *CommitRequest_Mode { - p := new(CommitRequest_Mode) - *p = x - return p -} -func (x CommitRequest_Mode) String() string { - return proto.EnumName(CommitRequest_Mode_name, int32(x)) -} -func (x *CommitRequest_Mode) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(CommitRequest_Mode_value, data, "CommitRequest_Mode") - if err != nil { - return err - } - *x = CommitRequest_Mode(value) - return nil -} - -// An identifier for a particular subset of entities. -// -// Entities are partitioned into various subsets, each used by different -// datasets and different namespaces within a dataset and so forth. -// -// All input partition IDs are normalized before use. -// A partition ID is normalized as follows: -// If the partition ID is unset or is set to an empty partition ID, replace it -// with the context partition ID. -// Otherwise, if the partition ID has no dataset ID, assign it the context -// partition ID's dataset ID. -// Unless otherwise documented, the context partition ID has the dataset ID set -// to the context dataset ID and no other partition dimension set. -// -// A partition ID is empty if all of its fields are unset. -// -// Partition dimension: -// A dimension may be unset. -// A dimension's value must never be "". -// A dimension's value must match [A-Za-z\d\.\-_]{1,100} -// If the value of any dimension matches regex "__.*__", -// the partition is reserved/read-only. -// A reserved/read-only partition ID is forbidden in certain documented contexts. -// -// Dataset ID: -// A dataset id's value must never be "". -// A dataset id's value must match -// ([a-z\d\-]{1,100}~)?([a-z\d][a-z\d\-\.]{0,99}:)?([a-z\d][a-z\d\-]{0,99} -type PartitionId struct { - // The dataset ID. - DatasetId *string `protobuf:"bytes,3,opt,name=dataset_id" json:"dataset_id,omitempty"` - // The namespace. - Namespace *string `protobuf:"bytes,4,opt,name=namespace" json:"namespace,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *PartitionId) Reset() { *m = PartitionId{} } -func (m *PartitionId) String() string { return proto.CompactTextString(m) } -func (*PartitionId) ProtoMessage() {} - -func (m *PartitionId) GetDatasetId() string { - if m != nil && m.DatasetId != nil { - return *m.DatasetId - } - return "" -} - -func (m *PartitionId) GetNamespace() string { - if m != nil && m.Namespace != nil { - return *m.Namespace - } - return "" -} - -// A unique identifier for an entity. -// If a key's partition id or any of its path kinds or names are -// reserved/read-only, the key is reserved/read-only. -// A reserved/read-only key is forbidden in certain documented contexts. -type Key struct { - // Entities are partitioned into subsets, currently identified by a dataset - // (usually implicitly specified by the project) and namespace ID. - // Queries are scoped to a single partition. - PartitionId *PartitionId `protobuf:"bytes,1,opt,name=partition_id" json:"partition_id,omitempty"` - // The entity path. - // An entity path consists of one or more elements composed of a kind and a - // string or numerical identifier, which identify entities. The first - // element identifies a root entity, the second element identifies - // a child of the root entity, the third element a child of the - // second entity, and so forth. The entities identified by all prefixes of - // the path are called the element's ancestors. - // An entity path is always fully complete: ALL of the entity's ancestors - // are required to be in the path along with the entity identifier itself. - // The only exception is that in some documented cases, the identifier in the - // last path element (for the entity) itself may be omitted. A path can never - // be empty. - PathElement []*Key_PathElement `protobuf:"bytes,2,rep,name=path_element" json:"path_element,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Key) Reset() { *m = Key{} } -func (m *Key) String() string { return proto.CompactTextString(m) } -func (*Key) ProtoMessage() {} - -func (m *Key) GetPartitionId() *PartitionId { - if m != nil { - return m.PartitionId - } - return nil -} - -func (m *Key) GetPathElement() []*Key_PathElement { - if m != nil { - return m.PathElement - } - return nil -} - -// A (kind, ID/name) pair used to construct a key path. -// -// At most one of name or ID may be set. -// If either is set, the element is complete. -// If neither is set, the element is incomplete. -type Key_PathElement struct { - // The kind of the entity. - // A kind matching regex "__.*__" is reserved/read-only. - // A kind must not contain more than 500 characters. - // Cannot be "". - Kind *string `protobuf:"bytes,1,req,name=kind" json:"kind,omitempty"` - // The ID of the entity. - // Never equal to zero. Values less than zero are discouraged and will not - // be supported in the future. - Id *int64 `protobuf:"varint,2,opt,name=id" json:"id,omitempty"` - // The name of the entity. - // A name matching regex "__.*__" is reserved/read-only. - // A name must not be more than 500 characters. - // Cannot be "". - Name *string `protobuf:"bytes,3,opt,name=name" json:"name,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Key_PathElement) Reset() { *m = Key_PathElement{} } -func (m *Key_PathElement) String() string { return proto.CompactTextString(m) } -func (*Key_PathElement) ProtoMessage() {} - -func (m *Key_PathElement) GetKind() string { - if m != nil && m.Kind != nil { - return *m.Kind - } - return "" -} - -func (m *Key_PathElement) GetId() int64 { - if m != nil && m.Id != nil { - return *m.Id - } - return 0 -} - -func (m *Key_PathElement) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -// A message that can hold any of the supported value types and associated -// metadata. -// -// At most one of the Value fields may be set. -// If none are set the value is "null". -// -type Value struct { - // A boolean value. - BooleanValue *bool `protobuf:"varint,1,opt,name=boolean_value" json:"boolean_value,omitempty"` - // An integer value. - IntegerValue *int64 `protobuf:"varint,2,opt,name=integer_value" json:"integer_value,omitempty"` - // A double value. - DoubleValue *float64 `protobuf:"fixed64,3,opt,name=double_value" json:"double_value,omitempty"` - // A timestamp value. - TimestampMicrosecondsValue *int64 `protobuf:"varint,4,opt,name=timestamp_microseconds_value" json:"timestamp_microseconds_value,omitempty"` - // A key value. - KeyValue *Key `protobuf:"bytes,5,opt,name=key_value" json:"key_value,omitempty"` - // A blob key value. - BlobKeyValue *string `protobuf:"bytes,16,opt,name=blob_key_value" json:"blob_key_value,omitempty"` - // A UTF-8 encoded string value. - StringValue *string `protobuf:"bytes,17,opt,name=string_value" json:"string_value,omitempty"` - // A blob value. - BlobValue []byte `protobuf:"bytes,18,opt,name=blob_value" json:"blob_value,omitempty"` - // An entity value. - // May have no key. - // May have a key with an incomplete key path. - // May have a reserved/read-only key. - EntityValue *Entity `protobuf:"bytes,6,opt,name=entity_value" json:"entity_value,omitempty"` - // A list value. - // Cannot contain another list value. - // Cannot also have a meaning and indexing set. - ListValue []*Value `protobuf:"bytes,7,rep,name=list_value" json:"list_value,omitempty"` - // The meaning field is reserved and should not be used. - Meaning *int32 `protobuf:"varint,14,opt,name=meaning" json:"meaning,omitempty"` - // If the value should be indexed. - // - // The indexed property may be set for a - // null value. - // When indexed is true, stringValue - // is limited to 500 characters and the blob value is limited to 500 bytes. - // Exception: If meaning is set to 2, string_value is limited to 2038 - // characters regardless of indexed. - // When indexed is true, meaning 15 and 22 are not allowed, and meaning 16 - // will be ignored on input (and will never be set on output). - // Input values by default have indexed set to - // true; however, you can explicitly set indexed to - // true if you want. (An output value never has - // indexed explicitly set to true.) If a value is - // itself an entity, it cannot have indexed set to - // true. - // Exception: An entity value with meaning 9, 20 or 21 may be indexed. - Indexed *bool `protobuf:"varint,15,opt,name=indexed,def=1" json:"indexed,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Value) Reset() { *m = Value{} } -func (m *Value) String() string { return proto.CompactTextString(m) } -func (*Value) ProtoMessage() {} - -const Default_Value_Indexed bool = true - -func (m *Value) GetBooleanValue() bool { - if m != nil && m.BooleanValue != nil { - return *m.BooleanValue - } - return false -} - -func (m *Value) GetIntegerValue() int64 { - if m != nil && m.IntegerValue != nil { - return *m.IntegerValue - } - return 0 -} - -func (m *Value) GetDoubleValue() float64 { - if m != nil && m.DoubleValue != nil { - return *m.DoubleValue - } - return 0 -} - -func (m *Value) GetTimestampMicrosecondsValue() int64 { - if m != nil && m.TimestampMicrosecondsValue != nil { - return *m.TimestampMicrosecondsValue - } - return 0 -} - -func (m *Value) GetKeyValue() *Key { - if m != nil { - return m.KeyValue - } - return nil -} - -func (m *Value) GetBlobKeyValue() string { - if m != nil && m.BlobKeyValue != nil { - return *m.BlobKeyValue - } - return "" -} - -func (m *Value) GetStringValue() string { - if m != nil && m.StringValue != nil { - return *m.StringValue - } - return "" -} - -func (m *Value) GetBlobValue() []byte { - if m != nil { - return m.BlobValue - } - return nil -} - -func (m *Value) GetEntityValue() *Entity { - if m != nil { - return m.EntityValue - } - return nil -} - -func (m *Value) GetListValue() []*Value { - if m != nil { - return m.ListValue - } - return nil -} - -func (m *Value) GetMeaning() int32 { - if m != nil && m.Meaning != nil { - return *m.Meaning - } - return 0 -} - -func (m *Value) GetIndexed() bool { - if m != nil && m.Indexed != nil { - return *m.Indexed - } - return Default_Value_Indexed -} - -// An entity property. -type Property struct { - // The name of the property. - // A property name matching regex "__.*__" is reserved. - // A reserved property name is forbidden in certain documented contexts. - // The name must not contain more than 500 characters. - // Cannot be "". - Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` - // The value(s) of the property. - // Each value can have only one value property populated. For example, - // you cannot have a values list of { value: { integerValue: 22, - // stringValue: "a" } }, but you can have { value: { listValue: - // [ { integerValue: 22 }, { stringValue: "a" } ] }. - Value *Value `protobuf:"bytes,4,req,name=value" json:"value,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Property) Reset() { *m = Property{} } -func (m *Property) String() string { return proto.CompactTextString(m) } -func (*Property) ProtoMessage() {} - -func (m *Property) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -func (m *Property) GetValue() *Value { - if m != nil { - return m.Value - } - return nil -} - -// An entity. -// -// An entity is limited to 1 megabyte when stored. That roughly -// corresponds to a limit of 1 megabyte for the serialized form of this -// message. -type Entity struct { - // The entity's key. - // - // An entity must have a key, unless otherwise documented (for example, - // an entity in Value.entityValue may have no key). - // An entity's kind is its key's path's last element's kind, - // or null if it has no key. - Key *Key `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` - // The entity's properties. - // Each property's name must be unique for its entity. - Property []*Property `protobuf:"bytes,2,rep,name=property" json:"property,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Entity) Reset() { *m = Entity{} } -func (m *Entity) String() string { return proto.CompactTextString(m) } -func (*Entity) ProtoMessage() {} - -func (m *Entity) GetKey() *Key { - if m != nil { - return m.Key - } - return nil -} - -func (m *Entity) GetProperty() []*Property { - if m != nil { - return m.Property - } - return nil -} - -// The result of fetching an entity from the datastore. -type EntityResult struct { - // The resulting entity. - Entity *Entity `protobuf:"bytes,1,req,name=entity" json:"entity,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *EntityResult) Reset() { *m = EntityResult{} } -func (m *EntityResult) String() string { return proto.CompactTextString(m) } -func (*EntityResult) ProtoMessage() {} - -func (m *EntityResult) GetEntity() *Entity { - if m != nil { - return m.Entity - } - return nil -} - -// A query. -type Query struct { - // The projection to return. If not set the entire entity is returned. - Projection []*PropertyExpression `protobuf:"bytes,2,rep,name=projection" json:"projection,omitempty"` - // The kinds to query (if empty, returns entities from all kinds). - Kind []*KindExpression `protobuf:"bytes,3,rep,name=kind" json:"kind,omitempty"` - // The filter to apply (optional). - Filter *Filter `protobuf:"bytes,4,opt,name=filter" json:"filter,omitempty"` - // The order to apply to the query results (if empty, order is unspecified). - Order []*PropertyOrder `protobuf:"bytes,5,rep,name=order" json:"order,omitempty"` - // The properties to group by (if empty, no grouping is applied to the - // result set). - GroupBy []*PropertyReference `protobuf:"bytes,6,rep,name=group_by" json:"group_by,omitempty"` - // A starting point for the query results. Optional. Query cursors are - // returned in query result batches. - StartCursor []byte `protobuf:"bytes,7,opt,name=start_cursor" json:"start_cursor,omitempty"` - // An ending point for the query results. Optional. Query cursors are - // returned in query result batches. - EndCursor []byte `protobuf:"bytes,8,opt,name=end_cursor" json:"end_cursor,omitempty"` - // The number of results to skip. Applies before limit, but after all other - // constraints (optional, defaults to 0). - Offset *int32 `protobuf:"varint,10,opt,name=offset,def=0" json:"offset,omitempty"` - // The maximum number of results to return. Applies after all other - // constraints. Optional. - Limit *int32 `protobuf:"varint,11,opt,name=limit" json:"limit,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Query) Reset() { *m = Query{} } -func (m *Query) String() string { return proto.CompactTextString(m) } -func (*Query) ProtoMessage() {} - -const Default_Query_Offset int32 = 0 - -func (m *Query) GetProjection() []*PropertyExpression { - if m != nil { - return m.Projection - } - return nil -} - -func (m *Query) GetKind() []*KindExpression { - if m != nil { - return m.Kind - } - return nil -} - -func (m *Query) GetFilter() *Filter { - if m != nil { - return m.Filter - } - return nil -} - -func (m *Query) GetOrder() []*PropertyOrder { - if m != nil { - return m.Order - } - return nil -} - -func (m *Query) GetGroupBy() []*PropertyReference { - if m != nil { - return m.GroupBy - } - return nil -} - -func (m *Query) GetStartCursor() []byte { - if m != nil { - return m.StartCursor - } - return nil -} - -func (m *Query) GetEndCursor() []byte { - if m != nil { - return m.EndCursor - } - return nil -} - -func (m *Query) GetOffset() int32 { - if m != nil && m.Offset != nil { - return *m.Offset - } - return Default_Query_Offset -} - -func (m *Query) GetLimit() int32 { - if m != nil && m.Limit != nil { - return *m.Limit - } - return 0 -} - -// A representation of a kind. -type KindExpression struct { - // The name of the kind. - Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *KindExpression) Reset() { *m = KindExpression{} } -func (m *KindExpression) String() string { return proto.CompactTextString(m) } -func (*KindExpression) ProtoMessage() {} - -func (m *KindExpression) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -// A reference to a property relative to the kind expressions. -// exactly. -type PropertyReference struct { - // The name of the property. - Name *string `protobuf:"bytes,2,req,name=name" json:"name,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *PropertyReference) Reset() { *m = PropertyReference{} } -func (m *PropertyReference) String() string { return proto.CompactTextString(m) } -func (*PropertyReference) ProtoMessage() {} - -func (m *PropertyReference) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -// A representation of a property in a projection. -type PropertyExpression struct { - // The property to project. - Property *PropertyReference `protobuf:"bytes,1,req,name=property" json:"property,omitempty"` - // The aggregation function to apply to the property. Optional. - // Can only be used when grouping by at least one property. Must - // then be set on all properties in the projection that are not - // being grouped by. - AggregationFunction *PropertyExpression_AggregationFunction `protobuf:"varint,2,opt,name=aggregation_function,enum=pb.PropertyExpression_AggregationFunction" json:"aggregation_function,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *PropertyExpression) Reset() { *m = PropertyExpression{} } -func (m *PropertyExpression) String() string { return proto.CompactTextString(m) } -func (*PropertyExpression) ProtoMessage() {} - -func (m *PropertyExpression) GetProperty() *PropertyReference { - if m != nil { - return m.Property - } - return nil -} - -func (m *PropertyExpression) GetAggregationFunction() PropertyExpression_AggregationFunction { - if m != nil && m.AggregationFunction != nil { - return *m.AggregationFunction - } - return PropertyExpression_FIRST -} - -// The desired order for a specific property. -type PropertyOrder struct { - // The property to order by. - Property *PropertyReference `protobuf:"bytes,1,req,name=property" json:"property,omitempty"` - // The direction to order by. - Direction *PropertyOrder_Direction `protobuf:"varint,2,opt,name=direction,enum=pb.PropertyOrder_Direction,def=1" json:"direction,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *PropertyOrder) Reset() { *m = PropertyOrder{} } -func (m *PropertyOrder) String() string { return proto.CompactTextString(m) } -func (*PropertyOrder) ProtoMessage() {} - -const Default_PropertyOrder_Direction PropertyOrder_Direction = PropertyOrder_ASCENDING - -func (m *PropertyOrder) GetProperty() *PropertyReference { - if m != nil { - return m.Property - } - return nil -} - -func (m *PropertyOrder) GetDirection() PropertyOrder_Direction { - if m != nil && m.Direction != nil { - return *m.Direction - } - return Default_PropertyOrder_Direction -} - -// A holder for any type of filter. Exactly one field should be specified. -type Filter struct { - // A composite filter. - CompositeFilter *CompositeFilter `protobuf:"bytes,1,opt,name=composite_filter" json:"composite_filter,omitempty"` - // A filter on a property. - PropertyFilter *PropertyFilter `protobuf:"bytes,2,opt,name=property_filter" json:"property_filter,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Filter) Reset() { *m = Filter{} } -func (m *Filter) String() string { return proto.CompactTextString(m) } -func (*Filter) ProtoMessage() {} - -func (m *Filter) GetCompositeFilter() *CompositeFilter { - if m != nil { - return m.CompositeFilter - } - return nil -} - -func (m *Filter) GetPropertyFilter() *PropertyFilter { - if m != nil { - return m.PropertyFilter - } - return nil -} - -// A filter that merges the multiple other filters using the given operation. -type CompositeFilter struct { - // The operator for combining multiple filters. - Operator *CompositeFilter_Operator `protobuf:"varint,1,req,name=operator,enum=pb.CompositeFilter_Operator" json:"operator,omitempty"` - // The list of filters to combine. - // Must contain at least one filter. - Filter []*Filter `protobuf:"bytes,2,rep,name=filter" json:"filter,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CompositeFilter) Reset() { *m = CompositeFilter{} } -func (m *CompositeFilter) String() string { return proto.CompactTextString(m) } -func (*CompositeFilter) ProtoMessage() {} - -func (m *CompositeFilter) GetOperator() CompositeFilter_Operator { - if m != nil && m.Operator != nil { - return *m.Operator - } - return CompositeFilter_AND -} - -func (m *CompositeFilter) GetFilter() []*Filter { - if m != nil { - return m.Filter - } - return nil -} - -// A filter on a specific property. -type PropertyFilter struct { - // The property to filter by. - Property *PropertyReference `protobuf:"bytes,1,req,name=property" json:"property,omitempty"` - // The operator to filter by. - Operator *PropertyFilter_Operator `protobuf:"varint,2,req,name=operator,enum=pb.PropertyFilter_Operator" json:"operator,omitempty"` - // The value to compare the property to. - Value *Value `protobuf:"bytes,3,req,name=value" json:"value,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *PropertyFilter) Reset() { *m = PropertyFilter{} } -func (m *PropertyFilter) String() string { return proto.CompactTextString(m) } -func (*PropertyFilter) ProtoMessage() {} - -func (m *PropertyFilter) GetProperty() *PropertyReference { - if m != nil { - return m.Property - } - return nil -} - -func (m *PropertyFilter) GetOperator() PropertyFilter_Operator { - if m != nil && m.Operator != nil { - return *m.Operator - } - return PropertyFilter_LESS_THAN -} - -func (m *PropertyFilter) GetValue() *Value { - if m != nil { - return m.Value - } - return nil -} - -// A GQL query. -type GqlQuery struct { - QueryString *string `protobuf:"bytes,1,req,name=query_string" json:"query_string,omitempty"` - // When false, the query string must not contain a literal. - AllowLiteral *bool `protobuf:"varint,2,opt,name=allow_literal,def=0" json:"allow_literal,omitempty"` - // A named argument must set field GqlQueryArg.name. - // No two named arguments may have the same name. - // For each non-reserved named binding site in the query string, - // there must be a named argument with that name, - // but not necessarily the inverse. - NameArg []*GqlQueryArg `protobuf:"bytes,3,rep,name=name_arg" json:"name_arg,omitempty"` - // Numbered binding site @1 references the first numbered argument, - // effectively using 1-based indexing, rather than the usual 0. - // A numbered argument must NOT set field GqlQueryArg.name. - // For each binding site numbered i in query_string, - // there must be an ith numbered argument. - // The inverse must also be true. - NumberArg []*GqlQueryArg `protobuf:"bytes,4,rep,name=number_arg" json:"number_arg,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GqlQuery) Reset() { *m = GqlQuery{} } -func (m *GqlQuery) String() string { return proto.CompactTextString(m) } -func (*GqlQuery) ProtoMessage() {} - -const Default_GqlQuery_AllowLiteral bool = false - -func (m *GqlQuery) GetQueryString() string { - if m != nil && m.QueryString != nil { - return *m.QueryString - } - return "" -} - -func (m *GqlQuery) GetAllowLiteral() bool { - if m != nil && m.AllowLiteral != nil { - return *m.AllowLiteral - } - return Default_GqlQuery_AllowLiteral -} - -func (m *GqlQuery) GetNameArg() []*GqlQueryArg { - if m != nil { - return m.NameArg - } - return nil -} - -func (m *GqlQuery) GetNumberArg() []*GqlQueryArg { - if m != nil { - return m.NumberArg - } - return nil -} - -// A binding argument for a GQL query. -// Exactly one of fields value and cursor must be set. -type GqlQueryArg struct { - // Must match regex "[A-Za-z_$][A-Za-z_$0-9]*". - // Must not match regex "__.*__". - // Must not be "". - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Value *Value `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` - Cursor []byte `protobuf:"bytes,3,opt,name=cursor" json:"cursor,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GqlQueryArg) Reset() { *m = GqlQueryArg{} } -func (m *GqlQueryArg) String() string { return proto.CompactTextString(m) } -func (*GqlQueryArg) ProtoMessage() {} - -func (m *GqlQueryArg) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -func (m *GqlQueryArg) GetValue() *Value { - if m != nil { - return m.Value - } - return nil -} - -func (m *GqlQueryArg) GetCursor() []byte { - if m != nil { - return m.Cursor - } - return nil -} - -// A batch of results produced by a query. -type QueryResultBatch struct { - // The result type for every entity in entityResults. - EntityResultType *EntityResult_ResultType `protobuf:"varint,1,req,name=entity_result_type,enum=pb.EntityResult_ResultType" json:"entity_result_type,omitempty"` - // The results for this batch. - EntityResult []*EntityResult `protobuf:"bytes,2,rep,name=entity_result" json:"entity_result,omitempty"` - // A cursor that points to the position after the last result in the batch. - // May be absent. - EndCursor []byte `protobuf:"bytes,4,opt,name=end_cursor" json:"end_cursor,omitempty"` - // The state of the query after the current batch. - MoreResults *QueryResultBatch_MoreResultsType `protobuf:"varint,5,req,name=more_results,enum=pb.QueryResultBatch_MoreResultsType" json:"more_results,omitempty"` - // The number of results skipped because of Query.offset. - SkippedResults *int32 `protobuf:"varint,6,opt,name=skipped_results" json:"skipped_results,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *QueryResultBatch) Reset() { *m = QueryResultBatch{} } -func (m *QueryResultBatch) String() string { return proto.CompactTextString(m) } -func (*QueryResultBatch) ProtoMessage() {} - -func (m *QueryResultBatch) GetEntityResultType() EntityResult_ResultType { - if m != nil && m.EntityResultType != nil { - return *m.EntityResultType - } - return EntityResult_FULL -} - -func (m *QueryResultBatch) GetEntityResult() []*EntityResult { - if m != nil { - return m.EntityResult - } - return nil -} - -func (m *QueryResultBatch) GetEndCursor() []byte { - if m != nil { - return m.EndCursor - } - return nil -} - -func (m *QueryResultBatch) GetMoreResults() QueryResultBatch_MoreResultsType { - if m != nil && m.MoreResults != nil { - return *m.MoreResults - } - return QueryResultBatch_NOT_FINISHED -} - -func (m *QueryResultBatch) GetSkippedResults() int32 { - if m != nil && m.SkippedResults != nil { - return *m.SkippedResults - } - return 0 -} - -// A set of changes to apply. -// -// No entity in this message may have a reserved property name, -// not even a property in an entity in a value. -// No value in this message may have meaning 18, -// not even a value in an entity in another value. -// -// If entities with duplicate keys are present, an arbitrary choice will -// be made as to which is written. -type Mutation struct { - // Entities to upsert. - // Each upserted entity's key must have a complete path and - // must not be reserved/read-only. - Upsert []*Entity `protobuf:"bytes,1,rep,name=upsert" json:"upsert,omitempty"` - // Entities to update. - // Each updated entity's key must have a complete path and - // must not be reserved/read-only. - Update []*Entity `protobuf:"bytes,2,rep,name=update" json:"update,omitempty"` - // Entities to insert. - // Each inserted entity's key must have a complete path and - // must not be reserved/read-only. - Insert []*Entity `protobuf:"bytes,3,rep,name=insert" json:"insert,omitempty"` - // Insert entities with a newly allocated ID. - // Each inserted entity's key must omit the final identifier in its path and - // must not be reserved/read-only. - InsertAutoId []*Entity `protobuf:"bytes,4,rep,name=insert_auto_id" json:"insert_auto_id,omitempty"` - // Keys of entities to delete. - // Each key must have a complete key path and must not be reserved/read-only. - Delete []*Key `protobuf:"bytes,5,rep,name=delete" json:"delete,omitempty"` - // Ignore a user specified read-only period. Optional. - Force *bool `protobuf:"varint,6,opt,name=force" json:"force,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Mutation) Reset() { *m = Mutation{} } -func (m *Mutation) String() string { return proto.CompactTextString(m) } -func (*Mutation) ProtoMessage() {} - -func (m *Mutation) GetUpsert() []*Entity { - if m != nil { - return m.Upsert - } - return nil -} - -func (m *Mutation) GetUpdate() []*Entity { - if m != nil { - return m.Update - } - return nil -} - -func (m *Mutation) GetInsert() []*Entity { - if m != nil { - return m.Insert - } - return nil -} - -func (m *Mutation) GetInsertAutoId() []*Entity { - if m != nil { - return m.InsertAutoId - } - return nil -} - -func (m *Mutation) GetDelete() []*Key { - if m != nil { - return m.Delete - } - return nil -} - -func (m *Mutation) GetForce() bool { - if m != nil && m.Force != nil { - return *m.Force - } - return false -} - -// The result of applying a mutation. -type MutationResult struct { - // Number of index writes. - IndexUpdates *int32 `protobuf:"varint,1,req,name=index_updates" json:"index_updates,omitempty"` - // Keys for insertAutoId entities. One per entity from the - // request, in the same order. - InsertAutoIdKey []*Key `protobuf:"bytes,2,rep,name=insert_auto_id_key" json:"insert_auto_id_key,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *MutationResult) Reset() { *m = MutationResult{} } -func (m *MutationResult) String() string { return proto.CompactTextString(m) } -func (*MutationResult) ProtoMessage() {} - -func (m *MutationResult) GetIndexUpdates() int32 { - if m != nil && m.IndexUpdates != nil { - return *m.IndexUpdates - } - return 0 -} - -func (m *MutationResult) GetInsertAutoIdKey() []*Key { - if m != nil { - return m.InsertAutoIdKey - } - return nil -} - -// Options shared by read requests. -type ReadOptions struct { - // The read consistency to use. - // Cannot be set when transaction is set. - // Lookup and ancestor queries default to STRONG, global queries default to - // EVENTUAL and cannot be set to STRONG. - ReadConsistency *ReadOptions_ReadConsistency `protobuf:"varint,1,opt,name=read_consistency,enum=pb.ReadOptions_ReadConsistency,def=0" json:"read_consistency,omitempty"` - // The transaction to use. Optional. - Transaction []byte `protobuf:"bytes,2,opt,name=transaction" json:"transaction,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *ReadOptions) Reset() { *m = ReadOptions{} } -func (m *ReadOptions) String() string { return proto.CompactTextString(m) } -func (*ReadOptions) ProtoMessage() {} - -const Default_ReadOptions_ReadConsistency ReadOptions_ReadConsistency = ReadOptions_DEFAULT - -func (m *ReadOptions) GetReadConsistency() ReadOptions_ReadConsistency { - if m != nil && m.ReadConsistency != nil { - return *m.ReadConsistency - } - return Default_ReadOptions_ReadConsistency -} - -func (m *ReadOptions) GetTransaction() []byte { - if m != nil { - return m.Transaction - } - return nil -} - -// The request for Lookup. -type LookupRequest struct { - // Options for this lookup request. Optional. - ReadOptions *ReadOptions `protobuf:"bytes,1,opt,name=read_options" json:"read_options,omitempty"` - // Keys of entities to look up from the datastore. - Key []*Key `protobuf:"bytes,3,rep,name=key" json:"key,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *LookupRequest) Reset() { *m = LookupRequest{} } -func (m *LookupRequest) String() string { return proto.CompactTextString(m) } -func (*LookupRequest) ProtoMessage() {} - -func (m *LookupRequest) GetReadOptions() *ReadOptions { - if m != nil { - return m.ReadOptions - } - return nil -} - -func (m *LookupRequest) GetKey() []*Key { - if m != nil { - return m.Key - } - return nil -} - -// The response for Lookup. -type LookupResponse struct { - // Entities found as ResultType.FULL entities. - Found []*EntityResult `protobuf:"bytes,1,rep,name=found" json:"found,omitempty"` - // Entities not found as ResultType.KEY_ONLY entities. - Missing []*EntityResult `protobuf:"bytes,2,rep,name=missing" json:"missing,omitempty"` - // A list of keys that were not looked up due to resource constraints. - Deferred []*Key `protobuf:"bytes,3,rep,name=deferred" json:"deferred,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *LookupResponse) Reset() { *m = LookupResponse{} } -func (m *LookupResponse) String() string { return proto.CompactTextString(m) } -func (*LookupResponse) ProtoMessage() {} - -func (m *LookupResponse) GetFound() []*EntityResult { - if m != nil { - return m.Found - } - return nil -} - -func (m *LookupResponse) GetMissing() []*EntityResult { - if m != nil { - return m.Missing - } - return nil -} - -func (m *LookupResponse) GetDeferred() []*Key { - if m != nil { - return m.Deferred - } - return nil -} - -// The request for RunQuery. -type RunQueryRequest struct { - // The options for this query. - ReadOptions *ReadOptions `protobuf:"bytes,1,opt,name=read_options" json:"read_options,omitempty"` - // Entities are partitioned into subsets, identified by a dataset (usually - // implicitly specified by the project) and namespace ID. Queries are scoped - // to a single partition. - // This partition ID is normalized with the standard default context - // partition ID, but all other partition IDs in RunQueryRequest are - // normalized with this partition ID as the context partition ID. - PartitionId *PartitionId `protobuf:"bytes,2,opt,name=partition_id" json:"partition_id,omitempty"` - // The query to run. - // Either this field or field gql_query must be set, but not both. - Query *Query `protobuf:"bytes,3,opt,name=query" json:"query,omitempty"` - // The GQL query to run. - // Either this field or field query must be set, but not both. - GqlQuery *GqlQuery `protobuf:"bytes,7,opt,name=gql_query" json:"gql_query,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *RunQueryRequest) Reset() { *m = RunQueryRequest{} } -func (m *RunQueryRequest) String() string { return proto.CompactTextString(m) } -func (*RunQueryRequest) ProtoMessage() {} - -func (m *RunQueryRequest) GetReadOptions() *ReadOptions { - if m != nil { - return m.ReadOptions - } - return nil -} - -func (m *RunQueryRequest) GetPartitionId() *PartitionId { - if m != nil { - return m.PartitionId - } - return nil -} - -func (m *RunQueryRequest) GetQuery() *Query { - if m != nil { - return m.Query - } - return nil -} - -func (m *RunQueryRequest) GetGqlQuery() *GqlQuery { - if m != nil { - return m.GqlQuery - } - return nil -} - -// The response for RunQuery. -type RunQueryResponse struct { - // A batch of query results (always present). - Batch *QueryResultBatch `protobuf:"bytes,1,opt,name=batch" json:"batch,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *RunQueryResponse) Reset() { *m = RunQueryResponse{} } -func (m *RunQueryResponse) String() string { return proto.CompactTextString(m) } -func (*RunQueryResponse) ProtoMessage() {} - -func (m *RunQueryResponse) GetBatch() *QueryResultBatch { - if m != nil { - return m.Batch - } - return nil -} - -// The request for BeginTransaction. -type BeginTransactionRequest struct { - // The transaction isolation level. - IsolationLevel *BeginTransactionRequest_IsolationLevel `protobuf:"varint,1,opt,name=isolation_level,enum=pb.BeginTransactionRequest_IsolationLevel,def=0" json:"isolation_level,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *BeginTransactionRequest) Reset() { *m = BeginTransactionRequest{} } -func (m *BeginTransactionRequest) String() string { return proto.CompactTextString(m) } -func (*BeginTransactionRequest) ProtoMessage() {} - -const Default_BeginTransactionRequest_IsolationLevel BeginTransactionRequest_IsolationLevel = BeginTransactionRequest_SNAPSHOT - -func (m *BeginTransactionRequest) GetIsolationLevel() BeginTransactionRequest_IsolationLevel { - if m != nil && m.IsolationLevel != nil { - return *m.IsolationLevel - } - return Default_BeginTransactionRequest_IsolationLevel -} - -// The response for BeginTransaction. -type BeginTransactionResponse struct { - // The transaction identifier (always present). - Transaction []byte `protobuf:"bytes,1,opt,name=transaction" json:"transaction,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *BeginTransactionResponse) Reset() { *m = BeginTransactionResponse{} } -func (m *BeginTransactionResponse) String() string { return proto.CompactTextString(m) } -func (*BeginTransactionResponse) ProtoMessage() {} - -func (m *BeginTransactionResponse) GetTransaction() []byte { - if m != nil { - return m.Transaction - } - return nil -} - -// The request for Rollback. -type RollbackRequest struct { - // The transaction identifier, returned by a call to - // beginTransaction. - Transaction []byte `protobuf:"bytes,1,req,name=transaction" json:"transaction,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *RollbackRequest) Reset() { *m = RollbackRequest{} } -func (m *RollbackRequest) String() string { return proto.CompactTextString(m) } -func (*RollbackRequest) ProtoMessage() {} - -func (m *RollbackRequest) GetTransaction() []byte { - if m != nil { - return m.Transaction - } - return nil -} - -// The response for Rollback. -type RollbackResponse struct { - XXX_unrecognized []byte `json:"-"` -} - -func (m *RollbackResponse) Reset() { *m = RollbackResponse{} } -func (m *RollbackResponse) String() string { return proto.CompactTextString(m) } -func (*RollbackResponse) ProtoMessage() {} - -// The request for Commit. -type CommitRequest struct { - // The transaction identifier, returned by a call to - // beginTransaction. Must be set when mode is TRANSACTIONAL. - Transaction []byte `protobuf:"bytes,1,opt,name=transaction" json:"transaction,omitempty"` - // The mutation to perform. Optional. - Mutation *Mutation `protobuf:"bytes,2,opt,name=mutation" json:"mutation,omitempty"` - // The type of commit to perform. Either TRANSACTIONAL or NON_TRANSACTIONAL. - Mode *CommitRequest_Mode `protobuf:"varint,5,opt,name=mode,enum=pb.CommitRequest_Mode,def=1" json:"mode,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CommitRequest) Reset() { *m = CommitRequest{} } -func (m *CommitRequest) String() string { return proto.CompactTextString(m) } -func (*CommitRequest) ProtoMessage() {} - -const Default_CommitRequest_Mode CommitRequest_Mode = CommitRequest_TRANSACTIONAL - -func (m *CommitRequest) GetTransaction() []byte { - if m != nil { - return m.Transaction - } - return nil -} - -func (m *CommitRequest) GetMutation() *Mutation { - if m != nil { - return m.Mutation - } - return nil -} - -func (m *CommitRequest) GetMode() CommitRequest_Mode { - if m != nil && m.Mode != nil { - return *m.Mode - } - return Default_CommitRequest_Mode -} - -// The response for Commit. -type CommitResponse struct { - // The result of performing the mutation (if any). - MutationResult *MutationResult `protobuf:"bytes,1,opt,name=mutation_result" json:"mutation_result,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CommitResponse) Reset() { *m = CommitResponse{} } -func (m *CommitResponse) String() string { return proto.CompactTextString(m) } -func (*CommitResponse) ProtoMessage() {} - -func (m *CommitResponse) GetMutationResult() *MutationResult { - if m != nil { - return m.MutationResult - } - return nil -} - -// The request for AllocateIds. -type AllocateIdsRequest struct { - // A list of keys with incomplete key paths to allocate IDs for. - // No key may be reserved/read-only. - Key []*Key `protobuf:"bytes,1,rep,name=key" json:"key,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *AllocateIdsRequest) Reset() { *m = AllocateIdsRequest{} } -func (m *AllocateIdsRequest) String() string { return proto.CompactTextString(m) } -func (*AllocateIdsRequest) ProtoMessage() {} - -func (m *AllocateIdsRequest) GetKey() []*Key { - if m != nil { - return m.Key - } - return nil -} - -// The response for AllocateIds. -type AllocateIdsResponse struct { - // The keys specified in the request (in the same order), each with - // its key path completed with a newly allocated ID. - Key []*Key `protobuf:"bytes,1,rep,name=key" json:"key,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *AllocateIdsResponse) Reset() { *m = AllocateIdsResponse{} } -func (m *AllocateIdsResponse) String() string { return proto.CompactTextString(m) } -func (*AllocateIdsResponse) ProtoMessage() {} - -func (m *AllocateIdsResponse) GetKey() []*Key { - if m != nil { - return m.Key - } - return nil -} - -func init() { - proto.RegisterEnum("pb.EntityResult_ResultType", EntityResult_ResultType_name, EntityResult_ResultType_value) - proto.RegisterEnum("pb.PropertyExpression_AggregationFunction", PropertyExpression_AggregationFunction_name, PropertyExpression_AggregationFunction_value) - proto.RegisterEnum("pb.PropertyOrder_Direction", PropertyOrder_Direction_name, PropertyOrder_Direction_value) - proto.RegisterEnum("pb.CompositeFilter_Operator", CompositeFilter_Operator_name, CompositeFilter_Operator_value) - proto.RegisterEnum("pb.PropertyFilter_Operator", PropertyFilter_Operator_name, PropertyFilter_Operator_value) - proto.RegisterEnum("pb.QueryResultBatch_MoreResultsType", QueryResultBatch_MoreResultsType_name, QueryResultBatch_MoreResultsType_value) - proto.RegisterEnum("pb.ReadOptions_ReadConsistency", ReadOptions_ReadConsistency_name, ReadOptions_ReadConsistency_value) - proto.RegisterEnum("pb.BeginTransactionRequest_IsolationLevel", BeginTransactionRequest_IsolationLevel_name, BeginTransactionRequest_IsolationLevel_value) - proto.RegisterEnum("pb.CommitRequest_Mode", CommitRequest_Mode_name, CommitRequest_Mode_value) -} diff --git a/Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.proto b/Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.proto deleted file mode 100644 index bb4c199b1..000000000 --- a/Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.proto +++ /dev/null @@ -1,594 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// The datastore v1 service proto definitions - -syntax = "proto2"; - -package pb; -option java_package = "com.google.api.services.datastore"; - - -// An identifier for a particular subset of entities. -// -// Entities are partitioned into various subsets, each used by different -// datasets and different namespaces within a dataset and so forth. -// -// All input partition IDs are normalized before use. -// A partition ID is normalized as follows: -// If the partition ID is unset or is set to an empty partition ID, replace it -// with the context partition ID. -// Otherwise, if the partition ID has no dataset ID, assign it the context -// partition ID's dataset ID. -// Unless otherwise documented, the context partition ID has the dataset ID set -// to the context dataset ID and no other partition dimension set. -// -// A partition ID is empty if all of its fields are unset. -// -// Partition dimension: -// A dimension may be unset. -// A dimension's value must never be "". -// A dimension's value must match [A-Za-z\d\.\-_]{1,100} -// If the value of any dimension matches regex "__.*__", -// the partition is reserved/read-only. -// A reserved/read-only partition ID is forbidden in certain documented contexts. -// -// Dataset ID: -// A dataset id's value must never be "". -// A dataset id's value must match -// ([a-z\d\-]{1,100}~)?([a-z\d][a-z\d\-\.]{0,99}:)?([a-z\d][a-z\d\-]{0,99} -message PartitionId { - // The dataset ID. - optional string dataset_id = 3; - // The namespace. - optional string namespace = 4; -} - -// A unique identifier for an entity. -// If a key's partition id or any of its path kinds or names are -// reserved/read-only, the key is reserved/read-only. -// A reserved/read-only key is forbidden in certain documented contexts. -message Key { - // Entities are partitioned into subsets, currently identified by a dataset - // (usually implicitly specified by the project) and namespace ID. - // Queries are scoped to a single partition. - optional PartitionId partition_id = 1; - - // A (kind, ID/name) pair used to construct a key path. - // - // At most one of name or ID may be set. - // If either is set, the element is complete. - // If neither is set, the element is incomplete. - message PathElement { - // The kind of the entity. - // A kind matching regex "__.*__" is reserved/read-only. - // A kind must not contain more than 500 characters. - // Cannot be "". - required string kind = 1; - // The ID of the entity. - // Never equal to zero. Values less than zero are discouraged and will not - // be supported in the future. - optional int64 id = 2; - // The name of the entity. - // A name matching regex "__.*__" is reserved/read-only. - // A name must not be more than 500 characters. - // Cannot be "". - optional string name = 3; - } - - // The entity path. - // An entity path consists of one or more elements composed of a kind and a - // string or numerical identifier, which identify entities. The first - // element identifies a root entity, the second element identifies - // a child of the root entity, the third element a child of the - // second entity, and so forth. The entities identified by all prefixes of - // the path are called the element's ancestors. - // An entity path is always fully complete: ALL of the entity's ancestors - // are required to be in the path along with the entity identifier itself. - // The only exception is that in some documented cases, the identifier in the - // last path element (for the entity) itself may be omitted. A path can never - // be empty. - repeated PathElement path_element = 2; -} - -// A message that can hold any of the supported value types and associated -// metadata. -// -// At most one of the Value fields may be set. -// If none are set the value is "null". -// -message Value { - // A boolean value. - optional bool boolean_value = 1; - // An integer value. - optional int64 integer_value = 2; - // A double value. - optional double double_value = 3; - // A timestamp value. - optional int64 timestamp_microseconds_value = 4; - // A key value. - optional Key key_value = 5; - // A blob key value. - optional string blob_key_value = 16; - // A UTF-8 encoded string value. - optional string string_value = 17; - // A blob value. - optional bytes blob_value = 18; - // An entity value. - // May have no key. - // May have a key with an incomplete key path. - // May have a reserved/read-only key. - optional Entity entity_value = 6; - // A list value. - // Cannot contain another list value. - // Cannot also have a meaning and indexing set. - repeated Value list_value = 7; - - // The meaning field is reserved and should not be used. - optional int32 meaning = 14; - - // If the value should be indexed. - // - // The indexed property may be set for a - // null value. - // When indexed is true, stringValue - // is limited to 500 characters and the blob value is limited to 500 bytes. - // Exception: If meaning is set to 2, string_value is limited to 2038 - // characters regardless of indexed. - // When indexed is true, meaning 15 and 22 are not allowed, and meaning 16 - // will be ignored on input (and will never be set on output). - // Input values by default have indexed set to - // true; however, you can explicitly set indexed to - // true if you want. (An output value never has - // indexed explicitly set to true.) If a value is - // itself an entity, it cannot have indexed set to - // true. - // Exception: An entity value with meaning 9, 20 or 21 may be indexed. - optional bool indexed = 15 [default = true]; -} - -// An entity property. -message Property { - // The name of the property. - // A property name matching regex "__.*__" is reserved. - // A reserved property name is forbidden in certain documented contexts. - // The name must not contain more than 500 characters. - // Cannot be "". - required string name = 1; - - // The value(s) of the property. - // Each value can have only one value property populated. For example, - // you cannot have a values list of { value: { integerValue: 22, - // stringValue: "a" } }, but you can have { value: { listValue: - // [ { integerValue: 22 }, { stringValue: "a" } ] }. - required Value value = 4; -} - -// An entity. -// -// An entity is limited to 1 megabyte when stored. That roughly -// corresponds to a limit of 1 megabyte for the serialized form of this -// message. -message Entity { - // The entity's key. - // - // An entity must have a key, unless otherwise documented (for example, - // an entity in Value.entityValue may have no key). - // An entity's kind is its key's path's last element's kind, - // or null if it has no key. - optional Key key = 1; - // The entity's properties. - // Each property's name must be unique for its entity. - repeated Property property = 2; -} - -// The result of fetching an entity from the datastore. -message EntityResult { - // Specifies what data the 'entity' field contains. - // A ResultType is either implied (for example, in LookupResponse.found it - // is always FULL) or specified by context (for example, in message - // QueryResultBatch, field 'entity_result_type' specifies a ResultType - // for all the values in field 'entity_result'). - enum ResultType { - FULL = 1; // The entire entity. - PROJECTION = 2; // A projected subset of properties. - // The entity may have no key. - // A property value may have meaning 18. - KEY_ONLY = 3; // Only the key. - } - - // The resulting entity. - required Entity entity = 1; -} - -// A query. -message Query { - // The projection to return. If not set the entire entity is returned. - repeated PropertyExpression projection = 2; - - // The kinds to query (if empty, returns entities from all kinds). - repeated KindExpression kind = 3; - - // The filter to apply (optional). - optional Filter filter = 4; - - // The order to apply to the query results (if empty, order is unspecified). - repeated PropertyOrder order = 5; - - // The properties to group by (if empty, no grouping is applied to the - // result set). - repeated PropertyReference group_by = 6; - - // A starting point for the query results. Optional. Query cursors are - // returned in query result batches. - optional bytes /* serialized QueryCursor */ start_cursor = 7; - - // An ending point for the query results. Optional. Query cursors are - // returned in query result batches. - optional bytes /* serialized QueryCursor */ end_cursor = 8; - - // The number of results to skip. Applies before limit, but after all other - // constraints (optional, defaults to 0). - optional int32 offset = 10 [default=0]; - - // The maximum number of results to return. Applies after all other - // constraints. Optional. - optional int32 limit = 11; -} - -// A representation of a kind. -message KindExpression { - // The name of the kind. - required string name = 1; -} - -// A reference to a property relative to the kind expressions. -// exactly. -message PropertyReference { - // The name of the property. - required string name = 2; -} - -// A representation of a property in a projection. -message PropertyExpression { - enum AggregationFunction { - FIRST = 1; - } - // The property to project. - required PropertyReference property = 1; - // The aggregation function to apply to the property. Optional. - // Can only be used when grouping by at least one property. Must - // then be set on all properties in the projection that are not - // being grouped by. - optional AggregationFunction aggregation_function = 2; -} - -// The desired order for a specific property. -message PropertyOrder { - enum Direction { - ASCENDING = 1; - DESCENDING = 2; - } - // The property to order by. - required PropertyReference property = 1; - // The direction to order by. - optional Direction direction = 2 [default=ASCENDING]; -} - -// A holder for any type of filter. Exactly one field should be specified. -message Filter { - // A composite filter. - optional CompositeFilter composite_filter = 1; - // A filter on a property. - optional PropertyFilter property_filter = 2; -} - -// A filter that merges the multiple other filters using the given operation. -message CompositeFilter { - enum Operator { - AND = 1; - } - - // The operator for combining multiple filters. - required Operator operator = 1; - // The list of filters to combine. - // Must contain at least one filter. - repeated Filter filter = 2; -} - -// A filter on a specific property. -message PropertyFilter { - enum Operator { - LESS_THAN = 1; - LESS_THAN_OR_EQUAL = 2; - GREATER_THAN = 3; - GREATER_THAN_OR_EQUAL = 4; - EQUAL = 5; - - HAS_ANCESTOR = 11; - } - - // The property to filter by. - required PropertyReference property = 1; - // The operator to filter by. - required Operator operator = 2; - // The value to compare the property to. - required Value value = 3; -} - -// A GQL query. -message GqlQuery { - required string query_string = 1; - // When false, the query string must not contain a literal. - optional bool allow_literal = 2 [default = false]; - // A named argument must set field GqlQueryArg.name. - // No two named arguments may have the same name. - // For each non-reserved named binding site in the query string, - // there must be a named argument with that name, - // but not necessarily the inverse. - repeated GqlQueryArg name_arg = 3; - // Numbered binding site @1 references the first numbered argument, - // effectively using 1-based indexing, rather than the usual 0. - // A numbered argument must NOT set field GqlQueryArg.name. - // For each binding site numbered i in query_string, - // there must be an ith numbered argument. - // The inverse must also be true. - repeated GqlQueryArg number_arg = 4; -} - -// A binding argument for a GQL query. -// Exactly one of fields value and cursor must be set. -message GqlQueryArg { - // Must match regex "[A-Za-z_$][A-Za-z_$0-9]*". - // Must not match regex "__.*__". - // Must not be "". - optional string name = 1; - optional Value value = 2; - optional bytes cursor = 3; -} - -// A batch of results produced by a query. -message QueryResultBatch { - // The possible values for the 'more_results' field. - enum MoreResultsType { - NOT_FINISHED = 1; // There are additional batches to fetch from this query. - MORE_RESULTS_AFTER_LIMIT = 2; // The query is finished, but there are more - // results after the limit. - NO_MORE_RESULTS = 3; // The query has been exhausted. - } - - // The result type for every entity in entityResults. - required EntityResult.ResultType entity_result_type = 1; - // The results for this batch. - repeated EntityResult entity_result = 2; - - // A cursor that points to the position after the last result in the batch. - // May be absent. - optional bytes /* serialized QueryCursor */ end_cursor = 4; - - // The state of the query after the current batch. - required MoreResultsType more_results = 5; - - // The number of results skipped because of Query.offset. - optional int32 skipped_results = 6; -} - -// A set of changes to apply. -// -// No entity in this message may have a reserved property name, -// not even a property in an entity in a value. -// No value in this message may have meaning 18, -// not even a value in an entity in another value. -// -// If entities with duplicate keys are present, an arbitrary choice will -// be made as to which is written. -message Mutation { - // Entities to upsert. - // Each upserted entity's key must have a complete path and - // must not be reserved/read-only. - repeated Entity upsert = 1; - // Entities to update. - // Each updated entity's key must have a complete path and - // must not be reserved/read-only. - repeated Entity update = 2; - // Entities to insert. - // Each inserted entity's key must have a complete path and - // must not be reserved/read-only. - repeated Entity insert = 3; - // Insert entities with a newly allocated ID. - // Each inserted entity's key must omit the final identifier in its path and - // must not be reserved/read-only. - repeated Entity insert_auto_id = 4; - // Keys of entities to delete. - // Each key must have a complete key path and must not be reserved/read-only. - repeated Key delete = 5; - // Ignore a user specified read-only period. Optional. - optional bool force = 6; -} - -// The result of applying a mutation. -message MutationResult { - // Number of index writes. - required int32 index_updates = 1; - // Keys for insertAutoId entities. One per entity from the - // request, in the same order. - repeated Key insert_auto_id_key = 2; -} - -// Options shared by read requests. -message ReadOptions { - enum ReadConsistency { - DEFAULT = 0; - STRONG = 1; - EVENTUAL = 2; - } - - // The read consistency to use. - // Cannot be set when transaction is set. - // Lookup and ancestor queries default to STRONG, global queries default to - // EVENTUAL and cannot be set to STRONG. - optional ReadConsistency read_consistency = 1 [default=DEFAULT]; - - // The transaction to use. Optional. - optional bytes /* serialized Transaction */ transaction = 2; -} - -// The request for Lookup. -message LookupRequest { - - // Options for this lookup request. Optional. - optional ReadOptions read_options = 1; - // Keys of entities to look up from the datastore. - repeated Key key = 3; -} - -// The response for Lookup. -message LookupResponse { - - // The order of results in these fields is undefined and has no relation to - // the order of the keys in the input. - - // Entities found as ResultType.FULL entities. - repeated EntityResult found = 1; - - // Entities not found as ResultType.KEY_ONLY entities. - repeated EntityResult missing = 2; - - // A list of keys that were not looked up due to resource constraints. - repeated Key deferred = 3; -} - - -// The request for RunQuery. -message RunQueryRequest { - - // The options for this query. - optional ReadOptions read_options = 1; - - // Entities are partitioned into subsets, identified by a dataset (usually - // implicitly specified by the project) and namespace ID. Queries are scoped - // to a single partition. - // This partition ID is normalized with the standard default context - // partition ID, but all other partition IDs in RunQueryRequest are - // normalized with this partition ID as the context partition ID. - optional PartitionId partition_id = 2; - - // The query to run. - // Either this field or field gql_query must be set, but not both. - optional Query query = 3; - // The GQL query to run. - // Either this field or field query must be set, but not both. - optional GqlQuery gql_query = 7; -} - -// The response for RunQuery. -message RunQueryResponse { - - // A batch of query results (always present). - optional QueryResultBatch batch = 1; - -} - -// The request for BeginTransaction. -message BeginTransactionRequest { - - enum IsolationLevel { - SNAPSHOT = 0; // Read from a consistent snapshot. Concurrent transactions - // conflict if their mutations conflict. For example: - // Read(A),Write(B) may not conflict with Read(B),Write(A), - // but Read(B),Write(B) does conflict with Read(B),Write(B). - SERIALIZABLE = 1; // Read from a consistent snapshot. Concurrent - // transactions conflict if they cannot be serialized. - // For example Read(A),Write(B) does conflict with - // Read(B),Write(A) but Read(A) may not conflict with - // Write(A). - } - - // The transaction isolation level. - optional IsolationLevel isolation_level = 1 [default=SNAPSHOT]; -} - -// The response for BeginTransaction. -message BeginTransactionResponse { - - // The transaction identifier (always present). - optional bytes /* serialized Transaction */ transaction = 1; -} - -// The request for Rollback. -message RollbackRequest { - - // The transaction identifier, returned by a call to - // beginTransaction. - required bytes /* serialized Transaction */ transaction = 1; -} - -// The response for Rollback. -message RollbackResponse { -// Empty -} - -// The request for Commit. -message CommitRequest { - - enum Mode { - TRANSACTIONAL = 1; - NON_TRANSACTIONAL = 2; - } - - // The transaction identifier, returned by a call to - // beginTransaction. Must be set when mode is TRANSACTIONAL. - optional bytes /* serialized Transaction */ transaction = 1; - // The mutation to perform. Optional. - optional Mutation mutation = 2; - // The type of commit to perform. Either TRANSACTIONAL or NON_TRANSACTIONAL. - optional Mode mode = 5 [default=TRANSACTIONAL]; -} - -// The response for Commit. -message CommitResponse { - - // The result of performing the mutation (if any). - optional MutationResult mutation_result = 1; -} - -// The request for AllocateIds. -message AllocateIdsRequest { - - // A list of keys with incomplete key paths to allocate IDs for. - // No key may be reserved/read-only. - repeated Key key = 1; -} - -// The response for AllocateIds. -message AllocateIdsResponse { - - // The keys specified in the request (in the same order), each with - // its key path completed with a newly allocated ID. - repeated Key key = 1; -} - -// Each rpc normalizes the partition IDs of the keys in its input entities, -// and always returns entities with keys with normalized partition IDs. -// (Note that applies to all entities, including entities in values.) -service DatastoreService { - // Look up some entities by key. - rpc Lookup(LookupRequest) returns (LookupResponse) { - }; - // Query for entities. - rpc RunQuery(RunQueryRequest) returns (RunQueryResponse) { - }; - // Begin a new transaction. - rpc BeginTransaction(BeginTransactionRequest) returns (BeginTransactionResponse) { - }; - // Commit a transaction, optionally creating, deleting or modifying some - // entities. - rpc Commit(CommitRequest) returns (CommitResponse) { - }; - // Roll back a transaction. - rpc Rollback(RollbackRequest) returns (RollbackResponse) { - }; - // Allocate IDs for incomplete keys (useful for referencing an entity before - // it is inserted). - rpc AllocateIds(AllocateIdsRequest) returns (AllocateIdsResponse) { - }; -} diff --git a/Godeps/_workspace/src/google.golang.org/cloud/internal/testutil/context.go b/Godeps/_workspace/src/google.golang.org/cloud/internal/testutil/context.go deleted file mode 100644 index aafd68387..000000000 --- a/Godeps/_workspace/src/google.golang.org/cloud/internal/testutil/context.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2014 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 testutil contains helper functions for writing tests. -package testutil - -import ( - "io/ioutil" - "log" - "net/http" - "os" - - "golang.org/x/net/context" - "golang.org/x/oauth2" - "golang.org/x/oauth2/google" - "google.golang.org/cloud" -) - -const ( - envProjID = "GCLOUD_TESTS_GOLANG_PROJECT_ID" - envPrivateKey = "GCLOUD_TESTS_GOLANG_KEY" -) - -func Context(scopes ...string) context.Context { - key, projID := os.Getenv(envPrivateKey), os.Getenv(envProjID) - if key == "" || projID == "" { - log.Fatal("GCLOUD_TESTS_GOLANG_KEY and GCLOUD_TESTS_GOLANG_PROJECT_ID must be set. See CONTRIBUTING.md for details.") - } - jsonKey, err := ioutil.ReadFile(key) - if err != nil { - log.Fatalf("Cannot read the JSON key file, err: %v", err) - } - conf, err := google.JWTConfigFromJSON(jsonKey, scopes...) - if err != nil { - log.Fatal(err) - } - return cloud.NewContext(projID, conf.Client(oauth2.NoContext)) -} - -func NoAuthContext() context.Context { - projID := os.Getenv(envProjID) - if projID == "" { - log.Fatal("GCLOUD_TESTS_GOLANG_PROJECT_ID must be set. See CONTRIBUTING.md for details.") - } - return cloud.NewContext(projID, &http.Client{Transport: http.DefaultTransport}) -} diff --git a/Godeps/_workspace/src/gopkg.in/yaml.v2/decode_test.go b/Godeps/_workspace/src/gopkg.in/yaml.v2/decode_test.go deleted file mode 100644 index 90ffcc017..000000000 --- a/Godeps/_workspace/src/gopkg.in/yaml.v2/decode_test.go +++ /dev/null @@ -1,902 +0,0 @@ -package yaml_test - -import ( - "errors" - . "gopkg.in/check.v1" - "gopkg.in/yaml.v2" - "math" - "net" - "reflect" - "strings" - "time" -) - -var unmarshalIntTest = 123 - -var unmarshalTests = []struct { - data string - value interface{} -}{ - { - "", - &struct{}{}, - }, { - "{}", &struct{}{}, - }, { - "v: hi", - map[string]string{"v": "hi"}, - }, { - "v: hi", map[string]interface{}{"v": "hi"}, - }, { - "v: true", - map[string]string{"v": "true"}, - }, { - "v: true", - map[string]interface{}{"v": true}, - }, { - "v: 10", - map[string]interface{}{"v": 10}, - }, { - "v: 0b10", - map[string]interface{}{"v": 2}, - }, { - "v: 0xA", - map[string]interface{}{"v": 10}, - }, { - "v: 4294967296", - map[string]int64{"v": 4294967296}, - }, { - "v: 0.1", - map[string]interface{}{"v": 0.1}, - }, { - "v: .1", - map[string]interface{}{"v": 0.1}, - }, { - "v: .Inf", - map[string]interface{}{"v": math.Inf(+1)}, - }, { - "v: -.Inf", - map[string]interface{}{"v": math.Inf(-1)}, - }, { - "v: -10", - map[string]interface{}{"v": -10}, - }, { - "v: -.1", - map[string]interface{}{"v": -0.1}, - }, - - // Simple values. - { - "123", - &unmarshalIntTest, - }, - - // Floats from spec - { - "canonical: 6.8523e+5", - map[string]interface{}{"canonical": 6.8523e+5}, - }, { - "expo: 685.230_15e+03", - map[string]interface{}{"expo": 685.23015e+03}, - }, { - "fixed: 685_230.15", - map[string]interface{}{"fixed": 685230.15}, - }, { - "neginf: -.inf", - map[string]interface{}{"neginf": math.Inf(-1)}, - }, { - "fixed: 685_230.15", - map[string]float64{"fixed": 685230.15}, - }, - //{"sexa: 190:20:30.15", map[string]interface{}{"sexa": 0}}, // Unsupported - //{"notanum: .NaN", map[string]interface{}{"notanum": math.NaN()}}, // Equality of NaN fails. - - // Bools from spec - { - "canonical: y", - map[string]interface{}{"canonical": true}, - }, { - "answer: NO", - map[string]interface{}{"answer": false}, - }, { - "logical: True", - map[string]interface{}{"logical": true}, - }, { - "option: on", - map[string]interface{}{"option": true}, - }, { - "option: on", - map[string]bool{"option": true}, - }, - // Ints from spec - { - "canonical: 685230", - map[string]interface{}{"canonical": 685230}, - }, { - "decimal: +685_230", - map[string]interface{}{"decimal": 685230}, - }, { - "octal: 02472256", - map[string]interface{}{"octal": 685230}, - }, { - "hexa: 0x_0A_74_AE", - map[string]interface{}{"hexa": 685230}, - }, { - "bin: 0b1010_0111_0100_1010_1110", - map[string]interface{}{"bin": 685230}, - }, { - "bin: -0b101010", - map[string]interface{}{"bin": -42}, - }, { - "decimal: +685_230", - map[string]int{"decimal": 685230}, - }, - - //{"sexa: 190:20:30", map[string]interface{}{"sexa": 0}}, // Unsupported - - // Nulls from spec - { - "empty:", - map[string]interface{}{"empty": nil}, - }, { - "canonical: ~", - map[string]interface{}{"canonical": nil}, - }, { - "english: null", - map[string]interface{}{"english": nil}, - }, { - "~: null key", - map[interface{}]string{nil: "null key"}, - }, { - "empty:", - map[string]*bool{"empty": nil}, - }, - - // Flow sequence - { - "seq: [A,B]", - map[string]interface{}{"seq": []interface{}{"A", "B"}}, - }, { - "seq: [A,B,C,]", - map[string][]string{"seq": []string{"A", "B", "C"}}, - }, { - "seq: [A,1,C]", - map[string][]string{"seq": []string{"A", "1", "C"}}, - }, { - "seq: [A,1,C]", - map[string][]int{"seq": []int{1}}, - }, { - "seq: [A,1,C]", - map[string]interface{}{"seq": []interface{}{"A", 1, "C"}}, - }, - // Block sequence - { - "seq:\n - A\n - B", - map[string]interface{}{"seq": []interface{}{"A", "B"}}, - }, { - "seq:\n - A\n - B\n - C", - map[string][]string{"seq": []string{"A", "B", "C"}}, - }, { - "seq:\n - A\n - 1\n - C", - map[string][]string{"seq": []string{"A", "1", "C"}}, - }, { - "seq:\n - A\n - 1\n - C", - map[string][]int{"seq": []int{1}}, - }, { - "seq:\n - A\n - 1\n - C", - map[string]interface{}{"seq": []interface{}{"A", 1, "C"}}, - }, - - // Literal block scalar - { - "scalar: | # Comment\n\n literal\n\n \ttext\n\n", - map[string]string{"scalar": "\nliteral\n\n\ttext\n"}, - }, - - // Folded block scalar - { - "scalar: > # Comment\n\n folded\n line\n \n next\n line\n * one\n * two\n\n last\n line\n\n", - map[string]string{"scalar": "\nfolded line\nnext line\n * one\n * two\n\nlast line\n"}, - }, - - // Map inside interface with no type hints. - { - "a: {b: c}", - map[interface{}]interface{}{"a": map[interface{}]interface{}{"b": "c"}}, - }, - - // Structs and type conversions. - { - "hello: world", - &struct{ Hello string }{"world"}, - }, { - "a: {b: c}", - &struct{ A struct{ B string } }{struct{ B string }{"c"}}, - }, { - "a: {b: c}", - &struct{ A *struct{ B string } }{&struct{ B string }{"c"}}, - }, { - "a: {b: c}", - &struct{ A map[string]string }{map[string]string{"b": "c"}}, - }, { - "a: {b: c}", - &struct{ A *map[string]string }{&map[string]string{"b": "c"}}, - }, { - "a:", - &struct{ A map[string]string }{}, - }, { - "a: 1", - &struct{ A int }{1}, - }, { - "a: 1", - &struct{ A float64 }{1}, - }, { - "a: 1.0", - &struct{ A int }{1}, - }, { - "a: 1.0", - &struct{ A uint }{1}, - }, { - "a: [1, 2]", - &struct{ A []int }{[]int{1, 2}}, - }, { - "a: 1", - &struct{ B int }{0}, - }, { - "a: 1", - &struct { - B int "a" - }{1}, - }, { - "a: y", - &struct{ A bool }{true}, - }, - - // Some cross type conversions - { - "v: 42", - map[string]uint{"v": 42}, - }, { - "v: -42", - map[string]uint{}, - }, { - "v: 4294967296", - map[string]uint64{"v": 4294967296}, - }, { - "v: -4294967296", - map[string]uint64{}, - }, - - // int - { - "int_max: 2147483647", - map[string]int{"int_max": math.MaxInt32}, - }, - { - "int_min: -2147483648", - map[string]int{"int_min": math.MinInt32}, - }, - { - "int_overflow: 9223372036854775808", // math.MaxInt64 + 1 - map[string]int{}, - }, - - // int64 - { - "int64_max: 9223372036854775807", - map[string]int64{"int64_max": math.MaxInt64}, - }, - { - "int64_max_base2: 0b111111111111111111111111111111111111111111111111111111111111111", - map[string]int64{"int64_max_base2": math.MaxInt64}, - }, - { - "int64_min: -9223372036854775808", - map[string]int64{"int64_min": math.MinInt64}, - }, - { - "int64_neg_base2: -0b111111111111111111111111111111111111111111111111111111111111111", - map[string]int64{"int64_neg_base2": -math.MaxInt64}, - }, - { - "int64_overflow: 9223372036854775808", // math.MaxInt64 + 1 - map[string]int64{}, - }, - - // uint - { - "uint_min: 0", - map[string]uint{"uint_min": 0}, - }, - { - "uint_max: 4294967295", - map[string]uint{"uint_max": math.MaxUint32}, - }, - { - "uint_underflow: -1", - map[string]uint{}, - }, - - // uint64 - { - "uint64_min: 0", - map[string]uint{"uint64_min": 0}, - }, - { - "uint64_max: 18446744073709551615", - map[string]uint64{"uint64_max": math.MaxUint64}, - }, - { - "uint64_max_base2: 0b1111111111111111111111111111111111111111111111111111111111111111", - map[string]uint64{"uint64_max_base2": math.MaxUint64}, - }, - { - "uint64_maxint64: 9223372036854775807", - map[string]uint64{"uint64_maxint64": math.MaxInt64}, - }, - { - "uint64_underflow: -1", - map[string]uint64{}, - }, - - // float32 - { - "float32_max: 3.40282346638528859811704183484516925440e+38", - map[string]float32{"float32_max": math.MaxFloat32}, - }, - { - "float32_nonzero: 1.401298464324817070923729583289916131280e-45", - map[string]float32{"float32_nonzero": math.SmallestNonzeroFloat32}, - }, - { - "float32_maxuint64: 18446744073709551615", - map[string]float32{"float32_maxuint64": float32(math.MaxUint64)}, - }, - { - "float32_maxuint64+1: 18446744073709551616", - map[string]float32{"float32_maxuint64+1": float32(math.MaxUint64 + 1)}, - }, - - // float64 - { - "float64_max: 1.797693134862315708145274237317043567981e+308", - map[string]float64{"float64_max": math.MaxFloat64}, - }, - { - "float64_nonzero: 4.940656458412465441765687928682213723651e-324", - map[string]float64{"float64_nonzero": math.SmallestNonzeroFloat64}, - }, - { - "float64_maxuint64: 18446744073709551615", - map[string]float64{"float64_maxuint64": float64(math.MaxUint64)}, - }, - { - "float64_maxuint64+1: 18446744073709551616", - map[string]float64{"float64_maxuint64+1": float64(math.MaxUint64 + 1)}, - }, - - // Overflow cases. - { - "v: 4294967297", - map[string]int32{}, - }, { - "v: 128", - map[string]int8{}, - }, - - // Quoted values. - { - "'1': '\"2\"'", - map[interface{}]interface{}{"1": "\"2\""}, - }, { - "v:\n- A\n- 'B\n\n C'\n", - map[string][]string{"v": []string{"A", "B\nC"}}, - }, - - // Explicit tags. - { - "v: !!float '1.1'", - map[string]interface{}{"v": 1.1}, - }, { - "v: !!null ''", - map[string]interface{}{"v": nil}, - }, { - "%TAG !y! tag:yaml.org,2002:\n---\nv: !y!int '1'", - map[string]interface{}{"v": 1}, - }, - - // Anchors and aliases. - { - "a: &x 1\nb: &y 2\nc: *x\nd: *y\n", - &struct{ A, B, C, D int }{1, 2, 1, 2}, - }, { - "a: &a {c: 1}\nb: *a", - &struct { - A, B struct { - C int - } - }{struct{ C int }{1}, struct{ C int }{1}}, - }, { - "a: &a [1, 2]\nb: *a", - &struct{ B []int }{[]int{1, 2}}, - }, - - // Bug #1133337 - { - "foo: ''", - map[string]*string{"foo": new(string)}, - }, { - "foo: null", - map[string]string{"foo": ""}, - }, { - "foo: null", - map[string]interface{}{"foo": nil}, - }, - - // Ignored field - { - "a: 1\nb: 2\n", - &struct { - A int - B int "-" - }{1, 0}, - }, - - // Bug #1191981 - { - "" + - "%YAML 1.1\n" + - "--- !!str\n" + - `"Generic line break (no glyph)\n\` + "\n" + - ` Generic line break (glyphed)\n\` + "\n" + - ` Line separator\u2028\` + "\n" + - ` Paragraph separator\u2029"` + "\n", - "" + - "Generic line break (no glyph)\n" + - "Generic line break (glyphed)\n" + - "Line separator\u2028Paragraph separator\u2029", - }, - - // Struct inlining - { - "a: 1\nb: 2\nc: 3\n", - &struct { - A int - C inlineB `yaml:",inline"` - }{1, inlineB{2, inlineC{3}}}, - }, - - // bug 1243827 - { - "a: -b_c", - map[string]interface{}{"a": "-b_c"}, - }, - { - "a: +b_c", - map[string]interface{}{"a": "+b_c"}, - }, - { - "a: 50cent_of_dollar", - map[string]interface{}{"a": "50cent_of_dollar"}, - }, - - // Duration - { - "a: 3s", - map[string]time.Duration{"a": 3 * time.Second}, - }, - - // Issue #24. - { - "a: ", - map[string]string{"a": ""}, - }, - - // Base 60 floats are obsolete and unsupported. - { - "a: 1:1\n", - map[string]string{"a": "1:1"}, - }, - - // Binary data. - { - "a: !!binary gIGC\n", - map[string]string{"a": "\x80\x81\x82"}, - }, { - "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", - map[string]string{"a": strings.Repeat("\x90", 54)}, - }, { - "a: !!binary |\n " + strings.Repeat("A", 70) + "\n ==\n", - map[string]string{"a": strings.Repeat("\x00", 52)}, - }, - - // Ordered maps. - { - "{b: 2, a: 1, d: 4, c: 3, sub: {e: 5}}", - &yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}}, - }, - - // Issue #39. - { - "a:\n b:\n c: d\n", - map[string]struct{ B interface{} }{"a": {map[interface{}]interface{}{"c": "d"}}}, - }, - - // Custom map type. - { - "a: {b: c}", - M{"a": M{"b": "c"}}, - }, - - // Support encoding.TextUnmarshaler. - { - "a: 1.2.3.4\n", - map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)}, - }, -} - -type M map[interface{}]interface{} - -type inlineB struct { - B int - inlineC `yaml:",inline"` -} - -type inlineC struct { - C int -} - -func (s *S) TestUnmarshal(c *C) { - for _, item := range unmarshalTests { - t := reflect.ValueOf(item.value).Type() - var value interface{} - switch t.Kind() { - case reflect.Map: - value = reflect.MakeMap(t).Interface() - case reflect.String: - value = reflect.New(t).Interface() - case reflect.Ptr: - value = reflect.New(t.Elem()).Interface() - default: - c.Fatalf("missing case for %s", t) - } - err := yaml.Unmarshal([]byte(item.data), value) - if _, ok := err.(*yaml.TypeError); !ok { - c.Assert(err, IsNil) - } - if t.Kind() == reflect.String { - c.Assert(*value.(*string), Equals, item.value) - } else { - c.Assert(value, DeepEquals, item.value) - } - } -} - -func (s *S) TestUnmarshalNaN(c *C) { - value := map[string]interface{}{} - err := yaml.Unmarshal([]byte("notanum: .NaN"), &value) - c.Assert(err, IsNil) - c.Assert(math.IsNaN(value["notanum"].(float64)), Equals, true) -} - -var unmarshalErrorTests = []struct { - data, error string -}{ - {"v: !!float 'error'", "yaml: cannot decode !!str `error` as a !!float"}, - {"v: [A,", "yaml: line 1: did not find expected node content"}, - {"v:\n- [A,", "yaml: line 2: did not find expected node content"}, - {"a: *b\n", "yaml: unknown anchor 'b' referenced"}, - {"a: &a\n b: *a\n", "yaml: anchor 'a' value contains itself"}, - {"value: -", "yaml: block sequence entries are not allowed in this context"}, - {"a: !!binary ==", "yaml: !!binary value contains invalid base64 data"}, - {"{[.]}", `yaml: invalid map key: \[\]interface \{\}\{"\."\}`}, - {"{{.}}", `yaml: invalid map key: map\[interface\ \{\}\]interface \{\}\{".":interface \{\}\(nil\)\}`}, -} - -func (s *S) TestUnmarshalErrors(c *C) { - for _, item := range unmarshalErrorTests { - var value interface{} - err := yaml.Unmarshal([]byte(item.data), &value) - c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value)) - } -} - -var unmarshalerTests = []struct { - data, tag string - value interface{} -}{ - {"_: {hi: there}", "!!map", map[interface{}]interface{}{"hi": "there"}}, - {"_: [1,A]", "!!seq", []interface{}{1, "A"}}, - {"_: 10", "!!int", 10}, - {"_: null", "!!null", nil}, - {`_: BAR!`, "!!str", "BAR!"}, - {`_: "BAR!"`, "!!str", "BAR!"}, - {"_: !!foo 'BAR!'", "!!foo", "BAR!"}, -} - -var unmarshalerResult = map[int]error{} - -type unmarshalerType struct { - value interface{} -} - -func (o *unmarshalerType) UnmarshalYAML(unmarshal func(v interface{}) error) error { - if err := unmarshal(&o.value); err != nil { - return err - } - if i, ok := o.value.(int); ok { - if result, ok := unmarshalerResult[i]; ok { - return result - } - } - return nil -} - -type unmarshalerPointer struct { - Field *unmarshalerType "_" -} - -type unmarshalerValue struct { - Field unmarshalerType "_" -} - -func (s *S) TestUnmarshalerPointerField(c *C) { - for _, item := range unmarshalerTests { - obj := &unmarshalerPointer{} - err := yaml.Unmarshal([]byte(item.data), obj) - c.Assert(err, IsNil) - if item.value == nil { - c.Assert(obj.Field, IsNil) - } else { - c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value)) - c.Assert(obj.Field.value, DeepEquals, item.value) - } - } -} - -func (s *S) TestUnmarshalerValueField(c *C) { - for _, item := range unmarshalerTests { - obj := &unmarshalerValue{} - err := yaml.Unmarshal([]byte(item.data), obj) - c.Assert(err, IsNil) - c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value)) - c.Assert(obj.Field.value, DeepEquals, item.value) - } -} - -func (s *S) TestUnmarshalerWholeDocument(c *C) { - obj := &unmarshalerType{} - err := yaml.Unmarshal([]byte(unmarshalerTests[0].data), obj) - c.Assert(err, IsNil) - value, ok := obj.value.(map[interface{}]interface{}) - c.Assert(ok, Equals, true, Commentf("value: %#v", obj.value)) - c.Assert(value["_"], DeepEquals, unmarshalerTests[0].value) -} - -func (s *S) TestUnmarshalerTypeError(c *C) { - unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}} - unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}} - defer func() { - delete(unmarshalerResult, 2) - delete(unmarshalerResult, 4) - }() - - type T struct { - Before int - After int - M map[string]*unmarshalerType - } - var v T - data := `{before: A, m: {abc: 1, def: 2, ghi: 3, jkl: 4}, after: B}` - err := yaml.Unmarshal([]byte(data), &v) - c.Assert(err, ErrorMatches, ""+ - "yaml: unmarshal errors:\n"+ - " line 1: cannot unmarshal !!str `A` into int\n"+ - " foo\n"+ - " bar\n"+ - " line 1: cannot unmarshal !!str `B` into int") - c.Assert(v.M["abc"], NotNil) - c.Assert(v.M["def"], IsNil) - c.Assert(v.M["ghi"], NotNil) - c.Assert(v.M["jkl"], IsNil) - - c.Assert(v.M["abc"].value, Equals, 1) - c.Assert(v.M["ghi"].value, Equals, 3) -} - -type proxyTypeError struct{} - -func (v *proxyTypeError) UnmarshalYAML(unmarshal func(interface{}) error) error { - var s string - var a int32 - var b int64 - if err := unmarshal(&s); err != nil { - panic(err) - } - if s == "a" { - if err := unmarshal(&b); err == nil { - panic("should have failed") - } - return unmarshal(&a) - } - if err := unmarshal(&a); err == nil { - panic("should have failed") - } - return unmarshal(&b) -} - -func (s *S) TestUnmarshalerTypeErrorProxying(c *C) { - type T struct { - Before int - After int - M map[string]*proxyTypeError - } - var v T - data := `{before: A, m: {abc: a, def: b}, after: B}` - err := yaml.Unmarshal([]byte(data), &v) - c.Assert(err, ErrorMatches, ""+ - "yaml: unmarshal errors:\n"+ - " line 1: cannot unmarshal !!str `A` into int\n"+ - " line 1: cannot unmarshal !!str `a` into int32\n"+ - " line 1: cannot unmarshal !!str `b` into int64\n"+ - " line 1: cannot unmarshal !!str `B` into int") -} - -type failingUnmarshaler struct{} - -var failingErr = errors.New("failingErr") - -func (ft *failingUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error { - return failingErr -} - -func (s *S) TestUnmarshalerError(c *C) { - err := yaml.Unmarshal([]byte("a: b"), &failingUnmarshaler{}) - c.Assert(err, Equals, failingErr) -} - -// From http://yaml.org/type/merge.html -var mergeTests = ` -anchors: - list: - - &CENTER { "x": 1, "y": 2 } - - &LEFT { "x": 0, "y": 2 } - - &BIG { "r": 10 } - - &SMALL { "r": 1 } - -# All the following maps are equal: - -plain: - # Explicit keys - "x": 1 - "y": 2 - "r": 10 - label: center/big - -mergeOne: - # Merge one map - << : *CENTER - "r": 10 - label: center/big - -mergeMultiple: - # Merge multiple maps - << : [ *CENTER, *BIG ] - label: center/big - -override: - # Override - << : [ *BIG, *LEFT, *SMALL ] - "x": 1 - label: center/big - -shortTag: - # Explicit short merge tag - !!merge "<<" : [ *CENTER, *BIG ] - label: center/big - -longTag: - # Explicit merge long tag - ! "<<" : [ *CENTER, *BIG ] - label: center/big - -inlineMap: - # Inlined map - << : {"x": 1, "y": 2, "r": 10} - label: center/big - -inlineSequenceMap: - # Inlined map in sequence - << : [ *CENTER, {"r": 10} ] - label: center/big -` - -func (s *S) TestMerge(c *C) { - var want = map[interface{}]interface{}{ - "x": 1, - "y": 2, - "r": 10, - "label": "center/big", - } - - var m map[interface{}]interface{} - err := yaml.Unmarshal([]byte(mergeTests), &m) - c.Assert(err, IsNil) - for name, test := range m { - if name == "anchors" { - continue - } - c.Assert(test, DeepEquals, want, Commentf("test %q failed", name)) - } -} - -func (s *S) TestMergeStruct(c *C) { - type Data struct { - X, Y, R int - Label string - } - want := Data{1, 2, 10, "center/big"} - - var m map[string]Data - err := yaml.Unmarshal([]byte(mergeTests), &m) - c.Assert(err, IsNil) - for name, test := range m { - if name == "anchors" { - continue - } - c.Assert(test, Equals, want, Commentf("test %q failed", name)) - } -} - -var unmarshalNullTests = []func() interface{}{ - func() interface{} { var v interface{}; v = "v"; return &v }, - func() interface{} { var s = "s"; return &s }, - func() interface{} { var s = "s"; sptr := &s; return &sptr }, - func() interface{} { var i = 1; return &i }, - func() interface{} { var i = 1; iptr := &i; return &iptr }, - func() interface{} { m := map[string]int{"s": 1}; return &m }, - func() interface{} { m := map[string]int{"s": 1}; return m }, -} - -func (s *S) TestUnmarshalNull(c *C) { - for _, test := range unmarshalNullTests { - item := test() - zero := reflect.Zero(reflect.TypeOf(item).Elem()).Interface() - err := yaml.Unmarshal([]byte("null"), item) - c.Assert(err, IsNil) - if reflect.TypeOf(item).Kind() == reflect.Map { - c.Assert(reflect.ValueOf(item).Interface(), DeepEquals, reflect.MakeMap(reflect.TypeOf(item)).Interface()) - } else { - c.Assert(reflect.ValueOf(item).Elem().Interface(), DeepEquals, zero) - } - } -} - -//var data []byte -//func init() { -// var err error -// data, err = ioutil.ReadFile("/tmp/file.yaml") -// if err != nil { -// panic(err) -// } -//} -// -//func (s *S) BenchmarkUnmarshal(c *C) { -// var err error -// for i := 0; i < c.N; i++ { -// var v map[string]interface{} -// err = yaml.Unmarshal(data, &v) -// } -// if err != nil { -// panic(err) -// } -//} -// -//func (s *S) BenchmarkMarshal(c *C) { -// var v map[string]interface{} -// yaml.Unmarshal(data, &v) -// c.ResetTimer() -// for i := 0; i < c.N; i++ { -// yaml.Marshal(&v) -// } -//} diff --git a/Godeps/_workspace/src/gopkg.in/yaml.v2/encode_test.go b/Godeps/_workspace/src/gopkg.in/yaml.v2/encode_test.go deleted file mode 100644 index cdbf64a9d..000000000 --- a/Godeps/_workspace/src/gopkg.in/yaml.v2/encode_test.go +++ /dev/null @@ -1,434 +0,0 @@ -package yaml_test - -import ( - "fmt" - "math" - "strconv" - "strings" - "time" - - . "gopkg.in/check.v1" - "gopkg.in/yaml.v2" - "net" -) - -var marshalIntTest = 123 - -var marshalTests = []struct { - value interface{} - data string -}{ - { - nil, - "null\n", - }, { - &struct{}{}, - "{}\n", - }, { - map[string]string{"v": "hi"}, - "v: hi\n", - }, { - map[string]interface{}{"v": "hi"}, - "v: hi\n", - }, { - map[string]string{"v": "true"}, - "v: \"true\"\n", - }, { - map[string]string{"v": "false"}, - "v: \"false\"\n", - }, { - map[string]interface{}{"v": true}, - "v: true\n", - }, { - map[string]interface{}{"v": false}, - "v: false\n", - }, { - map[string]interface{}{"v": 10}, - "v: 10\n", - }, { - map[string]interface{}{"v": -10}, - "v: -10\n", - }, { - map[string]uint{"v": 42}, - "v: 42\n", - }, { - map[string]interface{}{"v": int64(4294967296)}, - "v: 4294967296\n", - }, { - map[string]int64{"v": int64(4294967296)}, - "v: 4294967296\n", - }, { - map[string]uint64{"v": 4294967296}, - "v: 4294967296\n", - }, { - map[string]interface{}{"v": "10"}, - "v: \"10\"\n", - }, { - map[string]interface{}{"v": 0.1}, - "v: 0.1\n", - }, { - map[string]interface{}{"v": float64(0.1)}, - "v: 0.1\n", - }, { - map[string]interface{}{"v": -0.1}, - "v: -0.1\n", - }, { - map[string]interface{}{"v": math.Inf(+1)}, - "v: .inf\n", - }, { - map[string]interface{}{"v": math.Inf(-1)}, - "v: -.inf\n", - }, { - map[string]interface{}{"v": math.NaN()}, - "v: .nan\n", - }, { - map[string]interface{}{"v": nil}, - "v: null\n", - }, { - map[string]interface{}{"v": ""}, - "v: \"\"\n", - }, { - map[string][]string{"v": []string{"A", "B"}}, - "v:\n- A\n- B\n", - }, { - map[string][]string{"v": []string{"A", "B\nC"}}, - "v:\n- A\n- |-\n B\n C\n", - }, { - map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}}, - "v:\n- A\n- 1\n- B:\n - 2\n - 3\n", - }, { - map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}}, - "a:\n b: c\n", - }, { - map[string]interface{}{"a": "-"}, - "a: '-'\n", - }, - - // Simple values. - { - &marshalIntTest, - "123\n", - }, - - // Structures - { - &struct{ Hello string }{"world"}, - "hello: world\n", - }, { - &struct { - A struct { - B string - } - }{struct{ B string }{"c"}}, - "a:\n b: c\n", - }, { - &struct { - A *struct { - B string - } - }{&struct{ B string }{"c"}}, - "a:\n b: c\n", - }, { - &struct { - A *struct { - B string - } - }{}, - "a: null\n", - }, { - &struct{ A int }{1}, - "a: 1\n", - }, { - &struct{ A []int }{[]int{1, 2}}, - "a:\n- 1\n- 2\n", - }, { - &struct { - B int "a" - }{1}, - "a: 1\n", - }, { - &struct{ A bool }{true}, - "a: true\n", - }, - - // Conditional flag - { - &struct { - A int "a,omitempty" - B int "b,omitempty" - }{1, 0}, - "a: 1\n", - }, { - &struct { - A int "a,omitempty" - B int "b,omitempty" - }{0, 0}, - "{}\n", - }, { - &struct { - A *struct{ X int } "a,omitempty" - B int "b,omitempty" - }{nil, 0}, - "{}\n", - }, - - // Flow flag - { - &struct { - A []int "a,flow" - }{[]int{1, 2}}, - "a: [1, 2]\n", - }, { - &struct { - A map[string]string "a,flow" - }{map[string]string{"b": "c", "d": "e"}}, - "a: {b: c, d: e}\n", - }, { - &struct { - A struct { - B, D string - } "a,flow" - }{struct{ B, D string }{"c", "e"}}, - "a: {b: c, d: e}\n", - }, - - // Unexported field - { - &struct { - u int - A int - }{0, 1}, - "a: 1\n", - }, - - // Ignored field - { - &struct { - A int - B int "-" - }{1, 2}, - "a: 1\n", - }, - - // Struct inlining - { - &struct { - A int - C inlineB `yaml:",inline"` - }{1, inlineB{2, inlineC{3}}}, - "a: 1\nb: 2\nc: 3\n", - }, - - // Duration - { - map[string]time.Duration{"a": 3 * time.Second}, - "a: 3s\n", - }, - - // Issue #24: bug in map merging logic. - { - map[string]string{"a": ""}, - "a: \n", - }, - - // Issue #34: marshal unsupported base 60 floats quoted for compatibility - // with old YAML 1.1 parsers. - { - map[string]string{"a": "1:1"}, - "a: \"1:1\"\n", - }, - - // Binary data. - { - map[string]string{"a": "\x00"}, - "a: \"\\0\"\n", - }, { - map[string]string{"a": "\x80\x81\x82"}, - "a: !!binary gIGC\n", - }, { - map[string]string{"a": strings.Repeat("\x90", 54)}, - "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", - }, - - // Ordered maps. - { - &yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}}, - "b: 2\na: 1\nd: 4\nc: 3\nsub:\n e: 5\n", - }, - - // Encode unicode as utf-8 rather than in escaped form. - { - map[string]string{"a": "你好"}, - "a: 你好\n", - }, - - // Support encoding.TextMarshaler. - { - map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)}, - "a: 1.2.3.4\n", - }, -} - -func (s *S) TestMarshal(c *C) { - for _, item := range marshalTests { - data, err := yaml.Marshal(item.value) - c.Assert(err, IsNil) - c.Assert(string(data), Equals, item.data) - } -} - -var marshalErrorTests = []struct { - value interface{} - error string - panic string -}{{ - value: &struct { - B int - inlineB ",inline" - }{1, inlineB{2, inlineC{3}}}, - panic: `Duplicated key 'b' in struct struct \{ B int; .*`, -}} - -func (s *S) TestMarshalErrors(c *C) { - for _, item := range marshalErrorTests { - if item.panic != "" { - c.Assert(func() { yaml.Marshal(item.value) }, PanicMatches, item.panic) - } else { - _, err := yaml.Marshal(item.value) - c.Assert(err, ErrorMatches, item.error) - } - } -} - -func (s *S) TestMarshalTypeCache(c *C) { - var data []byte - var err error - func() { - type T struct{ A int } - data, err = yaml.Marshal(&T{}) - c.Assert(err, IsNil) - }() - func() { - type T struct{ B int } - data, err = yaml.Marshal(&T{}) - c.Assert(err, IsNil) - }() - c.Assert(string(data), Equals, "b: 0\n") -} - -var marshalerTests = []struct { - data string - value interface{} -}{ - {"_:\n hi: there\n", map[interface{}]interface{}{"hi": "there"}}, - {"_:\n- 1\n- A\n", []interface{}{1, "A"}}, - {"_: 10\n", 10}, - {"_: null\n", nil}, - {"_: BAR!\n", "BAR!"}, -} - -type marshalerType struct { - value interface{} -} - -func (o marshalerType) MarshalYAML() (interface{}, error) { - return o.value, nil -} - -type marshalerValue struct { - Field marshalerType "_" -} - -func (s *S) TestMarshaler(c *C) { - for _, item := range marshalerTests { - obj := &marshalerValue{} - obj.Field.value = item.value - data, err := yaml.Marshal(obj) - c.Assert(err, IsNil) - c.Assert(string(data), Equals, string(item.data)) - } -} - -func (s *S) TestMarshalerWholeDocument(c *C) { - obj := &marshalerType{} - obj.value = map[string]string{"hello": "world!"} - data, err := yaml.Marshal(obj) - c.Assert(err, IsNil) - c.Assert(string(data), Equals, "hello: world!\n") -} - -type failingMarshaler struct{} - -func (ft *failingMarshaler) MarshalYAML() (interface{}, error) { - return nil, failingErr -} - -func (s *S) TestMarshalerError(c *C) { - _, err := yaml.Marshal(&failingMarshaler{}) - c.Assert(err, Equals, failingErr) -} - -func (s *S) TestSortedOutput(c *C) { - order := []interface{}{ - false, - true, - 1, - uint(1), - 1.0, - 1.1, - 1.2, - 2, - uint(2), - 2.0, - 2.1, - "", - ".1", - ".2", - ".a", - "1", - "2", - "a!10", - "a/2", - "a/10", - "a~10", - "ab/1", - "b/1", - "b/01", - "b/2", - "b/02", - "b/3", - "b/03", - "b1", - "b01", - "b3", - "c2.10", - "c10.2", - "d1", - "d12", - "d12a", - } - m := make(map[interface{}]int) - for _, k := range order { - m[k] = 1 - } - data, err := yaml.Marshal(m) - c.Assert(err, IsNil) - out := "\n" + string(data) - last := 0 - for i, k := range order { - repr := fmt.Sprint(k) - if s, ok := k.(string); ok { - if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil { - repr = `"` + repr + `"` - } - } - index := strings.Index(out, "\n"+repr+":") - if index == -1 { - c.Fatalf("%#v is not in the output: %#v", k, out) - } - if index < last { - c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out) - } - last = index - } -} diff --git a/Godeps/_workspace/src/gopkg.in/yaml.v2/suite_test.go b/Godeps/_workspace/src/gopkg.in/yaml.v2/suite_test.go deleted file mode 100644 index c5cf1ed4f..000000000 --- a/Godeps/_workspace/src/gopkg.in/yaml.v2/suite_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package yaml_test - -import ( - . "gopkg.in/check.v1" - "testing" -) - -func Test(t *testing.T) { TestingT(t) } - -type S struct{} - -var _ = Suite(&S{}) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/LICENSE b/Godeps/_workspace/src/k8s.io/kubernetes/LICENSE new file mode 100644 index 000000000..6b4d837a4 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/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 2014 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. diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/OWNERS b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/OWNERS new file mode 100644 index 000000000..d28472e0f --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/OWNERS @@ -0,0 +1,6 @@ +assignees: + - bgrant0607 + - erictune + - lavalamp + - smarterclayton + - thockin diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/context_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/context_test.go deleted file mode 100644 index c384999ff..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/context_test.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright 2014 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 api_test - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" -) - -// TestNamespaceContext validates that a namespace can be get/set on a context object -func TestNamespaceContext(t *testing.T) { - ctx := api.NewDefaultContext() - result, ok := api.NamespaceFrom(ctx) - if !ok { - t.Errorf("Error getting namespace") - } - if api.NamespaceDefault != result { - t.Errorf("Expected: %v, Actual: %v", api.NamespaceDefault, result) - } - - ctx = api.NewContext() - result, ok = api.NamespaceFrom(ctx) - if ok { - t.Errorf("Should not be ok because there is no namespace on the context") - } -} - -// TestValidNamespace validates that namespace rules are enforced on a resource prior to create or update -func TestValidNamespace(t *testing.T) { - ctx := api.NewDefaultContext() - namespace, _ := api.NamespaceFrom(ctx) - resource := api.ReplicationController{} - if !api.ValidNamespace(ctx, &resource.ObjectMeta) { - t.Errorf("expected success") - } - if namespace != resource.Namespace { - t.Errorf("expected resource to have the default namespace assigned during validation") - } - resource = api.ReplicationController{ObjectMeta: api.ObjectMeta{Namespace: "other"}} - if api.ValidNamespace(ctx, &resource.ObjectMeta) { - t.Errorf("Expected error that resource and context errors do not match because resource has different namespace") - } - ctx = api.NewContext() - if api.ValidNamespace(ctx, &resource.ObjectMeta) { - t.Errorf("Expected error that resource and context errors do not match since context has no namespace") - } - - ctx = api.NewContext() - ns := api.NamespaceValue(ctx) - if ns != "" { - t.Errorf("Expected the empty string") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/conversion.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/conversion.go index 5599db346..e0605fa09 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/conversion.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/conversion.go @@ -41,6 +41,7 @@ func init() { Convert_unversioned_ListMeta_To_unversioned_ListMeta, Convert_intstr_IntOrString_To_intstr_IntOrString, Convert_unversioned_Time_To_unversioned_Time, + Convert_string_slice_To_unversioned_Time, Convert_string_To_labels_Selector, Convert_string_To_fields_Selector, Convert_bool_ref_To_bool, @@ -116,6 +117,16 @@ func Convert_unversioned_Time_To_unversioned_Time(in *unversioned.Time, out *unv *out = *in return nil } + +// Convert_string_slice_To_unversioned_Time allows converting a URL query parameter value +func Convert_string_slice_To_unversioned_Time(input *[]string, out *unversioned.Time, s conversion.Scope) error { + str := "" + if len(*input) > 0 { + str = (*input)[0] + } + return out.UnmarshalQueryParameter(str) +} + func Convert_string_To_labels_Selector(in *string, out *labels.Selector, s conversion.Scope) error { selector, err := labels.Parse(*in) if err != nil { diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/conversion_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/conversion_test.go deleted file mode 100644 index 827022270..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/conversion_test.go +++ /dev/null @@ -1,110 +0,0 @@ -/* -Copyright 2015 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 api_test - -import ( - "io/ioutil" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/runtime" -) - -func BenchmarkPodConversion(b *testing.B) { - data, err := ioutil.ReadFile("pod_example.json") - if err != nil { - b.Fatalf("Unexpected error while reading file: %v", err) - } - var pod api.Pod - if err := runtime.DecodeInto(testapi.Default.Codec(), data, &pod); err != nil { - b.Fatalf("Unexpected error decoding pod: %v", err) - } - - scheme := api.Scheme - var result *api.Pod - for i := 0; i < b.N; i++ { - versionedObj, err := scheme.ConvertToVersion(&pod, testapi.Default.GroupVersion().String()) - if err != nil { - b.Fatalf("Conversion error: %v", err) - } - obj, err := scheme.ConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion().String()) - if err != nil { - b.Fatalf("Conversion error: %v", err) - } - result = obj.(*api.Pod) - } - if !api.Semantic.DeepDerivative(pod, *result) { - b.Fatalf("Incorrect conversion: expected %v, got %v", pod, *result) - } -} - -func BenchmarkNodeConversion(b *testing.B) { - data, err := ioutil.ReadFile("node_example.json") - if err != nil { - b.Fatalf("Unexpected error while reading file: %v", err) - } - var node api.Node - if err := runtime.DecodeInto(testapi.Default.Codec(), data, &node); err != nil { - b.Fatalf("Unexpected error decoding node: %v", err) - } - - scheme := api.Scheme - var result *api.Node - for i := 0; i < b.N; i++ { - versionedObj, err := scheme.ConvertToVersion(&node, testapi.Default.GroupVersion().String()) - if err != nil { - b.Fatalf("Conversion error: %v", err) - } - obj, err := scheme.ConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion().String()) - if err != nil { - b.Fatalf("Conversion error: %v", err) - } - result = obj.(*api.Node) - } - if !api.Semantic.DeepDerivative(node, *result) { - b.Fatalf("Incorrect conversion: expected %v, got %v", node, *result) - } -} - -func BenchmarkReplicationControllerConversion(b *testing.B) { - data, err := ioutil.ReadFile("replication_controller_example.json") - if err != nil { - b.Fatalf("Unexpected error while reading file: %v", err) - } - var replicationController api.ReplicationController - if err := runtime.DecodeInto(testapi.Default.Codec(), data, &replicationController); err != nil { - b.Fatalf("Unexpected error decoding node: %v", err) - } - - scheme := api.Scheme - var result *api.ReplicationController - for i := 0; i < b.N; i++ { - versionedObj, err := scheme.ConvertToVersion(&replicationController, testapi.Default.GroupVersion().String()) - if err != nil { - b.Fatalf("Conversion error: %v", err) - } - obj, err := scheme.ConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion().String()) - if err != nil { - b.Fatalf("Conversion error: %v", err) - } - result = obj.(*api.ReplicationController) - } - if !api.Semantic.DeepDerivative(replicationController, *result) { - b.Fatalf("Incorrect conversion: expected %v, got %v", replicationController, *result) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/copy_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/copy_test.go deleted file mode 100644 index 194b5318a..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/copy_test.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright 2015 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 api_test - -import ( - "math/rand" - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - apitesting "k8s.io/kubernetes/pkg/api/testing" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/util" - - "github.com/google/gofuzz" -) - -func TestDeepCopyApiObjects(t *testing.T) { - for i := 0; i < *fuzzIters; i++ { - for _, version := range []unversioned.GroupVersion{testapi.Default.InternalGroupVersion(), *testapi.Default.GroupVersion()} { - f := apitesting.FuzzerFor(t, version, rand.NewSource(rand.Int63())) - for kind := range api.Scheme.KnownTypes(version) { - doDeepCopyTest(t, version.WithKind(kind), f) - } - } - } -} - -func doDeepCopyTest(t *testing.T, kind unversioned.GroupVersionKind, f *fuzz.Fuzzer) { - item, err := api.Scheme.New(kind) - if err != nil { - t.Fatalf("Could not create a %v: %s", kind, err) - } - f.Fuzz(item) - itemCopy, err := api.Scheme.DeepCopy(item) - if err != nil { - t.Errorf("Could not deep copy a %v: %s", kind, err) - return - } - - if !reflect.DeepEqual(item, itemCopy) { - t.Errorf("\nexpected: %#v\n\ngot: %#v\n\ndiff: %v", item, itemCopy, util.ObjectGoPrintSideBySide(item, itemCopy)) - } -} - -func TestDeepCopySingleType(t *testing.T) { - for i := 0; i < *fuzzIters; i++ { - for _, version := range []unversioned.GroupVersion{testapi.Default.InternalGroupVersion(), *testapi.Default.GroupVersion()} { - f := apitesting.FuzzerFor(t, version, rand.NewSource(rand.Int63())) - doDeepCopyTest(t, version.WithKind("Pod"), f) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/deep_copy_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/deep_copy_generated.go index ab938aac5..aa2712e10 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/deep_copy_generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/deep_copy_generated.go @@ -2400,6 +2400,7 @@ func DeepCopy_api_ReplicationControllerSpec(in ReplicationControllerSpec, out *R func DeepCopy_api_ReplicationControllerStatus(in ReplicationControllerStatus, out *ReplicationControllerStatus, c *conversion.Cloner) error { out.Replicas = in.Replicas + out.FullyLabeledReplicas = in.FullyLabeledReplicas out.ObservedGeneration = in.ObservedGeneration return nil } @@ -2455,6 +2456,15 @@ func DeepCopy_api_ResourceQuotaSpec(in ResourceQuotaSpec, out *ResourceQuotaSpec } else { out.Hard = nil } + if in.Scopes != nil { + in, out := in.Scopes, &out.Scopes + *out = make([]ResourceQuotaScope, len(in)) + for i := range in { + (*out)[i] = in[i] + } + } else { + out.Scopes = nil + } return nil } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/deep_copy_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/deep_copy_test.go deleted file mode 100644 index a251623a0..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/deep_copy_test.go +++ /dev/null @@ -1,95 +0,0 @@ -/* -Copyright 2015 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 api_test - -import ( - "io/ioutil" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/runtime" -) - -func BenchmarkPodCopy(b *testing.B) { - data, err := ioutil.ReadFile("pod_example.json") - if err != nil { - b.Fatalf("Unexpected error while reading file: %v", err) - } - var pod api.Pod - if err := runtime.DecodeInto(testapi.Default.Codec(), data, &pod); err != nil { - b.Fatalf("Unexpected error decoding pod: %v", err) - } - - var result *api.Pod - for i := 0; i < b.N; i++ { - obj, err := api.Scheme.DeepCopy(&pod) - if err != nil { - b.Fatalf("Unexpected error copying pod: %v", err) - } - result = obj.(*api.Pod) - } - if !api.Semantic.DeepEqual(pod, *result) { - b.Fatalf("Incorrect copy: expected %v, got %v", pod, *result) - } -} - -func BenchmarkNodeCopy(b *testing.B) { - data, err := ioutil.ReadFile("node_example.json") - if err != nil { - b.Fatalf("Unexpected error while reading file: %v", err) - } - var node api.Node - if err := runtime.DecodeInto(testapi.Default.Codec(), data, &node); err != nil { - b.Fatalf("Unexpected error decoding node: %v", err) - } - - var result *api.Node - for i := 0; i < b.N; i++ { - obj, err := api.Scheme.DeepCopy(&node) - if err != nil { - b.Fatalf("Unexpected error copying node: %v", err) - } - result = obj.(*api.Node) - } - if !api.Semantic.DeepEqual(node, *result) { - b.Fatalf("Incorrect copy: expected %v, got %v", node, *result) - } -} - -func BenchmarkReplicationControllerCopy(b *testing.B) { - data, err := ioutil.ReadFile("replication_controller_example.json") - if err != nil { - b.Fatalf("Unexpected error while reading file: %v", err) - } - var replicationController api.ReplicationController - if err := runtime.DecodeInto(testapi.Default.Codec(), data, &replicationController); err != nil { - b.Fatalf("Unexpected error decoding node: %v", err) - } - - var result *api.ReplicationController - for i := 0; i < b.N; i++ { - obj, err := api.Scheme.DeepCopy(&replicationController) - if err != nil { - b.Fatalf("Unexpected error copying replication controller: %v", err) - } - result = obj.(*api.ReplicationController) - } - if !api.Semantic.DeepEqual(replicationController, *result) { - b.Fatalf("Incorrect copy: expected %v, got %v", replicationController, *result) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/endpoints/util.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/endpoints/util.go index 91bc57166..7758434a1 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/endpoints/util.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/endpoints/util.go @@ -28,6 +28,16 @@ import ( hashutil "k8s.io/kubernetes/pkg/util/hash" ) +const ( + // Its value is the json representation of map[string(IP)][HostRecord] + // example: '{"10.245.1.6":{"HostName":"my-webserver"}}' + PodHostnamesAnnotation = "endpoints.beta.kubernetes.io/hostnames-map" +) + +type HostRecord struct { + HostName string +} + // RepackSubsets takes a slice of EndpointSubset objects, expands it to the full // representation, and then repacks that into the canonical layout. This // ensures that code which operates on these objects can rely on the common diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/endpoints/util_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/endpoints/util_test.go deleted file mode 100644 index a2f960317..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/endpoints/util_test.go +++ /dev/null @@ -1,444 +0,0 @@ -/* -Copyright 2015 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 endpoints - -import ( - "reflect" - "testing" - - "github.com/davecgh/go-spew/spew" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/types" -) - -func podRef(uid string) *api.ObjectReference { - ref := api.ObjectReference{UID: types.UID(uid)} - return &ref -} - -func TestPackSubsets(t *testing.T) { - // The downside of table-driven tests is that some things have to live outside the table. - fooObjRef := api.ObjectReference{Name: "foo"} - barObjRef := api.ObjectReference{Name: "bar"} - - testCases := []struct { - name string - given []api.EndpointSubset - expect []api.EndpointSubset - }{ - { - name: "empty everything", - given: []api.EndpointSubset{{Addresses: []api.EndpointAddress{}, Ports: []api.EndpointPort{}}}, - expect: []api.EndpointSubset{}, - }, { - name: "empty addresses", - given: []api.EndpointSubset{{Addresses: []api.EndpointAddress{}, Ports: []api.EndpointPort{{Port: 111}}}}, - expect: []api.EndpointSubset{}, - }, { - name: "empty ports", - given: []api.EndpointSubset{{Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, Ports: []api.EndpointPort{}}}, - expect: []api.EndpointSubset{}, - }, { - name: "empty ports", - given: []api.EndpointSubset{{NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, Ports: []api.EndpointPort{}}}, - expect: []api.EndpointSubset{}, - }, { - name: "one set, one ip, one port", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "one set, one notReady ip, one port", - given: []api.EndpointSubset{{ - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "one set, one ip, one UID, one port", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "one set, one notReady ip, one UID, one port", - given: []api.EndpointSubset{{ - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "one set, one ip, empty UID, one port", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "one set, one notReady ip, empty UID, one port", - given: []api.EndpointSubset{{ - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "one set, two ips, one port", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "5.6.7.8"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "5.6.7.8"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "one set, two mixed ips, one port", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - NotReadyAddresses: []api.EndpointAddress{{IP: "5.6.7.8"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - NotReadyAddresses: []api.EndpointAddress{{IP: "5.6.7.8"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "one set, two duplicate ips, one port, notReady is covered by ready", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "one set, one ip, two ports", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}, {Port: 222}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}, {Port: 222}}, - }}, - }, { - name: "one set, dup ips, one port", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "one set, dup ips with target-refs, one port", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{ - {IP: "1.2.3.4", TargetRef: &fooObjRef}, - {IP: "1.2.3.4", TargetRef: &barObjRef}, - }, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: &fooObjRef}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "one set, dup mixed ips with target-refs, one port", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{ - {IP: "1.2.3.4", TargetRef: &fooObjRef}, - }, - NotReadyAddresses: []api.EndpointAddress{ - {IP: "1.2.3.4", TargetRef: &barObjRef}, - }, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - // finding the same address twice is considered an error on input, only the first address+port - // reference is preserved - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: &fooObjRef}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "one set, one ip, dup ports", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}, {Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "two sets, dup ip, dup port", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "two sets, dup mixed ip, dup port", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "two sets, dup ip, two ports", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 222}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}, {Port: 222}}, - }}, - }, { - name: "two sets, dup ip, dup uids, two ports", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 222}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 111}, {Port: 222}}, - }}, - }, { - name: "two sets, dup mixed ip, dup uids, two ports", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 222}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 222}}, - }}, - }, { - name: "two sets, two ips, dup port", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - Addresses: []api.EndpointAddress{{IP: "5.6.7.8"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "5.6.7.8"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "two set, dup ip, two uids, dup ports", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-2")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{ - {IP: "1.2.3.4", TargetRef: podRef("uid-1")}, - {IP: "1.2.3.4", TargetRef: podRef("uid-2")}, - }, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "two set, dup ip, with and without uid, dup ports", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-2")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{ - {IP: "1.2.3.4"}, - {IP: "1.2.3.4", TargetRef: podRef("uid-2")}, - }, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "two sets, two ips, two dup ip with uid, dup port, wrong order", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "5.6.7.8"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - Addresses: []api.EndpointAddress{{IP: "5.6.7.8", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{ - {IP: "1.2.3.4"}, - {IP: "1.2.3.4", TargetRef: podRef("uid-1")}, - {IP: "5.6.7.8"}, - {IP: "5.6.7.8", TargetRef: podRef("uid-1")}, - }, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "two sets, two mixed ips, two dup ip with uid, dup port, wrong order, ends up with split addresses", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "5.6.7.8"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - NotReadyAddresses: []api.EndpointAddress{{IP: "5.6.7.8", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{ - {IP: "5.6.7.8"}, - }, - NotReadyAddresses: []api.EndpointAddress{ - {IP: "1.2.3.4"}, - {IP: "1.2.3.4", TargetRef: podRef("uid-1")}, - {IP: "5.6.7.8", TargetRef: podRef("uid-1")}, - }, - Ports: []api.EndpointPort{{Port: 111}}, - }}, - }, { - name: "two sets, two ips, two ports", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - Addresses: []api.EndpointAddress{{IP: "5.6.7.8"}}, - Ports: []api.EndpointPort{{Port: 222}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - Addresses: []api.EndpointAddress{{IP: "5.6.7.8"}}, - Ports: []api.EndpointPort{{Port: 222}}, - }}, - }, { - name: "four sets, three ips, three ports, jumbled", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - Addresses: []api.EndpointAddress{{IP: "1.2.3.5"}}, - Ports: []api.EndpointPort{{Port: 222}}, - }, { - Addresses: []api.EndpointAddress{{IP: "1.2.3.6"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - Addresses: []api.EndpointAddress{{IP: "1.2.3.5"}}, - Ports: []api.EndpointPort{{Port: 333}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "1.2.3.6"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - Addresses: []api.EndpointAddress{{IP: "1.2.3.5"}}, - Ports: []api.EndpointPort{{Port: 222}, {Port: 333}}, - }}, - }, { - name: "four sets, three mixed ips, three ports, jumbled", - given: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.5"}}, - Ports: []api.EndpointPort{{Port: 222}}, - }, { - Addresses: []api.EndpointAddress{{IP: "1.2.3.6"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.5"}}, - Ports: []api.EndpointPort{{Port: 333}}, - }}, - expect: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "1.2.3.6"}}, - Ports: []api.EndpointPort{{Port: 111}}, - }, { - NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.5"}}, - Ports: []api.EndpointPort{{Port: 222}, {Port: 333}}, - }}, - }, - } - - for _, tc := range testCases { - result := RepackSubsets(tc.given) - if !reflect.DeepEqual(result, SortSubsets(tc.expect)) { - t.Errorf("case %q: expected %s, got %s", tc.name, spew.Sprintf("%#v", SortSubsets(tc.expect)), spew.Sprintf("%#v", result)) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/errors/errors_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/errors/errors_test.go deleted file mode 100644 index 066bb25f2..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/errors/errors_test.go +++ /dev/null @@ -1,189 +0,0 @@ -/* -Copyright 2014 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 errors - -import ( - "errors" - "fmt" - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/validation/field" -) - -func TestErrorNew(t *testing.T) { - err := NewAlreadyExists(api.Resource("tests"), "1") - if !IsAlreadyExists(err) { - t.Errorf("expected to be %s", unversioned.StatusReasonAlreadyExists) - } - if IsConflict(err) { - t.Errorf("expected to not be %s", unversioned.StatusReasonConflict) - } - if IsNotFound(err) { - t.Errorf(fmt.Sprintf("expected to not be %s", unversioned.StatusReasonNotFound)) - } - if IsInvalid(err) { - t.Errorf("expected to not be %s", unversioned.StatusReasonInvalid) - } - if IsBadRequest(err) { - t.Errorf("expected to not be %s", unversioned.StatusReasonBadRequest) - } - if IsForbidden(err) { - t.Errorf("expected to not be %s", unversioned.StatusReasonForbidden) - } - if IsServerTimeout(err) { - t.Errorf("expected to not be %s", unversioned.StatusReasonServerTimeout) - } - if IsMethodNotSupported(err) { - t.Errorf("expected to not be %s", unversioned.StatusReasonMethodNotAllowed) - } - - if !IsConflict(NewConflict(api.Resource("tests"), "2", errors.New("message"))) { - t.Errorf("expected to be conflict") - } - if !IsNotFound(NewNotFound(api.Resource("tests"), "3")) { - t.Errorf("expected to be %s", unversioned.StatusReasonNotFound) - } - if !IsInvalid(NewInvalid(api.Kind("Test"), "2", nil)) { - t.Errorf("expected to be %s", unversioned.StatusReasonInvalid) - } - if !IsBadRequest(NewBadRequest("reason")) { - t.Errorf("expected to be %s", unversioned.StatusReasonBadRequest) - } - if !IsForbidden(NewForbidden(api.Resource("tests"), "2", errors.New("reason"))) { - t.Errorf("expected to be %s", unversioned.StatusReasonForbidden) - } - if !IsUnauthorized(NewUnauthorized("reason")) { - t.Errorf("expected to be %s", unversioned.StatusReasonUnauthorized) - } - if !IsServerTimeout(NewServerTimeout(api.Resource("tests"), "reason", 0)) { - t.Errorf("expected to be %s", unversioned.StatusReasonServerTimeout) - } - if time, ok := SuggestsClientDelay(NewServerTimeout(api.Resource("tests"), "doing something", 10)); time != 10 || !ok { - t.Errorf("expected to be %s", unversioned.StatusReasonServerTimeout) - } - if time, ok := SuggestsClientDelay(NewTimeoutError("test reason", 10)); time != 10 || !ok { - t.Errorf("expected to be %s", unversioned.StatusReasonTimeout) - } - if !IsMethodNotSupported(NewMethodNotSupported(api.Resource("foos"), "delete")) { - t.Errorf("expected to be %s", unversioned.StatusReasonMethodNotAllowed) - } -} - -func TestNewInvalid(t *testing.T) { - testCases := []struct { - Err *field.Error - Details *unversioned.StatusDetails - }{ - { - field.Duplicate(field.NewPath("field[0].name"), "bar"), - &unversioned.StatusDetails{ - Kind: "Kind", - Name: "name", - Causes: []unversioned.StatusCause{{ - Type: unversioned.CauseTypeFieldValueDuplicate, - Field: "field[0].name", - }}, - }, - }, - { - field.Invalid(field.NewPath("field[0].name"), "bar", "detail"), - &unversioned.StatusDetails{ - Kind: "Kind", - Name: "name", - Causes: []unversioned.StatusCause{{ - Type: unversioned.CauseTypeFieldValueInvalid, - Field: "field[0].name", - }}, - }, - }, - { - field.NotFound(field.NewPath("field[0].name"), "bar"), - &unversioned.StatusDetails{ - Kind: "Kind", - Name: "name", - Causes: []unversioned.StatusCause{{ - Type: unversioned.CauseTypeFieldValueNotFound, - Field: "field[0].name", - }}, - }, - }, - { - field.NotSupported(field.NewPath("field[0].name"), "bar", nil), - &unversioned.StatusDetails{ - Kind: "Kind", - Name: "name", - Causes: []unversioned.StatusCause{{ - Type: unversioned.CauseTypeFieldValueNotSupported, - Field: "field[0].name", - }}, - }, - }, - { - field.Required(field.NewPath("field[0].name"), ""), - &unversioned.StatusDetails{ - Kind: "Kind", - Name: "name", - Causes: []unversioned.StatusCause{{ - Type: unversioned.CauseTypeFieldValueRequired, - Field: "field[0].name", - }}, - }, - }, - } - for i, testCase := range testCases { - vErr, expected := testCase.Err, testCase.Details - expected.Causes[0].Message = vErr.ErrorBody() - err := NewInvalid(api.Kind("Kind"), "name", field.ErrorList{vErr}) - status := err.(*StatusError).ErrStatus - if status.Code != 422 || status.Reason != unversioned.StatusReasonInvalid { - t.Errorf("%d: unexpected status: %#v", i, status) - } - if !reflect.DeepEqual(expected, status.Details) { - t.Errorf("%d: expected %#v, got %#v", i, expected, status.Details) - } - } -} - -func Test_reasonForError(t *testing.T) { - if e, a := unversioned.StatusReasonUnknown, reasonForError(nil); e != a { - t.Errorf("unexpected reason type: %#v", a) - } -} - -type TestType struct{} - -func (obj *TestType) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind } - -func TestFromObject(t *testing.T) { - table := []struct { - obj runtime.Object - message string - }{ - {&unversioned.Status{Message: "foobar"}, "foobar"}, - {&TestType{}, "unexpected object: &{}"}, - } - - for _, item := range table { - if e, a := item.message, FromObject(item.obj).Error(); e != a { - t.Errorf("Expected %v, got %v", e, a) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/errors/etcd/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/errors/etcd/doc.go deleted file mode 100644 index 8cc0a832e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/errors/etcd/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2014 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 etcd provides conversion of etcd errors to API errors. -package etcd diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/errors/etcd/etcd.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/errors/etcd/etcd.go deleted file mode 100644 index 3e09aebaa..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/errors/etcd/etcd.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2014 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 etcd - -import ( - "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/storage" -) - -// InterpretListError converts a generic error on a retrieval -// operation into the appropriate API error. -func InterpretListError(err error, qualifiedResource unversioned.GroupResource) error { - switch { - case storage.IsNotFound(err): - return errors.NewNotFound(qualifiedResource, "") - case storage.IsUnreachable(err): - return errors.NewServerTimeout(qualifiedResource, "list", 2) // TODO: make configurable or handled at a higher level - default: - return err - } -} - -// InterpretGetError converts a generic error on a retrieval -// operation into the appropriate API error. -func InterpretGetError(err error, qualifiedResource unversioned.GroupResource, name string) error { - switch { - case storage.IsNotFound(err): - return errors.NewNotFound(qualifiedResource, name) - case storage.IsUnreachable(err): - return errors.NewServerTimeout(qualifiedResource, "get", 2) // TODO: make configurable or handled at a higher level - default: - return err - } -} - -// InterpretCreateError converts a generic error on a create -// operation into the appropriate API error. -func InterpretCreateError(err error, qualifiedResource unversioned.GroupResource, name string) error { - switch { - case storage.IsNodeExist(err): - return errors.NewAlreadyExists(qualifiedResource, name) - case storage.IsUnreachable(err): - return errors.NewServerTimeout(qualifiedResource, "create", 2) // TODO: make configurable or handled at a higher level - default: - return err - } -} - -// InterpretUpdateError converts a generic error on a update -// operation into the appropriate API error. -func InterpretUpdateError(err error, qualifiedResource unversioned.GroupResource, name string) error { - switch { - case storage.IsTestFailed(err), storage.IsNodeExist(err): - return errors.NewConflict(qualifiedResource, name, err) - case storage.IsUnreachable(err): - return errors.NewServerTimeout(qualifiedResource, "update", 2) // TODO: make configurable or handled at a higher level - default: - return err - } -} - -// InterpretDeleteError converts a generic error on a delete -// operation into the appropriate API error. -func InterpretDeleteError(err error, qualifiedResource unversioned.GroupResource, name string) error { - switch { - case storage.IsNotFound(err): - return errors.NewNotFound(qualifiedResource, name) - case storage.IsUnreachable(err): - return errors.NewServerTimeout(qualifiedResource, "delete", 2) // TODO: make configurable or handled at a higher level - default: - return err - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/field_constants.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/field_constants.go new file mode 100644 index 000000000..94a825caf --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/field_constants.go @@ -0,0 +1,38 @@ +/* +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 api + +// Field path constants that are specific to the internal API +// representation. +const ( + NodeUnschedulableField = "spec.unschedulable" + ObjectNameField = "metadata.name" + PodHostField = "spec.nodeName" + PodStatusField = "status.phase" + SecretTypeField = "type" + + EventReasonField = "reason" + EventSourceField = "source" + EventTypeField = "type" + EventInvolvedKindField = "involvedObject.kind" + EventInvolvedNamespaceField = "involvedObject.namespace" + EventInvolvedNameField = "involvedObject.name" + EventInvolvedUIDField = "involvedObject.uid" + EventInvolvedAPIVersionField = "involvedObject.apiVersion" + EventInvolvedResourceVersionField = "involvedObject.resourceVersion" + EventInvolvedFieldPathField = "involvedObject.fieldPath" +) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/generate_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/generate_test.go deleted file mode 100644 index fe3cf812b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/generate_test.go +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright 2014 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 api - -import ( - "strings" - "testing" -) - -type nameGeneratorFunc func(base string) string - -func (fn nameGeneratorFunc) GenerateName(base string) string { - return fn(base) -} - -func TestGenerateName(t *testing.T) { - testCases := []struct { - meta ObjectMeta - - base string - returned string - }{ - { - returned: "", - }, - { - meta: ObjectMeta{ - GenerateName: "test", - }, - base: "test", - returned: "test", - }, - { - meta: ObjectMeta{ - Name: "foo", - GenerateName: "test", - }, - base: "test", - returned: "foo", - }, - } - - for i, testCase := range testCases { - GenerateName(nameGeneratorFunc(func(base string) string { - if base != testCase.base { - t.Errorf("%d: unexpected call with base", i) - } - return "test" - }), &testCase.meta) - expect := testCase.returned - if expect != testCase.meta.Name { - t.Errorf("%d: unexpected name: %#v", i, testCase.meta) - } - } -} - -func TestSimpleNameGenerator(t *testing.T) { - meta := &ObjectMeta{ - GenerateName: "foo", - } - GenerateName(SimpleNameGenerator, meta) - if !strings.HasPrefix(meta.Name, "foo") || meta.Name == "foo" { - t.Errorf("unexpected name: %#v", meta) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/helpers.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/helpers.go index b373b9eee..aed3c5a7a 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/helpers.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/helpers.go @@ -79,15 +79,100 @@ var Semantic = conversion.EqualitiesOrDie( }, ) -var standardResources = sets.NewString( +var standardResourceQuotaScopes = sets.NewString( + string(ResourceQuotaScopeTerminating), + string(ResourceQuotaScopeNotTerminating), + string(ResourceQuotaScopeBestEffort), + string(ResourceQuotaScopeNotBestEffort), +) + +// IsStandardResourceQuotaScope returns true if the scope is a standard value +func IsStandardResourceQuotaScope(str string) bool { + return standardResourceQuotaScopes.Has(str) +} + +var podObjectCountQuotaResources = sets.NewString( + string(ResourcePods), +) + +var podComputeQuotaResources = sets.NewString( string(ResourceCPU), string(ResourceMemory), + string(ResourceLimitsCPU), + string(ResourceLimitsMemory), + string(ResourceRequestsCPU), + string(ResourceRequestsMemory), +) + +// IsResourceQuotaScopeValidForResource returns true if the resource applies to the specified scope +func IsResourceQuotaScopeValidForResource(scope ResourceQuotaScope, resource string) bool { + switch scope { + case ResourceQuotaScopeTerminating, ResourceQuotaScopeNotTerminating, ResourceQuotaScopeNotBestEffort: + return podObjectCountQuotaResources.Has(resource) || podComputeQuotaResources.Has(resource) + case ResourceQuotaScopeBestEffort: + return podObjectCountQuotaResources.Has(resource) + default: + return true + } +} + +var standardContainerResources = sets.NewString( + string(ResourceCPU), + string(ResourceMemory), +) + +// IsStandardContainerResourceName returns true if the container can make a resource request +// for the specified resource +func IsStandardContainerResourceName(str string) bool { + return standardContainerResources.Has(str) +} + +var standardLimitRangeTypes = sets.NewString( + string(LimitTypePod), + string(LimitTypeContainer), +) + +// IsStandardLimitRangeType returns true if the type is Pod or Container +func IsStandardLimitRangeType(str string) bool { + return standardLimitRangeTypes.Has(str) +} + +var standardQuotaResources = sets.NewString( + string(ResourceCPU), + string(ResourceMemory), + string(ResourceRequestsCPU), + string(ResourceRequestsMemory), + string(ResourceLimitsCPU), + string(ResourceLimitsMemory), string(ResourcePods), string(ResourceQuotas), string(ResourceServices), string(ResourceReplicationControllers), string(ResourceSecrets), string(ResourcePersistentVolumeClaims), + string(ResourceConfigMaps), +) + +// IsStandardQuotaResourceName returns true if the resource is known to +// the quota tracking system +func IsStandardQuotaResourceName(str string) bool { + return standardQuotaResources.Has(str) +} + +var standardResources = sets.NewString( + string(ResourceCPU), + string(ResourceMemory), + string(ResourceRequestsCPU), + string(ResourceRequestsMemory), + string(ResourceLimitsCPU), + string(ResourceLimitsMemory), + string(ResourcePods), + string(ResourceQuotas), + string(ResourceServices), + string(ResourceReplicationControllers), + string(ResourceSecrets), + string(ResourceConfigMaps), + string(ResourcePersistentVolumeClaims), string(ResourceStorage), ) @@ -102,6 +187,7 @@ var integerResources = sets.NewString( string(ResourceServices), string(ResourceReplicationControllers), string(ResourceSecrets), + string(ResourceConfigMaps), string(ResourcePersistentVolumeClaims), ) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/helpers_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/helpers_test.go deleted file mode 100644 index 2d6bd488f..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/helpers_test.go +++ /dev/null @@ -1,299 +0,0 @@ -/* -Copyright 2015 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 api - -import ( - "reflect" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/labels" - - "speter.net/go/exp/math/dec/inf" -) - -func TestConversionError(t *testing.T) { - var i int - var s string - i = 3 - s = "foo" - c := ConversionError{ - In: &i, Out: &s, - Message: "Can't make x into y, silly", - } - var e error - e = &c // ensure it implements error - msg := e.Error() - t.Logf("Message is %v", msg) - for _, part := range []string{"3", "int", "string", "Can't"} { - if !strings.Contains(msg, part) { - t.Errorf("didn't find %v", part) - } - } -} - -func TestSemantic(t *testing.T) { - table := []struct { - a, b interface{} - shouldEqual bool - }{ - {resource.MustParse("0"), resource.Quantity{}, true}, - {resource.Quantity{}, resource.MustParse("0"), true}, - {resource.Quantity{}, resource.MustParse("1m"), false}, - { - resource.Quantity{Amount: inf.NewDec(5, 0), Format: resource.BinarySI}, - resource.Quantity{Amount: inf.NewDec(5, 0), Format: resource.DecimalSI}, - true, - }, - {resource.MustParse("2m"), resource.MustParse("1m"), false}, - } - - for index, item := range table { - if e, a := item.shouldEqual, Semantic.DeepEqual(item.a, item.b); e != a { - t.Errorf("case[%d], expected %v, got %v.", index, e, a) - } - } -} - -func TestIsStandardResource(t *testing.T) { - testCases := []struct { - input string - output bool - }{ - {"cpu", true}, - {"memory", true}, - {"disk", false}, - {"blah", false}, - {"x.y.z", false}, - } - for i, tc := range testCases { - if IsStandardResourceName(tc.input) != tc.output { - t.Errorf("case[%d], expected: %t, got: %t", i, tc.output, !tc.output) - } - } -} - -func TestAddToNodeAddresses(t *testing.T) { - testCases := []struct { - existing []NodeAddress - toAdd []NodeAddress - expected []NodeAddress - }{ - { - existing: []NodeAddress{}, - toAdd: []NodeAddress{}, - expected: []NodeAddress{}, - }, - { - existing: []NodeAddress{}, - toAdd: []NodeAddress{ - {Type: NodeExternalIP, Address: "1.1.1.1"}, - {Type: NodeHostName, Address: "localhost"}, - }, - expected: []NodeAddress{ - {Type: NodeExternalIP, Address: "1.1.1.1"}, - {Type: NodeHostName, Address: "localhost"}, - }, - }, - { - existing: []NodeAddress{}, - toAdd: []NodeAddress{ - {Type: NodeExternalIP, Address: "1.1.1.1"}, - {Type: NodeExternalIP, Address: "1.1.1.1"}, - }, - expected: []NodeAddress{ - {Type: NodeExternalIP, Address: "1.1.1.1"}, - }, - }, - { - existing: []NodeAddress{ - {Type: NodeExternalIP, Address: "1.1.1.1"}, - {Type: NodeInternalIP, Address: "10.1.1.1"}, - }, - toAdd: []NodeAddress{ - {Type: NodeExternalIP, Address: "1.1.1.1"}, - {Type: NodeHostName, Address: "localhost"}, - }, - expected: []NodeAddress{ - {Type: NodeExternalIP, Address: "1.1.1.1"}, - {Type: NodeInternalIP, Address: "10.1.1.1"}, - {Type: NodeHostName, Address: "localhost"}, - }, - }, - } - - for i, tc := range testCases { - AddToNodeAddresses(&tc.existing, tc.toAdd...) - if !Semantic.DeepEqual(tc.expected, tc.existing) { - t.Errorf("case[%d], expected: %v, got: %v", i, tc.expected, tc.existing) - } - } -} - -func TestGetAccessModesFromString(t *testing.T) { - modes := GetAccessModesFromString("ROX") - if !containsAccessMode(modes, ReadOnlyMany) { - t.Errorf("Expected mode %s, but got %+v", ReadOnlyMany, modes) - } - - modes = GetAccessModesFromString("ROX,RWX") - if !containsAccessMode(modes, ReadOnlyMany) { - t.Errorf("Expected mode %s, but got %+v", ReadOnlyMany, modes) - } - if !containsAccessMode(modes, ReadWriteMany) { - t.Errorf("Expected mode %s, but got %+v", ReadWriteMany, modes) - } - - modes = GetAccessModesFromString("RWO,ROX,RWX") - if !containsAccessMode(modes, ReadOnlyMany) { - t.Errorf("Expected mode %s, but got %+v", ReadOnlyMany, modes) - } - if !containsAccessMode(modes, ReadWriteMany) { - t.Errorf("Expected mode %s, but got %+v", ReadWriteMany, modes) - } -} - -func TestRemoveDuplicateAccessModes(t *testing.T) { - modes := []PersistentVolumeAccessMode{ - ReadWriteOnce, ReadOnlyMany, ReadOnlyMany, ReadOnlyMany, - } - modes = removeDuplicateAccessModes(modes) - if len(modes) != 2 { - t.Errorf("Expected 2 distinct modes in set but found %v", len(modes)) - } -} - -func TestNodeSelectorRequirementsAsSelector(t *testing.T) { - matchExpressions := []NodeSelectorRequirement{{ - Key: "foo", - Operator: NodeSelectorOpIn, - Values: []string{"bar", "baz"}, - }} - mustParse := func(s string) labels.Selector { - out, e := labels.Parse(s) - if e != nil { - panic(e) - } - return out - } - tc := []struct { - in []NodeSelectorRequirement - out labels.Selector - expectErr bool - }{ - {in: nil, out: labels.Nothing()}, - {in: []NodeSelectorRequirement{}, out: labels.Nothing()}, - { - in: matchExpressions, - out: mustParse("foo in (baz,bar)"), - }, - { - in: []NodeSelectorRequirement{{ - Key: "foo", - Operator: NodeSelectorOpExists, - Values: []string{"bar", "baz"}, - }}, - expectErr: true, - }, - { - in: []NodeSelectorRequirement{{ - Key: "foo", - Operator: NodeSelectorOpGt, - Values: []string{"1.1"}, - }}, - out: mustParse("foo>1.1"), - }, - { - in: []NodeSelectorRequirement{{ - Key: "bar", - Operator: NodeSelectorOpLt, - Values: []string{"7.1"}, - }}, - out: mustParse("bar<7.1"), - }, - } - - for i, tc := range tc { - out, err := NodeSelectorRequirementsAsSelector(tc.in) - if err == nil && tc.expectErr { - t.Errorf("[%v]expected error but got none.", i) - } - if err != nil && !tc.expectErr { - t.Errorf("[%v]did not expect error but got: %v", i, err) - } - if !reflect.DeepEqual(out, tc.out) { - t.Errorf("[%v]expected:\n\t%+v\nbut got:\n\t%+v", i, tc.out, out) - } - } -} - -func TestGetAffinityFromPod(t *testing.T) { - testCases := []struct { - pod *Pod - expectErr bool - }{ - { - pod: &Pod{}, - expectErr: false, - }, - { - pod: &Pod{ - ObjectMeta: ObjectMeta{ - Annotations: map[string]string{ - AffinityAnnotationKey: ` - {"nodeAffinity": { "requiredDuringSchedulingIgnoredDuringExecution": { - "nodeSelectorTerms": [{ - "matchExpressions": [{ - "key": "foo", - "operator": "In", - "values": ["value1", "value2"] - }] - }] - }}}`, - }, - }, - }, - expectErr: false, - }, - { - pod: &Pod{ - ObjectMeta: ObjectMeta{ - Annotations: map[string]string{ - AffinityAnnotationKey: ` - {"nodeAffinity": { "requiredDuringSchedulingIgnoredDuringExecution": { - "nodeSelectorTerms": [{ - "matchExpressions": [{ - "key": "foo", - `, - }, - }, - }, - expectErr: true, - }, - } - - for i, tc := range testCases { - _, err := GetAffinityFromPodAnnotations(tc.pod.Annotations) - if err == nil && tc.expectErr { - t.Errorf("[%v]expected error but got none.", i) - } - if err != nil && !tc.expectErr { - t.Errorf("[%v]did not expect error but got: %v", i, err) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/install/install_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/install/install_test.go deleted file mode 100644 index 0b1615350..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/install/install_test.go +++ /dev/null @@ -1,129 +0,0 @@ -/* -Copyright 2014 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 install - -import ( - "encoding/json" - "reflect" - "testing" - - internal "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apimachinery/registered" - "k8s.io/kubernetes/pkg/runtime" -) - -func TestResourceVersioner(t *testing.T) { - pod := internal.Pod{ObjectMeta: internal.ObjectMeta{ResourceVersion: "10"}} - version, err := accessor.ResourceVersion(&pod) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if version != "10" { - t.Errorf("unexpected version %v", version) - } - - podList := internal.PodList{ListMeta: unversioned.ListMeta{ResourceVersion: "10"}} - version, err = accessor.ResourceVersion(&podList) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if version != "10" { - t.Errorf("unexpected version %v", version) - } -} - -func TestCodec(t *testing.T) { - pod := internal.Pod{} - // We do want to use package registered rather than testapi here, because we - // want to test if the package install and package registered work as expected. - data, err := runtime.Encode(internal.Codecs.LegacyCodec(registered.GroupOrDie(internal.GroupName).GroupVersion), &pod) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - other := internal.Pod{} - if err := json.Unmarshal(data, &other); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if other.APIVersion != registered.GroupOrDie(internal.GroupName).GroupVersion.Version || other.Kind != "Pod" { - t.Errorf("unexpected unmarshalled object %#v", other) - } -} - -func TestInterfacesFor(t *testing.T) { - if _, err := registered.GroupOrDie(internal.GroupName).InterfacesFor(internal.SchemeGroupVersion); err == nil { - t.Fatalf("unexpected non-error: %v", err) - } - for i, version := range registered.GroupOrDie(internal.GroupName).GroupVersions { - if vi, err := registered.GroupOrDie(internal.GroupName).InterfacesFor(version); err != nil || vi == nil { - t.Fatalf("%d: unexpected result: %v", i, err) - } - } -} - -func TestRESTMapper(t *testing.T) { - gv := unversioned.GroupVersion{Group: "", Version: "v1"} - rcGVK := gv.WithKind("ReplicationController") - podTemplateGVK := gv.WithKind("PodTemplate") - - if gvk, err := registered.GroupOrDie(internal.GroupName).RESTMapper.KindFor(internal.SchemeGroupVersion.WithResource("replicationcontrollers")); err != nil || gvk != rcGVK { - t.Errorf("unexpected version mapping: %v %v", gvk, err) - } - - if m, err := registered.GroupOrDie(internal.GroupName).RESTMapper.RESTMapping(podTemplateGVK.GroupKind(), ""); err != nil || m.GroupVersionKind != podTemplateGVK || m.Resource != "podtemplates" { - t.Errorf("unexpected version mapping: %#v %v", m, err) - } - - for _, version := range registered.GroupOrDie(internal.GroupName).GroupVersions { - mapping, err := registered.GroupOrDie(internal.GroupName).RESTMapper.RESTMapping(rcGVK.GroupKind(), version.Version) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if mapping.Resource != "replicationControllers" && mapping.Resource != "replicationcontrollers" { - t.Errorf("incorrect resource name: %#v", mapping) - } - if mapping.GroupVersionKind.GroupVersion() != version { - t.Errorf("incorrect version: %v", mapping) - } - - interfaces, _ := registered.GroupOrDie(internal.GroupName).InterfacesFor(version) - if mapping.ObjectConvertor != interfaces.ObjectConvertor { - t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces) - } - - rc := &internal.ReplicationController{ObjectMeta: internal.ObjectMeta{Name: "foo"}} - name, err := mapping.MetadataAccessor.Name(rc) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if name != "foo" { - t.Errorf("unable to retrieve object meta with: %v", mapping.MetadataAccessor) - } - } -} - -func TestUnversioned(t *testing.T) { - for _, obj := range []runtime.Object{ - &unversioned.Status{}, - &unversioned.ExportOptions{}, - } { - if unversioned, ok := internal.Scheme.IsUnversioned(obj); !unversioned || !ok { - t.Errorf("%v is expected to be unversioned", reflect.TypeOf(obj)) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/mapper.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/mapper.go index 054f74d5e..0216771ee 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/mapper.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/mapper.go @@ -43,10 +43,10 @@ func NewDefaultRESTMapper(defaultGroupVersions []unversioned.GroupVersion, inter for _, gv := range defaultGroupVersions { for kind, oType := range Scheme.KnownTypes(gv) { gvk := gv.WithKind(kind) - // TODO: Remove import path prefix check. - // We check the import path prefix because we currently stuff both "api" and "extensions" objects + // TODO: Remove import path check. + // We check the import path because we currently stuff both "api" and "extensions" objects // into the same group within Scheme since Scheme has no notion of groups yet. - if !strings.HasPrefix(oType.PkgPath(), importPathPrefix) || ignoredKinds.Has(kind) { + if !strings.Contains(oType.PkgPath(), importPathPrefix) || ignoredKinds.Has(kind) { continue } scope := meta.RESTScopeNamespace diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/help_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/help_test.go deleted file mode 100644 index 435784fdc..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/help_test.go +++ /dev/null @@ -1,253 +0,0 @@ -/* -Copyright 2014 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 meta_test - -import ( - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/v1" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util" - - "github.com/google/gofuzz" -) - -func TestIsList(t *testing.T) { - tests := []struct { - obj runtime.Object - isList bool - }{ - {&api.PodList{}, true}, - {&api.Pod{}, false}, - } - for _, item := range tests { - if e, a := item.isList, meta.IsListType(item.obj); e != a { - t.Errorf("%v: Expected %v, got %v", reflect.TypeOf(item.obj), e, a) - } - } -} - -func TestExtractList(t *testing.T) { - pl := &api.PodList{ - Items: []api.Pod{ - {ObjectMeta: api.ObjectMeta{Name: "1"}}, - {ObjectMeta: api.ObjectMeta{Name: "2"}}, - {ObjectMeta: api.ObjectMeta{Name: "3"}}, - }, - } - list, err := meta.ExtractList(pl) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if e, a := len(list), len(pl.Items); e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - for i := range list { - if e, a := list[i].(*api.Pod).Name, pl.Items[i].Name; e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - } -} - -func TestExtractListV1(t *testing.T) { - pl := &v1.PodList{ - Items: []v1.Pod{ - {ObjectMeta: v1.ObjectMeta{Name: "1"}}, - {ObjectMeta: v1.ObjectMeta{Name: "2"}}, - {ObjectMeta: v1.ObjectMeta{Name: "3"}}, - }, - } - list, err := meta.ExtractList(pl) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if e, a := len(list), len(pl.Items); e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - for i := range list { - if e, a := list[i].(*v1.Pod).Name, pl.Items[i].Name; e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - } -} - -func TestExtractListGeneric(t *testing.T) { - pl := &api.List{ - Items: []runtime.Object{ - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}}, - &api.Service{ObjectMeta: api.ObjectMeta{Name: "2"}}, - }, - } - list, err := meta.ExtractList(pl) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if e, a := len(list), len(pl.Items); e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - if obj, ok := list[0].(*api.Pod); !ok { - t.Fatalf("Expected list[0] to be *api.Pod, it is %#v", obj) - } - if obj, ok := list[1].(*api.Service); !ok { - t.Fatalf("Expected list[1] to be *api.Service, it is %#v", obj) - } -} - -func TestExtractListGenericV1(t *testing.T) { - pl := &v1.List{ - Items: []runtime.RawExtension{ - {RawJSON: []byte("foo")}, - {RawJSON: []byte("bar")}, - {Object: &v1.Pod{ObjectMeta: v1.ObjectMeta{Name: "other"}}}, - }, - } - list, err := meta.ExtractList(pl) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if e, a := len(list), len(pl.Items); e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - if obj, ok := list[0].(*runtime.Unknown); !ok { - t.Fatalf("Expected list[0] to be *runtime.Unknown, it is %#v", obj) - } - if obj, ok := list[1].(*runtime.Unknown); !ok { - t.Fatalf("Expected list[1] to be *runtime.Unknown, it is %#v", obj) - } - if obj, ok := list[2].(*v1.Pod); !ok { - t.Fatalf("Expected list[2] to be *runtime.Unknown, it is %#v", obj) - } -} - -type fakePtrInterfaceList struct { - Items *[]runtime.Object -} - -func (obj fakePtrInterfaceList) GetObjectKind() unversioned.ObjectKind { - return unversioned.EmptyObjectKind -} - -func TestExtractListOfInterfacePtrs(t *testing.T) { - pl := &fakePtrInterfaceList{ - Items: &[]runtime.Object{}, - } - list, err := meta.ExtractList(pl) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if len(list) > 0 { - t.Fatalf("Expected empty list, got %#v", list) - } -} - -type fakePtrValueList struct { - Items []*api.Pod -} - -func (obj fakePtrValueList) GetObjectKind() unversioned.ObjectKind { - return unversioned.EmptyObjectKind -} - -func TestExtractListOfValuePtrs(t *testing.T) { - pl := &fakePtrValueList{ - Items: []*api.Pod{ - {ObjectMeta: api.ObjectMeta{Name: "1"}}, - {ObjectMeta: api.ObjectMeta{Name: "2"}}, - }, - } - list, err := meta.ExtractList(pl) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if e, a := len(list), len(pl.Items); e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - for i := range list { - if obj, ok := list[i].(*api.Pod); !ok { - t.Fatalf("Expected list[%d] to be *api.Pod, it is %#v", i, obj) - } - } -} - -func TestSetList(t *testing.T) { - pl := &api.PodList{} - list := []runtime.Object{ - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}}, - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}}, - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "3"}}, - } - err := meta.SetList(pl, list) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if e, a := len(list), len(pl.Items); e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - for i := range list { - if e, a := list[i].(*api.Pod).Name, pl.Items[i].Name; e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - } -} - -func TestSetListToRuntimeObjectArray(t *testing.T) { - pl := &api.List{} - list := []runtime.Object{ - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}}, - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}}, - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "3"}}, - } - err := meta.SetList(pl, list) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if e, a := len(list), len(pl.Items); e != a { - t.Fatalf("Expected %v, got %v", e, a) - } - for i := range list { - if e, a := list[i], pl.Items[i]; e != a { - t.Fatalf("%d: unmatched: %s", i, util.ObjectDiff(e, a)) - } - } -} - -func TestSetExtractListRoundTrip(t *testing.T) { - fuzzer := fuzz.New().NilChance(0).NumElements(1, 5) - for i := 0; i < 5; i++ { - start := &api.PodList{} - fuzzer.Fuzz(&start.Items) - - list, err := meta.ExtractList(start) - if err != nil { - t.Errorf("Unexpected error %v", err) - continue - } - got := &api.PodList{} - err = meta.SetList(got, list) - if err != nil { - t.Errorf("Unexpected error %v", err) - continue - } - if e, a := start, got; !reflect.DeepEqual(e, a) { - t.Fatalf("Expected %#v, got %#v", e, a) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/meta_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/meta_test.go deleted file mode 100644 index 5b6c35230..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/meta_test.go +++ /dev/null @@ -1,778 +0,0 @@ -/* -Copyright 2014 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 meta_test - -import ( - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/types" -) - -func TestAPIObjectMeta(t *testing.T) { - j := &api.Pod{ - TypeMeta: unversioned.TypeMeta{APIVersion: "/a", Kind: "b"}, - ObjectMeta: api.ObjectMeta{ - Namespace: "bar", - Name: "foo", - GenerateName: "prefix", - UID: "uid", - ResourceVersion: "1", - SelfLink: "some/place/only/we/know", - Labels: map[string]string{"foo": "bar"}, - Annotations: map[string]string{"x": "y"}, - }, - } - var _ meta.Object = &j.ObjectMeta - var _ meta.ObjectMetaAccessor = j - accessor, err := meta.Accessor(j) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if accessor != meta.Object(&j.ObjectMeta) { - t.Fatalf("should have returned the same pointer: %#v %#v", accessor, j) - } - if e, a := "bar", accessor.GetNamespace(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "foo", accessor.GetName(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "prefix", accessor.GetGenerateName(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "uid", string(accessor.GetUID()); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "1", accessor.GetResourceVersion(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "some/place/only/we/know", accessor.GetSelfLink(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - - typeAccessor, err := meta.TypeAccessor(j) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if e, a := "a", typeAccessor.GetAPIVersion(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "b", typeAccessor.GetKind(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - - accessor.SetNamespace("baz") - accessor.SetName("bar") - accessor.SetGenerateName("generate") - accessor.SetUID("other") - typeAccessor.SetAPIVersion("c") - typeAccessor.SetKind("d") - accessor.SetResourceVersion("2") - accessor.SetSelfLink("google.com") - - // Prove that accessor changes the original object. - if e, a := "baz", j.Namespace; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "bar", j.Name; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "generate", j.GenerateName; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := types.UID("other"), j.UID; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "c", j.APIVersion; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "d", j.Kind; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "2", j.ResourceVersion; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "google.com", j.SelfLink; e != a { - t.Errorf("expected %v, got %v", e, a) - } - - typeAccessor.SetAPIVersion("d") - typeAccessor.SetKind("e") - if e, a := "d", j.APIVersion; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "e", j.Kind; e != a { - t.Errorf("expected %v, got %v", e, a) - } -} - -func TestGenericTypeMeta(t *testing.T) { - type TypeMeta struct { - Kind string `json:"kind,omitempty"` - Namespace string `json:"namespace,omitempty"` - Name string `json:"name,omitempty"` - GenerateName string `json:"generateName,omitempty"` - UID string `json:"uid,omitempty"` - CreationTimestamp unversioned.Time `json:"creationTimestamp,omitempty"` - SelfLink string `json:"selfLink,omitempty"` - ResourceVersion string `json:"resourceVersion,omitempty"` - APIVersion string `json:"apiVersion,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - Annotations map[string]string `json:"annotations,omitempty"` - } - type Object struct { - TypeMeta `json:",inline"` - } - j := Object{ - TypeMeta{ - Namespace: "bar", - Name: "foo", - GenerateName: "prefix", - UID: "uid", - APIVersion: "a", - Kind: "b", - ResourceVersion: "1", - SelfLink: "some/place/only/we/know", - Labels: map[string]string{"foo": "bar"}, - Annotations: map[string]string{"x": "y"}, - }, - } - accessor, err := meta.Accessor(&j) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if e, a := "bar", accessor.GetNamespace(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "foo", accessor.GetName(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "prefix", accessor.GetGenerateName(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "uid", string(accessor.GetUID()); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "1", accessor.GetResourceVersion(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "some/place/only/we/know", accessor.GetSelfLink(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - - typeAccessor, err := meta.TypeAccessor(&j) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if e, a := "a", typeAccessor.GetAPIVersion(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "b", typeAccessor.GetKind(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - - accessor.SetNamespace("baz") - accessor.SetName("bar") - accessor.SetGenerateName("generate") - accessor.SetUID("other") - typeAccessor.SetAPIVersion("c") - typeAccessor.SetKind("d") - accessor.SetResourceVersion("2") - accessor.SetSelfLink("google.com") - - // Prove that accessor changes the original object. - if e, a := "baz", j.Namespace; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "bar", j.Name; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "generate", j.GenerateName; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "other", j.UID; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "c", j.APIVersion; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "d", j.Kind; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "2", j.ResourceVersion; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "google.com", j.SelfLink; e != a { - t.Errorf("expected %v, got %v", e, a) - } - - typeAccessor.SetAPIVersion("d") - typeAccessor.SetKind("e") - if e, a := "d", j.APIVersion; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "e", j.Kind; e != a { - t.Errorf("expected %v, got %v", e, a) - } -} - -type InternalTypeMeta struct { - Kind string `json:"kind,omitempty"` - Namespace string `json:"namespace,omitempty"` - Name string `json:"name,omitempty"` - GenerateName string `json:"generateName,omitempty"` - UID string `json:"uid,omitempty"` - CreationTimestamp unversioned.Time `json:"creationTimestamp,omitempty"` - SelfLink string `json:"selfLink,omitempty"` - ResourceVersion string `json:"resourceVersion,omitempty"` - APIVersion string `json:"apiVersion,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - Annotations map[string]string `json:"annotations,omitempty"` -} -type InternalObject struct { - TypeMeta InternalTypeMeta `json:",inline"` -} - -func (obj *InternalObject) GetObjectKind() unversioned.ObjectKind { return obj } -func (obj *InternalObject) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind() -} -func (obj *InternalObject) GroupVersionKind() *unversioned.GroupVersionKind { - return unversioned.FromAPIVersionAndKind(obj.TypeMeta.APIVersion, obj.TypeMeta.Kind) -} - -func TestGenericTypeMetaAccessor(t *testing.T) { - j := &InternalObject{ - InternalTypeMeta{ - Namespace: "bar", - Name: "foo", - GenerateName: "prefix", - UID: "uid", - APIVersion: "/a", - Kind: "b", - ResourceVersion: "1", - SelfLink: "some/place/only/we/know", - Labels: map[string]string{"foo": "bar"}, - Annotations: map[string]string{"x": "y"}, - }, - } - accessor := meta.NewAccessor() - namespace, err := accessor.Namespace(j) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if e, a := "bar", namespace; e != a { - t.Errorf("expected %v, got %v", e, a) - } - name, err := accessor.Name(j) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if e, a := "foo", name; e != a { - t.Errorf("expected %v, got %v", e, a) - } - generateName, err := accessor.GenerateName(j) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if e, a := "prefix", generateName; e != a { - t.Errorf("expected %v, got %v", e, a) - } - uid, err := accessor.UID(j) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if e, a := "uid", string(uid); e != a { - t.Errorf("expected %v, got %v", e, a) - } - apiVersion, err := accessor.APIVersion(j) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if e, a := "a", apiVersion; e != a { - t.Errorf("expected %v, got %v", e, a) - } - kind, err := accessor.Kind(j) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if e, a := "b", kind; e != a { - t.Errorf("expected %v, got %v", e, a) - } - rv, err := accessor.ResourceVersion(j) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if e, a := "1", rv; e != a { - t.Errorf("expected %v, got %v", e, a) - } - selfLink, err := accessor.SelfLink(j) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if e, a := "some/place/only/we/know", selfLink; e != a { - t.Errorf("expected %v, got %v", e, a) - } - labels, err := accessor.Labels(j) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if e, a := 1, len(labels); e != a { - t.Errorf("expected %v, got %v", e, a) - } - annotations, err := accessor.Annotations(j) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if e, a := 1, len(annotations); e != a { - t.Errorf("expected %v, got %v", e, a) - } - - if err := accessor.SetNamespace(j, "baz"); err != nil { - t.Errorf("unexpected error: %v", err) - } - if err := accessor.SetName(j, "bar"); err != nil { - t.Errorf("unexpected error: %v", err) - } - if err := accessor.SetGenerateName(j, "generate"); err != nil { - t.Errorf("unexpected error: %v", err) - } - if err := accessor.SetUID(j, "other"); err != nil { - t.Errorf("unexpected error: %v", err) - } - if err := accessor.SetAPIVersion(j, "c"); err != nil { - t.Errorf("unexpected error: %v", err) - } - if err := accessor.SetKind(j, "d"); err != nil { - t.Errorf("unexpected error: %v", err) - } - if err := accessor.SetResourceVersion(j, "2"); err != nil { - t.Errorf("unexpected error: %v", err) - } - if err := accessor.SetSelfLink(j, "google.com"); err != nil { - t.Errorf("unexpected error: %v", err) - } - if err := accessor.SetLabels(j, map[string]string{}); err != nil { - t.Errorf("unexpected error: %v", err) - } - var nilMap map[string]string - if err := accessor.SetAnnotations(j, nilMap); err != nil { - t.Errorf("unexpected error: %v", err) - } - - // Prove that accessor changes the original object. - if e, a := "baz", j.TypeMeta.Namespace; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "bar", j.TypeMeta.Name; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "generate", j.TypeMeta.GenerateName; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "other", j.TypeMeta.UID; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "c", j.TypeMeta.APIVersion; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "d", j.TypeMeta.Kind; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "2", j.TypeMeta.ResourceVersion; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "google.com", j.TypeMeta.SelfLink; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := map[string]string{}, j.TypeMeta.Labels; !reflect.DeepEqual(e, a) { - t.Errorf("expected %#v, got %#v", e, a) - } - if e, a := nilMap, j.TypeMeta.Annotations; !reflect.DeepEqual(e, a) { - t.Errorf("expected %#v, got %#v", e, a) - } -} - -func TestGenericObjectMeta(t *testing.T) { - type TypeMeta struct { - Kind string `json:"kind,omitempty"` - APIVersion string `json:"apiVersion,omitempty"` - } - type ObjectMeta struct { - Namespace string `json:"namespace,omitempty"` - Name string `json:"name,omitempty"` - GenerateName string `json:"generateName,omitempty"` - UID string `json:"uid,omitempty"` - CreationTimestamp unversioned.Time `json:"creationTimestamp,omitempty"` - SelfLink string `json:"selfLink,omitempty"` - ResourceVersion string `json:"resourceVersion,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - Annotations map[string]string `json:"annotations,omitempty"` - } - type Object struct { - TypeMeta `json:",inline"` - ObjectMeta `json:"metadata"` - } - j := Object{ - TypeMeta{ - APIVersion: "a", - Kind: "b", - }, - ObjectMeta{ - Namespace: "bar", - Name: "foo", - GenerateName: "prefix", - UID: "uid", - ResourceVersion: "1", - SelfLink: "some/place/only/we/know", - Labels: map[string]string{"foo": "bar"}, - Annotations: map[string]string{"a": "b"}, - }, - } - accessor, err := meta.Accessor(&j) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if e, a := "bar", accessor.GetNamespace(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "foo", accessor.GetName(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "prefix", accessor.GetGenerateName(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "uid", string(accessor.GetUID()); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "1", accessor.GetResourceVersion(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "some/place/only/we/know", accessor.GetSelfLink(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := 1, len(accessor.GetLabels()); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := 1, len(accessor.GetAnnotations()); e != a { - t.Errorf("expected %v, got %v", e, a) - } - - typeAccessor, err := meta.TypeAccessor(&j) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if e, a := "a", typeAccessor.GetAPIVersion(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "b", typeAccessor.GetKind(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - - accessor.SetNamespace("baz") - accessor.SetName("bar") - accessor.SetGenerateName("generate") - accessor.SetUID("other") - typeAccessor.SetAPIVersion("c") - typeAccessor.SetKind("d") - accessor.SetResourceVersion("2") - accessor.SetSelfLink("google.com") - accessor.SetLabels(map[string]string{"other": "label"}) - accessor.SetAnnotations(map[string]string{"c": "d"}) - - // Prove that accessor changes the original object. - if e, a := "baz", j.Namespace; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "bar", j.Name; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "generate", j.GenerateName; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "other", j.UID; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "c", j.APIVersion; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "d", j.Kind; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "2", j.ResourceVersion; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "google.com", j.SelfLink; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := map[string]string{"other": "label"}, j.Labels; !reflect.DeepEqual(e, a) { - t.Errorf("expected %#v, got %#v", e, a) - } - if e, a := map[string]string{"c": "d"}, j.Annotations; !reflect.DeepEqual(e, a) { - t.Errorf("expected %#v, got %#v", e, a) - } -} - -func TestGenericListMeta(t *testing.T) { - type TypeMeta struct { - Kind string `json:"kind,omitempty"` - APIVersion string `json:"apiVersion,omitempty"` - } - type ListMeta struct { - SelfLink string `json:"selfLink,omitempty"` - ResourceVersion string `json:"resourceVersion,omitempty"` - } - type Object struct { - TypeMeta `json:",inline"` - ListMeta `json:"metadata"` - } - j := Object{ - TypeMeta{ - APIVersion: "a", - Kind: "b", - }, - ListMeta{ - ResourceVersion: "1", - SelfLink: "some/place/only/we/know", - }, - } - accessor, err := meta.Accessor(&j) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if e, a := "", accessor.GetName(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "", string(accessor.GetUID()); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "1", accessor.GetResourceVersion(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "some/place/only/we/know", accessor.GetSelfLink(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - - typeAccessor, err := meta.TypeAccessor(&j) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if e, a := "a", typeAccessor.GetAPIVersion(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "b", typeAccessor.GetKind(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - - accessor.SetName("bar") - accessor.SetUID("other") - typeAccessor.SetAPIVersion("c") - typeAccessor.SetKind("d") - accessor.SetResourceVersion("2") - accessor.SetSelfLink("google.com") - - // Prove that accessor changes the original object. - if e, a := "c", j.APIVersion; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "d", j.Kind; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "2", j.ResourceVersion; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "google.com", j.SelfLink; e != a { - t.Errorf("expected %v, got %v", e, a) - } -} - -type MyAPIObject struct { - TypeMeta InternalTypeMeta `json:",inline"` -} - -func (obj *MyAPIObject) GetObjectKind() unversioned.ObjectKind { return obj } -func (obj *MyAPIObject) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind() -} -func (obj *MyAPIObject) GroupVersionKind() *unversioned.GroupVersionKind { - return unversioned.FromAPIVersionAndKind(obj.TypeMeta.APIVersion, obj.TypeMeta.Kind) -} - -type MyIncorrectlyMarkedAsAPIObject struct{} - -func (obj *MyIncorrectlyMarkedAsAPIObject) GetObjectKind() unversioned.ObjectKind { - return unversioned.EmptyObjectKind -} - -func TestResourceVersionerOfAPI(t *testing.T) { - type T struct { - runtime.Object - Expected string - } - testCases := map[string]T{ - "empty api object": {&MyAPIObject{}, ""}, - "api object with version": {&MyAPIObject{TypeMeta: InternalTypeMeta{ResourceVersion: "1"}}, "1"}, - "pointer to api object with version": {&MyAPIObject{TypeMeta: InternalTypeMeta{ResourceVersion: "1"}}, "1"}, - } - versioning := meta.NewAccessor() - for key, testCase := range testCases { - actual, err := versioning.ResourceVersion(testCase.Object) - if err != nil { - t.Errorf("%s: unexpected error %#v", key, err) - } - if actual != testCase.Expected { - t.Errorf("%s: expected %v, got %v", key, testCase.Expected, actual) - } - } - - failingCases := map[string]struct { - runtime.Object - Expected string - }{ - "not a valid object to try": {&MyIncorrectlyMarkedAsAPIObject{}, "1"}, - } - for key, testCase := range failingCases { - _, err := versioning.ResourceVersion(testCase.Object) - if err == nil { - t.Errorf("%s: expected error, got nil", key) - } - } - - setCases := map[string]struct { - runtime.Object - Expected string - }{ - "pointer to api object with version": {&MyAPIObject{TypeMeta: InternalTypeMeta{ResourceVersion: "1"}}, "1"}, - } - for key, testCase := range setCases { - if err := versioning.SetResourceVersion(testCase.Object, "5"); err != nil { - t.Errorf("%s: unexpected error %#v", key, err) - } - actual, err := versioning.ResourceVersion(testCase.Object) - if err != nil { - t.Errorf("%s: unexpected error %#v", key, err) - } - if actual != "5" { - t.Errorf("%s: expected %v, got %v", key, "5", actual) - } - } -} - -func TestTypeMetaSelfLinker(t *testing.T) { - table := map[string]struct { - obj runtime.Object - expect string - try string - succeed bool - }{ - "normal": { - obj: &MyAPIObject{TypeMeta: InternalTypeMeta{SelfLink: "foobar"}}, - expect: "foobar", - try: "newbar", - succeed: true, - }, - "fail": { - obj: &MyIncorrectlyMarkedAsAPIObject{}, - succeed: false, - }, - } - - linker := runtime.SelfLinker(meta.NewAccessor()) - for name, item := range table { - got, err := linker.SelfLink(item.obj) - if e, a := item.succeed, err == nil; e != a { - t.Errorf("%v: expected %v, got %v", name, e, a) - } - if e, a := item.expect, got; item.succeed && e != a { - t.Errorf("%v: expected %v, got %v", name, e, a) - } - - err = linker.SetSelfLink(item.obj, item.try) - if e, a := item.succeed, err == nil; e != a { - t.Errorf("%v: expected %v, got %v", name, e, a) - } - if item.succeed { - got, err := linker.SelfLink(item.obj) - if err != nil { - t.Errorf("%v: expected no err, got %v", name, err) - } - if e, a := item.try, got; e != a { - t.Errorf("%v: expected %v, got %v", name, e, a) - } - } - } -} - -// BenchmarkAccessorSetFastPath shows the interface fast path -func BenchmarkAccessorSetFastPath(b *testing.B) { - obj := &api.Pod{ - TypeMeta: unversioned.TypeMeta{APIVersion: "/a", Kind: "b"}, - ObjectMeta: api.ObjectMeta{ - Namespace: "bar", - Name: "foo", - GenerateName: "prefix", - UID: "uid", - ResourceVersion: "1", - SelfLink: "some/place/only/we/know", - Labels: map[string]string{"foo": "bar"}, - Annotations: map[string]string{"x": "y"}, - }, - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - acc, err := meta.Accessor(obj) - if err != nil { - b.Fatal(err) - } - acc.SetNamespace("something") - } - b.StopTimer() -} - -// BenchmarkAccessorSetReflection provides a baseline for accessor performance -func BenchmarkAccessorSetReflection(b *testing.B) { - obj := &InternalObject{ - InternalTypeMeta{ - Namespace: "bar", - Name: "foo", - GenerateName: "prefix", - UID: "uid", - APIVersion: "a", - Kind: "b", - ResourceVersion: "1", - SelfLink: "some/place/only/we/know", - Labels: map[string]string{"foo": "bar"}, - Annotations: map[string]string{"x": "y"}, - }, - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - acc, err := meta.Accessor(obj) - if err != nil { - b.Fatal(err) - } - acc.SetNamespace("something") - } - b.StopTimer() -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/multirestmapper.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/multirestmapper.go index 4d284e62a..3071d4507 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/multirestmapper.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/multirestmapper.go @@ -21,6 +21,7 @@ import ( "strings" "k8s.io/kubernetes/pkg/api/unversioned" + utilerrors "k8s.io/kubernetes/pkg/util/errors" ) // MultiRESTMapper is a wrapper for multiple RESTMappers. @@ -50,72 +51,144 @@ func (m MultiRESTMapper) ResourceSingularizer(resource string) (singular string, } func (m MultiRESTMapper) ResourcesFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) { + allGVRs := []unversioned.GroupVersionResource{} for _, t := range m { gvrs, err := t.ResourcesFor(resource) // ignore "no match" errors, but any other error percolates back up - if !IsNoResourceMatchError(err) { - return gvrs, err + if IsNoResourceMatchError(err) { + continue + } + if err != nil { + return nil, err + } + + // walk the existing values to de-dup + for _, curr := range gvrs { + found := false + for _, existing := range allGVRs { + if curr == existing { + found = true + break + } + } + + if !found { + allGVRs = append(allGVRs, curr) + } } } - return nil, &NoResourceMatchError{PartialResource: resource} + + if len(allGVRs) == 0 { + return nil, &NoResourceMatchError{PartialResource: resource} + } + + return allGVRs, nil } -// KindsFor provides the Kind mappings for the REST resources. This implementation supports multiple REST schemas and returns -// the first match. func (m MultiRESTMapper) KindsFor(resource unversioned.GroupVersionResource) (gvk []unversioned.GroupVersionKind, err error) { + allGVKs := []unversioned.GroupVersionKind{} for _, t := range m { gvks, err := t.KindsFor(resource) // ignore "no match" errors, but any other error percolates back up - if !IsNoResourceMatchError(err) { - return gvks, err + if IsNoResourceMatchError(err) { + continue + } + if err != nil { + return nil, err + } + + // walk the existing values to de-dup + for _, curr := range gvks { + found := false + for _, existing := range allGVKs { + if curr == existing { + found = true + break + } + } + + if !found { + allGVKs = append(allGVKs, curr) + } } } - return nil, &NoResourceMatchError{PartialResource: resource} + + if len(allGVKs) == 0 { + return nil, &NoResourceMatchError{PartialResource: resource} + } + + return allGVKs, nil } func (m MultiRESTMapper) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) { - for _, t := range m { - gvr, err := t.ResourceFor(resource) - // ignore "no match" errors, but any other error percolates back up - if !IsNoResourceMatchError(err) { - return gvr, err - } + resources, err := m.ResourcesFor(resource) + if err != nil { + return unversioned.GroupVersionResource{}, err } - return unversioned.GroupVersionResource{}, &NoResourceMatchError{PartialResource: resource} + if len(resources) == 1 { + return resources[0], nil + } + + return unversioned.GroupVersionResource{}, &AmbiguousResourceError{PartialResource: resource, MatchingResources: resources} } -// KindsFor provides the Kind mapping for the REST resources. This implementation supports multiple REST schemas and returns -// the first match. func (m MultiRESTMapper) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) { - for _, t := range m { - gvk, err := t.KindFor(resource) - // ignore "no match" errors, but any other error percolates back up - if !IsNoResourceMatchError(err) { - return gvk, err - } + kinds, err := m.KindsFor(resource) + if err != nil { + return unversioned.GroupVersionKind{}, err } - return unversioned.GroupVersionKind{}, &NoResourceMatchError{PartialResource: resource} + if len(kinds) == 1 { + return kinds[0], nil + } + + return unversioned.GroupVersionKind{}, &AmbiguousResourceError{PartialResource: resource, MatchingKinds: kinds} } // RESTMapping provides the REST mapping for the resource based on the // kind and version. This implementation supports multiple REST schemas and // return the first match. -func (m MultiRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (mapping *RESTMapping, err error) { +func (m MultiRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (*RESTMapping, error) { + allMappings := []*RESTMapping{} + errors := []error{} + for _, t := range m { - mapping, err = t.RESTMapping(gk, versions...) - if err == nil { - return + currMapping, err := t.RESTMapping(gk, versions...) + // ignore "no match" errors, but any other error percolates back up + if IsNoResourceMatchError(err) { + continue } + if err != nil { + errors = append(errors, err) + continue + } + + allMappings = append(allMappings, currMapping) } - return + + // if we got exactly one mapping, then use it even if other requested failed + if len(allMappings) == 1 { + return allMappings[0], nil + } + if len(errors) > 0 { + return nil, utilerrors.NewAggregate(errors) + } + if len(allMappings) == 0 { + return nil, fmt.Errorf("no match found for %v in %v", gk, versions) + } + + return nil, fmt.Errorf("multiple matches found for %v in %v", gk, versions) } // AliasesForResource finds the first alias response for the provided mappers. -func (m MultiRESTMapper) AliasesForResource(alias string) (aliases []string, ok bool) { +func (m MultiRESTMapper) AliasesForResource(alias string) ([]string, bool) { + allAliases := []string{} + handled := false + for _, t := range m { - if aliases, ok = t.AliasesForResource(alias); ok { - return + if currAliases, currOk := t.AliasesForResource(alias); currOk { + allAliases = append(allAliases, currAliases...) + handled = true } } - return nil, false + return allAliases, handled } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/multirestmapper_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/multirestmapper_test.go deleted file mode 100644 index c4d543115..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/multirestmapper_test.go +++ /dev/null @@ -1,238 +0,0 @@ -/* -Copyright 2014 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 meta - -import ( - "errors" - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api/unversioned" -) - -func TestMultiRESTMapperResourceForErrorHandling(t *testing.T) { - tcs := []struct { - name string - - mapper MultiRESTMapper - input unversioned.GroupVersionResource - result unversioned.GroupVersionResource - err error - }{ - { - name: "empty", - mapper: MultiRESTMapper{}, - input: unversioned.GroupVersionResource{Resource: "foo"}, - result: unversioned.GroupVersionResource{}, - err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}}, - }, - { - name: "ignore not found", - mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "IGNORE_THIS"}}}}, - input: unversioned.GroupVersionResource{Resource: "foo"}, - result: unversioned.GroupVersionResource{}, - err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}}, - }, - { - name: "accept first failure", - mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{resourceFor: unversioned.GroupVersionResource{Resource: "unused"}}}, - input: unversioned.GroupVersionResource{Resource: "foo"}, - result: unversioned.GroupVersionResource{}, - err: errors.New("fail on this"), - }, - } - - for _, tc := range tcs { - actualResult, actualErr := tc.mapper.ResourceFor(tc.input) - if e, a := tc.result, actualResult; e != a { - t.Errorf("%s: expected %v, got %v", tc.name, e, a) - } - if e, a := tc.err.Error(), actualErr.Error(); e != a { - t.Errorf("%s: expected %v, got %v", tc.name, e, a) - } - } -} - -func TestMultiRESTMapperResourcesForErrorHandling(t *testing.T) { - tcs := []struct { - name string - - mapper MultiRESTMapper - input unversioned.GroupVersionResource - result []unversioned.GroupVersionResource - err error - }{ - { - name: "empty", - mapper: MultiRESTMapper{}, - input: unversioned.GroupVersionResource{Resource: "foo"}, - result: nil, - err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}}, - }, - { - name: "ignore not found", - mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "IGNORE_THIS"}}}}, - input: unversioned.GroupVersionResource{Resource: "foo"}, - result: nil, - err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}}, - }, - { - name: "accept first failure", - mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{resourcesFor: []unversioned.GroupVersionResource{{Resource: "unused"}}}}, - input: unversioned.GroupVersionResource{Resource: "foo"}, - result: nil, - err: errors.New("fail on this"), - }, - } - - for _, tc := range tcs { - actualResult, actualErr := tc.mapper.ResourcesFor(tc.input) - if e, a := tc.result, actualResult; !reflect.DeepEqual(e, a) { - t.Errorf("%s: expected %v, got %v", tc.name, e, a) - } - if e, a := tc.err.Error(), actualErr.Error(); e != a { - t.Errorf("%s: expected %v, got %v", tc.name, e, a) - } - } -} - -func TestMultiRESTMapperKindsForErrorHandling(t *testing.T) { - tcs := []struct { - name string - - mapper MultiRESTMapper - input unversioned.GroupVersionResource - result []unversioned.GroupVersionKind - err error - }{ - { - name: "empty", - mapper: MultiRESTMapper{}, - input: unversioned.GroupVersionResource{Resource: "foo"}, - result: nil, - err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}}, - }, - { - name: "ignore not found", - mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "IGNORE_THIS"}}}}, - input: unversioned.GroupVersionResource{Resource: "foo"}, - result: nil, - err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}}, - }, - { - name: "accept first failure", - mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{kindsFor: []unversioned.GroupVersionKind{{Kind: "unused"}}}}, - input: unversioned.GroupVersionResource{Resource: "foo"}, - result: nil, - err: errors.New("fail on this"), - }, - } - - for _, tc := range tcs { - actualResult, actualErr := tc.mapper.KindsFor(tc.input) - if e, a := tc.result, actualResult; !reflect.DeepEqual(e, a) { - t.Errorf("%s: expected %v, got %v", tc.name, e, a) - } - if e, a := tc.err.Error(), actualErr.Error(); e != a { - t.Errorf("%s: expected %v, got %v", tc.name, e, a) - } - } -} - -func TestMultiRESTMapperKindForErrorHandling(t *testing.T) { - tcs := []struct { - name string - - mapper MultiRESTMapper - input unversioned.GroupVersionResource - result unversioned.GroupVersionKind - err error - }{ - { - name: "empty", - mapper: MultiRESTMapper{}, - input: unversioned.GroupVersionResource{Resource: "foo"}, - result: unversioned.GroupVersionKind{}, - err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}}, - }, - { - name: "ignore not found", - mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "IGNORE_THIS"}}}}, - input: unversioned.GroupVersionResource{Resource: "foo"}, - result: unversioned.GroupVersionKind{}, - err: &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}}, - }, - { - name: "accept first failure", - mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{kindFor: unversioned.GroupVersionKind{Kind: "unused"}}}, - input: unversioned.GroupVersionResource{Resource: "foo"}, - result: unversioned.GroupVersionKind{}, - err: errors.New("fail on this"), - }, - } - - for _, tc := range tcs { - actualResult, actualErr := tc.mapper.KindFor(tc.input) - if e, a := tc.result, actualResult; e != a { - t.Errorf("%s: expected %v, got %v", tc.name, e, a) - } - if e, a := tc.err.Error(), actualErr.Error(); e != a { - t.Errorf("%s: expected %v, got %v", tc.name, e, a) - } - } -} - -type fixedRESTMapper struct { - resourcesFor []unversioned.GroupVersionResource - kindsFor []unversioned.GroupVersionKind - resourceFor unversioned.GroupVersionResource - kindFor unversioned.GroupVersionKind - - err error -} - -func (m fixedRESTMapper) ResourceSingularizer(resource string) (singular string, err error) { - return "", m.err -} - -func (m fixedRESTMapper) ResourcesFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) { - return m.resourcesFor, m.err -} - -func (m fixedRESTMapper) KindsFor(resource unversioned.GroupVersionResource) (gvk []unversioned.GroupVersionKind, err error) { - return m.kindsFor, m.err -} - -func (m fixedRESTMapper) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) { - return m.resourceFor, m.err -} - -func (m fixedRESTMapper) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) { - return m.kindFor, m.err -} - -func (m fixedRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (mapping *RESTMapping, err error) { - return nil, m.err -} - -func (m fixedRESTMapper) AliasesForResource(alias string) (aliases []string, ok bool) { - return nil, false -} - -func (m fixedRESTMapper) ResourceIsValid(resource unversioned.GroupVersionResource) bool { - return false -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/priority.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/priority.go new file mode 100644 index 000000000..24f38f78f --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/priority.go @@ -0,0 +1,173 @@ +/* +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 meta + +import ( + "fmt" + + "k8s.io/kubernetes/pkg/api/unversioned" +) + +const ( + AnyGroup = "*" + AnyVersion = "*" + AnyResource = "*" + AnyKind = "*" +) + +// PriorityRESTMapper is a wrapper for automatically choosing a particular Resource or Kind +// when multiple matches are possible +type PriorityRESTMapper struct { + // Delegate is the RESTMapper to use to locate all the Kind and Resource matches + Delegate RESTMapper + + // ResourcePriority is a list of priority patterns to apply to matching resources. + // The list of all matching resources is narrowed based on the patterns until only one remains. + // A pattern with no matches is skipped. A pattern with more than one match uses its + // matches as the list to continue matching against. + ResourcePriority []unversioned.GroupVersionResource + + // KindPriority is a list of priority patterns to apply to matching kinds. + // The list of all matching kinds is narrowed based on the patterns until only one remains. + // A pattern with no matches is skipped. A pattern with more than one match uses its + // matches as the list to continue matching against. + KindPriority []unversioned.GroupVersionKind +} + +func (m PriorityRESTMapper) String() string { + return fmt.Sprintf("PriorityRESTMapper{\n\t%v\n\t%v\n\t%v\n}", m.ResourcePriority, m.KindPriority, m.Delegate) +} + +// ResourceFor finds all resources, then passes them through the ResourcePriority patterns to find a single matching hit. +func (m PriorityRESTMapper) ResourceFor(partiallySpecifiedResource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) { + originalGVRs, err := m.Delegate.ResourcesFor(partiallySpecifiedResource) + if err != nil { + return unversioned.GroupVersionResource{}, err + } + if len(originalGVRs) == 1 { + return originalGVRs[0], nil + } + + remainingGVRs := append([]unversioned.GroupVersionResource{}, originalGVRs...) + for _, pattern := range m.ResourcePriority { + matchedGVRs := []unversioned.GroupVersionResource{} + for _, gvr := range remainingGVRs { + if resourceMatches(pattern, gvr) { + matchedGVRs = append(matchedGVRs, gvr) + } + } + + switch len(matchedGVRs) { + case 0: + // if you have no matches, then nothing matched this pattern just move to the next + continue + case 1: + // one match, return + return matchedGVRs[0], nil + default: + // more than one match, use the matched hits as the list moving to the next pattern. + // this way you can have a series of selection criteria + remainingGVRs = matchedGVRs + } + } + + return unversioned.GroupVersionResource{}, &AmbiguousResourceError{PartialResource: partiallySpecifiedResource, MatchingResources: originalGVRs} +} + +// KindFor finds all kinds, then passes them through the KindPriority patterns to find a single matching hit. +func (m PriorityRESTMapper) KindFor(partiallySpecifiedResource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) { + originalGVKs, err := m.Delegate.KindsFor(partiallySpecifiedResource) + if err != nil { + return unversioned.GroupVersionKind{}, err + } + if len(originalGVKs) == 1 { + return originalGVKs[0], nil + } + + remainingGVKs := append([]unversioned.GroupVersionKind{}, originalGVKs...) + for _, pattern := range m.KindPriority { + matchedGVKs := []unversioned.GroupVersionKind{} + for _, gvr := range remainingGVKs { + if kindMatches(pattern, gvr) { + matchedGVKs = append(matchedGVKs, gvr) + } + } + + switch len(matchedGVKs) { + case 0: + // if you have no matches, then nothing matched this pattern just move to the next + continue + case 1: + // one match, return + return matchedGVKs[0], nil + default: + // more than one match, use the matched hits as the list moving to the next pattern. + // this way you can have a series of selection criteria + remainingGVKs = matchedGVKs + } + } + + return unversioned.GroupVersionKind{}, &AmbiguousResourceError{PartialResource: partiallySpecifiedResource, MatchingKinds: originalGVKs} +} + +func resourceMatches(pattern unversioned.GroupVersionResource, resource unversioned.GroupVersionResource) bool { + if pattern.Group != AnyGroup && pattern.Group != resource.Group { + return false + } + if pattern.Version != AnyVersion && pattern.Version != resource.Version { + return false + } + if pattern.Resource != AnyResource && pattern.Resource != resource.Resource { + return false + } + + return true +} + +func kindMatches(pattern unversioned.GroupVersionKind, kind unversioned.GroupVersionKind) bool { + if pattern.Group != AnyGroup && pattern.Group != kind.Group { + return false + } + if pattern.Version != AnyVersion && pattern.Version != kind.Version { + return false + } + if pattern.Kind != AnyKind && pattern.Kind != kind.Kind { + return false + } + + return true +} + +func (m PriorityRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (mapping *RESTMapping, err error) { + return m.Delegate.RESTMapping(gk, versions...) +} + +func (m PriorityRESTMapper) AliasesForResource(alias string) (aliases []string, ok bool) { + return m.Delegate.AliasesForResource(alias) +} + +func (m PriorityRESTMapper) ResourceSingularizer(resource string) (singular string, err error) { + return m.Delegate.ResourceSingularizer(resource) +} + +func (m PriorityRESTMapper) ResourcesFor(partiallySpecifiedResource unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) { + return m.Delegate.ResourcesFor(partiallySpecifiedResource) +} + +func (m PriorityRESTMapper) KindsFor(partiallySpecifiedResource unversioned.GroupVersionResource) (gvk []unversioned.GroupVersionKind, err error) { + return m.Delegate.KindsFor(partiallySpecifiedResource) +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/restmapper.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/restmapper.go index 7b1856bc4..d56b18cea 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/restmapper.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/restmapper.go @@ -24,7 +24,6 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/sets" ) // Implements RESTScope interface @@ -330,23 +329,8 @@ func (m *DefaultRESTMapper) KindFor(resource unversioned.GroupVersionResource) ( if err != nil { return unversioned.GroupVersionKind{}, err } - - // TODO for each group, choose the most preferred (first) version. This keeps us consistent with code today. - // eventually, we'll need a RESTMapper that is aware of what's available server-side and deconflicts that with - // user preferences - oneKindPerGroup := []unversioned.GroupVersionKind{} - groupsAdded := sets.String{} - for _, kind := range kinds { - if groupsAdded.Has(kind.Group) { - continue - } - - oneKindPerGroup = append(oneKindPerGroup, kind) - groupsAdded.Insert(kind.Group) - } - - if len(oneKindPerGroup) == 1 { - return oneKindPerGroup[0], nil + if len(kinds) == 1 { + return kinds[0], nil } return unversioned.GroupVersionKind{}, &AmbiguousResourceError{PartialResource: resource, MatchingKinds: kinds} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/restmapper_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/restmapper_test.go deleted file mode 100644 index 8f494d0a6..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta/restmapper_test.go +++ /dev/null @@ -1,550 +0,0 @@ -/* -Copyright 2014 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 meta - -import ( - "errors" - "reflect" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" -) - -type fakeConvertor struct{} - -func (fakeConvertor) Convert(in, out interface{}) error { - return nil -} - -func (fakeConvertor) ConvertToVersion(in runtime.Object, _ string) (runtime.Object, error) { - return in, nil -} - -func (fakeConvertor) ConvertFieldLabel(version, kind, label, value string) (string, string, error) { - return label, value, nil -} - -var validAccessor = resourceAccessor{} -var validConvertor = fakeConvertor{} - -func fakeInterfaces(version unversioned.GroupVersion) (*VersionInterfaces, error) { - return &VersionInterfaces{ObjectConvertor: validConvertor, MetadataAccessor: validAccessor}, nil -} - -var unmatchedErr = errors.New("no version") - -func unmatchedVersionInterfaces(version unversioned.GroupVersion) (*VersionInterfaces, error) { - return nil, unmatchedErr -} - -func TestRESTMapperVersionAndKindForResource(t *testing.T) { - testGroup := "test.group" - testVersion := "test" - testGroupVersion := unversioned.GroupVersion{Group: testGroup, Version: testVersion} - - testCases := []struct { - Resource unversioned.GroupVersionResource - GroupVersionToRegister unversioned.GroupVersion - ExpectedGVK unversioned.GroupVersionKind - Err bool - }{ - {Resource: unversioned.GroupVersionResource{Resource: "internalobjec"}, Err: true}, - {Resource: unversioned.GroupVersionResource{Resource: "internalObjec"}, Err: true}, - - {Resource: unversioned.GroupVersionResource{Resource: "internalobject"}, ExpectedGVK: testGroupVersion.WithKind("InternalObject")}, - {Resource: unversioned.GroupVersionResource{Resource: "internalobjects"}, ExpectedGVK: testGroupVersion.WithKind("InternalObject")}, - } - for i, testCase := range testCases { - mapper := NewDefaultRESTMapper([]unversioned.GroupVersion{testGroupVersion}, fakeInterfaces) - if len(testCase.ExpectedGVK.Kind) != 0 { - mapper.Add(testCase.ExpectedGVK, RESTScopeNamespace) - } - actualGVK, err := mapper.KindFor(testCase.Resource) - - hasErr := err != nil - if hasErr != testCase.Err { - t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err) - continue - } - if err != nil { - continue - } - - if actualGVK != testCase.ExpectedGVK { - t.Errorf("%d: unexpected version and kind: e=%s a=%s", i, testCase.ExpectedGVK, actualGVK) - } - } -} - -func TestRESTMapperGroupForResource(t *testing.T) { - testCases := []struct { - Resource unversioned.GroupVersionResource - GroupVersionKind unversioned.GroupVersionKind - Err bool - }{ - {Resource: unversioned.GroupVersionResource{Resource: "myObject"}, GroupVersionKind: unversioned.GroupVersionKind{Group: "testapi", Version: "test", Kind: "MyObject"}}, - {Resource: unversioned.GroupVersionResource{Resource: "myobject"}, GroupVersionKind: unversioned.GroupVersionKind{Group: "testapi2", Version: "test", Kind: "MyObject"}}, - {Resource: unversioned.GroupVersionResource{Resource: "myObje"}, Err: true, GroupVersionKind: unversioned.GroupVersionKind{Group: "testapi", Version: "test", Kind: "MyObject"}}, - {Resource: unversioned.GroupVersionResource{Resource: "myobje"}, Err: true, GroupVersionKind: unversioned.GroupVersionKind{Group: "testapi", Version: "test", Kind: "MyObject"}}, - } - for i, testCase := range testCases { - mapper := NewDefaultRESTMapper([]unversioned.GroupVersion{testCase.GroupVersionKind.GroupVersion()}, fakeInterfaces) - mapper.Add(testCase.GroupVersionKind, RESTScopeNamespace) - - actualGVK, err := mapper.KindFor(testCase.Resource) - if testCase.Err { - if err == nil { - t.Errorf("%d: expected error", i) - } - } else if err != nil { - t.Errorf("%d: unexpected error: %v", i, err) - } else if actualGVK != testCase.GroupVersionKind { - t.Errorf("%d: expected group %q, got %q", i, testCase.GroupVersionKind, actualGVK) - } - } -} - -func TestRESTMapperKindsFor(t *testing.T) { - testCases := []struct { - Name string - PreferredOrder []unversioned.GroupVersion - KindsToRegister []unversioned.GroupVersionKind - PartialResourceToRequest unversioned.GroupVersionResource - - ExpectedKinds []unversioned.GroupVersionKind - ExpectedKindErr string - }{ - { - Name: "ambiguous groups, with preference order", - PreferredOrder: []unversioned.GroupVersion{ - {Group: "second-group", Version: "first-version"}, - {Group: "first-group", Version: "first-version"}, - }, - KindsToRegister: []unversioned.GroupVersionKind{ - {Group: "first-group", Version: "first-version", Kind: "my-kind"}, - {Group: "first-group", Version: "first-version", Kind: "your-kind"}, - {Group: "second-group", Version: "first-version", Kind: "my-kind"}, - {Group: "second-group", Version: "first-version", Kind: "your-kind"}, - }, - PartialResourceToRequest: unversioned.GroupVersionResource{Resource: "my-kinds"}, - - ExpectedKinds: []unversioned.GroupVersionKind{ - {Group: "second-group", Version: "first-version", Kind: "my-kind"}, - {Group: "first-group", Version: "first-version", Kind: "my-kind"}, - }, - ExpectedKindErr: " matches multiple kinds ", - }, - - { - Name: "ambiguous groups, with explicit group match", - PreferredOrder: []unversioned.GroupVersion{ - {Group: "second-group", Version: "first-version"}, - {Group: "first-group", Version: "first-version"}, - }, - KindsToRegister: []unversioned.GroupVersionKind{ - {Group: "first-group", Version: "first-version", Kind: "my-kind"}, - {Group: "first-group", Version: "first-version", Kind: "your-kind"}, - {Group: "second-group", Version: "first-version", Kind: "my-kind"}, - {Group: "second-group", Version: "first-version", Kind: "your-kind"}, - }, - PartialResourceToRequest: unversioned.GroupVersionResource{Group: "first-group", Resource: "my-kinds"}, - - ExpectedKinds: []unversioned.GroupVersionKind{ - {Group: "first-group", Version: "first-version", Kind: "my-kind"}, - }, - }, - - { - Name: "ambiguous groups, with ambiguous version match", - PreferredOrder: []unversioned.GroupVersion{ - {Group: "first-group", Version: "first-version"}, - {Group: "second-group", Version: "first-version"}, - }, - KindsToRegister: []unversioned.GroupVersionKind{ - {Group: "first-group", Version: "first-version", Kind: "my-kind"}, - {Group: "first-group", Version: "first-version", Kind: "your-kind"}, - {Group: "second-group", Version: "first-version", Kind: "my-kind"}, - {Group: "second-group", Version: "first-version", Kind: "your-kind"}, - }, - PartialResourceToRequest: unversioned.GroupVersionResource{Version: "first-version", Resource: "my-kinds"}, - - ExpectedKinds: []unversioned.GroupVersionKind{ - {Group: "first-group", Version: "first-version", Kind: "my-kind"}, - {Group: "second-group", Version: "first-version", Kind: "my-kind"}, - }, - ExpectedKindErr: " matches multiple kinds ", - }, - } - for _, testCase := range testCases { - tcName := testCase.Name - mapper := NewDefaultRESTMapper(testCase.PreferredOrder, fakeInterfaces) - for _, kind := range testCase.KindsToRegister { - mapper.Add(kind, RESTScopeNamespace) - } - - actualKinds, err := mapper.KindsFor(testCase.PartialResourceToRequest) - if err != nil { - t.Errorf("%s: unexpected error: %v", tcName, err) - continue - } - if !reflect.DeepEqual(testCase.ExpectedKinds, actualKinds) { - t.Errorf("%s: expected %v, got %v", tcName, testCase.ExpectedKinds, actualKinds) - } - - singleKind, err := mapper.KindFor(testCase.PartialResourceToRequest) - if err == nil && len(testCase.ExpectedKindErr) != 0 { - t.Errorf("%s: expected error: %v", tcName, testCase.ExpectedKindErr) - continue - } - if err != nil { - if len(testCase.ExpectedKindErr) == 0 { - t.Errorf("%s: unexpected error: %v", tcName, err) - continue - } else { - if !strings.Contains(err.Error(), testCase.ExpectedKindErr) { - t.Errorf("%s: expected %v, got %v", tcName, testCase.ExpectedKindErr, err) - continue - } - } - - } else { - if testCase.ExpectedKinds[0] != singleKind { - t.Errorf("%s: expected %v, got %v", tcName, testCase.ExpectedKinds[0], singleKind) - } - - } - } -} - -func TestRESTMapperResourcesFor(t *testing.T) { - testCases := []struct { - Name string - PreferredOrder []unversioned.GroupVersion - KindsToRegister []unversioned.GroupVersionKind - PluralPartialResourceToRequest unversioned.GroupVersionResource - SingularPartialResourceToRequest unversioned.GroupVersionResource - - ExpectedResources []unversioned.GroupVersionResource - ExpectedResourceErr string - }{ - { - Name: "ambiguous groups, with preference order", - PreferredOrder: []unversioned.GroupVersion{ - {Group: "second-group", Version: "first-version"}, - {Group: "first-group", Version: "first-version"}, - }, - KindsToRegister: []unversioned.GroupVersionKind{ - {Group: "first-group", Version: "first-version", Kind: "my-kind"}, - {Group: "first-group", Version: "first-version", Kind: "your-kind"}, - {Group: "second-group", Version: "first-version", Kind: "my-kind"}, - {Group: "second-group", Version: "first-version", Kind: "your-kind"}, - }, - PluralPartialResourceToRequest: unversioned.GroupVersionResource{Resource: "my-kinds"}, - SingularPartialResourceToRequest: unversioned.GroupVersionResource{Resource: "my-kind"}, - - ExpectedResources: []unversioned.GroupVersionResource{ - {Group: "second-group", Version: "first-version", Resource: "my-kinds"}, - {Group: "first-group", Version: "first-version", Resource: "my-kinds"}, - }, - ExpectedResourceErr: " matches multiple resources ", - }, - - { - Name: "ambiguous groups, with explicit group match", - PreferredOrder: []unversioned.GroupVersion{ - {Group: "second-group", Version: "first-version"}, - {Group: "first-group", Version: "first-version"}, - }, - KindsToRegister: []unversioned.GroupVersionKind{ - {Group: "first-group", Version: "first-version", Kind: "my-kind"}, - {Group: "first-group", Version: "first-version", Kind: "your-kind"}, - {Group: "second-group", Version: "first-version", Kind: "my-kind"}, - {Group: "second-group", Version: "first-version", Kind: "your-kind"}, - }, - PluralPartialResourceToRequest: unversioned.GroupVersionResource{Group: "first-group", Resource: "my-kinds"}, - SingularPartialResourceToRequest: unversioned.GroupVersionResource{Group: "first-group", Resource: "my-kind"}, - - ExpectedResources: []unversioned.GroupVersionResource{ - {Group: "first-group", Version: "first-version", Resource: "my-kinds"}, - }, - }, - - { - Name: "ambiguous groups, with ambiguous version match", - PreferredOrder: []unversioned.GroupVersion{ - {Group: "first-group", Version: "first-version"}, - {Group: "second-group", Version: "first-version"}, - }, - KindsToRegister: []unversioned.GroupVersionKind{ - {Group: "first-group", Version: "first-version", Kind: "my-kind"}, - {Group: "first-group", Version: "first-version", Kind: "your-kind"}, - {Group: "second-group", Version: "first-version", Kind: "my-kind"}, - {Group: "second-group", Version: "first-version", Kind: "your-kind"}, - }, - PluralPartialResourceToRequest: unversioned.GroupVersionResource{Version: "first-version", Resource: "my-kinds"}, - SingularPartialResourceToRequest: unversioned.GroupVersionResource{Version: "first-version", Resource: "my-kind"}, - - ExpectedResources: []unversioned.GroupVersionResource{ - {Group: "first-group", Version: "first-version", Resource: "my-kinds"}, - {Group: "second-group", Version: "first-version", Resource: "my-kinds"}, - }, - ExpectedResourceErr: " matches multiple resources ", - }, - } - for _, testCase := range testCases { - tcName := testCase.Name - - for _, partialResource := range []unversioned.GroupVersionResource{testCase.PluralPartialResourceToRequest, testCase.SingularPartialResourceToRequest} { - mapper := NewDefaultRESTMapper(testCase.PreferredOrder, fakeInterfaces) - for _, kind := range testCase.KindsToRegister { - mapper.Add(kind, RESTScopeNamespace) - } - - actualResources, err := mapper.ResourcesFor(partialResource) - if err != nil { - t.Errorf("%s: unexpected error: %v", tcName, err) - continue - } - if !reflect.DeepEqual(testCase.ExpectedResources, actualResources) { - t.Errorf("%s: expected %v, got %v", tcName, testCase.ExpectedResources, actualResources) - } - - singleResource, err := mapper.ResourceFor(partialResource) - if err == nil && len(testCase.ExpectedResourceErr) != 0 { - t.Errorf("%s: expected error: %v", tcName, testCase.ExpectedResourceErr) - continue - } - if err != nil { - if len(testCase.ExpectedResourceErr) == 0 { - t.Errorf("%s: unexpected error: %v", tcName, err) - continue - } else { - if !strings.Contains(err.Error(), testCase.ExpectedResourceErr) { - t.Errorf("%s: expected %v, got %v", tcName, testCase.ExpectedResourceErr, err) - continue - } - } - - } else { - if testCase.ExpectedResources[0] != singleResource { - t.Errorf("%s: expected %v, got %v", tcName, testCase.ExpectedResources[0], singleResource) - } - - } - } - } -} - -func TestKindToResource(t *testing.T) { - testCases := []struct { - Kind string - Plural, Singular string - }{ - {Kind: "Pod", Plural: "pods", Singular: "pod"}, - - {Kind: "ReplicationController", Plural: "replicationcontrollers", Singular: "replicationcontroller"}, - - // Add "ies" when ending with "y" - {Kind: "ImageRepository", Plural: "imagerepositories", Singular: "imagerepository"}, - // Add "es" when ending with "s" - {Kind: "miss", Plural: "misses", Singular: "miss"}, - // Add "s" otherwise - {Kind: "lowercase", Plural: "lowercases", Singular: "lowercase"}, - } - for i, testCase := range testCases { - version := unversioned.GroupVersion{} - - plural, singular := KindToResource(version.WithKind(testCase.Kind)) - if singular != version.WithResource(testCase.Singular) || plural != version.WithResource(testCase.Plural) { - t.Errorf("%d: unexpected plural and singular: %v %v", i, plural, singular) - } - } -} - -func TestRESTMapperResourceSingularizer(t *testing.T) { - testGroupVersion := unversioned.GroupVersion{Group: "tgroup", Version: "test"} - - testCases := []struct { - Kind string - Plural string - Singular string - }{ - {Kind: "Pod", Plural: "pods", Singular: "pod"}, - {Kind: "ReplicationController", Plural: "replicationcontrollers", Singular: "replicationcontroller"}, - {Kind: "ImageRepository", Plural: "imagerepositories", Singular: "imagerepository"}, - {Kind: "Status", Plural: "statuses", Singular: "status"}, - - {Kind: "lowercase", Plural: "lowercases", Singular: "lowercase"}, - // TODO this test is broken. This updates to reflect actual behavior. Kinds are expected to be singular - // old (incorrect), coment: Don't add extra s if the original object is already plural - {Kind: "lowercases", Plural: "lowercaseses", Singular: "lowercases"}, - } - for i, testCase := range testCases { - mapper := NewDefaultRESTMapper([]unversioned.GroupVersion{testGroupVersion}, fakeInterfaces) - // create singular/plural mapping - mapper.Add(testGroupVersion.WithKind(testCase.Kind), RESTScopeNamespace) - - singular, err := mapper.ResourceSingularizer(testCase.Plural) - if err != nil { - t.Errorf("%d: unexpected error: %v", i, err) - } - if singular != testCase.Singular { - t.Errorf("%d: mismatched singular: got %v, expected %v", i, singular, testCase.Singular) - } - } -} - -func TestRESTMapperRESTMapping(t *testing.T) { - testGroup := "tgroup" - testGroupVersion := unversioned.GroupVersion{Group: testGroup, Version: "test"} - internalGroupVersion := unversioned.GroupVersion{Group: testGroup, Version: "test"} - - testCases := []struct { - Kind string - APIGroupVersions []unversioned.GroupVersion - DefaultVersions []unversioned.GroupVersion - - Resource string - ExpectedGroupVersion *unversioned.GroupVersion - Err bool - }{ - {Kind: "Unknown", Err: true}, - {Kind: "InternalObject", Err: true}, - - {DefaultVersions: []unversioned.GroupVersion{testGroupVersion}, Kind: "Unknown", Err: true}, - - {DefaultVersions: []unversioned.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"}, - {DefaultVersions: []unversioned.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"}, - - {DefaultVersions: []unversioned.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"}, - - {DefaultVersions: []unversioned.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{}, Resource: "internalobjects", ExpectedGroupVersion: &unversioned.GroupVersion{Group: testGroup, Version: "test"}}, - - {DefaultVersions: []unversioned.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []unversioned.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"}, - - // TODO: add test for a resource that exists in one version but not another - } - for i, testCase := range testCases { - mapper := NewDefaultRESTMapper(testCase.DefaultVersions, fakeInterfaces) - mapper.Add(internalGroupVersion.WithKind("InternalObject"), RESTScopeNamespace) - - preferredVersions := []string{} - for _, gv := range testCase.APIGroupVersions { - preferredVersions = append(preferredVersions, gv.Version) - } - gk := unversioned.GroupKind{Group: testGroup, Kind: testCase.Kind} - - mapping, err := mapper.RESTMapping(gk, preferredVersions...) - hasErr := err != nil - if hasErr != testCase.Err { - t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err) - } - if hasErr { - continue - } - if mapping.Resource != testCase.Resource { - t.Errorf("%d: unexpected resource: %#v", i, mapping) - } - - if mapping.MetadataAccessor == nil || mapping.ObjectConvertor == nil { - t.Errorf("%d: missing codec and accessor: %#v", i, mapping) - } - - groupVersion := testCase.ExpectedGroupVersion - if groupVersion == nil { - groupVersion = &testCase.APIGroupVersions[0] - } - if mapping.GroupVersionKind.GroupVersion() != *groupVersion { - t.Errorf("%d: unexpected version: %#v", i, mapping) - } - - } -} - -func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) { - expectedGroupVersion1 := unversioned.GroupVersion{Group: "tgroup", Version: "test1"} - expectedGroupVersion2 := unversioned.GroupVersion{Group: "tgroup", Version: "test2"} - expectedGroupVersion3 := unversioned.GroupVersion{Group: "tgroup", Version: "test3"} - internalObjectGK := unversioned.GroupKind{Group: "tgroup", Kind: "InternalObject"} - otherObjectGK := unversioned.GroupKind{Group: "tgroup", Kind: "OtherObject"} - - mapper := NewDefaultRESTMapper([]unversioned.GroupVersion{expectedGroupVersion1, expectedGroupVersion2}, fakeInterfaces) - mapper.Add(expectedGroupVersion1.WithKind("InternalObject"), RESTScopeNamespace) - mapper.Add(expectedGroupVersion2.WithKind("OtherObject"), RESTScopeNamespace) - - // pick default matching object kind based on search order - mapping, err := mapper.RESTMapping(otherObjectGK) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if mapping.Resource != "otherobjects" || mapping.GroupVersionKind.GroupVersion() != expectedGroupVersion2 { - t.Errorf("unexpected mapping: %#v", mapping) - } - - mapping, err = mapper.RESTMapping(internalObjectGK) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if mapping.Resource != "internalobjects" || mapping.GroupVersionKind.GroupVersion() != expectedGroupVersion1 { - t.Errorf("unexpected mapping: %#v", mapping) - } - - // mismatch of version - mapping, err = mapper.RESTMapping(internalObjectGK, expectedGroupVersion2.Version) - if err == nil { - t.Errorf("unexpected non-error") - } - mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion1.Version) - if err == nil { - t.Errorf("unexpected non-error") - } - - // not in the search versions - mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion3.Version) - if err == nil { - t.Errorf("unexpected non-error") - } - - // explicit search order - mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion3.Version, expectedGroupVersion1.Version) - if err == nil { - t.Errorf("unexpected non-error") - } - - mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion3.Version, expectedGroupVersion2.Version) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if mapping.Resource != "otherobjects" || mapping.GroupVersionKind.GroupVersion() != expectedGroupVersion2 { - t.Errorf("unexpected mapping: %#v", mapping) - } -} - -func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) { - expectedGroupVersion1 := unversioned.GroupVersion{Group: "tgroup", Version: "test1"} - expectedGroupVersion2 := unversioned.GroupVersion{Group: "tgroup", Version: "test2"} - internalObjectGK := unversioned.GroupKind{Group: "tgroup", Kind: "InternalObject"} - - mapper := NewDefaultRESTMapper([]unversioned.GroupVersion{expectedGroupVersion1, expectedGroupVersion2}, unmatchedVersionInterfaces) - mapper.Add(expectedGroupVersion1.WithKind("InternalObject"), RESTScopeNamespace) - _, err := mapper.RESTMapping(internalObjectGK, expectedGroupVersion1.Version) - if err == nil { - t.Errorf("unexpected non-error") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta_test.go deleted file mode 100644 index e4a9657a0..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/meta_test.go +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2014 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 api_test - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/meta" -) - -var _ meta.Object = &api.ObjectMeta{} - -// TestFillObjectMetaSystemFields validates that system populated fields are set on an object -func TestFillObjectMetaSystemFields(t *testing.T) { - ctx := api.NewDefaultContext() - resource := api.ObjectMeta{} - api.FillObjectMetaSystemFields(ctx, &resource) - if resource.CreationTimestamp.Time.IsZero() { - t.Errorf("resource.CreationTimestamp is zero") - } else if len(resource.UID) == 0 { - t.Errorf("resource.UID missing") - } -} - -// TestHasObjectMetaSystemFieldValues validates that true is returned if and only if all fields are populated -func TestHasObjectMetaSystemFieldValues(t *testing.T) { - ctx := api.NewDefaultContext() - resource := api.ObjectMeta{} - if api.HasObjectMetaSystemFieldValues(&resource) { - t.Errorf("the resource does not have all fields yet populated, but incorrectly reports it does") - } - api.FillObjectMetaSystemFields(ctx, &resource) - if !api.HasObjectMetaSystemFieldValues(&resource) { - t.Errorf("the resource does have all fields populated, but incorrectly reports it does not") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/pod/util.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/pod/util.go index 8e70104aa..6b00c7e7d 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/pod/util.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/pod/util.go @@ -23,6 +23,18 @@ import ( "k8s.io/kubernetes/pkg/util/intstr" ) +const ( + // The annotation value is a string specifying the hostname to be used for the pod e.g 'my-webserver-1' + PodHostnameAnnotation = "pod.beta.kubernetes.io/hostname" + + // The annotation value is a string specifying the subdomain e.g. "my-web-service" + // If specified, on the the pod itself, ".my-web-service..svc." would resolve to + // the pod's IP. + // If there is a headless service named "my-web-service" in the same namespace as the pod, then, + // .my-web-service..svc." would be resolved by the cluster DNS Server. + PodSubdomainAnnotation = "pod.beta.kubernetes.io/subdomain" +) + // FindPort locates the container port for the given pod and portName. If the // targetPort is a number, use that. If the targetPort is a string, look that // string up in all named ports in all containers in the target pod. If no diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/pod/util_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/pod/util_test.go deleted file mode 100644 index 428f2bdcd..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/pod/util_test.go +++ /dev/null @@ -1,110 +0,0 @@ -/* -Copyright 2015 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 pod - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/util/intstr" -) - -func TestFindPort(t *testing.T) { - testCases := []struct { - name string - containers []api.Container - port intstr.IntOrString - expected int - pass bool - }{{ - name: "valid int, no ports", - containers: []api.Container{{}}, - port: intstr.FromInt(93), - expected: 93, - pass: true, - }, { - name: "valid int, with ports", - containers: []api.Container{{Ports: []api.ContainerPort{{ - Name: "", - ContainerPort: 11, - Protocol: "TCP", - }, { - Name: "p", - ContainerPort: 22, - Protocol: "TCP", - }}}}, - port: intstr.FromInt(93), - expected: 93, - pass: true, - }, { - name: "valid str, no ports", - containers: []api.Container{{}}, - port: intstr.FromString("p"), - expected: 0, - pass: false, - }, { - name: "valid str, one ctr with ports", - containers: []api.Container{{Ports: []api.ContainerPort{{ - Name: "", - ContainerPort: 11, - Protocol: "UDP", - }, { - Name: "p", - ContainerPort: 22, - Protocol: "TCP", - }, { - Name: "q", - ContainerPort: 33, - Protocol: "TCP", - }}}}, - port: intstr.FromString("q"), - expected: 33, - pass: true, - }, { - name: "valid str, two ctr with ports", - containers: []api.Container{{}, {Ports: []api.ContainerPort{{ - Name: "", - ContainerPort: 11, - Protocol: "UDP", - }, { - Name: "p", - ContainerPort: 22, - Protocol: "TCP", - }, { - Name: "q", - ContainerPort: 33, - Protocol: "TCP", - }}}}, - port: intstr.FromString("q"), - expected: 33, - pass: true, - }} - - for _, tc := range testCases { - port, err := FindPort(&api.Pod{Spec: api.PodSpec{Containers: tc.containers}}, - &api.ServicePort{Protocol: "TCP", TargetPort: tc.port}) - if err != nil && tc.pass { - t.Errorf("unexpected error for %s: %v", tc.name, err) - } - if err == nil && !tc.pass { - t.Errorf("unexpected non-error for %s: %d", tc.name, port) - } - if port != tc.expected { - t.Errorf("wrong result for %s: expected %d, got %d", tc.name, tc.expected, port) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/ref_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/ref_test.go deleted file mode 100644 index 4f716672e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/ref_test.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright 2014 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 api - -import ( - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" -) - -type FakeAPIObject struct{} - -func (obj *FakeAPIObject) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind } - -type ExtensionAPIObject struct { - unversioned.TypeMeta - ObjectMeta -} - -func (obj *ExtensionAPIObject) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } - -func TestGetReference(t *testing.T) { - table := map[string]struct { - obj runtime.Object - ref *ObjectReference - fieldPath string - shouldErr bool - }{ - "pod": { - obj: &Pod{ - ObjectMeta: ObjectMeta{ - Name: "foo", - UID: "bar", - ResourceVersion: "42", - SelfLink: "/api/version1/pods/foo", - }, - }, - fieldPath: ".desiredState.containers[0]", - ref: &ObjectReference{ - Kind: "Pod", - APIVersion: "version1", - Name: "foo", - UID: "bar", - ResourceVersion: "42", - FieldPath: ".desiredState.containers[0]", - }, - }, - "serviceList": { - obj: &ServiceList{ - ListMeta: unversioned.ListMeta{ - ResourceVersion: "42", - SelfLink: "/api/version2/services", - }, - }, - ref: &ObjectReference{ - Kind: "ServiceList", - APIVersion: "version2", - ResourceVersion: "42", - }, - }, - "extensionAPIObject": { - obj: &ExtensionAPIObject{ - TypeMeta: unversioned.TypeMeta{ - Kind: "ExtensionAPIObject", - }, - ObjectMeta: ObjectMeta{ - Name: "foo", - UID: "bar", - ResourceVersion: "42", - SelfLink: "/custom_prefix/version1/extensions/foo", - }, - }, - ref: &ObjectReference{ - Kind: "ExtensionAPIObject", - APIVersion: "version1", - Name: "foo", - UID: "bar", - ResourceVersion: "42", - }, - }, - "badSelfLink": { - obj: &ServiceList{ - ListMeta: unversioned.ListMeta{ - ResourceVersion: "42", - SelfLink: "version2/services", - }, - }, - shouldErr: true, - }, - "error": { - obj: &FakeAPIObject{}, - ref: nil, - shouldErr: true, - }, - "errorNil": { - obj: nil, - ref: nil, - shouldErr: true, - }, - } - - for name, item := range table { - ref, err := GetPartialReference(item.obj, item.fieldPath) - if e, a := item.shouldErr, (err != nil); e != a { - t.Errorf("%v: expected %v, got %v, err %v", name, e, a, err) - continue - } - if e, a := item.ref, ref; !reflect.DeepEqual(e, a) { - t.Errorf("%v: expected %#v, got %#v", name, e, a) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource/quantity_example_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource/quantity_example_test.go deleted file mode 100644 index 48c3d2555..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource/quantity_example_test.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright 2014 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 resource_test - -import ( - "fmt" - - "k8s.io/kubernetes/pkg/api/resource" -) - -func ExampleFormat() { - memorySize := resource.NewQuantity(5*1024*1024*1024, resource.BinarySI) - fmt.Printf("memorySize = %v\n", memorySize) - - diskSize := resource.NewQuantity(5*1000*1000*1000, resource.DecimalSI) - fmt.Printf("diskSize = %v\n", diskSize) - - cores := resource.NewMilliQuantity(5300, resource.DecimalSI) - fmt.Printf("cores = %v\n", cores) - - // Output: - // memorySize = 5Gi - // diskSize = 5G - // cores = 5300m -} - -func ExampleMustParse() { - memorySize := resource.MustParse("5Gi") - fmt.Printf("memorySize = %v (%v)\n", memorySize.Value(), memorySize.Format) - - diskSize := resource.MustParse("5G") - fmt.Printf("diskSize = %v (%v)\n", diskSize.Value(), diskSize.Format) - - cores := resource.MustParse("5300m") - fmt.Printf("milliCores = %v (%v)\n", cores.MilliValue(), cores.Format) - - cores2 := resource.MustParse("5.4") - fmt.Printf("milliCores = %v (%v)\n", cores2.MilliValue(), cores2.Format) - - // Output: - // memorySize = 5368709120 (BinarySI) - // diskSize = 5000000000 (DecimalSI) - // milliCores = 5300 (DecimalSI) - // milliCores = 5400 (DecimalSI) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource/quantity_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource/quantity_test.go deleted file mode 100644 index bb62251e2..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource/quantity_test.go +++ /dev/null @@ -1,810 +0,0 @@ -/* -Copyright 2014 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 resource - -import ( - //"reflect" - "encoding/json" - "testing" - - fuzz "github.com/google/gofuzz" - "github.com/spf13/pflag" - "speter.net/go/exp/math/dec/inf" -) - -var ( - testQuantityFlag = QuantityFlag("quantityFlag", "1M", "dummy flag for testing the quantity flag mechanism") -) - -func dec(i int64, exponent int) *inf.Dec { - // See the below test-- scale is the negative of an exponent. - return inf.NewDec(i, inf.Scale(-exponent)) -} - -func TestDec(t *testing.T) { - table := []struct { - got *inf.Dec - expect string - }{ - {dec(1, 0), "1"}, - {dec(1, 1), "10"}, - {dec(5, 2), "500"}, - {dec(8, 3), "8000"}, - {dec(2, 0), "2"}, - {dec(1, -1), "0.1"}, - {dec(3, -2), "0.03"}, - {dec(4, -3), "0.004"}, - } - - for _, item := range table { - if e, a := item.expect, item.got.String(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - } -} - -// TestQuantityParseZero ensures that when a 0 quantity is passed, its string value is 0 -func TestQuantityParseZero(t *testing.T) { - zero := MustParse("0") - if expected, actual := "0", zero.String(); expected != actual { - t.Errorf("Expected %v, actual %v", expected, actual) - } -} - -// TestQuantityAddZeroPreservesSuffix verifies that a suffix is preserved -// independent of the order of operations when adding a zero and non-zero val -func TestQuantityAddZeroPreservesSuffix(t *testing.T) { - testValues := []string{"100m", "1Gi"} - zero := MustParse("0") - for _, testValue := range testValues { - value := MustParse(testValue) - v1 := *value.Copy() - // ensure non-zero + zero = non-zero (suffix preserved) - err := v1.Add(zero) - if err != nil { - t.Errorf("Unexpected error %v", err) - } - // ensure zero + non-zero = non-zero (suffix preserved) - v2 := *zero.Copy() - err = v2.Add(value) - if err != nil { - t.Errorf("Unexpected error %v", err) - } - // ensure we preserved the input value - if v1.String() != testValue { - t.Errorf("Expected %v, actual %v", testValue, v1.String()) - } - if v2.String() != testValue { - t.Errorf("Expected %v, actual %v", testValue, v2.String()) - } - } -} - -// TestQuantitySubZeroPreservesSuffix verifies that a suffix is preserved -// independent of the order of operations when subtracting a zero and non-zero val -func TestQuantitySubZeroPreservesSuffix(t *testing.T) { - testValues := []string{"100m", "1Gi"} - zero := MustParse("0") - for _, testValue := range testValues { - value := MustParse(testValue) - v1 := *value.Copy() - // ensure non-zero - zero = non-zero (suffix preserved) - err := v1.Sub(zero) - if err != nil { - t.Errorf("Unexpected error %v", err) - } - // ensure we preserved the input value - if v1.String() != testValue { - t.Errorf("Expected %v, actual %v", testValue, v1.String()) - } - - // ensure zero - non-zero = -non-zero (suffix preserved) - v2 := *zero.Copy() - err = v2.Sub(value) - if err != nil { - t.Errorf("Unexpected error %v", err) - } - negVal := *value.Copy() - err = negVal.Neg(negVal) - if err != nil { - t.Errorf("Unexpected error %v", err) - } - if v2.String() != negVal.String() { - t.Errorf("Expected %v, actual %v", negVal.String(), v2.String()) - } - } -} - -// Verifies that you get 0 as canonical value if internal value is 0, and not 0 -func TestQuantityCanocicalizeZero(t *testing.T) { - val := MustParse("1000m") - x := val.Amount - y := dec(1, 0) - z := val.Amount.Sub(x, y) - zero := Quantity{z, DecimalSI} - if expected, actual := "0", zero.String(); expected != actual { - t.Errorf("Expected %v, actual %v", expected, actual) - } -} - -func TestQuantityCmp(t *testing.T) { - table := []struct { - x string - y string - expect int - }{ - {"0", "0", 0}, - {"100m", "50m", 1}, - {"50m", "100m", -1}, - {"10000T", "100Gi", 1}, - } - for _, testCase := range table { - q1 := MustParse(testCase.x) - q2 := MustParse(testCase.y) - if result := q1.Cmp(q2); result != testCase.expect { - t.Errorf("X: %v, Y: %v, Expected: %v, Actual: %v", testCase.x, testCase.y, testCase.expect, result) - } - } - - nils := []struct { - x *inf.Dec - y *inf.Dec - expect int - }{ - {dec(0, 0), dec(0, 0), 0}, - {nil, dec(0, 0), 0}, - {dec(0, 0), nil, 0}, - {nil, nil, 0}, - {nil, dec(10, 0), -1}, - {nil, dec(-10, 0), 1}, - {dec(10, 0), nil, 1}, - {dec(-10, 0), nil, -1}, - } - for _, nilCase := range nils { - q1 := Quantity{nilCase.x, DecimalSI} - q2 := Quantity{nilCase.y, DecimalSI} - if result := q1.Cmp(q2); result != nilCase.expect { - t.Errorf("X: %v, Y: %v, Expected: %v, Actual: %v", nilCase.x, nilCase.y, nilCase.expect, result) - } - } -} - -func TestQuantityParse(t *testing.T) { - table := []struct { - input string - expect Quantity - }{ - {"0", Quantity{dec(0, 0), DecimalSI}}, - {"0n", Quantity{dec(0, 0), DecimalSI}}, - {"0u", Quantity{dec(0, 0), DecimalSI}}, - {"0m", Quantity{dec(0, 0), DecimalSI}}, - {"0Ki", Quantity{dec(0, 0), BinarySI}}, - {"0k", Quantity{dec(0, 0), DecimalSI}}, - {"0Mi", Quantity{dec(0, 0), BinarySI}}, - {"0M", Quantity{dec(0, 0), DecimalSI}}, - {"0Gi", Quantity{dec(0, 0), BinarySI}}, - {"0G", Quantity{dec(0, 0), DecimalSI}}, - {"0Ti", Quantity{dec(0, 0), BinarySI}}, - {"0T", Quantity{dec(0, 0), DecimalSI}}, - - // Binary suffixes - {"1Ki", Quantity{dec(1024, 0), BinarySI}}, - {"8Ki", Quantity{dec(8*1024, 0), BinarySI}}, - {"7Mi", Quantity{dec(7*1024*1024, 0), BinarySI}}, - {"6Gi", Quantity{dec(6*1024*1024*1024, 0), BinarySI}}, - {"5Ti", Quantity{dec(5*1024*1024*1024*1024, 0), BinarySI}}, - {"4Pi", Quantity{dec(4*1024*1024*1024*1024*1024, 0), BinarySI}}, - {"3Ei", Quantity{dec(3*1024*1024*1024*1024*1024*1024, 0), BinarySI}}, - - {"10Ti", Quantity{dec(10*1024*1024*1024*1024, 0), BinarySI}}, - {"100Ti", Quantity{dec(100*1024*1024*1024*1024, 0), BinarySI}}, - - // Decimal suffixes - {"5n", Quantity{dec(5, -9), DecimalSI}}, - {"4u", Quantity{dec(4, -6), DecimalSI}}, - {"3m", Quantity{dec(3, -3), DecimalSI}}, - {"9", Quantity{dec(9, 0), DecimalSI}}, - {"8k", Quantity{dec(8, 3), DecimalSI}}, - {"7M", Quantity{dec(7, 6), DecimalSI}}, - {"6G", Quantity{dec(6, 9), DecimalSI}}, - {"5T", Quantity{dec(5, 12), DecimalSI}}, - {"40T", Quantity{dec(4, 13), DecimalSI}}, - {"300T", Quantity{dec(3, 14), DecimalSI}}, - {"2P", Quantity{dec(2, 15), DecimalSI}}, - {"1E", Quantity{dec(1, 18), DecimalSI}}, - - // Decimal exponents - {"1E-3", Quantity{dec(1, -3), DecimalExponent}}, - {"1e3", Quantity{dec(1, 3), DecimalExponent}}, - {"1E6", Quantity{dec(1, 6), DecimalExponent}}, - {"1e9", Quantity{dec(1, 9), DecimalExponent}}, - {"1E12", Quantity{dec(1, 12), DecimalExponent}}, - {"1e15", Quantity{dec(1, 15), DecimalExponent}}, - {"1E18", Quantity{dec(1, 18), DecimalExponent}}, - - // Nonstandard but still parsable - {"1e14", Quantity{dec(1, 14), DecimalExponent}}, - {"1e13", Quantity{dec(1, 13), DecimalExponent}}, - {"1e3", Quantity{dec(1, 3), DecimalExponent}}, - {"100.035k", Quantity{dec(100035, 0), DecimalSI}}, - - // Things that look like floating point - {"0.001", Quantity{dec(1, -3), DecimalSI}}, - {"0.0005k", Quantity{dec(5, -1), DecimalSI}}, - {"0.005", Quantity{dec(5, -3), DecimalSI}}, - {"0.05", Quantity{dec(5, -2), DecimalSI}}, - {"0.5", Quantity{dec(5, -1), DecimalSI}}, - {"0.00050k", Quantity{dec(5, -1), DecimalSI}}, - {"0.00500", Quantity{dec(5, -3), DecimalSI}}, - {"0.05000", Quantity{dec(5, -2), DecimalSI}}, - {"0.50000", Quantity{dec(5, -1), DecimalSI}}, - {"0.5e0", Quantity{dec(5, -1), DecimalExponent}}, - {"0.5e-1", Quantity{dec(5, -2), DecimalExponent}}, - {"0.5e-2", Quantity{dec(5, -3), DecimalExponent}}, - {"0.5e0", Quantity{dec(5, -1), DecimalExponent}}, - {"10.035M", Quantity{dec(10035, 3), DecimalSI}}, - - {"1.2e3", Quantity{dec(12, 2), DecimalExponent}}, - {"1.3E+6", Quantity{dec(13, 5), DecimalExponent}}, - {"1.40e9", Quantity{dec(14, 8), DecimalExponent}}, - {"1.53E12", Quantity{dec(153, 10), DecimalExponent}}, - {"1.6e15", Quantity{dec(16, 14), DecimalExponent}}, - {"1.7E18", Quantity{dec(17, 17), DecimalExponent}}, - - {"9.01", Quantity{dec(901, -2), DecimalSI}}, - {"8.1k", Quantity{dec(81, 2), DecimalSI}}, - {"7.123456M", Quantity{dec(7123456, 0), DecimalSI}}, - {"6.987654321G", Quantity{dec(6987654321, 0), DecimalSI}}, - {"5.444T", Quantity{dec(5444, 9), DecimalSI}}, - {"40.1T", Quantity{dec(401, 11), DecimalSI}}, - {"300.2T", Quantity{dec(3002, 11), DecimalSI}}, - {"2.5P", Quantity{dec(25, 14), DecimalSI}}, - {"1.01E", Quantity{dec(101, 16), DecimalSI}}, - - // Things that saturate/round - {"3.001n", Quantity{dec(4, -9), DecimalSI}}, - {"1.1E-9", Quantity{dec(2, -9), DecimalExponent}}, - {"0.0000000001", Quantity{dec(1, -9), DecimalSI}}, - {"0.0000000005", Quantity{dec(1, -9), DecimalSI}}, - {"0.00000000050", Quantity{dec(1, -9), DecimalSI}}, - {"0.5e-9", Quantity{dec(1, -9), DecimalExponent}}, - {"0.9n", Quantity{dec(1, -9), DecimalSI}}, - {"0.00000012345", Quantity{dec(124, -9), DecimalSI}}, - {"0.00000012354", Quantity{dec(124, -9), DecimalSI}}, - {"9Ei", Quantity{maxAllowed, BinarySI}}, - {"9223372036854775807Ki", Quantity{maxAllowed, BinarySI}}, - {"12E", Quantity{maxAllowed, DecimalSI}}, - - // We'll accept fractional binary stuff, too. - {"100.035Ki", Quantity{dec(10243584, -2), BinarySI}}, - {"0.5Mi", Quantity{dec(.5*1024*1024, 0), BinarySI}}, - {"0.05Gi", Quantity{dec(536870912, -1), BinarySI}}, - {"0.025Ti", Quantity{dec(274877906944, -1), BinarySI}}, - - // Things written by trolls - {"0.000000000001Ki", Quantity{dec(2, -9), DecimalSI}}, // rounds up, changes format - {".001", Quantity{dec(1, -3), DecimalSI}}, - {".0001k", Quantity{dec(100, -3), DecimalSI}}, - {"1.", Quantity{dec(1, 0), DecimalSI}}, - {"1.G", Quantity{dec(1, 9), DecimalSI}}, - } - - for _, item := range table { - got, err := ParseQuantity(item.input) - if err != nil { - t.Errorf("%v: unexpected error: %v", item.input, err) - continue - } - if e, a := item.expect.Amount, got.Amount; e.Cmp(a) != 0 { - t.Errorf("%v: expected %v, got %v", item.input, e, a) - } - if e, a := item.expect.Format, got.Format; e != a { - t.Errorf("%v: expected %#v, got %#v", item.input, e, a) - } - } - - // Try the negative version of everything - desired := &inf.Dec{} - for _, item := range table { - got, err := ParseQuantity("-" + item.input) - if err != nil { - t.Errorf("-%v: unexpected error: %v", item.input, err) - continue - } - desired.Neg(item.expect.Amount) - if e, a := desired, got.Amount; e.Cmp(a) != 0 { - t.Errorf("%v: expected %v, got %v", item.input, e, a) - } - if e, a := item.expect.Format, got.Format; e != a { - t.Errorf("%v: expected %#v, got %#v", item.input, e, a) - } - } - - // Try everything with an explicit + - for _, item := range table { - got, err := ParseQuantity("+" + item.input) - if err != nil { - t.Errorf("-%v: unexpected error: %v", item.input, err) - continue - } - if e, a := item.expect.Amount, got.Amount; e.Cmp(a) != 0 { - t.Errorf("%v: expected %v, got %v", item.input, e, a) - } - if e, a := item.expect.Format, got.Format; e != a { - t.Errorf("%v: expected %#v, got %#v", item.input, e, a) - } - } - - invalid := []string{ - "1.1.M", - "1+1.0M", - "0.1mi", - "0.1am", - "aoeu", - ".5i", - "1i", - "-3.01i", - } - for _, item := range invalid { - _, err := ParseQuantity(item) - if err == nil { - t.Errorf("%v parsed unexpectedly", item) - } - } -} - -func TestQuantityString(t *testing.T) { - table := []struct { - in Quantity - expect string - }{ - {Quantity{dec(1024*1024*1024, 0), BinarySI}, "1Gi"}, - {Quantity{dec(300*1024*1024, 0), BinarySI}, "300Mi"}, - {Quantity{dec(6*1024, 0), BinarySI}, "6Ki"}, - {Quantity{dec(1001*1024*1024*1024, 0), BinarySI}, "1001Gi"}, - {Quantity{dec(1024*1024*1024*1024, 0), BinarySI}, "1Ti"}, - {Quantity{dec(5, 0), BinarySI}, "5"}, - {Quantity{dec(500, -3), BinarySI}, "500m"}, - {Quantity{dec(1, 9), DecimalSI}, "1G"}, - {Quantity{dec(1000, 6), DecimalSI}, "1G"}, - {Quantity{dec(1000000, 3), DecimalSI}, "1G"}, - {Quantity{dec(1000000000, 0), DecimalSI}, "1G"}, - {Quantity{dec(1, -3), DecimalSI}, "1m"}, - {Quantity{dec(80, -3), DecimalSI}, "80m"}, - {Quantity{dec(1080, -3), DecimalSI}, "1080m"}, - {Quantity{dec(108, -2), DecimalSI}, "1080m"}, - {Quantity{dec(10800, -4), DecimalSI}, "1080m"}, - {Quantity{dec(300, 6), DecimalSI}, "300M"}, - {Quantity{dec(1, 12), DecimalSI}, "1T"}, - {Quantity{dec(1234567, 6), DecimalSI}, "1234567M"}, - {Quantity{dec(1234567, -3), BinarySI}, "1234567m"}, - {Quantity{dec(3, 3), DecimalSI}, "3k"}, - {Quantity{dec(1025, 0), BinarySI}, "1025"}, - {Quantity{dec(0, 0), DecimalSI}, "0"}, - {Quantity{dec(0, 0), BinarySI}, "0"}, - {Quantity{dec(1, 9), DecimalExponent}, "1e9"}, - {Quantity{dec(1, -3), DecimalExponent}, "1e-3"}, - {Quantity{dec(1, -9), DecimalExponent}, "1e-9"}, - {Quantity{dec(80, -3), DecimalExponent}, "80e-3"}, - {Quantity{dec(300, 6), DecimalExponent}, "300e6"}, - {Quantity{dec(1, 12), DecimalExponent}, "1e12"}, - {Quantity{dec(1, 3), DecimalExponent}, "1e3"}, - {Quantity{dec(3, 3), DecimalExponent}, "3e3"}, - {Quantity{dec(3, 3), DecimalSI}, "3k"}, - {Quantity{dec(0, 0), DecimalExponent}, "0"}, - {Quantity{dec(1, -9), DecimalSI}, "1n"}, - {Quantity{dec(80, -9), DecimalSI}, "80n"}, - {Quantity{dec(1080, -9), DecimalSI}, "1080n"}, - {Quantity{dec(108, -8), DecimalSI}, "1080n"}, - {Quantity{dec(10800, -10), DecimalSI}, "1080n"}, - {Quantity{dec(1, -6), DecimalSI}, "1u"}, - {Quantity{dec(80, -6), DecimalSI}, "80u"}, - {Quantity{dec(1080, -6), DecimalSI}, "1080u"}, - } - for _, item := range table { - got := item.in.String() - if e, a := item.expect, got; e != a { - t.Errorf("%#v: expected %v, got %v", item.in, e, a) - } - } - desired := &inf.Dec{} // Avoid modifying the values in the table. - for _, item := range table { - if item.in.Amount.Cmp(decZero) == 0 { - // Don't expect it to print "-0" ever - continue - } - q := item.in - q.Amount = desired.Neg(q.Amount) - if e, a := "-"+item.expect, q.String(); e != a { - t.Errorf("%#v: expected %v, got %v", item.in, e, a) - } - } -} - -func TestQuantityParseEmit(t *testing.T) { - table := []struct { - in string - expect string - }{ - {"1Ki", "1Ki"}, - {"1Mi", "1Mi"}, - {"1Gi", "1Gi"}, - {"1024Mi", "1Gi"}, - {"1000M", "1G"}, - {".001Ki", "1024m"}, - {".000001Ki", "1024u"}, - {".000000001Ki", "1024n"}, - {".000000000001Ki", "2n"}, - } - - for _, item := range table { - q, err := ParseQuantity(item.in) - if err != nil { - t.Errorf("Couldn't parse %v", item.in) - continue - } - if e, a := item.expect, q.String(); e != a { - t.Errorf("%#v: expected %v, got %v", item.in, e, a) - } - } - for _, item := range table { - q, err := ParseQuantity("-" + item.in) - if err != nil { - t.Errorf("Couldn't parse %v", item.in) - continue - } - if q.Amount.Cmp(decZero) == 0 { - continue - } - if e, a := "-"+item.expect, q.String(); e != a { - t.Errorf("%#v: expected %v, got %v", item.in, e, a) - } - } -} - -var fuzzer = fuzz.New().Funcs( - func(q *Quantity, c fuzz.Continue) { - q.Amount = &inf.Dec{} - if c.RandBool() { - q.Format = BinarySI - if c.RandBool() { - q.Amount.SetScale(0) - q.Amount.SetUnscaled(c.Int63()) - return - } - // Be sure to test cases like 1Mi - q.Amount.SetScale(0) - q.Amount.SetUnscaled(c.Int63n(1024) << uint(10*c.Intn(5))) - return - } - if c.RandBool() { - q.Format = DecimalSI - } else { - q.Format = DecimalExponent - } - if c.RandBool() { - q.Amount.SetScale(inf.Scale(c.Intn(4))) - q.Amount.SetUnscaled(c.Int63()) - return - } - // Be sure to test cases like 1M - q.Amount.SetScale(inf.Scale(3 - c.Intn(15))) - q.Amount.SetUnscaled(c.Int63n(1000)) - }, -) - -func TestJSON(t *testing.T) { - for i := 0; i < 500; i++ { - q := &Quantity{} - fuzzer.Fuzz(q) - b, err := json.Marshal(q) - if err != nil { - t.Errorf("error encoding %v", q) - } - q2 := &Quantity{} - err = json.Unmarshal(b, q2) - if err != nil { - t.Errorf("%v: error decoding %v", q, string(b)) - } - if q2.Amount.Cmp(q.Amount) != 0 { - t.Errorf("Expected equal: %v, %v (json was '%v')", q, q2, string(b)) - } - } -} - -func TestMilliNewSet(t *testing.T) { - table := []struct { - value int64 - format Format - expect string - exact bool - }{ - {1, DecimalSI, "1m", true}, - {1000, DecimalSI, "1", true}, - {1234000, DecimalSI, "1234", true}, - {1024, BinarySI, "1024m", false}, // Format changes - {1000000, "invalidFormatDefaultsToExponent", "1e3", true}, - {1024 * 1024, BinarySI, "1048576m", false}, // Format changes - } - - for _, item := range table { - q := NewMilliQuantity(item.value, item.format) - if e, a := item.expect, q.String(); e != a { - t.Errorf("Expected %v, got %v; %#v", e, a, q) - } - if !item.exact { - continue - } - q2, err := ParseQuantity(q.String()) - if err != nil { - t.Errorf("Round trip failed on %v", q) - } - if e, a := item.value, q2.MilliValue(); e != a { - t.Errorf("Expected %v, got %v", e, a) - } - } - - for _, item := range table { - q := NewQuantity(0, item.format) - q.SetMilli(item.value) - if e, a := item.expect, q.String(); e != a { - t.Errorf("Set: Expected %v, got %v; %#v", e, a, q) - } - } -} - -func TestNewSet(t *testing.T) { - table := []struct { - value int64 - format Format - expect string - }{ - {1, DecimalSI, "1"}, - {1000, DecimalSI, "1k"}, - {1234000, DecimalSI, "1234k"}, - {1024, BinarySI, "1Ki"}, - {1000000, "invalidFormatDefaultsToExponent", "1e6"}, - {1024 * 1024, BinarySI, "1Mi"}, - } - - for _, item := range table { - q := NewQuantity(item.value, item.format) - if e, a := item.expect, q.String(); e != a { - t.Errorf("Expected %v, got %v; %#v", e, a, q) - } - q2, err := ParseQuantity(q.String()) - if err != nil { - t.Errorf("Round trip failed on %v", q) - } - if e, a := item.value, q2.Value(); e != a { - t.Errorf("Expected %v, got %v", e, a) - } - } - - for _, item := range table { - q := NewQuantity(0, item.format) - q.Set(item.value) - if e, a := item.expect, q.String(); e != a { - t.Errorf("Set: Expected %v, got %v; %#v", e, a, q) - } - } -} - -func TestNewScaledSet(t *testing.T) { - table := []struct { - value int64 - scale Scale - expect string - }{ - {1, Nano, "1n"}, - {1000, Nano, "1u"}, - {1, Micro, "1u"}, - {1000, Micro, "1m"}, - {1, Milli, "1m"}, - {1000, Milli, "1"}, - {1, 0, "1"}, - {0, Nano, "0"}, - {0, Micro, "0"}, - {0, Milli, "0"}, - {0, 0, "0"}, - } - - for _, item := range table { - q := NewScaledQuantity(item.value, item.scale) - if e, a := item.expect, q.String(); e != a { - t.Errorf("Expected %v, got %v; %#v", e, a, q) - } - q2, err := ParseQuantity(q.String()) - if err != nil { - t.Errorf("Round trip failed on %v", q) - } - if e, a := item.value, q2.ScaledValue(item.scale); e != a { - t.Errorf("Expected %v, got %v", e, a) - } - q3 := NewQuantity(0, DecimalSI) - q3.SetScaled(item.value, item.scale) - if q.Cmp(*q3) != 0 { - t.Errorf("Expected %v and %v to be equal", q, q3) - } - } -} - -func TestScaledValue(t *testing.T) { - table := []struct { - fromScale Scale - toScale Scale - expected int64 - }{ - {Nano, Nano, 1}, - {Nano, Micro, 1}, - {Nano, Milli, 1}, - {Nano, 0, 1}, - {Micro, Nano, 1000}, - {Micro, Micro, 1}, - {Micro, Milli, 1}, - {Micro, 0, 1}, - {Milli, Nano, 1000 * 1000}, - {Milli, Micro, 1000}, - {Milli, Milli, 1}, - {Milli, 0, 1}, - {0, Nano, 1000 * 1000 * 1000}, - {0, Micro, 1000 * 1000}, - {0, Milli, 1000}, - {0, 0, 1}, - } - - for _, item := range table { - q := NewScaledQuantity(1, item.fromScale) - if e, a := item.expected, q.ScaledValue(item.toScale); e != a { - t.Errorf("%v to %v: Expected %v, got %v", item.fromScale, item.toScale, e, a) - } - } -} - -func TestUninitializedNoCrash(t *testing.T) { - var q Quantity - - q.Value() - q.MilliValue() - q.Copy() - _ = q.String() - q.MarshalJSON() -} - -func TestCopy(t *testing.T) { - q := NewQuantity(5, DecimalSI) - c := q.Copy() - c.Set(6) - if q.Value() == 6 { - t.Errorf("Copy didn't") - } -} - -func TestQFlagSet(t *testing.T) { - qf := qFlag{&Quantity{}} - qf.Set("1Ki") - if e, a := "1Ki", qf.String(); e != a { - t.Errorf("Unexpected result %v != %v", e, a) - } -} - -func TestQFlagIsPFlag(t *testing.T) { - var pfv pflag.Value = qFlag{} - if e, a := "quantity", pfv.Type(); e != a { - t.Errorf("Unexpected result %v != %v", e, a) - } -} - -func TestSub(t *testing.T) { - tests := []struct { - a Quantity - b Quantity - expected Quantity - }{ - {Quantity{dec(10, 0), DecimalSI}, Quantity{dec(1, 1), DecimalSI}, Quantity{dec(0, 0), DecimalSI}}, - {Quantity{dec(10, 0), DecimalSI}, Quantity{dec(1, 0), BinarySI}, Quantity{dec(9, 0), DecimalSI}}, - {Quantity{dec(10, 0), BinarySI}, Quantity{dec(1, 0), DecimalSI}, Quantity{dec(9, 0), BinarySI}}, - {Quantity{nil, DecimalSI}, Quantity{dec(50, 0), DecimalSI}, Quantity{dec(-50, 0), DecimalSI}}, - {Quantity{dec(50, 0), DecimalSI}, Quantity{nil, DecimalSI}, Quantity{dec(50, 0), DecimalSI}}, - {Quantity{nil, DecimalSI}, Quantity{nil, DecimalSI}, Quantity{dec(0, 0), DecimalSI}}, - } - - for i, test := range tests { - test.a.Sub(test.b) - if test.a.Cmp(test.expected) != 0 { - t.Errorf("[%d] Expected %q, got %q", i, test.expected.String(), test.a.String()) - } - } -} - -func TestNeg(t *testing.T) { - tests := []struct { - a Quantity - b Quantity - expected Quantity - }{ - { - a: Quantity{dec(0, 0), DecimalSI}, - b: Quantity{dec(10, 0), DecimalSI}, - expected: Quantity{dec(-10, 0), DecimalSI}, - }, - { - a: Quantity{dec(0, 0), DecimalSI}, - b: Quantity{dec(-10, 0), DecimalSI}, - expected: Quantity{dec(10, 0), DecimalSI}, - }, - { - a: Quantity{dec(0, 0), DecimalSI}, - b: Quantity{dec(10, 0), BinarySI}, - expected: Quantity{dec(-10, 0), BinarySI}, - }, - { - a: Quantity{dec(0, 0), DecimalSI}, - b: Quantity{dec(0, 0), BinarySI}, - expected: Quantity{dec(0, 0), BinarySI}, - }, - { - a: Quantity{}, - b: Quantity{dec(10, 0), BinarySI}, - expected: Quantity{dec(-10, 0), BinarySI}, - }, - { - a: Quantity{dec(10, 0), BinarySI}, - b: Quantity{}, - expected: Quantity{}, - }, - { - a: Quantity{dec(10, 0), BinarySI}, - b: Quantity{Format: DecimalSI}, - expected: Quantity{dec(0, 0), DecimalSI}, - }, - } - - for i, test := range tests { - test.a.Neg(test.b) - // ensure value is same - if test.a.Cmp(test.expected) != 0 { - t.Errorf("[%d] Expected %q, got %q", i, test.expected.String(), test.a.String()) - } - // ensure format is updated - if test.a.Format != test.expected.Format { - t.Errorf("[%d] Expected format %v, got format %v", i, test.expected.Format, test.a.Format) - } - } -} - -func TestAdd(t *testing.T) { - tests := []struct { - a Quantity - b Quantity - expected Quantity - }{ - {Quantity{dec(10, 0), DecimalSI}, Quantity{dec(1, 1), DecimalSI}, Quantity{dec(20, 0), DecimalSI}}, - {Quantity{dec(10, 0), DecimalSI}, Quantity{dec(1, 0), BinarySI}, Quantity{dec(11, 0), DecimalSI}}, - {Quantity{dec(10, 0), BinarySI}, Quantity{dec(1, 0), DecimalSI}, Quantity{dec(11, 0), BinarySI}}, - {Quantity{nil, DecimalSI}, Quantity{dec(50, 0), DecimalSI}, Quantity{dec(50, 0), DecimalSI}}, - {Quantity{dec(50, 0), DecimalSI}, Quantity{nil, DecimalSI}, Quantity{dec(50, 0), DecimalSI}}, - {Quantity{nil, DecimalSI}, Quantity{nil, DecimalSI}, Quantity{dec(0, 0), DecimalSI}}, - } - - for i, test := range tests { - test.a.Add(test.b) - if test.a.Cmp(test.expected) != 0 { - t.Errorf("[%d] Expected %q, got %q", i, test.expected.String(), test.a.String()) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource/scale_int_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource/scale_int_test.go deleted file mode 100644 index 1b4390e55..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource/scale_int_test.go +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright 2015 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 resource - -import ( - "math" - "math/big" - "testing" -) - -func TestScaledValueInternal(t *testing.T) { - tests := []struct { - unscaled *big.Int - scale int - newScale int - - want int64 - }{ - // remain scale - {big.NewInt(1000), 0, 0, 1000}, - - // scale down - {big.NewInt(1000), 0, -3, 1}, - {big.NewInt(1000), 3, 0, 1}, - {big.NewInt(0), 3, 0, 0}, - - // always round up - {big.NewInt(999), 3, 0, 1}, - {big.NewInt(500), 3, 0, 1}, - {big.NewInt(499), 3, 0, 1}, - {big.NewInt(1), 3, 0, 1}, - // large scaled value does not lose precision - {big.NewInt(0).Sub(maxInt64, bigOne), 1, 0, (math.MaxInt64-1)/10 + 1}, - // large intermidiate result. - {big.NewInt(1).Exp(big.NewInt(10), big.NewInt(100), nil), 100, 0, 1}, - - // scale up - {big.NewInt(0), 0, 3, 0}, - {big.NewInt(1), 0, 3, 1000}, - {big.NewInt(1), -3, 0, 1000}, - {big.NewInt(1000), -3, 2, 100000000}, - {big.NewInt(0).Div(big.NewInt(math.MaxInt64), bigThousand), 0, 3, - (math.MaxInt64 / 1000) * 1000}, - } - - for i, tt := range tests { - old := (&big.Int{}).Set(tt.unscaled) - got := scaledValue(tt.unscaled, tt.scale, tt.newScale) - if got != tt.want { - t.Errorf("#%d: got = %v, want %v", i, got, tt.want) - } - if tt.unscaled.Cmp(old) != 0 { - t.Errorf("#%d: unscaled = %v, want %v", i, tt.unscaled, old) - } - } -} - -func BenchmarkScaledValueSmall(b *testing.B) { - s := big.NewInt(1000) - for i := 0; i < b.N; i++ { - scaledValue(s, 3, 0) - } -} - -func BenchmarkScaledValueLarge(b *testing.B) { - s := big.NewInt(math.MaxInt64) - s.Mul(s, big.NewInt(1000)) - for i := 0; i < b.N; i++ { - scaledValue(s, 10, 0) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource_helpers_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource_helpers_test.go deleted file mode 100644 index 12c0ce7db..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/resource_helpers_test.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2015 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 api - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api/resource" -) - -func TestResourceHelpers(t *testing.T) { - cpuLimit := resource.MustParse("10") - memoryLimit := resource.MustParse("10G") - resourceSpec := ResourceRequirements{ - Limits: ResourceList{ - "cpu": cpuLimit, - "memory": memoryLimit, - "kube.io/storage": memoryLimit, - }, - } - if res := resourceSpec.Limits.Cpu(); *res != cpuLimit { - t.Errorf("expected cpulimit %v, got %v", cpuLimit, res) - } - if res := resourceSpec.Limits.Memory(); *res != memoryLimit { - t.Errorf("expected memorylimit %v, got %v", memoryLimit, res) - } - resourceSpec = ResourceRequirements{ - Limits: ResourceList{ - "memory": memoryLimit, - "kube.io/storage": memoryLimit, - }, - } - if res := resourceSpec.Limits.Cpu(); res.Value() != 0 { - t.Errorf("expected cpulimit %v, got %v", 0, res) - } - if res := resourceSpec.Limits.Memory(); *res != memoryLimit { - t.Errorf("expected memorylimit %v, got %v", memoryLimit, res) - } -} - -func TestDefaultResourceHelpers(t *testing.T) { - resourceList := ResourceList{} - if resourceList.Cpu().Format != resource.DecimalSI { - t.Errorf("expected %v, actual %v", resource.DecimalSI, resourceList.Cpu().Format) - } - if resourceList.Memory().Format != resource.BinarySI { - t.Errorf("expected %v, actual %v", resource.BinarySI, resourceList.Memory().Format) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/create.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/create.go deleted file mode 100644 index 4e3a8938b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/create.go +++ /dev/null @@ -1,126 +0,0 @@ -/* -Copyright 2014 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 rest - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/validation" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/validation/field" -) - -// RESTCreateStrategy defines the minimum validation, accepted input, and -// name generation behavior to create an object that follows Kubernetes -// API conventions. -type RESTCreateStrategy interface { - runtime.ObjectTyper - // The name generate is used when the standard GenerateName field is set. - // The NameGenerator will be invoked prior to validation. - api.NameGenerator - - // NamespaceScoped returns true if the object must be within a namespace. - NamespaceScoped() bool - // PrepareForCreate is invoked on create before validation to normalize - // the object. For example: remove fields that are not to be persisted, - // sort order-insensitive list fields, etc. This should not remove fields - // whose presence would be considered a validation error. - PrepareForCreate(obj runtime.Object) - // Validate is invoked after default fields in the object have been filled in before - // the object is persisted. This method should not mutate the object. - Validate(ctx api.Context, obj runtime.Object) field.ErrorList - // Canonicalize is invoked after validation has succeeded but before the - // object has been persisted. This method may mutate the object. - Canonicalize(obj runtime.Object) -} - -// BeforeCreate ensures that common operations for all resources are performed on creation. It only returns -// errors that can be converted to api.Status. It invokes PrepareForCreate, then GenerateName, then Validate. -// It returns nil if the object should be created. -func BeforeCreate(strategy RESTCreateStrategy, ctx api.Context, obj runtime.Object) error { - objectMeta, kind, kerr := objectMetaAndKind(strategy, obj) - if kerr != nil { - return kerr - } - - if strategy.NamespaceScoped() { - if !api.ValidNamespace(ctx, objectMeta) { - return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request") - } - } else { - objectMeta.Namespace = api.NamespaceNone - } - objectMeta.DeletionTimestamp = nil - objectMeta.DeletionGracePeriodSeconds = nil - strategy.PrepareForCreate(obj) - api.FillObjectMetaSystemFields(ctx, objectMeta) - api.GenerateName(strategy, objectMeta) - - if errs := strategy.Validate(ctx, obj); len(errs) > 0 { - return errors.NewInvalid(kind.GroupKind(), objectMeta.Name, errs) - } - - // Custom validation (including name validation) passed - // Now run common validation on object meta - // Do this *after* custom validation so that specific error messages are shown whenever possible - if errs := validation.ValidateObjectMeta(objectMeta, strategy.NamespaceScoped(), validation.ValidatePathSegmentName, field.NewPath("metadata")); len(errs) > 0 { - return errors.NewInvalid(kind.GroupKind(), objectMeta.Name, errs) - } - - strategy.Canonicalize(obj) - - return nil -} - -// CheckGeneratedNameError checks whether an error that occurred creating a resource is due -// to generation being unable to pick a valid name. -func CheckGeneratedNameError(strategy RESTCreateStrategy, err error, obj runtime.Object) error { - if !errors.IsAlreadyExists(err) { - return err - } - - objectMeta, kind, kerr := objectMetaAndKind(strategy, obj) - if kerr != nil { - return kerr - } - - if len(objectMeta.GenerateName) == 0 { - return err - } - - return errors.NewServerTimeoutForKind(kind.GroupKind(), "POST", 0) -} - -// objectMetaAndKind retrieves kind and ObjectMeta from a runtime object, or returns an error. -func objectMetaAndKind(typer runtime.ObjectTyper, obj runtime.Object) (*api.ObjectMeta, unversioned.GroupVersionKind, error) { - objectMeta, err := api.ObjectMetaFor(obj) - if err != nil { - return nil, unversioned.GroupVersionKind{}, errors.NewInternalError(err) - } - kind, err := typer.ObjectKind(obj) - if err != nil { - return nil, unversioned.GroupVersionKind{}, errors.NewInternalError(err) - } - return objectMeta, kind, nil -} - -// NamespaceScopedStrategy has a method to tell if the object must be in a namespace. -type NamespaceScopedStrategy interface { - // NamespaceScoped returns if the object must be in a namespace. - NamespaceScoped() bool -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/delete.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/delete.go deleted file mode 100644 index c05f3446d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/delete.go +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright 2014 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 rest - -import ( - "time" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" -) - -// RESTDeleteStrategy defines deletion behavior on an object that follows Kubernetes -// API conventions. -type RESTDeleteStrategy interface { - runtime.ObjectTyper - - // CheckGracefulDelete should return true if the object can be gracefully deleted and set - // any default values on the DeleteOptions. - CheckGracefulDelete(obj runtime.Object, options *api.DeleteOptions) bool -} - -// BeforeDelete tests whether the object can be gracefully deleted. If graceful is set the object -// should be gracefully deleted, if gracefulPending is set the object has already been gracefully deleted -// (and the provided grace period is longer than the time to deletion), and an error is returned if the -// condition cannot be checked or the gracePeriodSeconds is invalid. The options argument may be updated with -// default values if graceful is true. -func BeforeDelete(strategy RESTDeleteStrategy, ctx api.Context, obj runtime.Object, options *api.DeleteOptions) (graceful, gracefulPending bool, err error) { - if strategy == nil { - return false, false, nil - } - objectMeta, _, kerr := objectMetaAndKind(strategy, obj) - if kerr != nil { - return false, false, kerr - } - - // if the object is already being deleted - if objectMeta.DeletionTimestamp != nil { - // if we are already being deleted, we may only shorten the deletion grace period - // this means the object was gracefully deleted previously but deletionGracePeriodSeconds was not set, - // so we force deletion immediately - if objectMeta.DeletionGracePeriodSeconds == nil { - return false, false, nil - } - // only a shorter grace period may be provided by a user - if options.GracePeriodSeconds != nil { - period := int64(*options.GracePeriodSeconds) - if period > *objectMeta.DeletionGracePeriodSeconds { - return false, true, nil - } - now := unversioned.NewTime(unversioned.Now().Add(time.Second * time.Duration(*options.GracePeriodSeconds))) - objectMeta.DeletionTimestamp = &now - objectMeta.DeletionGracePeriodSeconds = &period - options.GracePeriodSeconds = &period - return true, false, nil - } - // graceful deletion is pending, do nothing - options.GracePeriodSeconds = objectMeta.DeletionGracePeriodSeconds - return false, true, nil - } - - if !strategy.CheckGracefulDelete(obj, options) { - return false, false, nil - } - now := unversioned.NewTime(unversioned.Now().Add(time.Second * time.Duration(*options.GracePeriodSeconds))) - objectMeta.DeletionTimestamp = &now - objectMeta.DeletionGracePeriodSeconds = options.GracePeriodSeconds - return true, false, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/doc.go deleted file mode 100644 index 8fed0e9f4..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2014 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 rest defines common logic around changes to Kubernetes resources. -package rest diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/export.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/export.go deleted file mode 100644 index e12f65de3..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/export.go +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright 2015 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 rest - -import ( - "k8s.io/kubernetes/pkg/runtime" -) - -// RESTExportStrategy is the interface that defines how to export a Kubernetes object -type RESTExportStrategy interface { - // Export strips fields that can not be set by the user. If 'exact' is false - // fields specific to the cluster are also stripped - Export(obj runtime.Object, exact bool) error -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/rest.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/rest.go deleted file mode 100644 index d07023dad..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/rest.go +++ /dev/null @@ -1,293 +0,0 @@ -/* -Copyright 2014 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 rest - -import ( - "io" - "net/http" - "net/url" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/watch" -) - -//TODO: -// Storage interfaces need to be separated into two groups; those that operate -// on collections and those that operate on individually named items. -// Collection interfaces: -// (Method: Current -> Proposed) -// GET: Lister -> CollectionGetter -// WATCH: Watcher -> CollectionWatcher -// CREATE: Creater -> CollectionCreater -// DELETE: (n/a) -> CollectionDeleter -// UPDATE: (n/a) -> CollectionUpdater -// -// Single item interfaces: -// (Method: Current -> Proposed) -// GET: Getter -> NamedGetter -// WATCH: (n/a) -> NamedWatcher -// CREATE: (n/a) -> NamedCreater -// DELETE: Deleter -> NamedDeleter -// UPDATE: Update -> NamedUpdater - -// Storage is a generic interface for RESTful storage services. -// Resources which are exported to the RESTful API of apiserver need to implement this interface. It is expected -// that objects may implement any of the below interfaces. -type Storage interface { - // New returns an empty object that can be used with Create and Update after request data has been put into it. - // This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object) - New() runtime.Object -} - -// KindProvider specifies a different kind for its API than for its internal storage. This is necessary for external -// objects that are not compiled into the api server. For such objects, there is no in-memory representation for -// the object, so they must be represented as generic objects (e.g. RawJSON), but when we present the object as part of -// API discovery we want to present the specific kind, not the generic internal representation. -type KindProvider interface { - Kind() string -} - -// Lister is an object that can retrieve resources that match the provided field and label criteria. -type Lister interface { - // NewList returns an empty object that can be used with the List call. - // This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object) - NewList() runtime.Object - // List selects resources in the storage which match to the selector. 'options' can be nil. - List(ctx api.Context, options *api.ListOptions) (runtime.Object, error) -} - -// Exporter is an object that knows how to strip a RESTful resource for export -type Exporter interface { - // Export an object. Fields that are not user specified (e.g. Status, ObjectMeta.ResourceVersion) are stripped out - // Returns the stripped object. If 'exact' is true, fields that are specific to the cluster (e.g. namespace) are - // retained, otherwise they are stripped also. - Export(ctx api.Context, name string, opts unversioned.ExportOptions) (runtime.Object, error) -} - -// Getter is an object that can retrieve a named RESTful resource. -type Getter interface { - // Get finds a resource in the storage by name and returns it. - // Although it can return an arbitrary error value, IsNotFound(err) is true for the - // returned error value err when the specified resource is not found. - Get(ctx api.Context, name string) (runtime.Object, error) -} - -// GetterWithOptions is an object that retrieve a named RESTful resource and takes -// additional options on the get request. It allows a caller to also receive the -// subpath of the GET request. -type GetterWithOptions interface { - // Get finds a resource in the storage by name and returns it. - // Although it can return an arbitrary error value, IsNotFound(err) is true for the - // returned error value err when the specified resource is not found. - // The options object passed to it is of the same type returned by the NewGetOptions - // method. - Get(ctx api.Context, name string, options runtime.Object) (runtime.Object, error) - - // NewGetOptions returns an empty options object that will be used to pass - // options to the Get method. It may return a bool and a string, if true, the - // value of the request path below the object will be included as the named - // string in the serialization of the runtime object. E.g., returning "path" - // will convert the trailing request scheme value to "path" in the map[string][]string - // passed to the converter. - NewGetOptions() (runtime.Object, bool, string) -} - -// Deleter is an object that can delete a named RESTful resource. -type Deleter interface { - // Delete finds a resource in the storage and deletes it. - // Although it can return an arbitrary error value, IsNotFound(err) is true for the - // returned error value err when the specified resource is not found. - // Delete *may* return the object that was deleted, or a status object indicating additional - // information about deletion. - Delete(ctx api.Context, name string) (runtime.Object, error) -} - -// GracefulDeleter knows how to pass deletion options to allow delayed deletion of a -// RESTful object. -type GracefulDeleter interface { - // Delete finds a resource in the storage and deletes it. - // If options are provided, the resource will attempt to honor them or return an invalid - // request error. - // Although it can return an arbitrary error value, IsNotFound(err) is true for the - // returned error value err when the specified resource is not found. - // Delete *may* return the object that was deleted, or a status object indicating additional - // information about deletion. - Delete(ctx api.Context, name string, options *api.DeleteOptions) (runtime.Object, error) -} - -// GracefulDeleteAdapter adapts the Deleter interface to GracefulDeleter -type GracefulDeleteAdapter struct { - Deleter -} - -// Delete implements RESTGracefulDeleter in terms of Deleter -func (w GracefulDeleteAdapter) Delete(ctx api.Context, name string, options *api.DeleteOptions) (runtime.Object, error) { - return w.Deleter.Delete(ctx, name) -} - -// CollectionDeleter is an object that can delete a collection -// of RESTful resources. -type CollectionDeleter interface { - // DeleteCollection selects all resources in the storage matching given 'listOptions' - // and deletes them. If 'options' are provided, the resource will attempt to honor - // them or return an invalid request error. - // DeleteCollection may not be atomic - i.e. it may delete some objects and still - // return an error after it. On success, returns a list of deleted objects. - DeleteCollection(ctx api.Context, options *api.DeleteOptions, listOptions *api.ListOptions) (runtime.Object, error) -} - -// Creater is an object that can create an instance of a RESTful object. -type Creater interface { - // New returns an empty object that can be used with Create after request data has been put into it. - // This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object) - New() runtime.Object - - // Create creates a new version of a resource. - Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) -} - -// NamedCreater is an object that can create an instance of a RESTful object using a name parameter. -type NamedCreater interface { - // New returns an empty object that can be used with Create after request data has been put into it. - // This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object) - New() runtime.Object - - // Create creates a new version of a resource. It expects a name parameter from the path. - // This is needed for create operations on subresources which include the name of the parent - // resource in the path. - Create(ctx api.Context, name string, obj runtime.Object) (runtime.Object, error) -} - -// Updater is an object that can update an instance of a RESTful object. -type Updater interface { - // New returns an empty object that can be used with Update after request data has been put into it. - // This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object) - New() runtime.Object - - // Update finds a resource in the storage and updates it. Some implementations - // may allow updates creates the object - they should set the created boolean - // to true. - Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) -} - -// CreaterUpdater is a storage object that must support both create and update. -// Go prevents embedded interfaces that implement the same method. -type CreaterUpdater interface { - Creater - Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) -} - -// CreaterUpdater must satisfy the Updater interface. -var _ Updater = CreaterUpdater(nil) - -// Patcher is a storage object that supports both get and update. -type Patcher interface { - Getter - Updater -} - -// Watcher should be implemented by all Storage objects that -// want to offer the ability to watch for changes through the watch api. -type Watcher interface { - // 'label' selects on labels; 'field' selects on the object's fields. Not all fields - // are supported; an error should be returned if 'field' tries to select on a field that - // isn't supported. 'resourceVersion' allows for continuing/starting a watch at a - // particular version. - Watch(ctx api.Context, options *api.ListOptions) (watch.Interface, error) -} - -// StandardStorage is an interface covering the common verbs. Provided for testing whether a -// resource satisfies the normal storage methods. Use Storage when passing opaque storage objects. -type StandardStorage interface { - Getter - Lister - CreaterUpdater - GracefulDeleter - CollectionDeleter - Watcher -} - -// Redirector know how to return a remote resource's location. -type Redirector interface { - // ResourceLocation should return the remote location of the given resource, and an optional transport to use to request it, or an error. - ResourceLocation(ctx api.Context, id string) (remoteLocation *url.URL, transport http.RoundTripper, err error) -} - -// Responder abstracts the normal response behavior for a REST method and is passed to callers that -// may wish to handle the response directly in some cases, but delegate to the normal error or object -// behavior in other cases. -type Responder interface { - // Object writes the provided object to the response. Invoking this method multiple times is undefined. - Object(statusCode int, obj runtime.Object) - // Error writes the provided error to the response. This method may only be invoked once. - Error(err error) -} - -// Connecter is a storage object that responds to a connection request. -type Connecter interface { - // Connect returns an http.Handler that will handle the request/response for a given API invocation. - // The provided responder may be used for common API responses. The responder will write both status - // code and body, so the ServeHTTP method should exit after invoking the responder. The Handler will - // be used for a single API request and then discarded. The Responder is guaranteed to write to the - // same http.ResponseWriter passed to ServeHTTP. - Connect(ctx api.Context, id string, options runtime.Object, r Responder) (http.Handler, error) - - // NewConnectOptions returns an empty options object that will be used to pass - // options to the Connect method. If nil, then a nil options object is passed to - // Connect. It may return a bool and a string. If true, the value of the request - // path below the object will be included as the named string in the serialization - // of the runtime object. - NewConnectOptions() (runtime.Object, bool, string) - - // ConnectMethods returns the list of HTTP methods handled by Connect - ConnectMethods() []string -} - -// ResourceStreamer is an interface implemented by objects that prefer to be streamed from the server -// instead of decoded directly. -type ResourceStreamer interface { - // InputStream should return an io.ReadCloser if the provided object supports streaming. The desired - // api version and a accept header (may be empty) are passed to the call. If no error occurs, - // the caller may return a flag indicating whether the result should be flushed as writes occur - // and a content type string that indicates the type of the stream. - // If a null stream is returned, a StatusNoContent response wil be generated. - InputStream(apiVersion, acceptHeader string) (stream io.ReadCloser, flush bool, mimeType string, err error) -} - -// StorageMetadata is an optional interface that callers can implement to provide additional -// information about their Storage objects. -type StorageMetadata interface { - // ProducesMIMETypes returns a list of the MIME types the specified HTTP verb (GET, POST, DELETE, - // PATCH) can respond with. - ProducesMIMETypes(verb string) []string -} - -// ConnectRequest is an object passed to admission control for Connect operations -type ConnectRequest struct { - // Name is the name of the object on which the connect request was made - Name string - - // Options is the options object passed to the connect request. See the NewConnectOptions method on Connecter - Options runtime.Object - - // ResourcePath is the path for the resource in the REST server (ie. "pods/proxy") - ResourcePath string -} - -func (obj *ConnectRequest) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/resttest/resttest.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/resttest/resttest.go deleted file mode 100644 index fbf0bf87d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/resttest/resttest.go +++ /dev/null @@ -1,1005 +0,0 @@ -/* -Copyright 2014 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 resttest - -import ( - "fmt" - "reflect" - "strings" - "testing" - "time" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/rest" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/validation" - "k8s.io/kubernetes/pkg/conversion" - "k8s.io/kubernetes/pkg/fields" - "k8s.io/kubernetes/pkg/labels" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/wait" -) - -type Tester struct { - *testing.T - storage rest.Storage - clusterScope bool - createOnUpdate bool - generatesName bool - returnDeletedObject bool -} - -func New(t *testing.T, storage rest.Storage) *Tester { - return &Tester{ - T: t, - storage: storage, - } -} - -func (t *Tester) ClusterScope() *Tester { - t.clusterScope = true - return t -} - -func (t *Tester) AllowCreateOnUpdate() *Tester { - t.createOnUpdate = true - return t -} - -func (t *Tester) GeneratesName() *Tester { - t.generatesName = true - return t -} - -func (t *Tester) ReturnDeletedObject() *Tester { - t.returnDeletedObject = true - return t -} - -// TestNamespace returns the namespace that will be used when creating contexts. -// Returns NamespaceNone for cluster-scoped objects. -func (t *Tester) TestNamespace() string { - if t.clusterScope { - return api.NamespaceNone - } - return "test" -} - -// TestContext returns a namespaced context that will be used when making storage calls. -// Namespace is determined by TestNamespace() -func (t *Tester) TestContext() api.Context { - if t.clusterScope { - return api.NewContext() - } - return api.WithNamespace(api.NewContext(), t.TestNamespace()) -} - -func (t *Tester) getObjectMetaOrFail(obj runtime.Object) *api.ObjectMeta { - meta, err := api.ObjectMetaFor(obj) - if err != nil { - t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, obj) - } - return meta -} - -func (t *Tester) setObjectMeta(obj runtime.Object, name string) { - meta := t.getObjectMetaOrFail(obj) - meta.Name = name - if t.clusterScope { - meta.Namespace = api.NamespaceNone - } else { - meta.Namespace = api.NamespaceValue(t.TestContext()) - } - meta.GenerateName = "" -} - -func copyOrDie(obj runtime.Object) runtime.Object { - out, err := api.Scheme.Copy(obj) - if err != nil { - panic(err) - } - return out -} - -type AssignFunc func([]runtime.Object) []runtime.Object -type EmitFunc func(runtime.Object, string) error -type GetFunc func(api.Context, runtime.Object) (runtime.Object, error) -type InitWatchFunc func() -type InjectErrFunc func(err error) -type IsErrorFunc func(err error) bool -type SetFunc func(api.Context, runtime.Object) error -type SetRVFunc func(uint64) -type UpdateFunc func(runtime.Object) runtime.Object - -// Test creating an object. -func (t *Tester) TestCreate(valid runtime.Object, setFn SetFunc, getFn GetFunc, invalid ...runtime.Object) { - t.testCreateHasMetadata(copyOrDie(valid)) - if !t.generatesName { - t.testCreateGeneratesName(copyOrDie(valid)) - } - t.testCreateEquals(copyOrDie(valid), getFn) - t.testCreateAlreadyExisting(copyOrDie(valid), setFn) - if t.clusterScope { - t.testCreateDiscardsObjectNamespace(copyOrDie(valid)) - t.testCreateIgnoresContextNamespace(copyOrDie(valid)) - t.testCreateIgnoresMismatchedNamespace(copyOrDie(valid)) - } else { - t.testCreateRejectsMismatchedNamespace(copyOrDie(valid)) - } - t.testCreateInvokesValidation(invalid...) - t.testCreateValidatesNames(copyOrDie(valid)) -} - -// Test updating an object. -func (t *Tester) TestUpdate(valid runtime.Object, setFn SetFunc, getFn GetFunc, updateFn UpdateFunc, invalidUpdateFn ...UpdateFunc) { - t.testUpdateEquals(copyOrDie(valid), setFn, getFn, updateFn) - t.testUpdateFailsOnVersionTooOld(copyOrDie(valid), setFn, getFn) - t.testUpdateOnNotFound(copyOrDie(valid)) - if !t.clusterScope { - t.testUpdateRejectsMismatchedNamespace(copyOrDie(valid), setFn) - } - t.testUpdateInvokesValidation(copyOrDie(valid), setFn, invalidUpdateFn...) -} - -// Test deleting an object. -func (t *Tester) TestDelete(valid runtime.Object, setFn SetFunc, getFn GetFunc, isNotFoundFn IsErrorFunc) { - t.testDeleteNonExist(copyOrDie(valid)) - t.testDeleteNoGraceful(copyOrDie(valid), setFn, getFn, isNotFoundFn) -} - -// Test gracefully deleting an object. -func (t *Tester) TestDeleteGraceful(valid runtime.Object, setFn SetFunc, getFn GetFunc, expectedGrace int64) { - t.testDeleteGracefulHasDefault(copyOrDie(valid), setFn, getFn, expectedGrace) - t.testDeleteGracefulWithValue(copyOrDie(valid), setFn, getFn, expectedGrace) - t.testDeleteGracefulUsesZeroOnNil(copyOrDie(valid), setFn, expectedGrace) - t.testDeleteGracefulExtend(copyOrDie(valid), setFn, getFn, expectedGrace) - t.testDeleteGracefulImmediate(copyOrDie(valid), setFn, getFn, expectedGrace) -} - -// Test getting object. -func (t *Tester) TestGet(valid runtime.Object) { - t.testGetFound(copyOrDie(valid)) - t.testGetNotFound(copyOrDie(valid)) - t.testGetMimatchedNamespace(copyOrDie(valid)) - if !t.clusterScope { - t.testGetDifferentNamespace(copyOrDie(valid)) - } -} - -// Test listing objects. -func (t *Tester) TestList(valid runtime.Object, assignFn AssignFunc) { - t.testListNotFound(assignFn) - t.testListFound(copyOrDie(valid), assignFn) - t.testListMatchLabels(copyOrDie(valid), assignFn) -} - -// Test watching objects. -func (t *Tester) TestWatch( - valid runtime.Object, emitFn EmitFunc, - labelsPass, labelsFail []labels.Set, fieldsPass, fieldsFail []fields.Set, actions []string) { - t.testWatchLabels(copyOrDie(valid), emitFn, labelsPass, labelsFail, actions) - t.testWatchFields(copyOrDie(valid), emitFn, fieldsPass, fieldsFail, actions) -} - -// ============================================================================= -// Creation tests. - -func (t *Tester) testCreateAlreadyExisting(obj runtime.Object, setFn SetFunc) { - ctx := t.TestContext() - - foo := copyOrDie(obj) - t.setObjectMeta(foo, "foo1") - if err := setFn(ctx, foo); err != nil { - t.Errorf("unexpected error: %v", err) - } - - _, err := t.storage.(rest.Creater).Create(ctx, foo) - if !errors.IsAlreadyExists(err) { - t.Errorf("expected already exists err, got %v", err) - } -} - -func (t *Tester) testCreateEquals(obj runtime.Object, getFn GetFunc) { - ctx := t.TestContext() - - foo := copyOrDie(obj) - t.setObjectMeta(foo, "foo2") - - created, err := t.storage.(rest.Creater).Create(ctx, foo) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - got, err := getFn(ctx, foo) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - // Set resource version which might be unset in created object. - createdMeta := t.getObjectMetaOrFail(created) - gotMeta := t.getObjectMetaOrFail(got) - createdMeta.ResourceVersion = gotMeta.ResourceVersion - - if e, a := created, got; !api.Semantic.DeepEqual(e, a) { - t.Errorf("unexpected obj: %#v, expected %#v", e, a) - } -} - -func (t *Tester) testCreateDiscardsObjectNamespace(valid runtime.Object) { - objectMeta := t.getObjectMetaOrFail(valid) - - // Ignore non-empty namespace in object meta - objectMeta.Namespace = "not-default" - - // Ideally, we'd get an error back here, but at least verify the namespace wasn't persisted - created, err := t.storage.(rest.Creater).Create(t.TestContext(), copyOrDie(valid)) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - createdObjectMeta := t.getObjectMetaOrFail(created) - if createdObjectMeta.Namespace != api.NamespaceNone { - t.Errorf("Expected empty namespace on created object, got '%v'", createdObjectMeta.Namespace) - } -} - -func (t *Tester) testCreateGeneratesName(valid runtime.Object) { - objectMeta := t.getObjectMetaOrFail(valid) - objectMeta.Name = "" - objectMeta.GenerateName = "test-" - - _, err := t.storage.(rest.Creater).Create(t.TestContext(), valid) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if objectMeta.Name == "test-" || !strings.HasPrefix(objectMeta.Name, "test-") { - t.Errorf("unexpected name: %#v", valid) - } -} - -func (t *Tester) testCreateHasMetadata(valid runtime.Object) { - objectMeta := t.getObjectMetaOrFail(valid) - objectMeta.Name = "" - objectMeta.GenerateName = "test-" - objectMeta.Namespace = t.TestNamespace() - - obj, err := t.storage.(rest.Creater).Create(t.TestContext(), valid) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if obj == nil { - t.Fatalf("Unexpected object from result: %#v", obj) - } - if !api.HasObjectMetaSystemFieldValues(objectMeta) { - t.Errorf("storage did not populate object meta field values") - } -} - -func (t *Tester) testCreateIgnoresContextNamespace(valid runtime.Object) { - // Ignore non-empty namespace in context - ctx := api.WithNamespace(api.NewContext(), "not-default2") - - // Ideally, we'd get an error back here, but at least verify the namespace wasn't persisted - created, err := t.storage.(rest.Creater).Create(ctx, copyOrDie(valid)) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - createdObjectMeta := t.getObjectMetaOrFail(created) - if createdObjectMeta.Namespace != api.NamespaceNone { - t.Errorf("Expected empty namespace on created object, got '%v'", createdObjectMeta.Namespace) - } -} - -func (t *Tester) testCreateIgnoresMismatchedNamespace(valid runtime.Object) { - objectMeta := t.getObjectMetaOrFail(valid) - - // Ignore non-empty namespace in object meta - objectMeta.Namespace = "not-default" - ctx := api.WithNamespace(api.NewContext(), "not-default2") - - // Ideally, we'd get an error back here, but at least verify the namespace wasn't persisted - created, err := t.storage.(rest.Creater).Create(ctx, copyOrDie(valid)) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - createdObjectMeta := t.getObjectMetaOrFail(created) - if createdObjectMeta.Namespace != api.NamespaceNone { - t.Errorf("Expected empty namespace on created object, got '%v'", createdObjectMeta.Namespace) - } -} - -func (t *Tester) testCreateValidatesNames(valid runtime.Object) { - for _, invalidName := range validation.NameMayNotBe { - objCopy := copyOrDie(valid) - objCopyMeta := t.getObjectMetaOrFail(objCopy) - objCopyMeta.Name = invalidName - - ctx := t.TestContext() - _, err := t.storage.(rest.Creater).Create(ctx, objCopy) - if !errors.IsInvalid(err) { - t.Errorf("%s: Expected to get an invalid resource error, got %v", invalidName, err) - } - } - - for _, invalidSuffix := range validation.NameMayNotContain { - objCopy := copyOrDie(valid) - objCopyMeta := t.getObjectMetaOrFail(objCopy) - objCopyMeta.Name += invalidSuffix - - ctx := t.TestContext() - _, err := t.storage.(rest.Creater).Create(ctx, objCopy) - if !errors.IsInvalid(err) { - t.Errorf("%s: Expected to get an invalid resource error, got %v", invalidSuffix, err) - } - } -} - -func (t *Tester) testCreateInvokesValidation(invalid ...runtime.Object) { - for i, obj := range invalid { - ctx := t.TestContext() - _, err := t.storage.(rest.Creater).Create(ctx, obj) - if !errors.IsInvalid(err) { - t.Errorf("%d: Expected to get an invalid resource error, got %v", i, err) - } - } -} - -func (t *Tester) testCreateRejectsMismatchedNamespace(valid runtime.Object) { - objectMeta := t.getObjectMetaOrFail(valid) - objectMeta.Namespace = "not-default" - - _, err := t.storage.(rest.Creater).Create(t.TestContext(), valid) - if err == nil { - t.Errorf("Expected an error, but we didn't get one") - } else if !strings.Contains(err.Error(), "does not match the namespace sent on the request") { - t.Errorf("Expected 'does not match the namespace sent on the request' error, got '%v'", err.Error()) - } -} - -func (t *Tester) testCreateResetsUserData(valid runtime.Object) { - objectMeta := t.getObjectMetaOrFail(valid) - now := unversioned.Now() - objectMeta.UID = "bad-uid" - objectMeta.CreationTimestamp = now - - obj, err := t.storage.(rest.Creater).Create(t.TestContext(), valid) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if obj == nil { - t.Fatalf("Unexpected object from result: %#v", obj) - } - if objectMeta.UID == "bad-uid" || objectMeta.CreationTimestamp == now { - t.Errorf("ObjectMeta did not reset basic fields: %#v", objectMeta) - } -} - -// ============================================================================= -// Update tests. - -func (t *Tester) testUpdateEquals(obj runtime.Object, setFn SetFunc, getFn GetFunc, updateFn UpdateFunc) { - ctx := t.TestContext() - - foo := copyOrDie(obj) - t.setObjectMeta(foo, "foo2") - if err := setFn(ctx, foo); err != nil { - t.Errorf("unexpected error: %v", err) - } - - toUpdate, err := getFn(ctx, foo) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - toUpdate = updateFn(toUpdate) - updated, created, err := t.storage.(rest.Updater).Update(ctx, toUpdate) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if created { - t.Errorf("unexpected creation") - } - got, err := getFn(ctx, foo) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - // Set resource version which might be unset in created object. - updatedMeta := t.getObjectMetaOrFail(updated) - gotMeta := t.getObjectMetaOrFail(got) - updatedMeta.ResourceVersion = gotMeta.ResourceVersion - - if e, a := updated, got; !api.Semantic.DeepEqual(e, a) { - t.Errorf("unexpected obj: %#v, expected %#v", e, a) - } -} - -func (t *Tester) testUpdateFailsOnVersionTooOld(obj runtime.Object, setFn SetFunc, getFn GetFunc) { - ctx := t.TestContext() - - foo := copyOrDie(obj) - t.setObjectMeta(foo, "foo3") - - if err := setFn(ctx, foo); err != nil { - t.Errorf("unexpected error: %v", err) - } - - storedFoo, err := getFn(ctx, foo) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - older := copyOrDie(storedFoo) - olderMeta := t.getObjectMetaOrFail(older) - olderMeta.ResourceVersion = "1" - - _, _, err = t.storage.(rest.Updater).Update(t.TestContext(), older) - if err == nil { - t.Errorf("Expected an error, but we didn't get one") - } else if !errors.IsConflict(err) { - t.Errorf("Expected Conflict error, got '%v'", err) - } -} - -func (t *Tester) testUpdateInvokesValidation(obj runtime.Object, setFn SetFunc, invalidUpdateFn ...UpdateFunc) { - ctx := t.TestContext() - - foo := copyOrDie(obj) - t.setObjectMeta(foo, "foo4") - if err := setFn(ctx, foo); err != nil { - t.Errorf("unexpected error: %v", err) - } - - for _, update := range invalidUpdateFn { - toUpdate := update(copyOrDie(foo)) - got, created, err := t.storage.(rest.Updater).Update(t.TestContext(), toUpdate) - if got != nil || created { - t.Errorf("expected nil object and no creation for object: %v", toUpdate) - } - if !errors.IsInvalid(err) && !errors.IsBadRequest(err) { - t.Errorf("expected invalid or bad request error, got %v", err) - } - } -} - -func (t *Tester) testUpdateOnNotFound(obj runtime.Object) { - t.setObjectMeta(obj, "foo") - _, created, err := t.storage.(rest.Updater).Update(t.TestContext(), obj) - if t.createOnUpdate { - if err != nil { - t.Errorf("creation allowed on updated, but got an error: %v", err) - } - if !created { - t.Errorf("creation allowed on update, but object not created") - } - } else { - if err == nil { - t.Errorf("Expected an error, but we didn't get one") - } else if !errors.IsNotFound(err) { - t.Errorf("Expected NotFound error, got '%v'", err) - } - } -} - -func (t *Tester) testUpdateRejectsMismatchedNamespace(obj runtime.Object, setFn SetFunc) { - ctx := t.TestContext() - - foo := copyOrDie(obj) - t.setObjectMeta(foo, "foo1") - if err := setFn(ctx, foo); err != nil { - t.Errorf("unexpected error: %v", err) - } - - objectMeta := t.getObjectMetaOrFail(obj) - objectMeta.Name = "foo1" - objectMeta.Namespace = "not-default" - - obj, updated, err := t.storage.(rest.Updater).Update(t.TestContext(), obj) - if obj != nil || updated { - t.Errorf("expected nil object and not updated") - } - if err == nil { - t.Errorf("expected an error, but didn't get one") - } else if !strings.Contains(err.Error(), "does not match the namespace sent on the request") { - t.Errorf("expected 'does not match the namespace sent on the request' error, got '%v'", err.Error()) - } -} - -// ============================================================================= -// Deletion tests. - -func (t *Tester) testDeleteNoGraceful(obj runtime.Object, setFn SetFunc, getFn GetFunc, isNotFoundFn IsErrorFunc) { - ctx := t.TestContext() - - foo := copyOrDie(obj) - t.setObjectMeta(foo, "foo1") - if err := setFn(ctx, foo); err != nil { - t.Errorf("unexpected error: %v", err) - } - objectMeta := t.getObjectMetaOrFail(foo) - obj, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(10)) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !t.returnDeletedObject { - if status, ok := obj.(*unversioned.Status); !ok { - t.Errorf("expected status of delete, got %v", status) - } else if status.Status != unversioned.StatusSuccess { - t.Errorf("expected success, got: %v", status.Status) - } - } - - _, err = getFn(ctx, foo) - if err == nil || !isNotFoundFn(err) { - t.Errorf("unexpected error: %v", err) - } -} - -func (t *Tester) testDeleteNonExist(obj runtime.Object) { - objectMeta := t.getObjectMetaOrFail(obj) - - _, err := t.storage.(rest.GracefulDeleter).Delete(t.TestContext(), objectMeta.Name, nil) - if err == nil || !errors.IsNotFound(err) { - t.Errorf("unexpected error: %v", err) - } - -} - -// ============================================================================= -// Graceful Deletion tests. - -func (t *Tester) testDeleteGracefulHasDefault(obj runtime.Object, setFn SetFunc, getFn GetFunc, expectedGrace int64) { - ctx := t.TestContext() - - foo := copyOrDie(obj) - t.setObjectMeta(foo, "foo1") - if err := setFn(ctx, foo); err != nil { - t.Errorf("unexpected error: %v", err) - } - objectMeta := t.getObjectMetaOrFail(foo) - _, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, &api.DeleteOptions{}) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if _, err := getFn(ctx, foo); err != nil { - t.Fatalf("did not gracefully delete resource", err) - } - - object, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name) - if err != nil { - t.Fatalf("unexpected error, object should exist: %v", err) - } - objectMeta = t.getObjectMetaOrFail(object) - if objectMeta.DeletionTimestamp == nil || objectMeta.DeletionGracePeriodSeconds == nil || *objectMeta.DeletionGracePeriodSeconds != expectedGrace { - t.Errorf("unexpected deleted meta: %#v", objectMeta) - } -} - -func (t *Tester) testDeleteGracefulWithValue(obj runtime.Object, setFn SetFunc, getFn GetFunc, expectedGrace int64) { - ctx := t.TestContext() - - foo := copyOrDie(obj) - t.setObjectMeta(foo, "foo2") - if err := setFn(ctx, foo); err != nil { - t.Errorf("unexpected error: %v", err) - } - objectMeta := t.getObjectMetaOrFail(foo) - _, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace+2)) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if _, err := getFn(ctx, foo); err != nil { - t.Fatalf("did not gracefully delete resource", err) - } - - object, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name) - if err != nil { - t.Errorf("unexpected error, object should exist: %v", err) - } - objectMeta = t.getObjectMetaOrFail(object) - if objectMeta.DeletionTimestamp == nil || objectMeta.DeletionGracePeriodSeconds == nil || *objectMeta.DeletionGracePeriodSeconds != expectedGrace+2 { - t.Errorf("unexpected deleted meta: %#v", objectMeta) - } -} - -func (t *Tester) testDeleteGracefulExtend(obj runtime.Object, setFn SetFunc, getFn GetFunc, expectedGrace int64) { - ctx := t.TestContext() - - foo := copyOrDie(obj) - t.setObjectMeta(foo, "foo3") - if err := setFn(ctx, foo); err != nil { - t.Errorf("unexpected error: %v", err) - } - objectMeta := t.getObjectMetaOrFail(foo) - _, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace)) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if _, err := getFn(ctx, foo); err != nil { - t.Fatalf("did not gracefully delete resource", err) - } - - // second delete duration is ignored - _, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace+2)) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - object, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name) - if err != nil { - t.Errorf("unexpected error, object should exist: %v", err) - } - objectMeta = t.getObjectMetaOrFail(object) - if objectMeta.DeletionTimestamp == nil || objectMeta.DeletionGracePeriodSeconds == nil || *objectMeta.DeletionGracePeriodSeconds != expectedGrace { - t.Errorf("unexpected deleted meta: %#v", objectMeta) - } -} - -func (t *Tester) testDeleteGracefulImmediate(obj runtime.Object, setFn SetFunc, getFn GetFunc, expectedGrace int64) { - ctx := t.TestContext() - - foo := copyOrDie(obj) - t.setObjectMeta(foo, "foo4") - if err := setFn(ctx, foo); err != nil { - t.Errorf("unexpected error: %v", err) - } - objectMeta := t.getObjectMetaOrFail(foo) - _, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace)) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if _, err := getFn(ctx, foo); err != nil { - t.Fatalf("did not gracefully delete resource", err) - } - - // second delete is immediate, resource is deleted - out, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(0)) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - _, err = t.storage.(rest.Getter).Get(ctx, objectMeta.Name) - if !errors.IsNotFound(err) { - t.Errorf("unexpected error, object should be deleted immediately: %v", err) - } - objectMeta = t.getObjectMetaOrFail(out) - // the second delete shouldn't update the object, so the objectMeta.DeletionGracePeriodSeconds should eqaul to the value set in the first delete. - if objectMeta.DeletionTimestamp == nil || objectMeta.DeletionGracePeriodSeconds == nil || *objectMeta.DeletionGracePeriodSeconds != expectedGrace { - t.Errorf("unexpected deleted meta: %#v", objectMeta) - } -} - -func (t *Tester) testDeleteGracefulUsesZeroOnNil(obj runtime.Object, setFn SetFunc, expectedGrace int64) { - ctx := t.TestContext() - - foo := copyOrDie(obj) - t.setObjectMeta(foo, "foo5") - if err := setFn(ctx, foo); err != nil { - t.Errorf("unexpected error: %v", err) - } - objectMeta := t.getObjectMetaOrFail(foo) - _, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, nil) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); !errors.IsNotFound(err) { - t.Errorf("unexpected error, object should not exist: %v", err) - } -} - -// ============================================================================= -// Get tests. - -// testGetDifferentNamespace ensures same-name objects in different namespaces do not clash -func (t *Tester) testGetDifferentNamespace(obj runtime.Object) { - if t.clusterScope { - t.Fatalf("the test does not work in in cluster-scope") - } - - objMeta := t.getObjectMetaOrFail(obj) - objMeta.Name = "foo5" - - ctx1 := api.WithNamespace(api.NewContext(), "bar3") - objMeta.Namespace = api.NamespaceValue(ctx1) - _, err := t.storage.(rest.Creater).Create(ctx1, obj) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - ctx2 := api.WithNamespace(api.NewContext(), "bar4") - objMeta.Namespace = api.NamespaceValue(ctx2) - _, err = t.storage.(rest.Creater).Create(ctx2, obj) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - got1, err := t.storage.(rest.Getter).Get(ctx1, objMeta.Name) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - got1Meta := t.getObjectMetaOrFail(got1) - if got1Meta.Name != objMeta.Name { - t.Errorf("unexpected name of object: %#v, expected: %s", got1, objMeta.Name) - } - if got1Meta.Namespace != api.NamespaceValue(ctx1) { - t.Errorf("unexpected namespace of object: %#v, expected: %s", got1, api.NamespaceValue(ctx1)) - } - - got2, err := t.storage.(rest.Getter).Get(ctx2, objMeta.Name) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - got2Meta := t.getObjectMetaOrFail(got2) - if got2Meta.Name != objMeta.Name { - t.Errorf("unexpected name of object: %#v, expected: %s", got2, objMeta.Name) - } - if got2Meta.Namespace != api.NamespaceValue(ctx2) { - t.Errorf("unexpected namespace of object: %#v, expected: %s", got2, api.NamespaceValue(ctx2)) - } -} - -func (t *Tester) testGetFound(obj runtime.Object) { - ctx := t.TestContext() - t.setObjectMeta(obj, "foo1") - - existing, err := t.storage.(rest.Creater).Create(ctx, obj) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - existingMeta := t.getObjectMetaOrFail(existing) - - got, err := t.storage.(rest.Getter).Get(ctx, "foo1") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - gotMeta := t.getObjectMetaOrFail(got) - gotMeta.ResourceVersion = existingMeta.ResourceVersion - if e, a := existing, got; !api.Semantic.DeepEqual(e, a) { - t.Errorf("unexpected obj: %#v, expected %#v", e, a) - } -} - -func (t *Tester) testGetMimatchedNamespace(obj runtime.Object) { - ctx1 := api.WithNamespace(api.NewContext(), "bar1") - ctx2 := api.WithNamespace(api.NewContext(), "bar2") - objMeta := t.getObjectMetaOrFail(obj) - objMeta.Name = "foo4" - objMeta.Namespace = api.NamespaceValue(ctx1) - _, err := t.storage.(rest.Creater).Create(ctx1, obj) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - _, err = t.storage.(rest.Getter).Get(ctx2, "foo4") - if t.clusterScope { - if err != nil { - t.Errorf("unexpected error: %v", err) - } - } else { - if !errors.IsNotFound(err) { - t.Errorf("unexpected error returned: %#v", err) - } - } -} - -func (t *Tester) testGetNotFound(obj runtime.Object) { - ctx := t.TestContext() - t.setObjectMeta(obj, "foo2") - _, err := t.storage.(rest.Creater).Create(ctx, obj) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - _, err = t.storage.(rest.Getter).Get(ctx, "foo3") - if !errors.IsNotFound(err) { - t.Errorf("unexpected error returned: %#v", err) - } -} - -// ============================================================================= -// List tests. - -func listToItems(listObj runtime.Object) ([]runtime.Object, error) { - v, err := conversion.EnforcePtr(listObj) - if err != nil { - return nil, fmt.Errorf("unexpected error: %v", err) - } - items := v.FieldByName("Items") - if !items.IsValid() { - return nil, fmt.Errorf("unexpected Items field in %v", listObj) - } - if items.Type().Kind() != reflect.Slice { - return nil, fmt.Errorf("unexpected Items field type: %v", items.Type().Kind()) - } - result := make([]runtime.Object, items.Len()) - for i := 0; i < items.Len(); i++ { - result[i] = items.Index(i).Addr().Interface().(runtime.Object) - } - return result, nil -} - -func (t *Tester) testListFound(obj runtime.Object, assignFn AssignFunc) { - ctx := t.TestContext() - - foo1 := copyOrDie(obj) - t.setObjectMeta(foo1, "foo1") - foo2 := copyOrDie(obj) - t.setObjectMeta(foo2, "foo2") - - existing := assignFn([]runtime.Object{foo1, foo2}) - - listObj, err := t.storage.(rest.Lister).List(ctx, nil) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - items, err := listToItems(listObj) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if len(items) != len(existing) { - t.Errorf("unexpected number of items: %v", len(items)) - } - if !api.Semantic.DeepEqual(existing, items) { - t.Errorf("expected: %#v, got: %#v", existing, items) - } -} - -func (t *Tester) testListMatchLabels(obj runtime.Object, assignFn AssignFunc) { - ctx := t.TestContext() - testLabels := map[string]string{"key": "value"} - - foo3 := copyOrDie(obj) - t.setObjectMeta(foo3, "foo3") - foo4 := copyOrDie(obj) - foo4Meta := t.getObjectMetaOrFail(foo4) - foo4Meta.Name = "foo4" - foo4Meta.Namespace = api.NamespaceValue(ctx) - foo4Meta.Labels = testLabels - - objs := ([]runtime.Object{foo3, foo4}) - - assignFn(objs) - filtered := []runtime.Object{objs[1]} - - selector := labels.SelectorFromSet(labels.Set(testLabels)) - options := &api.ListOptions{LabelSelector: selector} - listObj, err := t.storage.(rest.Lister).List(ctx, options) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - items, err := listToItems(listObj) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if len(items) != len(filtered) { - t.Errorf("unexpected number of items: %v", len(items)) - } - if !api.Semantic.DeepEqual(filtered, items) { - t.Errorf("expected: %#v, got: %#v", filtered, items) - } -} - -func (t *Tester) testListNotFound(assignFn AssignFunc) { - ctx := t.TestContext() - _ = assignFn([]runtime.Object{}) - - listObj, err := t.storage.(rest.Lister).List(ctx, nil) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - items, err := listToItems(listObj) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if len(items) != 0 { - t.Errorf("unexpected items: %#v", items) - } -} - -// ============================================================================= -// Watching tests. - -func (t *Tester) testWatchFields(obj runtime.Object, emitFn EmitFunc, fieldsPass, fieldsFail []fields.Set, actions []string) { - ctx := t.TestContext() - - for _, field := range fieldsPass { - for _, action := range actions { - options := &api.ListOptions{FieldSelector: field.AsSelector(), ResourceVersion: "1"} - watcher, err := t.storage.(rest.Watcher).Watch(ctx, options) - if err != nil { - t.Errorf("unexpected error: %v, %v", err, action) - } - - if err := emitFn(obj, action); err != nil { - t.Errorf("unexpected error: %v", err) - } - - select { - case _, ok := <-watcher.ResultChan(): - if !ok { - t.Errorf("watch channel should be open") - } - case <-time.After(wait.ForeverTestTimeout): - t.Errorf("unexpected timeout from result channel") - } - watcher.Stop() - } - } - - for _, field := range fieldsFail { - for _, action := range actions { - options := &api.ListOptions{FieldSelector: field.AsSelector(), ResourceVersion: "1"} - watcher, err := t.storage.(rest.Watcher).Watch(ctx, options) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if err := emitFn(obj, action); err != nil { - t.Errorf("unexpected error: %v", err) - } - - select { - case <-watcher.ResultChan(): - t.Errorf("unexpected result from result channel") - case <-time.After(time.Millisecond * 500): - // expected case - } - watcher.Stop() - } - } -} - -func (t *Tester) testWatchLabels(obj runtime.Object, emitFn EmitFunc, labelsPass, labelsFail []labels.Set, actions []string) { - ctx := t.TestContext() - - for _, label := range labelsPass { - for _, action := range actions { - options := &api.ListOptions{LabelSelector: label.AsSelector(), ResourceVersion: "1"} - watcher, err := t.storage.(rest.Watcher).Watch(ctx, options) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if err := emitFn(obj, action); err != nil { - t.Errorf("unexpected error: %v", err) - } - - select { - case _, ok := <-watcher.ResultChan(): - if !ok { - t.Errorf("watch channel should be open") - } - case <-time.After(wait.ForeverTestTimeout): - t.Errorf("unexpected timeout from result channel") - } - watcher.Stop() - } - } - - for _, label := range labelsFail { - for _, action := range actions { - options := &api.ListOptions{LabelSelector: label.AsSelector(), ResourceVersion: "1"} - watcher, err := t.storage.(rest.Watcher).Watch(ctx, options) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if err := emitFn(obj, action); err != nil { - t.Errorf("unexpected error: %v", err) - } - - select { - case <-watcher.ResultChan(): - t.Errorf("unexpected result from result channel") - case <-time.After(time.Millisecond * 500): - // expected case - } - watcher.Stop() - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/types.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/types.go deleted file mode 100644 index 0e7f048ba..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/types.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2014 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 rest - -import ( - "k8s.io/kubernetes/pkg/runtime" -) - -// ObjectFunc is a function to act on a given object. An error may be returned -// if the hook cannot be completed. An ObjectFunc may transform the provided -// object. -type ObjectFunc func(obj runtime.Object) error - -// AllFuncs returns an ObjectFunc that attempts to run all of the provided functions -// in order, returning early if there are any errors. -func AllFuncs(fns ...ObjectFunc) ObjectFunc { - return func(obj runtime.Object) error { - for _, fn := range fns { - if fn == nil { - continue - } - if err := fn(obj); err != nil { - return err - } - } - return nil - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/update.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/update.go deleted file mode 100644 index 80ad14f86..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/rest/update.go +++ /dev/null @@ -1,105 +0,0 @@ -/* -Copyright 2014 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 rest - -import ( - "fmt" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/validation" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/validation/field" -) - -// RESTUpdateStrategy defines the minimum validation, accepted input, and -// name generation behavior to update an object that follows Kubernetes -// API conventions. A resource may have many UpdateStrategies, depending on -// the call pattern in use. -type RESTUpdateStrategy interface { - runtime.ObjectTyper - // NamespaceScoped returns true if the object must be within a namespace. - NamespaceScoped() bool - // AllowCreateOnUpdate returns true if the object can be created by a PUT. - AllowCreateOnUpdate() bool - // PrepareForUpdate is invoked on update before validation to normalize - // the object. For example: remove fields that are not to be persisted, - // sort order-insensitive list fields, etc. This should not remove fields - // whose presence would be considered a validation error. - PrepareForUpdate(obj, old runtime.Object) - // ValidateUpdate is invoked after default fields in the object have been - // filled in before the object is persisted. This method should not mutate - // the object. - ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList - // Canonicalize is invoked after validation has succeeded but before the - // object has been persisted. This method may mutate the object. - Canonicalize(obj runtime.Object) - // AllowUnconditionalUpdate returns true if the object can be updated - // unconditionally (irrespective of the latest resource version), when - // there is no resource version specified in the object. - AllowUnconditionalUpdate() bool -} - -// TODO: add other common fields that require global validation. -func validateCommonFields(obj, old runtime.Object) (field.ErrorList, error) { - allErrs := field.ErrorList{} - objectMeta, err := api.ObjectMetaFor(obj) - if err != nil { - return nil, fmt.Errorf("failed to get new object metadata: %v", err) - } - oldObjectMeta, err := api.ObjectMetaFor(old) - if err != nil { - return nil, fmt.Errorf("failed to get old object metadata: %v", err) - } - allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(objectMeta, oldObjectMeta, field.NewPath("metadata"))...) - - return allErrs, nil -} - -// BeforeUpdate ensures that common operations for all resources are performed on update. It only returns -// errors that can be converted to api.Status. It will invoke update validation with the provided existing -// and updated objects. -func BeforeUpdate(strategy RESTUpdateStrategy, ctx api.Context, obj, old runtime.Object) error { - objectMeta, kind, kerr := objectMetaAndKind(strategy, obj) - if kerr != nil { - return kerr - } - if strategy.NamespaceScoped() { - if !api.ValidNamespace(ctx, objectMeta) { - return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request") - } - } else { - objectMeta.Namespace = api.NamespaceNone - } - - strategy.PrepareForUpdate(obj, old) - - // Ensure some common fields, like UID, are validated for all resources. - errs, err := validateCommonFields(obj, old) - if err != nil { - return errors.NewInternalError(err) - } - - errs = append(errs, strategy.ValidateUpdate(ctx, obj, old)...) - if len(errs) > 0 { - return errors.NewInvalid(kind.GroupKind(), objectMeta.Name, errs) - } - - strategy.Canonicalize(obj) - - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/serialization_proto_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/serialization_proto_test.go deleted file mode 100644 index d74c1865d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/serialization_proto_test.go +++ /dev/null @@ -1,95 +0,0 @@ -// +build proto - -/* -Copyright 2015 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 api_test - -import ( - "encoding/hex" - "math/rand" - "testing" - - "github.com/gogo/protobuf/proto" - "k8s.io/kubernetes/pkg/api" - apitesting "k8s.io/kubernetes/pkg/api/testing" - "k8s.io/kubernetes/pkg/api/v1" - _ "k8s.io/kubernetes/pkg/apis/extensions" - _ "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/runtime/protobuf" - "k8s.io/kubernetes/pkg/util" -) - -func init() { - codecsToTest = append(codecsToTest, func(version string, item runtime.Object) (runtime.Codec, error) { - return protobuf.NewCodec(version, api.Scheme, api.Scheme, api.Scheme), nil - }) -} - -func TestProtobufRoundTrip(t *testing.T) { - obj := &v1.Pod{} - apitesting.FuzzerFor(t, "v1", rand.NewSource(benchmarkSeed)).Fuzz(obj) - data, err := obj.Marshal() - if err != nil { - t.Fatal(err) - } - out := &v1.Pod{} - if err := out.Unmarshal(data); err != nil { - t.Fatal(err) - } - if !api.Semantic.Equalities.DeepEqual(out, obj) { - t.Logf("marshal\n%s", hex.Dump(data)) - t.Fatalf("Unmarshal is unequal\n%s", util.ObjectGoPrintSideBySide(out, obj)) - } -} - -func BenchmarkEncodeProtobufGeneratedMarshal(b *testing.B) { - items := benchmarkItems() - width := len(items) - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err := items[i%width].Marshal(); err != nil { - b.Fatal(err) - } - } - b.StopTimer() -} - -// BenchmarkDecodeJSON provides a baseline for regular JSON decode performance -func BenchmarkDecodeIntoProtobuf(b *testing.B) { - items := benchmarkItems() - width := len(items) - encoded := make([][]byte, width) - for i := range items { - data, err := (&items[i]).Marshal() - if err != nil { - b.Fatal(err) - } - encoded[i] = data - validate := &v1.Pod{} - if err := proto.Unmarshal(data, validate); err != nil { - b.Fatalf("Failed to unmarshal %d: %v\n%#v", i, err, items[i]) - } - } - - for i := 0; i < b.N; i++ { - obj := v1.Pod{} - if err := proto.Unmarshal(encoded[i%width], &obj); err != nil { - b.Fatal(err) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/serialization_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/serialization_test.go deleted file mode 100644 index 7b1f17f81..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/serialization_test.go +++ /dev/null @@ -1,417 +0,0 @@ -/* -Copyright 2014 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 api_test - -import ( - "encoding/json" - "math/rand" - "reflect" - "testing" - - "github.com/davecgh/go-spew/spew" - flag "github.com/spf13/pflag" - "github.com/ugorji/go/codec" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/testapi" - apitesting "k8s.io/kubernetes/pkg/api/testing" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/v1" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util" - "k8s.io/kubernetes/pkg/util/sets" -) - -var fuzzIters = flag.Int("fuzz-iters", 20, "How many fuzzing iterations to do.") - -var codecsToTest = []func(version unversioned.GroupVersion, item runtime.Object) (runtime.Codec, error){ - func(version unversioned.GroupVersion, item runtime.Object) (runtime.Codec, error) { - return testapi.GetCodecForObject(item) - }, -} - -func fuzzInternalObject(t *testing.T, forVersion unversioned.GroupVersion, item runtime.Object, seed int64) runtime.Object { - apitesting.FuzzerFor(t, forVersion, rand.NewSource(seed)).Fuzz(item) - - j, err := meta.TypeAccessor(item) - if err != nil { - t.Fatalf("Unexpected error %v for %#v", err, item) - } - j.SetKind("") - j.SetAPIVersion("") - - return item -} - -func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) { - //t.Logf("codec: %#v", codec) - - printer := spew.ConfigState{DisableMethods: true} - - name := reflect.TypeOf(item).Elem().Name() - data, err := runtime.Encode(codec, item) - if err != nil { - t.Errorf("%v: %v (%s)", name, err, printer.Sprintf("%#v", item)) - return - } - - obj2, err := runtime.Decode(codec, data) - if err != nil { - t.Errorf("0: %v: %v\nCodec: %v\nData: %s\nSource: %#v", name, err, codec, string(data), printer.Sprintf("%#v", item)) - return - } - if !api.Semantic.DeepEqual(item, obj2) { - t.Errorf("\n1: %v: diff: %v\nCodec: %v\nSource:\n\n%#v\n\nEncoded:\n\n%s\n\nFinal:\n\n%#v", name, util.ObjectGoPrintDiff(item, obj2), codec, printer.Sprintf("%#v", item), string(data), printer.Sprintf("%#v", obj2)) - return - } - - obj3 := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object) - if err := runtime.DecodeInto(codec, data, obj3); err != nil { - t.Errorf("2: %v: %v", name, err) - return - } - if !api.Semantic.DeepEqual(item, obj3) { - t.Errorf("3: %v: diff: %v\nCodec: %v", name, util.ObjectDiff(item, obj3), codec) - return - } -} - -// roundTripSame verifies the same source object is tested in all API versions. -func roundTripSame(t *testing.T, group testapi.TestGroup, item runtime.Object, except ...string) { - set := sets.NewString(except...) - seed := rand.Int63() - fuzzInternalObject(t, group.InternalGroupVersion(), item, seed) - - version := *group.GroupVersion() - codecs := []runtime.Codec{} - for _, fn := range codecsToTest { - codec, err := fn(version, item) - if err != nil { - t.Errorf("unable to get codec: %v", err) - return - } - codecs = append(codecs, codec) - } - - if !set.Has(version.String()) { - fuzzInternalObject(t, version, item, seed) - for _, codec := range codecs { - roundTrip(t, codec, item) - } - } -} - -// For debugging problems -func TestSpecificKind(t *testing.T) { - // api.Scheme.Log(t) - // defer api.Scheme.Log(nil) - - kind := "DaemonSet" - for i := 0; i < *fuzzIters; i++ { - doRoundTripTest(testapi.Groups["extensions"], kind, t) - if t.Failed() { - break - } - } -} - -func TestList(t *testing.T) { - // api.Scheme.Log(t) - // defer api.Scheme.Log(nil) - - kind := "List" - item, err := api.Scheme.New(api.SchemeGroupVersion.WithKind(kind)) - if err != nil { - t.Errorf("Couldn't make a %v? %v", kind, err) - return - } - roundTripSame(t, testapi.Default, item) -} - -var nonRoundTrippableTypes = sets.NewString("ExportOptions") - -var nonInternalRoundTrippableTypes = sets.NewString("List", "ListOptions", "ExportOptions") -var nonRoundTrippableTypesByVersion = map[string][]string{} - -func TestRoundTripTypes(t *testing.T) { - // api.Scheme.Log(t) - // defer api.Scheme.Log(nil) - - for groupKey, group := range testapi.Groups { - for kind := range api.Scheme.KnownTypes(group.InternalGroupVersion()) { - t.Logf("working on %v in %v", kind, groupKey) - if nonRoundTrippableTypes.Has(kind) { - continue - } - // Try a few times, since runTest uses random values. - for i := 0; i < *fuzzIters; i++ { - doRoundTripTest(group, kind, t) - if t.Failed() { - break - } - } - } - } -} - -func doRoundTripTest(group testapi.TestGroup, kind string, t *testing.T) { - item, err := api.Scheme.New(group.InternalGroupVersion().WithKind(kind)) - if err != nil { - t.Fatalf("Couldn't make a %v? %v", kind, err) - } - if _, err := meta.TypeAccessor(item); err != nil { - t.Fatalf("%q is not a TypeMeta and cannot be tested - add it to nonRoundTrippableTypes: %v", kind, err) - } - if api.Scheme.Recognizes(group.GroupVersion().WithKind(kind)) { - roundTripSame(t, group, item, nonRoundTrippableTypesByVersion[kind]...) - } - if !nonInternalRoundTrippableTypes.Has(kind) && api.Scheme.Recognizes(group.GroupVersion().WithKind(kind)) { - roundTrip(t, group.Codec(), fuzzInternalObject(t, group.InternalGroupVersion(), item, rand.Int63())) - } -} - -func TestEncode_Ptr(t *testing.T) { - grace := int64(30) - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"name": "foo"}, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - - TerminationGracePeriodSeconds: &grace, - - SecurityContext: &api.PodSecurityContext{}, - }, - } - obj := runtime.Object(pod) - data, err := runtime.Encode(testapi.Default.Codec(), obj) - obj2, err2 := runtime.Decode(testapi.Default.Codec(), data) - if err != nil || err2 != nil { - t.Fatalf("Failure: '%v' '%v'", err, err2) - } - if _, ok := obj2.(*api.Pod); !ok { - t.Fatalf("Got wrong type") - } - if !api.Semantic.DeepEqual(obj2, pod) { - t.Errorf("\nExpected:\n\n %#v,\n\nGot:\n\n %#vDiff: %v\n\n", pod, obj2, util.ObjectDiff(obj2, pod)) - - } -} - -func TestBadJSONRejection(t *testing.T) { - badJSONMissingKind := []byte(`{ }`) - if _, err := runtime.Decode(testapi.Default.Codec(), badJSONMissingKind); err == nil { - t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind) - } - badJSONUnknownType := []byte(`{"kind": "bar"}`) - if _, err1 := runtime.Decode(testapi.Default.Codec(), badJSONUnknownType); err1 == nil { - t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType) - } - /*badJSONKindMismatch := []byte(`{"kind": "Pod"}`) - if err2 := DecodeInto(badJSONKindMismatch, &Minion{}); err2 == nil { - t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch) - }*/ -} - -func TestUnversionedTypes(t *testing.T) { - testcases := []runtime.Object{ - &unversioned.Status{Status: "Failure", Message: "something went wrong"}, - &unversioned.APIVersions{Versions: []string{"A", "B", "C"}}, - &unversioned.APIGroupList{Groups: []unversioned.APIGroup{{Name: "mygroup"}}}, - &unversioned.APIGroup{Name: "mygroup"}, - &unversioned.APIResourceList{GroupVersion: "mygroup/myversion"}, - } - - for _, obj := range testcases { - // Make sure the unversioned codec can encode - unversionedJSON, err := runtime.Encode(testapi.Default.Codec(), obj) - if err != nil { - t.Errorf("%v: unexpected error: %v", obj, err) - continue - } - - // Make sure the versioned codec under test can decode - versionDecodedObject, err := runtime.Decode(testapi.Default.Codec(), unversionedJSON) - if err != nil { - t.Errorf("%v: unexpected error: %v", obj, err) - continue - } - // Make sure it decodes correctly - if !reflect.DeepEqual(obj, versionDecodedObject) { - t.Errorf("%v: expected %#v, got %#v", obj, obj, versionDecodedObject) - continue - } - } -} - -const benchmarkSeed = 100 - -func benchmarkItems() []v1.Pod { - apiObjectFuzzer := apitesting.FuzzerFor(nil, api.SchemeGroupVersion, rand.NewSource(benchmarkSeed)) - items := make([]v1.Pod, 2) - for i := range items { - apiObjectFuzzer.Fuzz(&items[i]) - } - return items -} - -// BenchmarkEncodeCodec measures the cost of performing a codec encode, which includes -// reflection (to clear APIVersion and Kind) -func BenchmarkEncodeCodec(b *testing.B) { - items := benchmarkItems() - width := len(items) - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err := runtime.Encode(testapi.Default.Codec(), &items[i%width]); err != nil { - b.Fatal(err) - } - } - b.StopTimer() -} - -// BenchmarkEncodeJSONMarshal provides a baseline for regular JSON encode performance -func BenchmarkEncodeJSONMarshal(b *testing.B) { - items := benchmarkItems() - width := len(items) - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err := json.Marshal(&items[i%width]); err != nil { - b.Fatal(err) - } - } - b.StopTimer() -} - -func BenchmarkDecodeCodec(b *testing.B) { - codec := testapi.Default.Codec() - items := benchmarkItems() - width := len(items) - encoded := make([][]byte, width) - for i := range items { - data, err := runtime.Encode(codec, &items[i]) - if err != nil { - b.Fatal(err) - } - encoded[i] = data - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err := runtime.Decode(codec, encoded[i%width]); err != nil { - b.Fatal(err) - } - } - b.StopTimer() -} - -func BenchmarkDecodeIntoExternalCodec(b *testing.B) { - codec := testapi.Default.Codec() - items := benchmarkItems() - width := len(items) - encoded := make([][]byte, width) - for i := range items { - data, err := runtime.Encode(codec, &items[i]) - if err != nil { - b.Fatal(err) - } - encoded[i] = data - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - obj := v1.Pod{} - if err := runtime.DecodeInto(codec, encoded[i%width], &obj); err != nil { - b.Fatal(err) - } - } - b.StopTimer() -} - -func BenchmarkDecodeIntoInternalCodec(b *testing.B) { - codec := testapi.Default.Codec() - items := benchmarkItems() - width := len(items) - encoded := make([][]byte, width) - for i := range items { - data, err := runtime.Encode(codec, &items[i]) - if err != nil { - b.Fatal(err) - } - encoded[i] = data - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - obj := api.Pod{} - if err := runtime.DecodeInto(codec, encoded[i%width], &obj); err != nil { - b.Fatal(err) - } - } - b.StopTimer() -} - -// BenchmarkDecodeJSON provides a baseline for regular JSON decode performance -func BenchmarkDecodeIntoJSON(b *testing.B) { - codec := testapi.Default.Codec() - items := benchmarkItems() - width := len(items) - encoded := make([][]byte, width) - for i := range items { - data, err := runtime.Encode(codec, &items[i]) - if err != nil { - b.Fatal(err) - } - encoded[i] = data - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - obj := v1.Pod{} - if err := json.Unmarshal(encoded[i%width], &obj); err != nil { - b.Fatal(err) - } - } - b.StopTimer() -} - -// BenchmarkDecodeJSON provides a baseline for codecgen JSON decode performance -func BenchmarkDecodeIntoJSONCodecGen(b *testing.B) { - kcodec := testapi.Default.Codec() - items := benchmarkItems() - width := len(items) - encoded := make([][]byte, width) - for i := range items { - data, err := runtime.Encode(kcodec, &items[i]) - if err != nil { - b.Fatal(err) - } - encoded[i] = data - } - handler := &codec.JsonHandle{} - - b.ResetTimer() - for i := 0; i < b.N; i++ { - obj := v1.Pod{} - if err := codec.NewDecoderBytes(encoded[i%width], handler).Decode(&obj); err != nil { - b.Fatal(err) - } - } - b.StopTimer() -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flock/flock_other.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/service/annotations.go similarity index 51% rename from Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flock/flock_other.go rename to Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/service/annotations.go index b80981882..9d57fa4c2 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flock/flock_other.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/service/annotations.go @@ -1,5 +1,3 @@ -// +build !linux,!darwin,!freebsd,!openbsd,!netbsd,!dragonfly - /* Copyright 2016 The Kubernetes Authors All rights reserved. @@ -16,9 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -package flock +package service -// Acquire is not implemented on non-unix systems. -func Acquire(path string) error { - return nil -} +const ( + // AnnotationLoadBalancerSourceRangesKey is the key of the annotation on a service to set allowed ingress ranges on their LoadBalancers + // + // It should be a comma-separated list of CIDRs, e.g. `0.0.0.0/0` to + // allow full access (the default) or `18.0.0.0/8,56.0.0.0/8` to allow + // access only from the CIDRs currently allocated to MIT & the USPS. + // + // Not all cloud providers support this annotation, though AWS & GCE do. + AnnotationLoadBalancerSourceRangesKey = "service.beta.kubernetes.io/load-balancer-source-ranges" +) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/service/util.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/service/util.go new file mode 100644 index 000000000..a77e5b9c7 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/service/util.go @@ -0,0 +1,54 @@ +/* +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 service + +import ( + "fmt" + "strings" + + netsets "k8s.io/kubernetes/pkg/util/net/sets" +) + +const ( + defaultLoadBalancerSourceRanges = "0.0.0.0/0" +) + +// IsAllowAll checks whether the netsets.IPNet allows traffic from 0.0.0.0/0 +func IsAllowAll(ipnets netsets.IPNet) bool { + for _, s := range ipnets.StringSlice() { + if s == "0.0.0.0/0" { + return true + } + } + return false +} + +// GetLoadBalancerSourceRanges verifies and parses the AnnotationLoadBalancerSourceRangesKey annotation from a service, +// extracting the source ranges to allow, and if not present returns a default (allow-all) value. +func GetLoadBalancerSourceRanges(annotations map[string]string) (netsets.IPNet, error) { + val := annotations[AnnotationLoadBalancerSourceRangesKey] + val = strings.TrimSpace(val) + if val == "" { + val = defaultLoadBalancerSourceRanges + } + specs := strings.Split(val, ",") + ipnets, err := netsets.ParseIPNets(specs...) + if err != nil { + return nil, fmt.Errorf("Service annotation %s:%s is not valid. Expecting a comma-separated list of source IP ranges. For example, 10.0.0.0/24,192.168.2.0/24", AnnotationLoadBalancerSourceRangesKey, val) + } + return ipnets, nil +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testapi/testapi.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testapi/testapi.go index f6f75e879..d5d5452ad 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testapi/testapi.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testapi/testapi.go @@ -20,6 +20,7 @@ package testapi import ( "fmt" "os" + "reflect" "strings" "k8s.io/kubernetes/pkg/api" @@ -27,11 +28,13 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/autoscaling" + "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/runtime" _ "k8s.io/kubernetes/pkg/api/install" _ "k8s.io/kubernetes/pkg/apis/autoscaling/install" + _ "k8s.io/kubernetes/pkg/apis/batch/install" _ "k8s.io/kubernetes/pkg/apis/componentconfig/install" _ "k8s.io/kubernetes/pkg/apis/extensions/install" _ "k8s.io/kubernetes/pkg/apis/metrics/install" @@ -41,12 +44,14 @@ var ( Groups = make(map[string]TestGroup) Default TestGroup Autoscaling TestGroup + Batch TestGroup Extensions TestGroup ) type TestGroup struct { externalGroupVersion unversioned.GroupVersion internalGroupVersion unversioned.GroupVersion + internalTypes map[string]reflect.Type } func init() { @@ -59,9 +64,11 @@ func init() { panic(fmt.Sprintf("Error parsing groupversion %v: %v", gvString, err)) } + internalGroupVersion := unversioned.GroupVersion{Group: groupVersion.Group, Version: runtime.APIVersionInternal} Groups[groupVersion.Group] = TestGroup{ externalGroupVersion: groupVersion, - internalGroupVersion: unversioned.GroupVersion{Group: groupVersion.Group, Version: runtime.APIVersionInternal}, + internalGroupVersion: internalGroupVersion, + internalTypes: api.Scheme.KnownTypes(internalGroupVersion), } } } @@ -70,23 +77,55 @@ func init() { Groups[api.GroupName] = TestGroup{ externalGroupVersion: unversioned.GroupVersion{Group: api.GroupName, Version: registered.GroupOrDie(api.GroupName).GroupVersion.Version}, internalGroupVersion: api.SchemeGroupVersion, - } - } - if _, ok := Groups[autoscaling.GroupName]; !ok { - Groups[autoscaling.GroupName] = TestGroup{ - externalGroupVersion: unversioned.GroupVersion{Group: autoscaling.GroupName, Version: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.Version}, - internalGroupVersion: extensions.SchemeGroupVersion, + internalTypes: api.Scheme.KnownTypes(api.SchemeGroupVersion), } } if _, ok := Groups[extensions.GroupName]; !ok { Groups[extensions.GroupName] = TestGroup{ externalGroupVersion: unversioned.GroupVersion{Group: extensions.GroupName, Version: registered.GroupOrDie(extensions.GroupName).GroupVersion.Version}, internalGroupVersion: extensions.SchemeGroupVersion, + internalTypes: api.Scheme.KnownTypes(extensions.SchemeGroupVersion), + } + } + if _, ok := Groups[autoscaling.GroupName]; !ok { + internalTypes := make(map[string]reflect.Type) + for k, t := range api.Scheme.KnownTypes(extensions.SchemeGroupVersion) { + if k == "Scale" { + continue + } + internalTypes[k] = t + } + Groups[autoscaling.GroupName] = TestGroup{ + externalGroupVersion: unversioned.GroupVersion{Group: autoscaling.GroupName, Version: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.Version}, + internalGroupVersion: extensions.SchemeGroupVersion, + internalTypes: internalTypes, + } + } + if _, ok := Groups[autoscaling.GroupName+"IntraGroup"]; !ok { + internalTypes := make(map[string]reflect.Type) + for k, t := range api.Scheme.KnownTypes(extensions.SchemeGroupVersion) { + if k == "Scale" { + internalTypes[k] = t + break + } + } + Groups[autoscaling.GroupName] = TestGroup{ + externalGroupVersion: unversioned.GroupVersion{Group: autoscaling.GroupName, Version: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.Version}, + internalGroupVersion: autoscaling.SchemeGroupVersion, + internalTypes: internalTypes, + } + } + if _, ok := Groups[batch.GroupName]; !ok { + Groups[batch.GroupName] = TestGroup{ + externalGroupVersion: unversioned.GroupVersion{Group: batch.GroupName, Version: registered.GroupOrDie(batch.GroupName).GroupVersion.Version}, + internalGroupVersion: extensions.SchemeGroupVersion, + internalTypes: api.Scheme.KnownTypes(extensions.SchemeGroupVersion), } } Default = Groups[api.GroupName] Autoscaling = Groups[autoscaling.GroupName] + Batch = Groups[batch.GroupName] Extensions = Groups[extensions.GroupName] } @@ -105,6 +144,11 @@ func (g TestGroup) InternalGroupVersion() unversioned.GroupVersion { return g.internalGroupVersion } +// InternalTypes returns a map of internal API types' kind names to their Go types. +func (g TestGroup) InternalTypes() map[string]reflect.Type { + return g.internalTypes +} + // Codec returns the codec for the API version to test against, as set by the // KUBE_TEST_API env var. func (g TestGroup) Codec() runtime.Codec { @@ -218,6 +262,6 @@ func GetCodecForObject(obj runtime.Object) (runtime.Codec, error) { return nil, fmt.Errorf("unexpected kind: %v", kind) } -func NewTestGroup(external, internal unversioned.GroupVersion) TestGroup { - return TestGroup{external, internal} +func NewTestGroup(external, internal unversioned.GroupVersion, internalTypes map[string]reflect.Type) TestGroup { + return TestGroup{external, internal, internalTypes} } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testapi/testapi_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testapi/testapi_test.go deleted file mode 100644 index 7d9ad0ec0..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testapi/testapi_test.go +++ /dev/null @@ -1,133 +0,0 @@ -/* -Copyright 2015 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 testapi - -import ( - "encoding/json" - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" -) - -// TODO these tests don't add much value for testing things that have groups - -func TestResourcePathWithPrefix(t *testing.T) { - testCases := []struct { - prefix string - resource string - namespace string - name string - expected string - }{ - {"prefix", "resource", "mynamespace", "myresource", "/api/" + Default.GroupVersion().Version + "/prefix/namespaces/mynamespace/resource/myresource"}, - {"prefix", "resource", "", "myresource", "/api/" + Default.GroupVersion().Version + "/prefix/resource/myresource"}, - {"prefix", "resource", "mynamespace", "", "/api/" + Default.GroupVersion().Version + "/prefix/namespaces/mynamespace/resource"}, - {"prefix", "resource", "", "", "/api/" + Default.GroupVersion().Version + "/prefix/resource"}, - {"", "resource", "mynamespace", "myresource", "/api/" + Default.GroupVersion().Version + "/namespaces/mynamespace/resource/myresource"}, - } - for _, item := range testCases { - if actual := Default.ResourcePathWithPrefix(item.prefix, item.resource, item.namespace, item.name); actual != item.expected { - t.Errorf("Expected: %s, got: %s for prefix: %s, resource: %s, namespace: %s and name: %s", item.expected, actual, item.prefix, item.resource, item.namespace, item.name) - } - } -} - -func TestResourcePath(t *testing.T) { - testCases := []struct { - resource string - namespace string - name string - expected string - }{ - {"resource", "mynamespace", "myresource", "/api/" + Default.GroupVersion().Version + "/namespaces/mynamespace/resource/myresource"}, - {"resource", "", "myresource", "/api/" + Default.GroupVersion().Version + "/resource/myresource"}, - {"resource", "mynamespace", "", "/api/" + Default.GroupVersion().Version + "/namespaces/mynamespace/resource"}, - {"resource", "", "", "/api/" + Default.GroupVersion().Version + "/resource"}, - } - for _, item := range testCases { - if actual := Default.ResourcePath(item.resource, item.namespace, item.name); actual != item.expected { - t.Errorf("Expected: %s, got: %s for resource: %s, namespace: %s and name: %s", item.expected, actual, item.resource, item.namespace, item.name) - } - } -} - -var status = &unversioned.Status{ - Status: unversioned.StatusFailure, - Code: 200, - Reason: unversioned.StatusReasonUnknown, - Message: "", -} - -func TestV1EncodeDecodeStatus(t *testing.T) { - v1Codec := Default.Codec() - - encoded, err := runtime.Encode(v1Codec, status) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - typeMeta := unversioned.TypeMeta{} - if err := json.Unmarshal(encoded, &typeMeta); err != nil { - t.Errorf("unexpected error: %v", err) - } - if typeMeta.Kind != "Status" { - t.Errorf("Kind is not set to \"Status\". Got %v", string(encoded)) - } - if typeMeta.APIVersion != "v1" { - t.Errorf("APIVersion is not set to \"v1\". Got %v", string(encoded)) - } - decoded, err := runtime.Decode(v1Codec, encoded) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !reflect.DeepEqual(status, decoded) { - t.Errorf("expected: %#v, got: %#v", status, decoded) - } -} - -func testEncodeDecodeStatus(t *testing.T, codec runtime.Codec) { - encoded, err := runtime.Encode(codec, status) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - typeMeta := unversioned.TypeMeta{} - if err := json.Unmarshal(encoded, &typeMeta); err != nil { - t.Errorf("unexpected error: %v", err) - } - if typeMeta.Kind != "Status" { - t.Errorf("Kind is not set to \"Status\". Got %s", encoded) - } - if typeMeta.APIVersion != "v1" { - t.Errorf("APIVersion is not set to \"\". Got %s", encoded) - } - decoded, err := runtime.Decode(codec, encoded) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !reflect.DeepEqual(status, decoded) { - t.Errorf("expected: %v, got: %v", status, decoded) - } -} - -func TestAutoscalingEncodeDecodeStatus(t *testing.T) { - testEncodeDecodeStatus(t, Autoscaling.Codec()) -} - -func TestExperimentalEncodeDecodeStatus(t *testing.T) { - testEncodeDecodeStatus(t, Extensions.Codec()) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/compat/compatibility_tester.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/compat/compatibility_tester.go deleted file mode 100644 index 82f165a33..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/compat/compatibility_tester.go +++ /dev/null @@ -1,144 +0,0 @@ -/* -Copyright 2015 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 compat - -import ( - "encoding/json" - "fmt" - "os" - "reflect" - "regexp" - "strconv" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/kubectl" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/validation/field" -) - -// Based on: https://github.com/openshift/origin/blob/master/pkg/api/compatibility_test.go -// -// TestCompatibility reencodes the input using the codec for the given -// version and checks for the presence of the expected keys and absent -// keys in the resulting JSON. -func TestCompatibility( - t *testing.T, - version unversioned.GroupVersion, - input []byte, - validator func(obj runtime.Object) field.ErrorList, - expectedKeys map[string]string, - absentKeys []string, -) { - - // Decode - codec := api.Codecs.LegacyCodec(version) - obj, err := runtime.Decode(codec, input) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - // Validate - errs := validator(obj) - if len(errs) != 0 { - t.Fatalf("Unexpected validation errors: %v", errs) - } - - // Encode - output, err := runtime.Encode(codec, obj) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - // Validate old and new fields are encoded - generic := map[string]interface{}{} - if err := json.Unmarshal(output, &generic); err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - hasError := false - for k, expectedValue := range expectedKeys { - keys := strings.Split(k, ".") - if actualValue, ok, err := getJSONValue(generic, keys...); err != nil || !ok { - t.Errorf("Unexpected error for %s: %v", k, err) - hasError = true - } else if !reflect.DeepEqual(expectedValue, fmt.Sprintf("%v", actualValue)) { - hasError = true - t.Errorf("Unexpected value for %v: expected %v, got %v", k, expectedValue, actualValue) - } - } - - for _, absentKey := range absentKeys { - keys := strings.Split(absentKey, ".") - actualValue, ok, err := getJSONValue(generic, keys...) - if err == nil || ok { - t.Errorf("Unexpected value found for for key %s: %v", absentKey, actualValue) - hasError = true - } - } - - if hasError { - printer := new(kubectl.JSONPrinter) - printer.PrintObj(obj, os.Stdout) - t.Logf("2: Encoded value: %#v", string(output)) - } -} - -func getJSONValue(data map[string]interface{}, keys ...string) (interface{}, bool, error) { - // No keys, current value is it - if len(keys) == 0 { - return data, true, nil - } - - // Get the key (and optional index) - key := keys[0] - index := -1 - if matches := regexp.MustCompile(`^(.*)\[(\d+)\]$`).FindStringSubmatch(key); len(matches) > 0 { - key = matches[1] - index, _ = strconv.Atoi(matches[2]) - } - - // Look up the value - value, ok := data[key] - if !ok { - return nil, false, fmt.Errorf("No key %s found", key) - } - - // Get the indexed value if an index is specified - if index >= 0 { - valueSlice, ok := value.([]interface{}) - if !ok { - return nil, false, fmt.Errorf("Key %s did not hold a slice", key) - } - if index >= len(valueSlice) { - return nil, false, fmt.Errorf("Index %d out of bounds for slice at key: %v", index, key) - } - value = valueSlice[index] - } - - if len(keys) == 1 { - return value, true, nil - } - - childData, ok := value.(map[string]interface{}) - if !ok { - return nil, false, fmt.Errorf("Key %s did not hold a map", keys[0]) - } - return getJSONValue(childData, keys[1:]...) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/conversion.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/conversion.go deleted file mode 100644 index 6f91427e7..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/conversion.go +++ /dev/null @@ -1,72 +0,0 @@ -/* -Copyright 2015 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 testing - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/labels" -) - -// TestSelectableFieldLabelConversions verifies that given resource have field -// label conversion defined for each its selectable field. -// fields contains selectable fields of the resource. -// labelMap maps deprecated labels to their canonical names. -func TestSelectableFieldLabelConversionsOfKind(t *testing.T, apiVersion string, kind string, fields labels.Set, labelMap map[string]string) { - badFieldLabels := []string{ - "name", - ".name", - "bad", - "metadata", - "foo.bar", - } - - value := "value" - - if len(fields) == 0 { - t.Logf("no selectable fields for kind %q, skipping", kind) - } - for label := range fields { - if label == "name" { - t.Logf("FIXME: \"name\" is deprecated by \"metadata.name\", it should be removed from selectable fields of kind=%s", kind) - continue - } - newLabel, newValue, err := api.Scheme.ConvertFieldLabel(apiVersion, kind, label, value) - if err != nil { - t.Errorf("kind=%s label=%s: got unexpected error: %v", kind, label, err) - } else { - expectedLabel := label - if l, exists := labelMap[label]; exists { - expectedLabel = l - } - if newLabel != expectedLabel { - t.Errorf("kind=%s label=%s: got unexpected label name (%q != %q)", kind, label, newLabel, expectedLabel) - } - if newValue != value { - t.Errorf("kind=%s label=%s: got unexpected new value (%q != %q)", kind, label, newValue, value) - } - } - } - - for _, label := range badFieldLabels { - _, _, err := api.Scheme.ConvertFieldLabel(apiVersion, kind, label, "value") - if err == nil { - t.Errorf("kind=%s label=%s: got unexpected non-error", kind, label) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/fuzzer.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/fuzzer.go deleted file mode 100644 index 092802d11..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/fuzzer.go +++ /dev/null @@ -1,413 +0,0 @@ -/* -Copyright 2015 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 testing - -import ( - "fmt" - "math/rand" - "reflect" - "strconv" - "testing" - - docker "github.com/fsouza/go-dockerclient" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/fields" - "k8s.io/kubernetes/pkg/labels" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/types" - "k8s.io/kubernetes/pkg/util/intstr" - - "github.com/google/gofuzz" -) - -// FuzzerFor can randomly populate api objects that are destined for version. -func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source) *fuzz.Fuzzer { - f := fuzz.New().NilChance(.5).NumElements(1, 1) - if src != nil { - f.RandSource(src) - } - f.Funcs( - func(j *int, c fuzz.Continue) { - *j = int(c.Int31()) - }, - func(j **int, c fuzz.Continue) { - if c.RandBool() { - i := int(c.Int31()) - *j = &i - } else { - *j = nil - } - }, - func(q *resource.Quantity, c fuzz.Continue) { - *q = *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent) - }, - func(j *runtime.TypeMeta, c fuzz.Continue) { - // We have to customize the randomization of TypeMetas because their - // APIVersion and Kind must remain blank in memory. - j.APIVersion = "" - j.Kind = "" - }, - func(j *unversioned.TypeMeta, c fuzz.Continue) { - // We have to customize the randomization of TypeMetas because their - // APIVersion and Kind must remain blank in memory. - j.APIVersion = "" - j.Kind = "" - }, - func(j *api.ObjectMeta, c fuzz.Continue) { - j.Name = c.RandString() - j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) - j.SelfLink = c.RandString() - j.UID = types.UID(c.RandString()) - j.GenerateName = c.RandString() - - var sec, nsec int64 - c.Fuzz(&sec) - c.Fuzz(&nsec) - j.CreationTimestamp = unversioned.Unix(sec, nsec).Rfc3339Copy() - }, - func(j *api.ObjectReference, c fuzz.Continue) { - // We have to customize the randomization of TypeMetas because their - // APIVersion and Kind must remain blank in memory. - j.APIVersion = c.RandString() - j.Kind = c.RandString() - j.Namespace = c.RandString() - j.Name = c.RandString() - j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) - j.FieldPath = c.RandString() - }, - func(j *unversioned.ListMeta, c fuzz.Continue) { - j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) - j.SelfLink = c.RandString() - }, - func(j *api.ListOptions, c fuzz.Continue) { - label, _ := labels.Parse("a=b") - j.LabelSelector = label - field, _ := fields.ParseSelector("a=b") - j.FieldSelector = field - }, - func(j *api.PodExecOptions, c fuzz.Continue) { - j.Stdout = true - j.Stderr = true - }, - func(j *api.PodAttachOptions, c fuzz.Continue) { - j.Stdout = true - j.Stderr = true - }, - func(s *api.PodSpec, c fuzz.Continue) { - c.FuzzNoCustom(s) - // has a default value - ttl := int64(30) - if c.RandBool() { - ttl = int64(c.Uint32()) - } - s.TerminationGracePeriodSeconds = &ttl - - c.Fuzz(s.SecurityContext) - - if s.SecurityContext == nil { - s.SecurityContext = new(api.PodSecurityContext) - } - }, - func(j *api.PodPhase, c fuzz.Continue) { - statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown} - *j = statuses[c.Rand.Intn(len(statuses))] - }, - func(j *api.Binding, c fuzz.Continue) { - c.Fuzz(&j.ObjectMeta) - j.Target.Name = c.RandString() - }, - func(j *api.ReplicationControllerSpec, c fuzz.Continue) { - c.FuzzNoCustom(j) // fuzz self without calling this function again - //j.TemplateRef = nil // this is required for round trip - }, - func(j *extensions.DeploymentStrategy, c fuzz.Continue) { - c.FuzzNoCustom(j) // fuzz self without calling this function again - // Ensure that strategyType is one of valid values. - strategyTypes := []extensions.DeploymentStrategyType{extensions.RecreateDeploymentStrategyType, extensions.RollingUpdateDeploymentStrategyType} - j.Type = strategyTypes[c.Rand.Intn(len(strategyTypes))] - if j.Type != extensions.RollingUpdateDeploymentStrategyType { - j.RollingUpdate = nil - } else { - rollingUpdate := extensions.RollingUpdateDeployment{} - if c.RandBool() { - rollingUpdate.MaxUnavailable = intstr.FromInt(int(c.RandUint64())) - rollingUpdate.MaxSurge = intstr.FromInt(int(c.RandUint64())) - } else { - rollingUpdate.MaxSurge = intstr.FromString(fmt.Sprintf("%d%%", c.RandUint64())) - } - j.RollingUpdate = &rollingUpdate - } - }, - func(j *extensions.JobSpec, c fuzz.Continue) { - c.FuzzNoCustom(j) // fuzz self without calling this function again - completions := int(c.Rand.Int31()) - parallelism := int(c.Rand.Int31()) - j.Completions = &completions - j.Parallelism = ¶llelism - }, - func(j *api.List, c fuzz.Continue) { - c.FuzzNoCustom(j) // fuzz self without calling this function again - // TODO: uncomment when round trip starts from a versioned object - if false { //j.Items == nil { - j.Items = []runtime.Object{} - } - }, - func(j *runtime.Object, c fuzz.Continue) { - // TODO: uncomment when round trip starts from a versioned object - if true { //c.RandBool() { - *j = &runtime.Unknown{ - // We do not set TypeMeta here because it is not carried through a round trip - RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`), - } - } else { - types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}} - t := types[c.Rand.Intn(len(types))] - c.Fuzz(t) - *j = t - } - }, - func(pb map[docker.Port][]docker.PortBinding, c fuzz.Continue) { - // This is necessary because keys with nil values get omitted. - // TODO: Is this a bug? - pb[docker.Port(c.RandString())] = []docker.PortBinding{ - {c.RandString(), c.RandString()}, - {c.RandString(), c.RandString()}, - } - }, - func(pm map[string]docker.PortMapping, c fuzz.Continue) { - // This is necessary because keys with nil values get omitted. - // TODO: Is this a bug? - pm[c.RandString()] = docker.PortMapping{ - c.RandString(): c.RandString(), - } - }, - func(q *api.ResourceRequirements, c fuzz.Continue) { - randomQuantity := func() resource.Quantity { - var q resource.Quantity - c.Fuzz(&q) - return q - } - q.Limits = make(api.ResourceList) - q.Requests = make(api.ResourceList) - cpuLimit := randomQuantity() - q.Limits[api.ResourceCPU] = *cpuLimit.Copy() - q.Requests[api.ResourceCPU] = *cpuLimit.Copy() - memoryLimit := randomQuantity() - q.Limits[api.ResourceMemory] = *memoryLimit.Copy() - q.Requests[api.ResourceMemory] = *memoryLimit.Copy() - storageLimit := randomQuantity() - q.Limits[api.ResourceStorage] = *storageLimit.Copy() - q.Requests[api.ResourceStorage] = *storageLimit.Copy() - }, - func(q *api.LimitRangeItem, c fuzz.Continue) { - var cpuLimit resource.Quantity - c.Fuzz(&cpuLimit) - - q.Type = api.LimitTypeContainer - q.Default = make(api.ResourceList) - q.Default[api.ResourceCPU] = *(cpuLimit.Copy()) - - q.DefaultRequest = make(api.ResourceList) - q.DefaultRequest[api.ResourceCPU] = *(cpuLimit.Copy()) - - q.Max = make(api.ResourceList) - q.Max[api.ResourceCPU] = *(cpuLimit.Copy()) - - q.Min = make(api.ResourceList) - q.Min[api.ResourceCPU] = *(cpuLimit.Copy()) - - q.MaxLimitRequestRatio = make(api.ResourceList) - q.MaxLimitRequestRatio[api.ResourceCPU] = resource.MustParse("10") - }, - func(p *api.PullPolicy, c fuzz.Continue) { - policies := []api.PullPolicy{api.PullAlways, api.PullNever, api.PullIfNotPresent} - *p = policies[c.Rand.Intn(len(policies))] - }, - func(rp *api.RestartPolicy, c fuzz.Continue) { - policies := []api.RestartPolicy{api.RestartPolicyAlways, api.RestartPolicyNever, api.RestartPolicyOnFailure} - *rp = policies[c.Rand.Intn(len(policies))] - }, - // Only api.DownwardAPIVolumeFile needs to have a specific func since FieldRef has to be - // defaulted to a version otherwise roundtrip will fail - // For the remaining volume plugins the default fuzzer is enough. - func(m *api.DownwardAPIVolumeFile, c fuzz.Continue) { - m.Path = c.RandString() - versions := []string{"v1"} - m.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))] - m.FieldRef.FieldPath = c.RandString() - }, - func(vs *api.VolumeSource, c fuzz.Continue) { - // Exactly one of the fields must be set. - v := reflect.ValueOf(vs).Elem() - i := int(c.RandUint64() % uint64(v.NumField())) - t := v.Field(i).Addr() - for v.Field(i).IsNil() { - c.Fuzz(t.Interface()) - } - }, - func(i *api.ISCSIVolumeSource, c fuzz.Continue) { - i.ISCSIInterface = c.RandString() - if i.ISCSIInterface == "" { - i.ISCSIInterface = "default" - } - }, - func(d *api.DNSPolicy, c fuzz.Continue) { - policies := []api.DNSPolicy{api.DNSClusterFirst, api.DNSDefault} - *d = policies[c.Rand.Intn(len(policies))] - }, - func(p *api.Protocol, c fuzz.Continue) { - protocols := []api.Protocol{api.ProtocolTCP, api.ProtocolUDP} - *p = protocols[c.Rand.Intn(len(protocols))] - }, - func(p *api.ServiceAffinity, c fuzz.Continue) { - types := []api.ServiceAffinity{api.ServiceAffinityClientIP, api.ServiceAffinityNone} - *p = types[c.Rand.Intn(len(types))] - }, - func(p *api.ServiceType, c fuzz.Continue) { - types := []api.ServiceType{api.ServiceTypeClusterIP, api.ServiceTypeNodePort, api.ServiceTypeLoadBalancer} - *p = types[c.Rand.Intn(len(types))] - }, - func(ct *api.Container, c fuzz.Continue) { - c.FuzzNoCustom(ct) // fuzz self without calling this function again - ct.TerminationMessagePath = "/" + ct.TerminationMessagePath // Must be non-empty - }, - func(p *api.Probe, c fuzz.Continue) { - c.FuzzNoCustom(p) - // These fields have default values. - intFieldsWithDefaults := [...]string{"TimeoutSeconds", "PeriodSeconds", "SuccessThreshold", "FailureThreshold"} - v := reflect.ValueOf(p).Elem() - for _, field := range intFieldsWithDefaults { - f := v.FieldByName(field) - if f.Int() == 0 { - f.SetInt(1) - } - } - }, - func(ev *api.EnvVar, c fuzz.Continue) { - ev.Name = c.RandString() - if c.RandBool() { - ev.Value = c.RandString() - } else { - ev.ValueFrom = &api.EnvVarSource{} - ev.ValueFrom.FieldRef = &api.ObjectFieldSelector{} - - var versions []unversioned.GroupVersion - for _, testGroup := range testapi.Groups { - versions = append(versions, *testGroup.GroupVersion()) - } - - ev.ValueFrom.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))].String() - ev.ValueFrom.FieldRef.FieldPath = c.RandString() - } - }, - func(sc *api.SecurityContext, c fuzz.Continue) { - c.FuzzNoCustom(sc) // fuzz self without calling this function again - if c.RandBool() { - priv := c.RandBool() - sc.Privileged = &priv - } - - if c.RandBool() { - sc.Capabilities = &api.Capabilities{ - Add: make([]api.Capability, 0), - Drop: make([]api.Capability, 0), - } - c.Fuzz(&sc.Capabilities.Add) - c.Fuzz(&sc.Capabilities.Drop) - } - }, - func(s *api.Secret, c fuzz.Continue) { - c.FuzzNoCustom(s) // fuzz self without calling this function again - s.Type = api.SecretTypeOpaque - }, - func(pv *api.PersistentVolume, c fuzz.Continue) { - c.FuzzNoCustom(pv) // fuzz self without calling this function again - types := []api.PersistentVolumePhase{api.VolumeAvailable, api.VolumePending, api.VolumeBound, api.VolumeReleased, api.VolumeFailed} - pv.Status.Phase = types[c.Rand.Intn(len(types))] - pv.Status.Message = c.RandString() - reclamationPolicies := []api.PersistentVolumeReclaimPolicy{api.PersistentVolumeReclaimRecycle, api.PersistentVolumeReclaimRetain} - pv.Spec.PersistentVolumeReclaimPolicy = reclamationPolicies[c.Rand.Intn(len(reclamationPolicies))] - }, - func(pvc *api.PersistentVolumeClaim, c fuzz.Continue) { - c.FuzzNoCustom(pvc) // fuzz self without calling this function again - types := []api.PersistentVolumeClaimPhase{api.ClaimBound, api.ClaimPending} - pvc.Status.Phase = types[c.Rand.Intn(len(types))] - }, - func(s *api.NamespaceSpec, c fuzz.Continue) { - s.Finalizers = []api.FinalizerName{api.FinalizerKubernetes} - }, - func(s *api.NamespaceStatus, c fuzz.Continue) { - s.Phase = api.NamespaceActive - }, - func(http *api.HTTPGetAction, c fuzz.Continue) { - c.FuzzNoCustom(http) // fuzz self without calling this function again - http.Path = "/" + http.Path // can't be blank - http.Scheme = "x" + http.Scheme // can't be blank - }, - func(ss *api.ServiceSpec, c fuzz.Continue) { - c.FuzzNoCustom(ss) // fuzz self without calling this function again - if len(ss.Ports) == 0 { - // There must be at least 1 port. - ss.Ports = append(ss.Ports, api.ServicePort{}) - c.Fuzz(&ss.Ports[0]) - } - for i := range ss.Ports { - switch ss.Ports[i].TargetPort.Type { - case intstr.Int: - ss.Ports[i].TargetPort.IntVal = 1 + ss.Ports[i].TargetPort.IntVal%65535 // non-zero - case intstr.String: - ss.Ports[i].TargetPort.StrVal = "x" + ss.Ports[i].TargetPort.StrVal // non-empty - } - } - }, - func(n *api.Node, c fuzz.Continue) { - c.FuzzNoCustom(n) - n.Spec.ExternalID = "external" - }, - func(s *api.NodeStatus, c fuzz.Continue) { - c.FuzzNoCustom(s) - s.Allocatable = s.Capacity - }, - func(s *extensions.APIVersion, c fuzz.Continue) { - // We can't use c.RandString() here because it may generate empty - // string, which will cause tests failure. - s.APIGroup = "something" - }, - func(s *extensions.HorizontalPodAutoscalerSpec, c fuzz.Continue) { - c.FuzzNoCustom(s) // fuzz self without calling this function again - minReplicas := int(c.Rand.Int31()) - s.MinReplicas = &minReplicas - s.CPUUtilization = &extensions.CPUTargetUtilization{TargetPercentage: int(int32(c.RandUint64()))} - }, - func(s *extensions.SubresourceReference, c fuzz.Continue) { - c.FuzzNoCustom(s) // fuzz self without calling this function again - s.Subresource = "scale" - }, - func(psp *extensions.PodSecurityPolicySpec, c fuzz.Continue) { - c.FuzzNoCustom(psp) // fuzz self without calling this function again - userTypes := []extensions.RunAsUserStrategy{extensions.RunAsUserStrategyMustRunAsNonRoot, extensions.RunAsUserStrategyMustRunAs, extensions.RunAsUserStrategyRunAsAny} - psp.RunAsUser.Type = userTypes[c.Rand.Intn(len(userTypes))] - seLinuxTypes := []extensions.SELinuxContextStrategy{extensions.SELinuxStrategyRunAsAny, extensions.SELinuxStrategyMustRunAs} - psp.SELinuxContext.Type = seLinuxTypes[c.Rand.Intn(len(seLinuxTypes))] - }, - ) - return f -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/pod_specs.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/pod_specs.go deleted file mode 100644 index 2020b3f7f..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/testing/pod_specs.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2015 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 testing - -import ( - "k8s.io/kubernetes/pkg/api" -) - -// DeepEqualSafePodSpec returns a PodSpec which is ready to be used with api.Semantic.DeepEqual -func DeepEqualSafePodSpec() api.PodSpec { - grace := int64(30) - return api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - SecurityContext: &api.PodSecurityContext{}, - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/types.generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/types.generated.go index 1d5f537f8..b4ccb56cc 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/types.generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/types.generated.go @@ -26953,13 +26953,14 @@ func (x *ReplicationControllerStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [2]bool + var yyq2 [3]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false - yyq2[1] = x.ObservedGeneration != 0 + yyq2[1] = x.FullyLabeledReplicas != 0 + yyq2[2] = x.ObservedGeneration != 0 var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(2) + r.EncodeArrayStart(3) } else { yynn2 = 1 for _, b := range yyq2 { @@ -26996,7 +26997,7 @@ func (x *ReplicationControllerStatus) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym7 if false { } else { - r.EncodeInt(int64(x.ObservedGeneration)) + r.EncodeInt(int64(x.FullyLabeledReplicas)) } } else { r.EncodeInt(0) @@ -27004,11 +27005,36 @@ func (x *ReplicationControllerStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { if yyq2[1] { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("observedGeneration")) + r.EncodeString(codecSelferC_UTF81234, string("fullyLabeledReplicas")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym8 := z.EncBinary() _ = yym8 if false { + } else { + r.EncodeInt(int64(x.FullyLabeledReplicas)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yym10 := z.EncBinary() + _ = yym10 + if false { + } else { + r.EncodeInt(int64(x.ObservedGeneration)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("observedGeneration")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym11 := z.EncBinary() + _ = yym11 + if false { } else { r.EncodeInt(int64(x.ObservedGeneration)) } @@ -27081,6 +27107,12 @@ func (x *ReplicationControllerStatus) codecDecodeSelfFromMap(l int, d *codec1978 } else { x.Replicas = int(r.DecodeInt(codecSelferBitsize1234)) } + case "fullyLabeledReplicas": + if r.TryDecodeAsNil() { + x.FullyLabeledReplicas = 0 + } else { + x.FullyLabeledReplicas = int(r.DecodeInt(codecSelferBitsize1234)) + } case "observedGeneration": if r.TryDecodeAsNil() { x.ObservedGeneration = 0 @@ -27098,16 +27130,16 @@ func (x *ReplicationControllerStatus) codecDecodeSelfFromArray(l int, d *codec19 var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj6 int - var yyb6 bool - var yyhl6 bool = l >= 0 - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l + var yyj7 int + var yyb7 bool + var yyhl7 bool = l >= 0 + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb6 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb6 { + if yyb7 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -27117,13 +27149,29 @@ func (x *ReplicationControllerStatus) codecDecodeSelfFromArray(l int, d *codec19 } else { x.Replicas = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb6 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb6 { + if yyb7 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.FullyLabeledReplicas = 0 + } else { + x.FullyLabeledReplicas = int(r.DecodeInt(codecSelferBitsize1234)) + } + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l + } else { + yyb7 = r.CheckBreak() + } + if yyb7 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -27134,17 +27182,17 @@ func (x *ReplicationControllerStatus) codecDecodeSelfFromArray(l int, d *codec19 x.ObservedGeneration = int64(r.DecodeInt(64)) } for { - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb6 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb6 { + if yyb7 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj6-1, "") + z.DecStructFieldNotFound(yyj7-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -44187,6 +44235,32 @@ func (x *LimitRangeList) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } +func (x ResourceQuotaScope) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x)) + } +} + +func (x *ResourceQuotaScope) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + *((*string)(x)) = r.DecodeString() + } +} + func (x *ResourceQuotaSpec) CodecEncodeSelf(e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) @@ -44201,13 +44275,14 @@ func (x *ResourceQuotaSpec) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [1]bool + var yyq2 [2]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false yyq2[0] = len(x.Hard) != 0 + yyq2[1] = len(x.Scopes) != 0 var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(1) + r.EncodeArrayStart(2) } else { yynn2 = 0 for _, b := range yyq2 { @@ -44241,6 +44316,39 @@ func (x *ResourceQuotaSpec) CodecEncodeSelf(e *codec1978.Encoder) { } } } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + if x.Scopes == nil { + r.EncodeNil() + } else { + yym7 := z.EncBinary() + _ = yym7 + if false { + } else { + h.encSliceResourceQuotaScope(([]ResourceQuotaScope)(x.Scopes), e) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("scopes")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.Scopes == nil { + r.EncodeNil() + } else { + yym8 := z.EncBinary() + _ = yym8 + if false { + } else { + h.encSliceResourceQuotaScope(([]ResourceQuotaScope)(x.Scopes), e) + } + } + } + } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayEnd1234) } else { @@ -44309,6 +44417,18 @@ func (x *ResourceQuotaSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) yyv4 := &x.Hard yyv4.CodecDecodeSelf(d) } + case "scopes": + if r.TryDecodeAsNil() { + x.Scopes = nil + } else { + yyv5 := &x.Scopes + yym6 := z.DecBinary() + _ = yym6 + if false { + } else { + h.decSliceResourceQuotaScope((*[]ResourceQuotaScope)(yyv5), d) + } + } default: z.DecStructFieldNotFound(-1, yys3) } // end switch yys3 @@ -44320,16 +44440,16 @@ func (x *ResourceQuotaSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj5 int - var yyb5 bool - var yyhl5 bool = l >= 0 - yyj5++ - if yyhl5 { - yyb5 = yyj5 > l + var yyj7 int + var yyb7 bool + var yyhl7 bool = l >= 0 + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb5 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb5 { + if yyb7 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -44337,21 +44457,43 @@ func (x *ResourceQuotaSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder if r.TryDecodeAsNil() { x.Hard = nil } else { - yyv6 := &x.Hard - yyv6.CodecDecodeSelf(d) + yyv8 := &x.Hard + yyv8.CodecDecodeSelf(d) + } + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l + } else { + yyb7 = r.CheckBreak() + } + if yyb7 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Scopes = nil + } else { + yyv9 := &x.Scopes + yym10 := z.DecBinary() + _ = yym10 + if false { + } else { + h.decSliceResourceQuotaScope((*[]ResourceQuotaScope)(yyv9), d) + } } for { - yyj5++ - if yyhl5 { - yyb5 = yyj5 > l + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb5 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb5 { + if yyb7 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj5-1, "") + z.DecStructFieldNotFound(yyj7-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -51295,7 +51437,7 @@ func (x codecSelfer1234) decSliceReplicationController(v *[]ReplicationControlle yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 232) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 240) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] @@ -53768,6 +53910,116 @@ func (x codecSelfer1234) decSliceLimitRange(v *[]LimitRange, d *codec1978.Decode } } +func (x codecSelfer1234) encSliceResourceQuotaScope(v []ResourceQuotaScope, e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + r.EncodeArrayStart(len(v)) + for _, yyv1 := range v { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yyv1.CodecEncodeSelf(e) + } + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x codecSelfer1234) decSliceResourceQuotaScope(v *[]ResourceQuotaScope, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + + yyv1 := *v + yyh1, yyl1 := z.DecSliceHelperStart() + var yyc1 bool + _ = yyc1 + if yyl1 == 0 { + if yyv1 == nil { + yyv1 = []ResourceQuotaScope{} + yyc1 = true + } else if len(yyv1) != 0 { + yyv1 = yyv1[:0] + yyc1 = true + } + } else if yyl1 > 0 { + var yyrr1, yyrl1 int + var yyrt1 bool + _, _ = yyrl1, yyrt1 + yyrr1 = yyl1 // len(yyv1) + if yyl1 > cap(yyv1) { + + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 16) + if yyrt1 { + if yyrl1 <= cap(yyv1) { + yyv1 = yyv1[:yyrl1] + } else { + yyv1 = make([]ResourceQuotaScope, yyrl1) + } + } else { + yyv1 = make([]ResourceQuotaScope, yyrl1) + } + yyc1 = true + yyrr1 = len(yyv1) + } else if yyl1 != len(yyv1) { + yyv1 = yyv1[:yyl1] + yyc1 = true + } + yyj1 := 0 + for ; yyj1 < yyrr1; yyj1++ { + yyh1.ElemContainerState(yyj1) + if r.TryDecodeAsNil() { + yyv1[yyj1] = "" + } else { + yyv1[yyj1] = ResourceQuotaScope(r.DecodeString()) + } + + } + if yyrt1 { + for ; yyj1 < yyl1; yyj1++ { + yyv1 = append(yyv1, "") + yyh1.ElemContainerState(yyj1) + if r.TryDecodeAsNil() { + yyv1[yyj1] = "" + } else { + yyv1[yyj1] = ResourceQuotaScope(r.DecodeString()) + } + + } + } + + } else { + yyj1 := 0 + for ; !r.CheckBreak(); yyj1++ { + + if yyj1 >= len(yyv1) { + yyv1 = append(yyv1, "") // var yyz1 ResourceQuotaScope + yyc1 = true + } + yyh1.ElemContainerState(yyj1) + if yyj1 < len(yyv1) { + if r.TryDecodeAsNil() { + yyv1[yyj1] = "" + } else { + yyv1[yyj1] = ResourceQuotaScope(r.DecodeString()) + } + + } else { + z.DecSwallow() + } + + } + if yyj1 < len(yyv1) { + yyv1 = yyv1[:yyj1] + yyc1 = true + } else if yyj1 == 0 && yyv1 == nil { + yyv1 = []ResourceQuotaScope{} + yyc1 = true + } + } + yyh1.End() + if yyc1 { + *v = yyv1 + } +} + func (x codecSelfer1234) encSliceResourceQuota(v []ResourceQuota, e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) @@ -53807,7 +54059,7 @@ func (x codecSelfer1234) decSliceResourceQuota(v *[]ResourceQuota, d *codec1978. yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 216) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 240) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/types.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/types.go index 1312ef086..a82aa45c0 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/types.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/types.go @@ -98,7 +98,7 @@ type ObjectMeta struct { ResourceVersion string `json:"resourceVersion,omitempty"` // A sequence number representing a specific generation of the desired state. - // Currently only implemented by replication controllers. + // Populated by the system. Read-only. Generation int64 `json:"generation,omitempty"` // CreationTimestamp is a timestamp representing the server time when this object was @@ -441,10 +441,10 @@ const ( // Represents a Persistent Disk resource in Google Compute Engine. // -// A GCE PD must exist and be formatted before mounting to a container. -// The disk must also be in the same GCE project and zone as the kubelet. -// A GCE PD can only be mounted as read/write once. -// GCE PDs support ownership management and SELinux relabeling. +// A GCE PD must exist before mounting to a container. The disk must +// also be in the same GCE project and zone as the kubelet. A GCE PD +// can only be mounted as read/write once or read-only many times. GCE +// PDs support ownership management and SELinux relabeling. type GCEPersistentDiskVolumeSource struct { // Unique name of the PD resource. Used to identify the disk in GCE PDName string `json:"pdName"` @@ -523,10 +523,10 @@ type FlexVolumeSource struct { // Represents a Persistent Disk resource in AWS. // -// An AWS EBS disk must exist and be formatted before mounting to a container. -// The disk must also be in the same AWS zone as the kubelet. -// A AWS EBS disk can only be mounted as read/write once. -// AWS EBS volumes support ownership management and SELinux relabeling. +// An AWS EBS disk must exist before mounting to a container. The disk +// must also be in the same AWS zone as the kubelet. A AWS EBS disk +// can only be mounted as read/write once. AWS EBS volumes support +// ownership management and SELinux relabeling. type AWSElasticBlockStoreVolumeSource struct { // Unique id of the persistent disk resource. Used to identify the disk in AWS VolumeID string `json:"volumeID"` @@ -623,10 +623,10 @@ type RBDVolumeSource struct { ReadOnly bool `json:"readOnly,omitempty"` } -// Represents a cinder volume resource in Openstack. -// A Cinder volume must exist and be formatted before mounting to a container. -// The volume must also be in the same region as the kubelet. -// Cinder volumes support ownership management and SELinux relabeling. +// Represents a cinder volume resource in Openstack. A Cinder volume +// must exist before mounting to a container. The volume must also be +// in the same region as the kubelet. Cinder volumes support ownership +// management and SELinux relabeling. type CinderVolumeSource struct { // Unique id of the volume used to identify the cinder volume VolumeID string `json:"volumeID"` @@ -742,7 +742,7 @@ type VolumeMount struct { Name string `json:"name"` // Optional: Defaults to false (read-write). ReadOnly bool `json:"readOnly,omitempty"` - // Required. + // Required. Must not contain ':'. MountPath string `json:"mountPath"` } @@ -1380,6 +1380,9 @@ type ReplicationControllerStatus struct { // Replicas is the number of actual replicas. Replicas int `json:"replicas"` + // The number of pods that have labels matching the labels of the pod template of the replication controller. + FullyLabeledReplicas int `json:"fullyLabeledReplicas,omitempty"` + // ObservedGeneration is the most recent generation observed by the controller. ObservedGeneration int64 `json:"observedGeneration,omitempty"` } @@ -1526,8 +1529,10 @@ type ServicePort struct { // Optional: The target port on pods selected by this service. If this // is a string, it will be looked up as a named port in the target - // Pod's container ports. If this is not specified, the default value - // is the sames as the Port field (an identity map). + // Pod's container ports. If this is not specified, the value + // of the 'port' field is used (an identity map). + // This field is ignored for services with clusterIP=None, and should be + // omitted or set equal to the 'port' field. TargetPort intstr.IntOrString `json:"targetPort"` // The port on each node on which this service is exposed. @@ -2185,14 +2190,41 @@ const ( ResourceQuotas ResourceName = "resourcequotas" // ResourceSecrets, number ResourceSecrets ResourceName = "secrets" + // ResourceConfigMaps, number + ResourceConfigMaps ResourceName = "configmaps" // ResourcePersistentVolumeClaims, number ResourcePersistentVolumeClaims ResourceName = "persistentvolumeclaims" + // CPU request, in cores. (500m = .5 cores) + ResourceRequestsCPU ResourceName = "requests.cpu" + // Memory request, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + ResourceRequestsMemory ResourceName = "requests.memory" + // CPU limit, in cores. (500m = .5 cores) + ResourceLimitsCPU ResourceName = "limits.cpu" + // Memory limit, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + ResourceLimitsMemory ResourceName = "limits.memory" +) + +// A ResourceQuotaScope defines a filter that must match each object tracked by a quota +type ResourceQuotaScope string + +const ( + // Match all pod objects where spec.activeDeadlineSeconds + ResourceQuotaScopeTerminating ResourceQuotaScope = "Terminating" + // Match all pod objects where !spec.activeDeadlineSeconds + ResourceQuotaScopeNotTerminating ResourceQuotaScope = "NotTerminating" + // Match all pod objects that have best effort quality of service + ResourceQuotaScopeBestEffort ResourceQuotaScope = "BestEffort" + // Match all pod objects that do not have best effort quality of service + ResourceQuotaScopeNotBestEffort ResourceQuotaScope = "NotBestEffort" ) // ResourceQuotaSpec defines the desired hard limits to enforce for Quota type ResourceQuotaSpec struct { // Hard is the set of desired hard limits for each named resource Hard ResourceList `json:"hard,omitempty"` + // A collection of filters that must match each object tracked by a quota. + // If not specified, the quota matches all objects. + Scopes []ResourceQuotaScope `json:"scopes,omitempty"` } // ResourceQuotaStatus defines the enforced hard limits and observed use diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/duration_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/duration_test.go deleted file mode 100644 index 6650ca9aa..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/duration_test.go +++ /dev/null @@ -1,153 +0,0 @@ -/* -Copyright 2014 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 unversioned - -import ( - "encoding/json" - "testing" - "time" - - "github.com/ghodss/yaml" -) - -type DurationHolder struct { - D Duration `json:"d"` -} - -func TestDurationMarshalYAML(t *testing.T) { - cases := []struct { - input Duration - result string - }{ - {Duration{5 * time.Second}, "d: 5s\n"}, - {Duration{2 * time.Minute}, "d: 2m0s\n"}, - {Duration{time.Hour + 3*time.Millisecond}, "d: 1h0m0.003s\n"}, - } - - for _, c := range cases { - input := DurationHolder{c.input} - result, err := yaml.Marshal(&input) - if err != nil { - t.Errorf("Failed to marshal input: %q: %v", input, err) - } - if string(result) != c.result { - t.Errorf("Failed to marshal input: %q: expected %q, got %q", input, c.result, string(result)) - } - } -} - -func TestDurationUnmarshalYAML(t *testing.T) { - cases := []struct { - input string - result Duration - }{ - {"d: 0s\n", Duration{}}, - {"d: 5s\n", Duration{5 * time.Second}}, - {"d: 2m0s\n", Duration{2 * time.Minute}}, - {"d: 1h0m0.003s\n", Duration{time.Hour + 3*time.Millisecond}}, - - // Units with zero values can optionally be dropped - {"d: 2m\n", Duration{2 * time.Minute}}, - {"d: 1h0.003s\n", Duration{time.Hour + 3*time.Millisecond}}, - } - - for _, c := range cases { - var result DurationHolder - if err := yaml.Unmarshal([]byte(c.input), &result); err != nil { - t.Errorf("Failed to unmarshal input %q: %v", c.input, err) - } - if result.D != c.result { - t.Errorf("Failed to unmarshal input %q: expected %q, got %q", c.input, c.result, result) - } - } -} - -func TestDurationMarshalJSON(t *testing.T) { - cases := []struct { - input Duration - result string - }{ - {Duration{5 * time.Second}, `{"d":"5s"}`}, - {Duration{2 * time.Minute}, `{"d":"2m0s"}`}, - {Duration{time.Hour + 3*time.Millisecond}, `{"d":"1h0m0.003s"}`}, - } - - for _, c := range cases { - input := DurationHolder{c.input} - result, err := json.Marshal(&input) - if err != nil { - t.Errorf("Failed to marshal input: %q: %v", input, err) - } - if string(result) != c.result { - t.Errorf("Failed to marshal input: %q: expected %q, got %q", input, c.result, string(result)) - } - } -} - -func TestDurationUnmarshalJSON(t *testing.T) { - cases := []struct { - input string - result Duration - }{ - {`{"d":"0s"}`, Duration{}}, - {`{"d":"5s"}`, Duration{5 * time.Second}}, - {`{"d":"2m0s"}`, Duration{2 * time.Minute}}, - {`{"d":"1h0m0.003s"}`, Duration{time.Hour + 3*time.Millisecond}}, - - // Units with zero values can optionally be dropped - {`{"d":"2m"}`, Duration{2 * time.Minute}}, - {`{"d":"1h0.003s"}`, Duration{time.Hour + 3*time.Millisecond}}, - } - - for _, c := range cases { - var result DurationHolder - if err := json.Unmarshal([]byte(c.input), &result); err != nil { - t.Errorf("Failed to unmarshal input %q: %v", c.input, err) - } - if result.D != c.result { - t.Errorf("Failed to unmarshal input %q: expected %q, got %q", c.input, c.result, result) - } - } -} - -func TestDurationMarshalJSONUnmarshalYAML(t *testing.T) { - cases := []struct { - input Duration - }{ - {Duration{}}, - {Duration{5 * time.Second}}, - {Duration{2 * time.Minute}}, - {Duration{time.Hour + 3*time.Millisecond}}, - } - - for i, c := range cases { - input := DurationHolder{c.input} - jsonMarshalled, err := json.Marshal(&input) - if err != nil { - t.Errorf("%d-1: Failed to marshal input: '%v': %v", i, input, err) - } - - var result DurationHolder - if err := yaml.Unmarshal(jsonMarshalled, &result); err != nil { - t.Errorf("%d-2: Failed to unmarshal '%+v': %v", i, string(jsonMarshalled), err) - } - - if input.D != result.D { - t.Errorf("%d-4: Failed to marshal input '%#v': got %#v", i, input, result) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/group_version.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/group_version.go index dc1dc9672..5d350432c 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/group_version.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/group_version.go @@ -22,6 +22,21 @@ import ( "strings" ) +// ParseResourceArg takes the common style of string which may be either `resource.group.com` or `resource.version.group.com` +// and parses it out into both possibilities. This code takes no responsibility for knowing which representation was intended +// but with a knowledge of all GroupVersions, calling code can take a very good guess. If there are only two segments, then +// `*GroupVersionResource` is nil. +// `resource.group.com` -> `group=com, version=group, resource=resource` and `group=group.com, resource=resource` +func ParseResourceArg(arg string) (*GroupVersionResource, GroupResource) { + var gvr *GroupVersionResource + s := strings.SplitN(arg, ".", 3) + if len(s) == 3 { + gvr = &GroupVersionResource{Group: s[2], Version: s[1], Resource: s[0]} + } + + return gvr, ParseGroupResource(arg) +} + // GroupResource specifies a Group and a Resource, but does not force a version. This is useful for identifying // concepts during lookup stages without having partially valid types // @@ -46,6 +61,17 @@ func (gr *GroupResource) String() string { return gr.Resource + "." + gr.Group } +// ParseGroupResource turns "resource.group" string into a GroupResource struct. Empty strings are allowed +// for each field. +func ParseGroupResource(gr string) GroupResource { + s := strings.SplitN(gr, ".", 2) + if len(s) == 1 { + return GroupResource{Resource: s[0]} + } + + return GroupResource{Group: s[1], Resource: s[0]} +} + // GroupVersionResource unambiguously identifies a resource. It doesn't anonymously include GroupVersion // to avoid automatic coersion. It doesn't use a GroupVersion to avoid custom marshalling // diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/group_version_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/group_version_test.go deleted file mode 100644 index 4a26fbb10..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/group_version_test.go +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright 2015 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 unversioned - -import ( - "encoding/json" - "reflect" - "testing" - - "github.com/ugorji/go/codec" -) - -type GroupVersionHolder struct { - GV GroupVersion `json:"val"` -} - -func TestGroupVersionUnmarshalJSON(t *testing.T) { - cases := []struct { - input []byte - expect GroupVersion - }{ - {[]byte(`{"val": "v1"}`), GroupVersion{"", "v1"}}, - {[]byte(`{"val": "extensions/v1beta1"}`), GroupVersion{"extensions", "v1beta1"}}, - } - - for _, c := range cases { - var result GroupVersionHolder - // test golang lib's JSON codec - if err := json.Unmarshal([]byte(c.input), &result); err != nil { - t.Errorf("JSON codec failed to unmarshal input '%v': %v", c.input, err) - } - if !reflect.DeepEqual(result.GV, c.expect) { - t.Errorf("JSON codec failed to unmarshal input '%s': expected %+v, got %+v", c.input, c.expect, result.GV) - } - // test the Ugorji codec - if err := codec.NewDecoderBytes(c.input, new(codec.JsonHandle)).Decode(&result); err != nil { - t.Errorf("Ugorji codec failed to unmarshal input '%v': %v", c.input, err) - } - if !reflect.DeepEqual(result.GV, c.expect) { - t.Errorf("Ugorji codec failed to unmarshal input '%s': expected %+v, got %+v", c.input, c.expect, result.GV) - } - } -} - -func TestGroupVersionMarshalJSON(t *testing.T) { - cases := []struct { - input GroupVersion - expect []byte - }{ - {GroupVersion{"", "v1"}, []byte(`{"val":"v1"}`)}, - {GroupVersion{"extensions", "v1beta1"}, []byte(`{"val":"extensions/v1beta1"}`)}, - } - - for _, c := range cases { - input := GroupVersionHolder{c.input} - result, err := json.Marshal(&input) - if err != nil { - t.Errorf("Failed to marshal input '%v': %v", input, err) - } - if !reflect.DeepEqual(result, c.expect) { - t.Errorf("Failed to marshal input '%+v': expected: %s, got: %s", input, c.expect, result) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/helpers.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/helpers.go index b26948b4d..b71297ec5 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/helpers.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/helpers.go @@ -25,6 +25,7 @@ import ( // LabelSelectorAsSelector converts the LabelSelector api type into a struct that implements // labels.Selector +// Note: This function should be kept in sync with the selector methods in pkg/labels/selector.go func LabelSelectorAsSelector(ps *LabelSelector) (labels.Selector, error) { if ps == nil { return labels.Nothing(), nil @@ -34,7 +35,7 @@ func LabelSelectorAsSelector(ps *LabelSelector) (labels.Selector, error) { } selector := labels.NewSelector() for k, v := range ps.MatchLabels { - r, err := labels.NewRequirement(k, labels.InOperator, sets.NewString(v)) + r, err := labels.NewRequirement(k, labels.EqualsOperator, sets.NewString(v)) if err != nil { return nil, err } @@ -63,6 +64,55 @@ func LabelSelectorAsSelector(ps *LabelSelector) (labels.Selector, error) { return selector, nil } +// ParseToLabelSelector parses a string representing a selector into a LabelSelector object. +// Note: This function should be kept in sync with the parser in pkg/labels/selector.go +func ParseToLabelSelector(selector string) (*LabelSelector, error) { + reqs, err := labels.ParseToRequirements(selector) + if err != nil { + return nil, fmt.Errorf("couldn't parse the selector string \"%s\": %v", selector, err) + } + + labelSelector := &LabelSelector{ + MatchLabels: map[string]string{}, + MatchExpressions: []LabelSelectorRequirement{}, + } + for _, req := range reqs { + var op LabelSelectorOperator + switch req.Operator() { + case labels.EqualsOperator, labels.DoubleEqualsOperator: + vals := req.Values() + if vals.Len() != 1 { + return nil, fmt.Errorf("equals operator must have exactly one value") + } + val, ok := vals.PopAny() + if !ok { + return nil, fmt.Errorf("equals operator has exactly one value but it cannot be retrieved") + } + labelSelector.MatchLabels[req.Key()] = val + continue + case labels.InOperator: + op = LabelSelectorOpIn + case labels.NotInOperator: + op = LabelSelectorOpNotIn + case labels.ExistsOperator: + op = LabelSelectorOpExists + case labels.DoesNotExistOperator: + op = LabelSelectorOpDoesNotExist + case labels.GreaterThanOperator, labels.LessThanOperator: + // Adding a separate case for these operators to indicate that this is deliberate + return nil, fmt.Errorf("%q isn't supported in label selectors", req.Operator()) + default: + return nil, fmt.Errorf("%q is not a valid label selector operator", req.Operator()) + } + labelSelector.MatchExpressions = append(labelSelector.MatchExpressions, LabelSelectorRequirement{ + Key: req.Key(), + Operator: op, + Values: req.Values().List(), + }) + } + return labelSelector, nil +} + // SetAsLabelSelector converts the labels.Set object into a LabelSelector api object. func SetAsLabelSelector(ls labels.Set) *LabelSelector { if ls == nil { @@ -92,3 +142,13 @@ func FormatLabelSelector(labelSelector *LabelSelector) string { } return l } + +func ExtractGroupVersions(l *APIGroupList) []string { + var groupVersions []string + for _, g := range l.Groups { + for _, gv := range g.Versions { + groupVersions = append(groupVersions, gv.GroupVersion) + } + } + return groupVersions +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/helpers_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/helpers_test.go deleted file mode 100644 index 334c78597..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/helpers_test.go +++ /dev/null @@ -1,83 +0,0 @@ -/* -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 unversioned - -import ( - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/labels" -) - -func TestLabelSelectorAsSelector(t *testing.T) { - matchLabels := map[string]string{"foo": "bar"} - matchExpressions := []LabelSelectorRequirement{{ - Key: "baz", - Operator: LabelSelectorOpIn, - Values: []string{"qux", "norf"}, - }} - mustParse := func(s string) labels.Selector { - out, e := labels.Parse(s) - if e != nil { - panic(e) - } - return out - } - tc := []struct { - in *LabelSelector - out labels.Selector - expectErr bool - }{ - {in: nil, out: labels.Nothing()}, - {in: &LabelSelector{}, out: labels.Everything()}, - { - in: &LabelSelector{MatchLabels: matchLabels}, - out: mustParse("foo in (bar)"), - }, - { - in: &LabelSelector{MatchExpressions: matchExpressions}, - out: mustParse("baz in (norf,qux)"), - }, - { - in: &LabelSelector{MatchLabels: matchLabels, MatchExpressions: matchExpressions}, - out: mustParse("foo in (bar),baz in (norf,qux)"), - }, - { - in: &LabelSelector{ - MatchExpressions: []LabelSelectorRequirement{{ - Key: "baz", - Operator: LabelSelectorOpExists, - Values: []string{"qux", "norf"}, - }}, - }, - expectErr: true, - }, - } - - for i, tc := range tc { - out, err := LabelSelectorAsSelector(tc.in) - if err == nil && tc.expectErr { - t.Errorf("[%v]expected error but got none.", i) - } - if err != nil && !tc.expectErr { - t.Errorf("[%v]did not expect error but got: %v", i, err) - } - if !reflect.DeepEqual(out, tc.out) { - t.Errorf("[%v]expected:\n\t%+v\nbut got:\n\t%+v", i, tc.out, out) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/time.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/time.go index 4072833d9..1180e6bd1 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/time.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/time.go @@ -97,6 +97,27 @@ func (t *Time) UnmarshalJSON(b []byte) error { return nil } +// UnmarshalQueryParameter converts from a URL query parameter value to an object +func (t *Time) UnmarshalQueryParameter(str string) error { + if len(str) == 0 { + t.Time = time.Time{} + return nil + } + // Tolerate requests from older clients that used JSON serialization to build query params + if len(str) == 4 && str == "null" { + t.Time = time.Time{} + return nil + } + + pt, err := time.Parse(time.RFC3339, str) + if err != nil { + return err + } + + t.Time = pt.Local() + return nil +} + // MarshalJSON implements the json.Marshaler interface. func (t Time) MarshalJSON() ([]byte, error) { if t.IsZero() { @@ -107,6 +128,16 @@ func (t Time) MarshalJSON() ([]byte, error) { return json.Marshal(t.UTC().Format(time.RFC3339)) } +// MarshalQueryParameter converts to a URL query parameter value +func (t Time) MarshalQueryParameter() (string, error) { + if t.IsZero() { + // Encode unset/nil objects as an empty string + return "", nil + } + + return t.UTC().Format(time.RFC3339), nil +} + // Fuzz satisfies fuzz.Interface. func (t *Time) Fuzz(c fuzz.Continue) { if t == nil { diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/time_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/time_test.go deleted file mode 100644 index 2f4f3696d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/time_test.go +++ /dev/null @@ -1,147 +0,0 @@ -/* -Copyright 2014 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 unversioned - -import ( - "encoding/json" - "testing" - "time" - - "github.com/ghodss/yaml" -) - -type TimeHolder struct { - T Time `json:"t"` -} - -func TestTimeMarshalYAML(t *testing.T) { - cases := []struct { - input Time - result string - }{ - {Time{}, "t: null\n"}, - {Date(1998, time.May, 5, 1, 5, 5, 50, time.FixedZone("test", -4*60*60)), "t: 1998-05-05T05:05:05Z\n"}, - {Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC), "t: 1998-05-05T05:05:05Z\n"}, - } - - for _, c := range cases { - input := TimeHolder{c.input} - result, err := yaml.Marshal(&input) - if err != nil { - t.Errorf("Failed to marshal input: '%v': %v", input, err) - } - if string(result) != c.result { - t.Errorf("Failed to marshal input: '%v': expected %+v, got %q", input, c.result, string(result)) - } - } -} - -func TestTimeUnmarshalYAML(t *testing.T) { - cases := []struct { - input string - result Time - }{ - {"t: null\n", Time{}}, - {"t: 1998-05-05T05:05:05Z\n", Time{Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC).Local()}}, - } - - for _, c := range cases { - var result TimeHolder - if err := yaml.Unmarshal([]byte(c.input), &result); err != nil { - t.Errorf("Failed to unmarshal input '%v': %v", c.input, err) - } - if result.T != c.result { - t.Errorf("Failed to unmarshal input '%v': expected %+v, got %+v", c.input, c.result, result) - } - } -} - -func TestTimeMarshalJSON(t *testing.T) { - cases := []struct { - input Time - result string - }{ - {Time{}, "{\"t\":null}"}, - {Date(1998, time.May, 5, 5, 5, 5, 50, time.UTC), "{\"t\":\"1998-05-05T05:05:05Z\"}"}, - {Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC), "{\"t\":\"1998-05-05T05:05:05Z\"}"}, - } - - for _, c := range cases { - input := TimeHolder{c.input} - result, err := json.Marshal(&input) - if err != nil { - t.Errorf("Failed to marshal input: '%v': %v", input, err) - } - if string(result) != c.result { - t.Errorf("Failed to marshal input: '%v': expected %+v, got %q", input, c.result, string(result)) - } - } -} - -func TestTimeUnmarshalJSON(t *testing.T) { - cases := []struct { - input string - result Time - }{ - {"{\"t\":null}", Time{}}, - {"{\"t\":\"1998-05-05T05:05:05Z\"}", Time{Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC).Local()}}, - } - - for _, c := range cases { - var result TimeHolder - if err := json.Unmarshal([]byte(c.input), &result); err != nil { - t.Errorf("Failed to unmarshal input '%v': %v", c.input, err) - } - if result.T != c.result { - t.Errorf("Failed to unmarshal input '%v': expected %+v, got %+v", c.input, c.result, result) - } - } -} - -func TestTimeMarshalJSONUnmarshalYAML(t *testing.T) { - cases := []struct { - input Time - }{ - {Time{}}, - {Date(1998, time.May, 5, 5, 5, 5, 50, time.Local).Rfc3339Copy()}, - {Date(1998, time.May, 5, 5, 5, 5, 0, time.Local).Rfc3339Copy()}, - } - - for i, c := range cases { - input := TimeHolder{c.input} - jsonMarshalled, err := json.Marshal(&input) - if err != nil { - t.Errorf("%d-1: Failed to marshal input: '%v': %v", i, input, err) - } - - var result TimeHolder - err = yaml.Unmarshal(jsonMarshalled, &result) - if err != nil { - t.Errorf("%d-2: Failed to unmarshal '%+v': %v", i, string(jsonMarshalled), err) - } - - iN, iO := input.T.Zone() - oN, oO := result.T.Zone() - if iN != oN || iO != oO { - t.Errorf("%d-3: Time zones differ before and after serialization %s:%d %s:%d", i, iN, iO, oN, oO) - } - - if input.T.UnixNano() != result.T.UnixNano() { - t.Errorf("%d-4: Failed to marshal input '%#v': got %#v", i, input, result) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/types.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/types.go index 4d7a8f7d8..786be86cf 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/types.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/types.go @@ -35,13 +35,13 @@ type TypeMeta struct { // Servers may infer this from the endpoint the client submits requests to. // Cannot be updated. // In CamelCase. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds Kind string `json:"kind,omitempty"` // 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: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#resources APIVersion string `json:"apiVersion,omitempty"` } @@ -58,7 +58,7 @@ type ListMeta struct { // Value must be treated as opaque by clients and passed unmodified back to the server. // Populated by the system. // Read-only. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#concurrency-control-and-consistency ResourceVersion string `json:"resourceVersion,omitempty"` } @@ -75,12 +75,12 @@ type ExportOptions struct { type Status struct { TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds ListMeta `json:"metadata,omitempty"` // Status of the operation. // One of: "Success" or "Failure". - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Status string `json:"status,omitempty"` // A human-readable description of the status of this operation. Message string `json:"message,omitempty"` @@ -112,7 +112,7 @@ type StatusDetails struct { Group string `json:"group,omitempty"` // The kind attribute of the resource associated with the status StatusReason. // On some operations may differ from the requested resource Kind. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds Kind string `json:"kind,omitempty"` // The Causes array includes more details associated with the StatusReason // failure. Not all StatusReasons may provide detailed causes. diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/types_swagger_doc_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/types_swagger_doc_generated.go index 9df14a044..b45a46e62 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/types_swagger_doc_generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/types_swagger_doc_generated.go @@ -16,7 +16,7 @@ limitations under the License. package unversioned -// This file contains a collection of methods that can be used from go-resful to +// This file contains a collection of methods that can be used from go-restful to // generate Swagger API documentation for its models. Please read this PR for more // information on the implementation: https://github.com/emicklei/go-restful/pull/215 // @@ -123,7 +123,7 @@ func (LabelSelectorRequirement) SwaggerDoc() map[string]string { var map_ListMeta = map[string]string{ "": "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.", "selfLink": "SelfLink is a URL representing this object. Populated by the system. Read-only.", - "resourceVersion": "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency", + "resourceVersion": "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#concurrency-control-and-consistency", } func (ListMeta) SwaggerDoc() map[string]string { @@ -159,8 +159,8 @@ func (ServerAddressByClientCIDR) SwaggerDoc() map[string]string { var map_Status = map[string]string{ "": "Status is a return value for calls that don't return other objects.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", - "status": "Status of the operation. One of: \"Success\" or \"Failure\". More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", + "status": "Status of the operation. One of: \"Success\" or \"Failure\". More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", "message": "A human-readable description of the status of this operation.", "reason": "A machine-readable description of why this operation is in the \"Failure\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.", "details": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", @@ -186,7 +186,7 @@ var map_StatusDetails = map[string]string{ "": "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.", "name": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).", "group": "The group attribute of the resource associated with the status StatusReason.", - "kind": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", + "kind": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", "causes": "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.", "retryAfterSeconds": "If specified, the time in seconds before the operation should be retried.", } @@ -197,8 +197,8 @@ func (StatusDetails) SwaggerDoc() map[string]string { var map_TypeMeta = map[string]string{ "": "TypeMeta describes an individual object in an API response or request with strings representing the type of the object and its API schema version. Structures that are versioned or persisted should inline TypeMeta.", - "kind": "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: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", - "apiVersion": "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: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources", + "kind": "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: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", + "apiVersion": "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: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#resources", } func (TypeMeta) SwaggerDoc() map[string]string { diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/validation/validation.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/validation/validation.go deleted file mode 100644 index f42f37b04..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/unversioned/validation/validation.go +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2015 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 validation - -import ( - "k8s.io/kubernetes/pkg/api/unversioned" - apivalidation "k8s.io/kubernetes/pkg/api/validation" - "k8s.io/kubernetes/pkg/util/validation/field" -) - -func ValidateLabelSelector(ps *unversioned.LabelSelector, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - if ps == nil { - return allErrs - } - allErrs = append(allErrs, apivalidation.ValidateLabels(ps.MatchLabels, fldPath.Child("matchLabels"))...) - for i, expr := range ps.MatchExpressions { - allErrs = append(allErrs, ValidateLabelSelectorRequirement(expr, fldPath.Child("matchExpressions").Index(i))...) - } - return allErrs -} - -func ValidateLabelSelectorRequirement(sr unversioned.LabelSelectorRequirement, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - switch sr.Operator { - case unversioned.LabelSelectorOpIn, unversioned.LabelSelectorOpNotIn: - if len(sr.Values) == 0 { - allErrs = append(allErrs, field.Required(fldPath.Child("values"), "must be specified when `operator` is 'In' or 'NotIn'")) - } - case unversioned.LabelSelectorOpExists, unversioned.LabelSelectorOpDoesNotExist: - if len(sr.Values) > 0 { - allErrs = append(allErrs, field.Forbidden(fldPath.Child("values"), "may not be specified when `operator` is 'Exists' or 'DoesNotExist'")) - } - default: - allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), sr.Operator, "not a valid selector operator")) - } - allErrs = append(allErrs, apivalidation.ValidateLabelName(sr.Key, fldPath.Child("key"))...) - return allErrs -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/util/group_version_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/util/group_version_test.go deleted file mode 100644 index d53b5f4e5..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/util/group_version_test.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2014 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 util - -import "testing" - -func TestGetVersion(t *testing.T) { - testCases := []struct { - groupVersion string - output string - }{ - { - "v1", - "v1", - }, - { - "extensions/v1beta1", - "v1beta1", - }, - } - for _, test := range testCases { - actual := GetVersion(test.groupVersion) - if test.output != actual { - t.Errorf("expect version: %s, got: %s\n", test.output, actual) - } - } -} - -func TestGetGroup(t *testing.T) { - testCases := []struct { - groupVersion string - output string - }{ - { - "v1", - "", - }, - { - "extensions/v1beta1", - "extensions", - }, - } - for _, test := range testCases { - actual := GetGroup(test.groupVersion) - if test.output != actual { - t.Errorf("expect version: %s, got: %s\n", test.output, actual) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/backward_compatibility_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/backward_compatibility_test.go deleted file mode 100644 index 2ef3d926f..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/backward_compatibility_test.go +++ /dev/null @@ -1,229 +0,0 @@ -/* -Copyright 2015 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 v1_test - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testing/compat" - "k8s.io/kubernetes/pkg/api/v1" - "k8s.io/kubernetes/pkg/api/validation" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/validation/field" -) - -func TestCompatibility_v1_PodSecurityContext(t *testing.T) { - cases := []struct { - name string - input string - expectedKeys map[string]string - absentKeys []string - }{ - { - name: "hostNetwork = true", - input: ` -{ - "kind":"Pod", - "apiVersion":"v1", - "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, - "spec": { - "hostNetwork": true, - "containers":[{ - "name":"a", - "image":"my-container-image" - }] - } -} -`, - expectedKeys: map[string]string{ - "spec.hostNetwork": "true", - }, - }, - { - name: "hostNetwork = false", - input: ` -{ - "kind":"Pod", - "apiVersion":"v1", - "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, - "spec": { - "hostNetwork": false, - "containers":[{ - "name":"a", - "image":"my-container-image" - }] - } -} -`, - absentKeys: []string{ - "spec.hostNetwork", - }, - }, - { - name: "hostIPC = true", - input: ` -{ - "kind":"Pod", - "apiVersion":"v1", - "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, - "spec": { - "hostIPC": true, - "containers":[{ - "name":"a", - "image":"my-container-image" - }] - } -} -`, - expectedKeys: map[string]string{ - "spec.hostIPC": "true", - }, - }, - { - name: "hostIPC = false", - input: ` -{ - "kind":"Pod", - "apiVersion":"v1", - "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, - "spec": { - "hostIPC": false, - "containers":[{ - "name":"a", - "image":"my-container-image" - }] - } -} -`, - absentKeys: []string{ - "spec.hostIPC", - }, - }, - { - name: "hostPID = true", - input: ` -{ - "kind":"Pod", - "apiVersion":"v1", - "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, - "spec": { - "hostPID": true, - "containers":[{ - "name":"a", - "image":"my-container-image" - }] - } -} -`, - expectedKeys: map[string]string{ - "spec.hostPID": "true", - }, - }, - { - name: "hostPID = false", - input: ` -{ - "kind":"Pod", - "apiVersion":"v1", - "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, - "spec": { - "hostPID": false, - "containers":[{ - "name":"a", - "image":"my-container-image" - }] - } -} -`, - absentKeys: []string{ - "spec.hostPID", - }, - }, - { - name: "reseting defaults for pre-v1.1 mirror pods", - input: ` -{ - "kind":"Pod", - "apiVersion":"v1", - "metadata":{ - "name":"my-pod-name", - "namespace":"my-pod-namespace", - "annotations": { - "kubernetes.io/config.mirror": "mirror" - } - }, - "spec": { - "containers":[{ - "name":"a", - "image":"my-container-image", - "resources": { - "limits": { - "cpu": "100m" - } - } - }] - } -} -`, - absentKeys: []string{ - "spec.terminationGracePeriodSeconds", - "spec.containers[0].resources.requests", - }, - }, - { - name: "preserving defaults for v1.1+ mirror pods", - input: ` - { - "kind":"Pod", - "apiVersion":"v1", - "metadata":{ - "name":"my-pod-name", - "namespace":"my-pod-namespace", - "annotations": { - "kubernetes.io/config.mirror": "cbe924f710c7e26f7693d6a341bcfad0" - } - }, - "spec": { - "containers":[{ - "name":"a", - "image":"my-container-image", - "resources": { - "limits": { - "cpu": "100m" - } - } - }] - } - } - `, - expectedKeys: map[string]string{ - "spec.terminationGracePeriodSeconds": "30", - "spec.containers[0].resources.requests": "map[cpu:100m]", - }, - }, - } - - validator := func(obj runtime.Object) field.ErrorList { - return validation.ValidatePodSpec(&(obj.(*api.Pod).Spec), field.NewPath("spec")) - } - - for _, tc := range cases { - t.Logf("Testing 1.0.0 backward compatibility for %v", tc.name) - compat.TestCompatibility(t, v1.SchemeGroupVersion, []byte(tc.input), validator, tc.expectedKeys, tc.absentKeys) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/conversion_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/conversion_generated.go index a0e405e9e..687da425a 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/conversion_generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/conversion_generated.go @@ -2598,6 +2598,7 @@ func autoConvert_api_ReplicationControllerStatus_To_v1_ReplicationControllerStat defaulting.(func(*api.ReplicationControllerStatus))(in) } out.Replicas = int32(in.Replicas) + out.FullyLabeledReplicas = int32(in.FullyLabeledReplicas) out.ObservedGeneration = in.ObservedGeneration return nil } @@ -2666,6 +2667,14 @@ func autoConvert_api_ResourceQuotaSpec_To_v1_ResourceQuotaSpec(in *api.ResourceQ } else { out.Hard = nil } + if in.Scopes != nil { + out.Scopes = make([]ResourceQuotaScope, len(in.Scopes)) + for i := range in.Scopes { + out.Scopes[i] = ResourceQuotaScope(in.Scopes[i]) + } + } else { + out.Scopes = nil + } return nil } @@ -5828,6 +5837,7 @@ func autoConvert_v1_ReplicationControllerStatus_To_api_ReplicationControllerStat defaulting.(func(*ReplicationControllerStatus))(in) } out.Replicas = int(in.Replicas) + out.FullyLabeledReplicas = int(in.FullyLabeledReplicas) out.ObservedGeneration = in.ObservedGeneration return nil } @@ -5887,6 +5897,14 @@ func autoConvert_v1_ResourceQuotaSpec_To_api_ResourceQuotaSpec(in *ResourceQuota if err := s.Convert(&in.Hard, &out.Hard, 0); err != nil { return err } + if in.Scopes != nil { + out.Scopes = make([]api.ResourceQuotaScope, len(in.Scopes)) + for i := range in.Scopes { + out.Scopes[i] = api.ResourceQuotaScope(in.Scopes[i]) + } + } else { + out.Scopes = nil + } return nil } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/conversion_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/conversion_test.go deleted file mode 100644 index 8cc2f0474..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/conversion_test.go +++ /dev/null @@ -1,125 +0,0 @@ -/* -Copyright 2015 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 v1_test - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/resource" - versioned "k8s.io/kubernetes/pkg/api/v1" -) - -// TestPodSpecConversion tests that ServiceAccount is an alias for -// ServiceAccountName. -func TestPodSpecConversion(t *testing.T) { - name, other := "foo", "bar" - - // Test internal -> v1. Should have both alias (DeprecatedServiceAccount) - // and new field (ServiceAccountName). - i := &api.PodSpec{ - ServiceAccountName: name, - } - v := versioned.PodSpec{} - if err := api.Scheme.Convert(i, &v); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if v.ServiceAccountName != name { - t.Fatalf("want v1.ServiceAccountName %q, got %q", name, v.ServiceAccountName) - } - if v.DeprecatedServiceAccount != name { - t.Fatalf("want v1.DeprecatedServiceAccount %q, got %q", name, v.DeprecatedServiceAccount) - } - - // Test v1 -> internal. Either DeprecatedServiceAccount, ServiceAccountName, - // or both should translate to ServiceAccountName. ServiceAccountName wins - // if both are set. - testCases := []*versioned.PodSpec{ - // New - {ServiceAccountName: name}, - // Alias - {DeprecatedServiceAccount: name}, - // Both: same - {ServiceAccountName: name, DeprecatedServiceAccount: name}, - // Both: different - {ServiceAccountName: name, DeprecatedServiceAccount: other}, - } - for k, v := range testCases { - got := api.PodSpec{} - err := api.Scheme.Convert(v, &got) - if err != nil { - t.Fatalf("unexpected error for case %d: %v", k, err) - } - if got.ServiceAccountName != name { - t.Fatalf("want api.ServiceAccountName %q, got %q", name, got.ServiceAccountName) - } - } -} - -func TestResourceListConversion(t *testing.T) { - bigMilliQuantity := resource.NewQuantity(resource.MaxMilliValue, resource.DecimalSI) - bigMilliQuantity.Add(resource.MustParse("12345m")) - - tests := []struct { - input versioned.ResourceList - expected api.ResourceList - }{ - { // No changes necessary. - input: versioned.ResourceList{ - versioned.ResourceMemory: resource.MustParse("30M"), - versioned.ResourceCPU: resource.MustParse("100m"), - versioned.ResourceStorage: resource.MustParse("1G"), - }, - expected: api.ResourceList{ - api.ResourceMemory: resource.MustParse("30M"), - api.ResourceCPU: resource.MustParse("100m"), - api.ResourceStorage: resource.MustParse("1G"), - }, - }, - { // Nano-scale values should be rounded up to milli-scale. - input: versioned.ResourceList{ - versioned.ResourceCPU: resource.MustParse("3.000023m"), - versioned.ResourceMemory: resource.MustParse("500.000050m"), - }, - expected: api.ResourceList{ - api.ResourceCPU: resource.MustParse("4m"), - api.ResourceMemory: resource.MustParse("501m"), - }, - }, - { // Large values should still be accurate. - input: versioned.ResourceList{ - versioned.ResourceCPU: *bigMilliQuantity.Copy(), - versioned.ResourceStorage: *bigMilliQuantity.Copy(), - }, - expected: api.ResourceList{ - api.ResourceCPU: *bigMilliQuantity.Copy(), - api.ResourceStorage: *bigMilliQuantity.Copy(), - }, - }, - } - - output := api.ResourceList{} - for i, test := range tests { - err := api.Scheme.Convert(&test.input, &output) - if err != nil { - t.Fatalf("unexpected error for case %d: %v", i, err) - } - if !api.Semantic.DeepEqual(test.expected, output) { - t.Errorf("unexpected conversion for case %d: Expected %+v; Got %+v", i, test.expected, output) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/deep_copy_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/deep_copy_generated.go index f6aaedc88..1252bf4e6 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/deep_copy_generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/deep_copy_generated.go @@ -2031,6 +2031,7 @@ func deepCopy_v1_ReplicationControllerSpec(in ReplicationControllerSpec, out *Re func deepCopy_v1_ReplicationControllerStatus(in ReplicationControllerStatus, out *ReplicationControllerStatus, c *conversion.Cloner) error { out.Replicas = in.Replicas + out.FullyLabeledReplicas = in.FullyLabeledReplicas out.ObservedGeneration = in.ObservedGeneration return nil } @@ -2084,6 +2085,14 @@ func deepCopy_v1_ResourceQuotaSpec(in ResourceQuotaSpec, out *ResourceQuotaSpec, } else { out.Hard = nil } + if in.Scopes != nil { + out.Scopes = make([]ResourceQuotaScope, len(in.Scopes)) + for i := range in.Scopes { + out.Scopes[i] = in.Scopes[i] + } + } else { + out.Scopes = nil + } return nil } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/defaults_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/defaults_test.go deleted file mode 100644 index b1a78e758..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/defaults_test.go +++ /dev/null @@ -1,645 +0,0 @@ -/* -Copyright 2015 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 v1_test - -import ( - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/resource" - versioned "k8s.io/kubernetes/pkg/api/v1" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/intstr" -) - -func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { - codec := api.Codecs.LegacyCodec(versioned.SchemeGroupVersion) - data, err := runtime.Encode(codec, obj) - if err != nil { - t.Errorf("%v\n %#v", err, obj) - return nil - } - obj2, err := runtime.Decode(codec, data) - if err != nil { - t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj) - return nil - } - obj3 := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object) - err = api.Scheme.Convert(obj2, obj3) - if err != nil { - t.Errorf("%v\nSource: %#v", err, obj2) - return nil - } - return obj3 -} - -func TestSetDefaultReplicationController(t *testing.T) { - tests := []struct { - rc *versioned.ReplicationController - expectLabels bool - expectSelector bool - }{ - { - rc: &versioned.ReplicationController{ - Spec: versioned.ReplicationControllerSpec{ - Template: &versioned.PodTemplateSpec{ - ObjectMeta: versioned.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - }, - expectLabels: true, - expectSelector: true, - }, - { - rc: &versioned.ReplicationController{ - ObjectMeta: versioned.ObjectMeta{ - Labels: map[string]string{ - "bar": "foo", - }, - }, - Spec: versioned.ReplicationControllerSpec{ - Template: &versioned.PodTemplateSpec{ - ObjectMeta: versioned.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - }, - expectLabels: false, - expectSelector: true, - }, - { - rc: &versioned.ReplicationController{ - ObjectMeta: versioned.ObjectMeta{ - Labels: map[string]string{ - "bar": "foo", - }, - }, - Spec: versioned.ReplicationControllerSpec{ - Selector: map[string]string{ - "some": "other", - }, - Template: &versioned.PodTemplateSpec{ - ObjectMeta: versioned.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - }, - expectLabels: false, - expectSelector: false, - }, - { - rc: &versioned.ReplicationController{ - Spec: versioned.ReplicationControllerSpec{ - Selector: map[string]string{ - "some": "other", - }, - Template: &versioned.PodTemplateSpec{ - ObjectMeta: versioned.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - }, - expectLabels: true, - expectSelector: false, - }, - } - - for _, test := range tests { - rc := test.rc - obj2 := roundTrip(t, runtime.Object(rc)) - rc2, ok := obj2.(*versioned.ReplicationController) - if !ok { - t.Errorf("unexpected object: %v", rc2) - t.FailNow() - } - if test.expectSelector != reflect.DeepEqual(rc2.Spec.Selector, rc2.Spec.Template.Labels) { - if test.expectSelector { - t.Errorf("expected: %v, got: %v", rc2.Spec.Template.Labels, rc2.Spec.Selector) - } else { - t.Errorf("unexpected equality: %v", rc.Spec.Selector) - } - } - if test.expectLabels != reflect.DeepEqual(rc2.Labels, rc2.Spec.Template.Labels) { - if test.expectLabels { - t.Errorf("expected: %v, got: %v", rc2.Spec.Template.Labels, rc2.Labels) - } else { - t.Errorf("unexpected equality: %v", rc.Labels) - } - } - } -} - -func newInt(val int32) *int32 { - p := new(int32) - *p = val - return p -} - -func TestSetDefaultReplicationControllerReplicas(t *testing.T) { - tests := []struct { - rc versioned.ReplicationController - expectReplicas int32 - }{ - { - rc: versioned.ReplicationController{ - Spec: versioned.ReplicationControllerSpec{ - Template: &versioned.PodTemplateSpec{ - ObjectMeta: versioned.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - }, - expectReplicas: 1, - }, - { - rc: versioned.ReplicationController{ - Spec: versioned.ReplicationControllerSpec{ - Replicas: newInt(0), - Template: &versioned.PodTemplateSpec{ - ObjectMeta: versioned.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - }, - expectReplicas: 0, - }, - { - rc: versioned.ReplicationController{ - Spec: versioned.ReplicationControllerSpec{ - Replicas: newInt(3), - Template: &versioned.PodTemplateSpec{ - ObjectMeta: versioned.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - }, - expectReplicas: 3, - }, - } - - for _, test := range tests { - rc := &test.rc - obj2 := roundTrip(t, runtime.Object(rc)) - rc2, ok := obj2.(*versioned.ReplicationController) - if !ok { - t.Errorf("unexpected object: %v", rc2) - t.FailNow() - } - if rc2.Spec.Replicas == nil { - t.Errorf("unexpected nil Replicas") - } else if test.expectReplicas != *rc2.Spec.Replicas { - t.Errorf("expected: %d replicas, got: %d", test.expectReplicas, *rc2.Spec.Replicas) - } - } -} - -func TestSetDefaultService(t *testing.T) { - svc := &versioned.Service{} - obj2 := roundTrip(t, runtime.Object(svc)) - svc2 := obj2.(*versioned.Service) - if svc2.Spec.SessionAffinity != versioned.ServiceAffinityNone { - t.Errorf("Expected default session affinity type:%s, got: %s", versioned.ServiceAffinityNone, svc2.Spec.SessionAffinity) - } - if svc2.Spec.Type != versioned.ServiceTypeClusterIP { - t.Errorf("Expected default type:%s, got: %s", versioned.ServiceTypeClusterIP, svc2.Spec.Type) - } -} - -func TestSetDefaultSecret(t *testing.T) { - s := &versioned.Secret{} - obj2 := roundTrip(t, runtime.Object(s)) - s2 := obj2.(*versioned.Secret) - - if s2.Type != versioned.SecretTypeOpaque { - t.Errorf("Expected secret type %v, got %v", versioned.SecretTypeOpaque, s2.Type) - } -} - -func TestSetDefaultPersistentVolume(t *testing.T) { - pv := &versioned.PersistentVolume{} - obj2 := roundTrip(t, runtime.Object(pv)) - pv2 := obj2.(*versioned.PersistentVolume) - - if pv2.Status.Phase != versioned.VolumePending { - t.Errorf("Expected volume phase %v, got %v", versioned.VolumePending, pv2.Status.Phase) - } - if pv2.Spec.PersistentVolumeReclaimPolicy != versioned.PersistentVolumeReclaimRetain { - t.Errorf("Expected pv reclaim policy %v, got %v", versioned.PersistentVolumeReclaimRetain, pv2.Spec.PersistentVolumeReclaimPolicy) - } -} - -func TestSetDefaultPersistentVolumeClaim(t *testing.T) { - pvc := &versioned.PersistentVolumeClaim{} - obj2 := roundTrip(t, runtime.Object(pvc)) - pvc2 := obj2.(*versioned.PersistentVolumeClaim) - - if pvc2.Status.Phase != versioned.ClaimPending { - t.Errorf("Expected claim phase %v, got %v", versioned.ClaimPending, pvc2.Status.Phase) - } -} - -func TestSetDefaulEndpointsProtocol(t *testing.T) { - in := &versioned.Endpoints{Subsets: []versioned.EndpointSubset{ - {Ports: []versioned.EndpointPort{{}, {Protocol: "UDP"}, {}}}, - }} - obj := roundTrip(t, runtime.Object(in)) - out := obj.(*versioned.Endpoints) - - for i := range out.Subsets { - for j := range out.Subsets[i].Ports { - if in.Subsets[i].Ports[j].Protocol == "" { - if out.Subsets[i].Ports[j].Protocol != versioned.ProtocolTCP { - t.Errorf("Expected protocol %s, got %s", versioned.ProtocolTCP, out.Subsets[i].Ports[j].Protocol) - } - } else { - if out.Subsets[i].Ports[j].Protocol != in.Subsets[i].Ports[j].Protocol { - t.Errorf("Expected protocol %s, got %s", in.Subsets[i].Ports[j].Protocol, out.Subsets[i].Ports[j].Protocol) - } - } - } - } -} - -func TestSetDefaulServiceTargetPort(t *testing.T) { - in := &versioned.Service{Spec: versioned.ServiceSpec{Ports: []versioned.ServicePort{{Port: 1234}}}} - obj := roundTrip(t, runtime.Object(in)) - out := obj.(*versioned.Service) - if out.Spec.Ports[0].TargetPort != intstr.FromInt(1234) { - t.Errorf("Expected TargetPort to be defaulted, got %v", out.Spec.Ports[0].TargetPort) - } - - in = &versioned.Service{Spec: versioned.ServiceSpec{Ports: []versioned.ServicePort{{Port: 1234, TargetPort: intstr.FromInt(5678)}}}} - obj = roundTrip(t, runtime.Object(in)) - out = obj.(*versioned.Service) - if out.Spec.Ports[0].TargetPort != intstr.FromInt(5678) { - t.Errorf("Expected TargetPort to be unchanged, got %v", out.Spec.Ports[0].TargetPort) - } -} - -func TestSetDefaultServicePort(t *testing.T) { - // Unchanged if set. - in := &versioned.Service{Spec: versioned.ServiceSpec{ - Ports: []versioned.ServicePort{ - {Protocol: "UDP", Port: 9376, TargetPort: intstr.FromString("p")}, - {Protocol: "UDP", Port: 8675, TargetPort: intstr.FromInt(309)}, - }, - }} - out := roundTrip(t, runtime.Object(in)).(*versioned.Service) - if out.Spec.Ports[0].Protocol != versioned.ProtocolUDP { - t.Errorf("Expected protocol %s, got %s", versioned.ProtocolUDP, out.Spec.Ports[0].Protocol) - } - if out.Spec.Ports[0].TargetPort != intstr.FromString("p") { - t.Errorf("Expected port %v, got %v", in.Spec.Ports[0].Port, out.Spec.Ports[0].TargetPort) - } - if out.Spec.Ports[1].Protocol != versioned.ProtocolUDP { - t.Errorf("Expected protocol %s, got %s", versioned.ProtocolUDP, out.Spec.Ports[1].Protocol) - } - if out.Spec.Ports[1].TargetPort != intstr.FromInt(309) { - t.Errorf("Expected port %v, got %v", in.Spec.Ports[1].Port, out.Spec.Ports[1].TargetPort) - } - - // Defaulted. - in = &versioned.Service{Spec: versioned.ServiceSpec{ - Ports: []versioned.ServicePort{ - {Protocol: "", Port: 9376, TargetPort: intstr.FromString("")}, - {Protocol: "", Port: 8675, TargetPort: intstr.FromInt(0)}, - }, - }} - out = roundTrip(t, runtime.Object(in)).(*versioned.Service) - if out.Spec.Ports[0].Protocol != versioned.ProtocolTCP { - t.Errorf("Expected protocol %s, got %s", versioned.ProtocolTCP, out.Spec.Ports[0].Protocol) - } - if out.Spec.Ports[0].TargetPort != intstr.FromInt(int(in.Spec.Ports[0].Port)) { - t.Errorf("Expected port %v, got %v", in.Spec.Ports[0].Port, out.Spec.Ports[0].TargetPort) - } - if out.Spec.Ports[1].Protocol != versioned.ProtocolTCP { - t.Errorf("Expected protocol %s, got %s", versioned.ProtocolTCP, out.Spec.Ports[1].Protocol) - } - if out.Spec.Ports[1].TargetPort != intstr.FromInt(int(in.Spec.Ports[1].Port)) { - t.Errorf("Expected port %v, got %v", in.Spec.Ports[1].Port, out.Spec.Ports[1].TargetPort) - } -} - -func TestSetDefaultNamespace(t *testing.T) { - s := &versioned.Namespace{} - obj2 := roundTrip(t, runtime.Object(s)) - s2 := obj2.(*versioned.Namespace) - - if s2.Status.Phase != versioned.NamespaceActive { - t.Errorf("Expected phase %v, got %v", versioned.NamespaceActive, s2.Status.Phase) - } -} - -func TestSetDefaultPodSpecHostNetwork(t *testing.T) { - portNum := int32(8080) - s := versioned.PodSpec{} - s.HostNetwork = true - s.Containers = []versioned.Container{ - { - Ports: []versioned.ContainerPort{ - { - ContainerPort: portNum, - }, - }, - }, - } - pod := &versioned.Pod{ - Spec: s, - } - obj2 := roundTrip(t, runtime.Object(pod)) - pod2 := obj2.(*versioned.Pod) - s2 := pod2.Spec - - hostPortNum := s2.Containers[0].Ports[0].HostPort - if hostPortNum != portNum { - t.Errorf("Expected container port to be defaulted, was made %d instead of %d", hostPortNum, portNum) - } -} - -func TestSetDefaultNodeExternalID(t *testing.T) { - name := "node0" - n := &versioned.Node{} - n.Name = name - obj2 := roundTrip(t, runtime.Object(n)) - n2 := obj2.(*versioned.Node) - if n2.Spec.ExternalID != name { - t.Errorf("Expected default External ID: %s, got: %s", name, n2.Spec.ExternalID) - } - if n2.Spec.ProviderID != "" { - t.Errorf("Expected empty default Cloud Provider ID, got: %s", n2.Spec.ProviderID) - } -} - -func TestSetDefaultNodeStatusAllocatable(t *testing.T) { - capacity := versioned.ResourceList{ - versioned.ResourceCPU: resource.MustParse("1000m"), - versioned.ResourceMemory: resource.MustParse("10G"), - } - allocatable := versioned.ResourceList{ - versioned.ResourceCPU: resource.MustParse("500m"), - versioned.ResourceMemory: resource.MustParse("5G"), - } - tests := []struct { - capacity versioned.ResourceList - allocatable versioned.ResourceList - expectedAllocatable versioned.ResourceList - }{{ // Everything set, no defaulting. - capacity: capacity, - allocatable: allocatable, - expectedAllocatable: allocatable, - }, { // Allocatable set, no defaulting. - capacity: nil, - allocatable: allocatable, - expectedAllocatable: allocatable, - }, { // Capacity set, allocatable defaults to capacity. - capacity: capacity, - allocatable: nil, - expectedAllocatable: capacity, - }, { // Nothing set, allocatable "defaults" to capacity. - capacity: nil, - allocatable: nil, - expectedAllocatable: nil, - }} - - copyResourceList := func(rl versioned.ResourceList) versioned.ResourceList { - if rl == nil { - return nil - } - copy := make(versioned.ResourceList, len(rl)) - for k, v := range rl { - copy[k] = *v.Copy() - } - return copy - } - - resourceListsEqual := func(a versioned.ResourceList, b versioned.ResourceList) bool { - if len(a) != len(b) { - return false - } - for k, v := range a { - vb, found := b[k] - if !found { - return false - } - if v.Cmp(vb) != 0 { - return false - } - } - return true - } - - for i, testcase := range tests { - node := versioned.Node{ - Status: versioned.NodeStatus{ - Capacity: copyResourceList(testcase.capacity), - Allocatable: copyResourceList(testcase.allocatable), - }, - } - node2 := roundTrip(t, runtime.Object(&node)).(*versioned.Node) - actual := node2.Status.Allocatable - expected := testcase.expectedAllocatable - if !resourceListsEqual(expected, actual) { - t.Errorf("[%d] Expected NodeStatus.Allocatable: %+v; Got: %+v", i, expected, actual) - } - } -} - -func TestSetDefaultObjectFieldSelectorAPIVersion(t *testing.T) { - s := versioned.PodSpec{ - Containers: []versioned.Container{ - { - Env: []versioned.EnvVar{ - { - ValueFrom: &versioned.EnvVarSource{ - FieldRef: &versioned.ObjectFieldSelector{}, - }, - }, - }, - }, - }, - } - pod := &versioned.Pod{ - Spec: s, - } - obj2 := roundTrip(t, runtime.Object(pod)) - pod2 := obj2.(*versioned.Pod) - s2 := pod2.Spec - - apiVersion := s2.Containers[0].Env[0].ValueFrom.FieldRef.APIVersion - if apiVersion != "v1" { - t.Errorf("Expected default APIVersion v1, got: %v", apiVersion) - } -} - -func TestSetDefaultRequestsPod(t *testing.T) { - // verify we default if limits are specified - s := versioned.PodSpec{} - s.Containers = []versioned.Container{ - { - Resources: versioned.ResourceRequirements{ - Limits: versioned.ResourceList{ - versioned.ResourceCPU: resource.MustParse("100m"), - }, - }, - }, - } - pod := &versioned.Pod{ - Spec: s, - } - output := roundTrip(t, runtime.Object(pod)) - pod2 := output.(*versioned.Pod) - defaultRequest := pod2.Spec.Containers[0].Resources.Requests - requestValue := defaultRequest[versioned.ResourceCPU] - if requestValue.String() != "100m" { - t.Errorf("Expected request cpu: %s, got: %s", "100m", requestValue.String()) - } - - // verify we do nothing if no limits are specified - s = versioned.PodSpec{} - s.Containers = []versioned.Container{{}} - pod = &versioned.Pod{ - Spec: s, - } - output = roundTrip(t, runtime.Object(pod)) - pod2 = output.(*versioned.Pod) - defaultRequest = pod2.Spec.Containers[0].Resources.Requests - requestValue = defaultRequest[versioned.ResourceCPU] - if requestValue.String() != "0" { - t.Errorf("Expected 0 request value, got: %s", requestValue.String()) - } -} - -func TestDefaultRequestIsNotSetForReplicationController(t *testing.T) { - s := versioned.PodSpec{} - s.Containers = []versioned.Container{ - { - Resources: versioned.ResourceRequirements{ - Limits: versioned.ResourceList{ - versioned.ResourceCPU: resource.MustParse("100m"), - }, - }, - }, - } - rc := &versioned.ReplicationController{ - Spec: versioned.ReplicationControllerSpec{ - Replicas: newInt(3), - Template: &versioned.PodTemplateSpec{ - ObjectMeta: versioned.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - Spec: s, - }, - }, - } - output := roundTrip(t, runtime.Object(rc)) - rc2 := output.(*versioned.ReplicationController) - defaultRequest := rc2.Spec.Template.Spec.Containers[0].Resources.Requests - requestValue := defaultRequest[versioned.ResourceCPU] - if requestValue.String() != "0" { - t.Errorf("Expected 0 request value, got: %s", requestValue.String()) - } -} - -func TestSetDefaultLimitRangeItem(t *testing.T) { - limitRange := &versioned.LimitRange{ - ObjectMeta: versioned.ObjectMeta{ - Name: "test-defaults", - }, - Spec: versioned.LimitRangeSpec{ - Limits: []versioned.LimitRangeItem{{ - Type: versioned.LimitTypeContainer, - Max: versioned.ResourceList{ - versioned.ResourceCPU: resource.MustParse("100m"), - }, - Min: versioned.ResourceList{ - versioned.ResourceMemory: resource.MustParse("100Mi"), - }, - Default: versioned.ResourceList{}, - DefaultRequest: versioned.ResourceList{}, - }}, - }, - } - - output := roundTrip(t, runtime.Object(limitRange)) - limitRange2 := output.(*versioned.LimitRange) - defaultLimit := limitRange2.Spec.Limits[0].Default - defaultRequest := limitRange2.Spec.Limits[0].DefaultRequest - - // verify that default cpu was set to the max - defaultValue := defaultLimit[versioned.ResourceCPU] - if defaultValue.String() != "100m" { - t.Errorf("Expected default cpu: %s, got: %s", "100m", defaultValue.String()) - } - // verify that default request was set to the limit - requestValue := defaultRequest[versioned.ResourceCPU] - if requestValue.String() != "100m" { - t.Errorf("Expected request cpu: %s, got: %s", "100m", requestValue.String()) - } - // verify that if a min is provided, it will be the default if no limit is specified - requestMinValue := defaultRequest[versioned.ResourceMemory] - if requestMinValue.String() != "100Mi" { - t.Errorf("Expected request memory: %s, got: %s", "100Mi", requestMinValue.String()) - } -} - -func TestSetDefaultProbe(t *testing.T) { - originalProbe := versioned.Probe{} - expectedProbe := versioned.Probe{ - InitialDelaySeconds: 0, - TimeoutSeconds: 1, - PeriodSeconds: 10, - SuccessThreshold: 1, - FailureThreshold: 3, - } - - pod := &versioned.Pod{ - Spec: versioned.PodSpec{ - Containers: []versioned.Container{{LivenessProbe: &originalProbe}}, - }, - } - - output := roundTrip(t, runtime.Object(pod)).(*versioned.Pod) - actualProbe := *output.Spec.Containers[0].LivenessProbe - if actualProbe != expectedProbe { - t.Errorf("Expected probe: %+v\ngot: %+v\n", expectedProbe, actualProbe) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/types.generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/types.generated.go index 18ae2139a..1dc4938a8 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/types.generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/types.generated.go @@ -26683,13 +26683,14 @@ func (x *ReplicationControllerStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [2]bool + var yyq2 [3]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false - yyq2[1] = x.ObservedGeneration != 0 + yyq2[1] = x.FullyLabeledReplicas != 0 + yyq2[2] = x.ObservedGeneration != 0 var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(2) + r.EncodeArrayStart(3) } else { yynn2 = 1 for _, b := range yyq2 { @@ -26726,7 +26727,7 @@ func (x *ReplicationControllerStatus) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym7 if false { } else { - r.EncodeInt(int64(x.ObservedGeneration)) + r.EncodeInt(int64(x.FullyLabeledReplicas)) } } else { r.EncodeInt(0) @@ -26734,11 +26735,36 @@ func (x *ReplicationControllerStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { if yyq2[1] { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("observedGeneration")) + r.EncodeString(codecSelferC_UTF81234, string("fullyLabeledReplicas")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym8 := z.EncBinary() _ = yym8 if false { + } else { + r.EncodeInt(int64(x.FullyLabeledReplicas)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yym10 := z.EncBinary() + _ = yym10 + if false { + } else { + r.EncodeInt(int64(x.ObservedGeneration)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("observedGeneration")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym11 := z.EncBinary() + _ = yym11 + if false { } else { r.EncodeInt(int64(x.ObservedGeneration)) } @@ -26811,6 +26837,12 @@ func (x *ReplicationControllerStatus) codecDecodeSelfFromMap(l int, d *codec1978 } else { x.Replicas = int32(r.DecodeInt(32)) } + case "fullyLabeledReplicas": + if r.TryDecodeAsNil() { + x.FullyLabeledReplicas = 0 + } else { + x.FullyLabeledReplicas = int32(r.DecodeInt(32)) + } case "observedGeneration": if r.TryDecodeAsNil() { x.ObservedGeneration = 0 @@ -26828,16 +26860,16 @@ func (x *ReplicationControllerStatus) codecDecodeSelfFromArray(l int, d *codec19 var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj6 int - var yyb6 bool - var yyhl6 bool = l >= 0 - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l + var yyj7 int + var yyb7 bool + var yyhl7 bool = l >= 0 + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb6 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb6 { + if yyb7 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -26847,13 +26879,29 @@ func (x *ReplicationControllerStatus) codecDecodeSelfFromArray(l int, d *codec19 } else { x.Replicas = int32(r.DecodeInt(32)) } - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb6 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb6 { + if yyb7 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.FullyLabeledReplicas = 0 + } else { + x.FullyLabeledReplicas = int32(r.DecodeInt(32)) + } + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l + } else { + yyb7 = r.CheckBreak() + } + if yyb7 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -26864,17 +26912,17 @@ func (x *ReplicationControllerStatus) codecDecodeSelfFromArray(l int, d *codec19 x.ObservedGeneration = int64(r.DecodeInt(64)) } for { - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb6 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb6 { + if yyb7 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj6-1, "") + z.DecStructFieldNotFound(yyj7-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -43999,6 +44047,32 @@ func (x *LimitRangeList) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } +func (x ResourceQuotaScope) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x)) + } +} + +func (x *ResourceQuotaScope) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + *((*string)(x)) = r.DecodeString() + } +} + func (x *ResourceQuotaSpec) CodecEncodeSelf(e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) @@ -44013,13 +44087,14 @@ func (x *ResourceQuotaSpec) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [1]bool + var yyq2 [2]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false yyq2[0] = len(x.Hard) != 0 + yyq2[1] = len(x.Scopes) != 0 var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(1) + r.EncodeArrayStart(2) } else { yynn2 = 0 for _, b := range yyq2 { @@ -44053,6 +44128,39 @@ func (x *ResourceQuotaSpec) CodecEncodeSelf(e *codec1978.Encoder) { } } } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + if x.Scopes == nil { + r.EncodeNil() + } else { + yym7 := z.EncBinary() + _ = yym7 + if false { + } else { + h.encSliceResourceQuotaScope(([]ResourceQuotaScope)(x.Scopes), e) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("scopes")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.Scopes == nil { + r.EncodeNil() + } else { + yym8 := z.EncBinary() + _ = yym8 + if false { + } else { + h.encSliceResourceQuotaScope(([]ResourceQuotaScope)(x.Scopes), e) + } + } + } + } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayEnd1234) } else { @@ -44121,6 +44229,18 @@ func (x *ResourceQuotaSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) yyv4 := &x.Hard yyv4.CodecDecodeSelf(d) } + case "scopes": + if r.TryDecodeAsNil() { + x.Scopes = nil + } else { + yyv5 := &x.Scopes + yym6 := z.DecBinary() + _ = yym6 + if false { + } else { + h.decSliceResourceQuotaScope((*[]ResourceQuotaScope)(yyv5), d) + } + } default: z.DecStructFieldNotFound(-1, yys3) } // end switch yys3 @@ -44132,16 +44252,16 @@ func (x *ResourceQuotaSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj5 int - var yyb5 bool - var yyhl5 bool = l >= 0 - yyj5++ - if yyhl5 { - yyb5 = yyj5 > l + var yyj7 int + var yyb7 bool + var yyhl7 bool = l >= 0 + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb5 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb5 { + if yyb7 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -44149,21 +44269,43 @@ func (x *ResourceQuotaSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder if r.TryDecodeAsNil() { x.Hard = nil } else { - yyv6 := &x.Hard - yyv6.CodecDecodeSelf(d) + yyv8 := &x.Hard + yyv8.CodecDecodeSelf(d) + } + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l + } else { + yyb7 = r.CheckBreak() + } + if yyb7 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Scopes = nil + } else { + yyv9 := &x.Scopes + yym10 := z.DecBinary() + _ = yym10 + if false { + } else { + h.decSliceResourceQuotaScope((*[]ResourceQuotaScope)(yyv9), d) + } } for { - yyj5++ - if yyhl5 { - yyb5 = yyj5 > l + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb5 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb5 { + if yyb7 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj5-1, "") + z.DecStructFieldNotFound(yyj7-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -53826,6 +53968,116 @@ func (x codecSelfer1234) decSliceLimitRange(v *[]LimitRange, d *codec1978.Decode } } +func (x codecSelfer1234) encSliceResourceQuotaScope(v []ResourceQuotaScope, e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + r.EncodeArrayStart(len(v)) + for _, yyv1 := range v { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yyv1.CodecEncodeSelf(e) + } + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x codecSelfer1234) decSliceResourceQuotaScope(v *[]ResourceQuotaScope, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + + yyv1 := *v + yyh1, yyl1 := z.DecSliceHelperStart() + var yyc1 bool + _ = yyc1 + if yyl1 == 0 { + if yyv1 == nil { + yyv1 = []ResourceQuotaScope{} + yyc1 = true + } else if len(yyv1) != 0 { + yyv1 = yyv1[:0] + yyc1 = true + } + } else if yyl1 > 0 { + var yyrr1, yyrl1 int + var yyrt1 bool + _, _ = yyrl1, yyrt1 + yyrr1 = yyl1 // len(yyv1) + if yyl1 > cap(yyv1) { + + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 16) + if yyrt1 { + if yyrl1 <= cap(yyv1) { + yyv1 = yyv1[:yyrl1] + } else { + yyv1 = make([]ResourceQuotaScope, yyrl1) + } + } else { + yyv1 = make([]ResourceQuotaScope, yyrl1) + } + yyc1 = true + yyrr1 = len(yyv1) + } else if yyl1 != len(yyv1) { + yyv1 = yyv1[:yyl1] + yyc1 = true + } + yyj1 := 0 + for ; yyj1 < yyrr1; yyj1++ { + yyh1.ElemContainerState(yyj1) + if r.TryDecodeAsNil() { + yyv1[yyj1] = "" + } else { + yyv1[yyj1] = ResourceQuotaScope(r.DecodeString()) + } + + } + if yyrt1 { + for ; yyj1 < yyl1; yyj1++ { + yyv1 = append(yyv1, "") + yyh1.ElemContainerState(yyj1) + if r.TryDecodeAsNil() { + yyv1[yyj1] = "" + } else { + yyv1[yyj1] = ResourceQuotaScope(r.DecodeString()) + } + + } + } + + } else { + yyj1 := 0 + for ; !r.CheckBreak(); yyj1++ { + + if yyj1 >= len(yyv1) { + yyv1 = append(yyv1, "") // var yyz1 ResourceQuotaScope + yyc1 = true + } + yyh1.ElemContainerState(yyj1) + if yyj1 < len(yyv1) { + if r.TryDecodeAsNil() { + yyv1[yyj1] = "" + } else { + yyv1[yyj1] = ResourceQuotaScope(r.DecodeString()) + } + + } else { + z.DecSwallow() + } + + } + if yyj1 < len(yyv1) { + yyv1 = yyv1[:yyj1] + yyc1 = true + } else if yyj1 == 0 && yyv1 == nil { + yyv1 = []ResourceQuotaScope{} + yyc1 = true + } + } + yyh1.End() + if yyc1 { + *v = yyv1 + } +} + func (x codecSelfer1234) encSliceResourceQuota(v []ResourceQuota, e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) @@ -53865,7 +54117,7 @@ func (x codecSelfer1234) decSliceResourceQuota(v *[]ResourceQuota, d *codec1978. yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 216) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 240) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/types.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/types.go index ad4686ec9..12ad83191 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/types.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/types.go @@ -71,7 +71,7 @@ type ObjectMeta struct { // automatically. Name is primarily intended for creation idempotence and configuration // definition. // Cannot be updated. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#names Name string `json:"name,omitempty"` // GenerateName is an optional prefix, used by the server, to generate a unique @@ -88,7 +88,7 @@ type ObjectMeta struct { // should retry (optionally after the time indicated in the Retry-After header). // // Applied only if Name is not specified. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#idempotency + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#idempotency GenerateName string `json:"generateName,omitempty"` // Namespace defines the space within each name must be unique. An empty namespace is @@ -98,7 +98,7 @@ type ObjectMeta struct { // // Must be a DNS_LABEL. // Cannot be updated. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/namespaces.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/namespaces.md Namespace string `json:"namespace,omitempty"` // SelfLink is a URL representing this object. @@ -112,7 +112,7 @@ type ObjectMeta struct { // // Populated by the system. // Read-only. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#uids + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#uids UID types.UID `json:"uid,omitempty"` // An opaque value that represents the internal version of this object that can @@ -124,13 +124,11 @@ type ObjectMeta struct { // Populated by the system. // Read-only. // Value must be treated as opaque by clients and . - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#concurrency-control-and-consistency ResourceVersion string `json:"resourceVersion,omitempty"` // A sequence number representing a specific generation of the desired state. - // Currently only implemented by replication controllers. - // Populated by the system. - // Read-only. + // Populated by the system. Read-only. Generation int64 `json:"generation,omitempty"` // CreationTimestamp is a timestamp representing the server time when this object was @@ -140,7 +138,7 @@ type ObjectMeta struct { // Populated by the system. // Read-only. // Null for lists. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata CreationTimestamp unversioned.Time `json:"creationTimestamp,omitempty"` // DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This @@ -156,7 +154,7 @@ type ObjectMeta struct { // // Populated by the system when a graceful deletion is requested. // Read-only. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata DeletionTimestamp *unversioned.Time `json:"deletionTimestamp,omitempty"` // Number of seconds allowed for this object to gracefully terminate before @@ -168,14 +166,14 @@ type ObjectMeta struct { // Map of string keys and values that can be used to organize and categorize // (scope and select) objects. May match selectors of replication controllers // and services. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md // TODO: replace map[string]string with labels.LabelSet type Labels map[string]string `json:"labels,omitempty"` // Annotations is an unstructured key value map stored with a resource that may be // set by external tools to store and retrieve arbitrary metadata. They are not // queryable and should be preserved when modifying objects. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/annotations.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/annotations.md Annotations map[string]string `json:"annotations,omitempty"` } @@ -190,7 +188,7 @@ const ( type Volume struct { // Volume's name. // Must be a DNS_LABEL and unique within the pod. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#names Name string `json:"name"` // VolumeSource represents the location and type of the mounted volume. // If not specified, the Volume is implied to be an EmptyDir. @@ -205,50 +203,50 @@ type VolumeSource struct { // machine that is directly exposed to the container. This is generally // used for system agents or other privileged things that are allowed // to see the host machine. Most containers will NOT need this. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#hostpath + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#hostpath // --- // TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not // mount host directories as read/write. HostPath *HostPathVolumeSource `json:"hostPath,omitempty"` // EmptyDir represents a temporary directory that shares a pod's lifetime. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#emptydir + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#emptydir EmptyDir *EmptyDirVolumeSource `json:"emptyDir,omitempty"` // GCEPersistentDisk represents a GCE Disk resource that is attached to a // kubelet's host machine and then exposed to the pod. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#gcepersistentdisk + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#gcepersistentdisk GCEPersistentDisk *GCEPersistentDiskVolumeSource `json:"gcePersistentDisk,omitempty"` // AWSElasticBlockStore represents an AWS Disk resource that is attached to a // kubelet's host machine and then exposed to the pod. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#awselasticblockstore + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#awselasticblockstore AWSElasticBlockStore *AWSElasticBlockStoreVolumeSource `json:"awsElasticBlockStore,omitempty"` // GitRepo represents a git repository at a particular revision. GitRepo *GitRepoVolumeSource `json:"gitRepo,omitempty"` // Secret represents a secret that should populate this volume. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#secrets + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#secrets Secret *SecretVolumeSource `json:"secret,omitempty"` // NFS represents an NFS mount on the host that shares a pod's lifetime - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#nfs + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#nfs NFS *NFSVolumeSource `json:"nfs,omitempty"` // ISCSI represents an ISCSI Disk resource that is attached to a // kubelet's host machine and then exposed to the pod. - // More info: http://releases.k8s.io/HEAD/examples/iscsi/README.md + // More info: http://releases.k8s.io/release-1.2/examples/iscsi/README.md ISCSI *ISCSIVolumeSource `json:"iscsi,omitempty"` // Glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. - // More info: http://releases.k8s.io/HEAD/examples/glusterfs/README.md + // More info: http://releases.k8s.io/release-1.2/examples/glusterfs/README.md Glusterfs *GlusterfsVolumeSource `json:"glusterfs,omitempty"` // PersistentVolumeClaimVolumeSource represents a reference to a // PersistentVolumeClaim in the same namespace. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#persistentvolumeclaims + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#persistentvolumeclaims PersistentVolumeClaim *PersistentVolumeClaimVolumeSource `json:"persistentVolumeClaim,omitempty"` // RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. - // More info: http://releases.k8s.io/HEAD/examples/rbd/README.md + // More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md RBD *RBDVolumeSource `json:"rbd,omitempty"` // FlexVolume represents a generic volume resource that is // provisioned/attached using a exec based plugin. This is an // alpha feature and may change in future. FlexVolume *FlexVolumeSource `json:"flexVolume,omitempty"` // Cinder represents a cinder volume attached and mounted on kubelets host machine - // More info: http://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md + // More info: http://releases.k8s.io/release-1.2/examples/mysql-cinder-pd/README.md Cinder *CinderVolumeSource `json:"cinder,omitempty"` // CephFS represents a Ceph FS mount on the host that shares a pod's lifetime @@ -273,7 +271,7 @@ type VolumeSource struct { // type of volume that is owned by someone else (the system). type PersistentVolumeClaimVolumeSource struct { // ClaimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#persistentvolumeclaims + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#persistentvolumeclaims ClaimName string `json:"claimName"` // Will force the ReadOnly setting in VolumeMounts. // Default false. @@ -285,33 +283,33 @@ type PersistentVolumeClaimVolumeSource struct { type PersistentVolumeSource struct { // GCEPersistentDisk represents a GCE Disk resource that is attached to a // kubelet's host machine and then exposed to the pod. Provisioned by an admin. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#gcepersistentdisk + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#gcepersistentdisk GCEPersistentDisk *GCEPersistentDiskVolumeSource `json:"gcePersistentDisk,omitempty"` // AWSElasticBlockStore represents an AWS Disk resource that is attached to a // kubelet's host machine and then exposed to the pod. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#awselasticblockstore + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#awselasticblockstore AWSElasticBlockStore *AWSElasticBlockStoreVolumeSource `json:"awsElasticBlockStore,omitempty"` // HostPath represents a directory on the host. // Provisioned by a developer or tester. // This is useful for single-node development and testing only! // On-host storage is not supported in any way and WILL NOT WORK in a multi-node cluster. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#hostpath + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#hostpath HostPath *HostPathVolumeSource `json:"hostPath,omitempty"` // Glusterfs represents a Glusterfs volume that is attached to a host and // exposed to the pod. Provisioned by an admin. - // More info: http://releases.k8s.io/HEAD/examples/glusterfs/README.md + // More info: http://releases.k8s.io/release-1.2/examples/glusterfs/README.md Glusterfs *GlusterfsVolumeSource `json:"glusterfs,omitempty"` // NFS represents an NFS mount on the host. Provisioned by an admin. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#nfs + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#nfs NFS *NFSVolumeSource `json:"nfs,omitempty"` // RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. - // More info: http://releases.k8s.io/HEAD/examples/rbd/README.md + // More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md RBD *RBDVolumeSource `json:"rbd,omitempty"` // ISCSI represents an ISCSI Disk resource that is attached to a // kubelet's host machine and then exposed to the pod. Provisioned by an admin. ISCSI *ISCSIVolumeSource `json:"iscsi,omitempty"` // Cinder represents a cinder volume attached and mounted on kubelets host machine - // More info: http://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md + // More info: http://releases.k8s.io/release-1.2/examples/mysql-cinder-pd/README.md Cinder *CinderVolumeSource `json:"cinder,omitempty"` // CephFS represents a Ceph FS mount on the host that shares a pod's lifetime CephFS *CephFSVolumeSource `json:"cephfs,omitempty"` @@ -331,44 +329,44 @@ type PersistentVolumeSource struct { // PersistentVolume (PV) is a storage resource provisioned by an administrator. // It is analogous to a node. -// More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md +// More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md type PersistentVolume struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Spec defines a specification of a persistent volume owned by the cluster. // Provisioned by an administrator. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#persistent-volumes + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#persistent-volumes Spec PersistentVolumeSpec `json:"spec,omitempty"` // Status represents the current information/status for the persistent volume. // Populated by the system. // Read-only. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#persistent-volumes + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#persistent-volumes Status PersistentVolumeStatus `json:"status,omitempty"` } // PersistentVolumeSpec is the specification of a persistent volume. type PersistentVolumeSpec struct { // A description of the persistent volume's resources and capacity. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#capacity + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#capacity Capacity ResourceList `json:"capacity,omitempty"` // The actual volume backing the persistent volume. PersistentVolumeSource `json:",inline"` // AccessModes contains all ways the volume can be mounted. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#access-modes + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#access-modes AccessModes []PersistentVolumeAccessMode `json:"accessModes,omitempty"` // ClaimRef is part of a bi-directional binding between PersistentVolume and PersistentVolumeClaim. // Expected to be non-nil when bound. // claim.VolumeName is the authoritative bind between PV and PVC. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#binding + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#binding ClaimRef *ObjectReference `json:"claimRef,omitempty"` // What happens to a persistent volume when released from its claim. // Valid options are Retain (default) and Recycle. // Recyling must be supported by the volume plugin underlying this persistent volume. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#recycling-policy + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#recycling-policy PersistentVolumeReclaimPolicy PersistentVolumeReclaimPolicy `json:"persistentVolumeReclaimPolicy,omitempty"` } @@ -390,7 +388,7 @@ const ( // PersistentVolumeStatus is the current status of a persistent volume. type PersistentVolumeStatus struct { // Phase indicates if a volume is available, bound to a claim, or released by a claim. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#phase + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#phase Phase PersistentVolumePhase `json:"phase,omitempty"` // A human-readable message indicating details about why the volume is in this state. Message string `json:"message,omitempty"` @@ -403,10 +401,10 @@ type PersistentVolumeStatus struct { type PersistentVolumeList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // List of persistent volumes. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md Items []PersistentVolume `json:"items"` } @@ -414,16 +412,16 @@ type PersistentVolumeList struct { type PersistentVolumeClaim struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Spec defines the desired characteristics of a volume requested by a pod author. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#persistentvolumeclaims + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#persistentvolumeclaims Spec PersistentVolumeClaimSpec `json:"spec,omitempty"` // Status represents the current information/status of a persistent volume claim. // Read-only. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#persistentvolumeclaims + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#persistentvolumeclaims Status PersistentVolumeClaimStatus `json:"status,omitempty"` } @@ -431,10 +429,10 @@ type PersistentVolumeClaim struct { type PersistentVolumeClaimList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // A list of persistent volume claims. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#persistentvolumeclaims + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#persistentvolumeclaims Items []PersistentVolumeClaim `json:"items"` } @@ -442,10 +440,10 @@ type PersistentVolumeClaimList struct { // and allows a Source for provider-specific attributes type PersistentVolumeClaimSpec struct { // AccessModes contains the desired access modes the volume should have. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#access-modes-1 + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#access-modes-1 AccessModes []PersistentVolumeAccessMode `json:"accessModes,omitempty"` // Resources represents the minimum resources the volume should have. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#resources + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#resources Resources ResourceRequirements `json:"resources,omitempty"` // VolumeName is the binding reference to the PersistentVolume backing this claim. VolumeName string `json:"volumeName,omitempty"` @@ -456,7 +454,7 @@ type PersistentVolumeClaimStatus struct { // Phase represents the current phase of PersistentVolumeClaim. Phase PersistentVolumeClaimPhase `json:"phase,omitempty"` // AccessModes contains the actual access modes the volume backing the PVC has. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#access-modes-1 + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#access-modes-1 AccessModes []PersistentVolumeAccessMode `json:"accessModes,omitempty"` // Represents the actual resources of the underlying volume. Capacity ResourceList `json:"capacity,omitempty"` @@ -504,7 +502,7 @@ const ( // Host path volumes do not support ownership management or SELinux relabeling. type HostPathVolumeSource struct { // Path of the directory on the host. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#hostpath + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#hostpath Path string `json:"path"` } @@ -514,7 +512,7 @@ type EmptyDirVolumeSource struct { // What type of storage medium should back this directory. // The default is "" which means to use the node's default medium. // Must be an empty string (default) or Memory. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#emptydir + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#emptydir Medium StorageMedium `json:"medium,omitempty"` } @@ -522,16 +520,16 @@ type EmptyDirVolumeSource struct { // Glusterfs volumes do not support ownership management or SELinux relabeling. type GlusterfsVolumeSource struct { // EndpointsName is the endpoint name that details Glusterfs topology. - // More info: http://releases.k8s.io/HEAD/examples/glusterfs/README.md#create-a-pod + // More info: http://releases.k8s.io/release-1.2/examples/glusterfs/README.md#create-a-pod EndpointsName string `json:"endpoints"` // Path is the Glusterfs volume path. - // More info: http://releases.k8s.io/HEAD/examples/glusterfs/README.md#create-a-pod + // More info: http://releases.k8s.io/release-1.2/examples/glusterfs/README.md#create-a-pod Path string `json:"path"` // ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. // Defaults to false. - // More info: http://releases.k8s.io/HEAD/examples/glusterfs/README.md#create-a-pod + // More info: http://releases.k8s.io/release-1.2/examples/glusterfs/README.md#create-a-pod ReadOnly bool `json:"readOnly,omitempty"` } @@ -539,37 +537,37 @@ type GlusterfsVolumeSource struct { // RBD volumes support ownership management and SELinux relabeling. type RBDVolumeSource struct { // A collection of Ceph monitors. - // More info: http://releases.k8s.io/HEAD/examples/rbd/README.md#how-to-use-it + // More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md#how-to-use-it CephMonitors []string `json:"monitors"` // The rados image name. - // More info: http://releases.k8s.io/HEAD/examples/rbd/README.md#how-to-use-it + // More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md#how-to-use-it RBDImage string `json:"image"` // Filesystem type of the volume that you want to mount. // Tip: Ensure that the filesystem type is supported by the host operating system. // Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#rbd + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#rbd // TODO: how do we prevent errors in the filesystem from compromising the machine FSType string `json:"fsType,omitempty"` // The rados pool name. // Default is rbd. - // More info: http://releases.k8s.io/HEAD/examples/rbd/README.md#how-to-use-it. + // More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md#how-to-use-it. RBDPool string `json:"pool"` // The rados user name. // Default is admin. - // More info: http://releases.k8s.io/HEAD/examples/rbd/README.md#how-to-use-it + // More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md#how-to-use-it RadosUser string `json:"user"` // Keyring is the path to key ring for RBDUser. // Default is /etc/ceph/keyring. - // More info: http://releases.k8s.io/HEAD/examples/rbd/README.md#how-to-use-it + // More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md#how-to-use-it Keyring string `json:"keyring"` // SecretRef is name of the authentication secret for RBDUser. If provided // overrides keyring. // Default is empty. - // More info: http://releases.k8s.io/HEAD/examples/rbd/README.md#how-to-use-it + // More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md#how-to-use-it SecretRef *LocalObjectReference `json:"secretRef"` // ReadOnly here will force the ReadOnly setting in VolumeMounts. // Defaults to false. - // More info: http://releases.k8s.io/HEAD/examples/rbd/README.md#how-to-use-it + // More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md#how-to-use-it ReadOnly bool `json:"readOnly,omitempty"` } @@ -579,16 +577,16 @@ type RBDVolumeSource struct { // Cinder volumes support ownership management and SELinux relabeling. type CinderVolumeSource struct { // volume id used to identify the volume in cinder - // More info: http://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md + // More info: http://releases.k8s.io/release-1.2/examples/mysql-cinder-pd/README.md VolumeID string `json:"volumeID"` // Filesystem type to mount. // Must be a filesystem type supported by the host operating system. // Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - // More info: http://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md + // More info: http://releases.k8s.io/release-1.2/examples/mysql-cinder-pd/README.md FSType string `json:"fsType,omitempty"` // Optional: Defaults to false (read/write). ReadOnly here will force // the ReadOnly setting in VolumeMounts. - // More info: http://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md + // More info: http://releases.k8s.io/release-1.2/examples/mysql-cinder-pd/README.md ReadOnly bool `json:"readOnly,omitempty"` } @@ -596,22 +594,22 @@ type CinderVolumeSource struct { // Cephfs volumes do not support ownership management or SELinux relabeling. type CephFSVolumeSource struct { // Required: Monitors is a collection of Ceph monitors - // More info: http://releases.k8s.io/HEAD/examples/cephfs/README.md#how-to-use-it + // More info: http://releases.k8s.io/release-1.2/examples/cephfs/README.md#how-to-use-it Monitors []string `json:"monitors"` // Optional: Used as the mounted root, rather than the full Ceph tree, default is / Path string `json:"path,omitempty"` // Optional: User is the rados user name, default is admin - // More info: http://releases.k8s.io/HEAD/examples/cephfs/README.md#how-to-use-it + // More info: http://releases.k8s.io/release-1.2/examples/cephfs/README.md#how-to-use-it User string `json:"user,omitempty"` // Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret - // More info: http://releases.k8s.io/HEAD/examples/cephfs/README.md#how-to-use-it + // More info: http://releases.k8s.io/release-1.2/examples/cephfs/README.md#how-to-use-it SecretFile string `json:"secretFile,omitempty"` // Optional: SecretRef is reference to the authentication secret for User, default is empty. - // More info: http://releases.k8s.io/HEAD/examples/cephfs/README.md#how-to-use-it + // More info: http://releases.k8s.io/release-1.2/examples/cephfs/README.md#how-to-use-it SecretRef *LocalObjectReference `json:"secretRef,omitempty"` // Optional: Defaults to false (read/write). ReadOnly here will force // the ReadOnly setting in VolumeMounts. - // More info: http://releases.k8s.io/HEAD/examples/cephfs/README.md#how-to-use-it + // More info: http://releases.k8s.io/release-1.2/examples/cephfs/README.md#how-to-use-it ReadOnly bool `json:"readOnly,omitempty"` } @@ -642,29 +640,29 @@ const ( // Represents a Persistent Disk resource in Google Compute Engine. // -// A GCE PD must exist and be formatted before mounting to a container. -// The disk must also be in the same GCE project and zone as the kubelet. -// A GCE PD can only be mounted as read/write once. -// GCE PDs support ownership management and SELinux relabeling. +// A GCE PD must exist before mounting to a container. The disk must +// also be in the same GCE project and zone as the kubelet. A GCE PD +// can only be mounted as read/write once or read-only many times. GCE +// PDs support ownership management and SELinux relabeling. type GCEPersistentDiskVolumeSource struct { // Unique name of the PD resource in GCE. Used to identify the disk in GCE. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#gcepersistentdisk + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#gcepersistentdisk PDName string `json:"pdName"` // Filesystem type of the volume that you want to mount. // Tip: Ensure that the filesystem type is supported by the host operating system. // Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#gcepersistentdisk + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#gcepersistentdisk // TODO: how do we prevent errors in the filesystem from compromising the machine FSType string `json:"fsType,omitempty"` // The partition in the volume that you want to mount. // If omitted, the default is to mount by volume name. // Examples: For volume /dev/sda1, you specify the partition as "1". // Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#gcepersistentdisk + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#gcepersistentdisk Partition int32 `json:"partition,omitempty"` // ReadOnly here will force the ReadOnly setting in VolumeMounts. // Defaults to false. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#gcepersistentdisk + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#gcepersistentdisk ReadOnly bool `json:"readOnly,omitempty"` } @@ -688,18 +686,18 @@ type FlexVolumeSource struct { // Represents a Persistent Disk resource in AWS. // -// An AWS EBS disk must exist and be formatted before mounting to a container. -// The disk must also be in the same AWS zone as the kubelet. -// An AWS EBS disk can only be mounted as read/write once. -// AWS EBS volumes support ownership management and SELinux relabeling. +// An AWS EBS disk must exist before mounting to a container. The disk +// must also be in the same AWS zone as the kubelet. An AWS EBS disk +// can only be mounted as read/write once. AWS EBS volumes support +// ownership management and SELinux relabeling. type AWSElasticBlockStoreVolumeSource struct { // Unique ID of the persistent disk resource in AWS (Amazon EBS volume). - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#awselasticblockstore + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#awselasticblockstore VolumeID string `json:"volumeID"` // Filesystem type of the volume that you want to mount. // Tip: Ensure that the filesystem type is supported by the host operating system. // Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#awselasticblockstore + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#awselasticblockstore // TODO: how do we prevent errors in the filesystem from compromising the machine FSType string `json:"fsType,omitempty"` // The partition in the volume that you want to mount. @@ -709,7 +707,7 @@ type AWSElasticBlockStoreVolumeSource struct { Partition int32 `json:"partition,omitempty"` // Specify "true" to force and set the ReadOnly property in VolumeMounts to "true". // If omitted, the default is "false". - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#awselasticblockstore + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#awselasticblockstore ReadOnly bool `json:"readOnly,omitempty"` } @@ -735,7 +733,7 @@ type GitRepoVolumeSource struct { // Secret volumes support ownership management and SELinux relabeling. type SecretVolumeSource struct { // Name of the secret in the pod's namespace to use. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#secrets + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#secrets SecretName string `json:"secretName,omitempty"` } @@ -743,17 +741,17 @@ type SecretVolumeSource struct { // NFS volumes do not support ownership management or SELinux relabeling. type NFSVolumeSource struct { // Server is the hostname or IP address of the NFS server. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#nfs + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#nfs Server string `json:"server"` // Path that is exported by the NFS server. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#nfs + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#nfs Path string `json:"path"` // ReadOnly here will force // the NFS export to be mounted with read-only permissions. // Defaults to false. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#nfs + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#nfs ReadOnly bool `json:"readOnly,omitempty"` } @@ -773,7 +771,7 @@ type ISCSIVolumeSource struct { // Filesystem type of the volume that you want to mount. // Tip: Ensure that the filesystem type is supported by the host operating system. // Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#iscsi + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#iscsi // TODO: how do we prevent errors in the filesystem from compromising the machine FSType string `json:"fsType,omitempty"` // ReadOnly here will force the ReadOnly setting in VolumeMounts. @@ -868,7 +866,8 @@ type VolumeMount struct { // Mounted read-only if true, read-write otherwise (false or unspecified). // Defaults to false. ReadOnly bool `json:"readOnly,omitempty"` - // Path within the container at which the volume should be mounted. + // Path within the container at which the volume should be mounted. Must + // not contain ':'. MountPath string `json:"mountPath"` } @@ -986,11 +985,11 @@ type Probe struct { // The action taken to determine the health of a container Handler `json:",inline"` // Number of seconds after the container has started before liveness probes are initiated. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#container-probes InitialDelaySeconds int32 `json:"initialDelaySeconds,omitempty"` // Number of seconds after which the probe times out. // Defaults to 1 second. Minimum value is 1. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#container-probes TimeoutSeconds int32 `json:"timeoutSeconds,omitempty"` // How often (in seconds) to perform the probe. // Default to 10 seconds. Minimum value is 1. @@ -1029,12 +1028,12 @@ type Capabilities struct { // ResourceRequirements describes the compute resource requirements. type ResourceRequirements struct { // Limits describes the maximum amount of compute resources allowed. - // More info: http://releases.k8s.io/HEAD/docs/design/resources.md#resource-specifications + // More info: http://releases.k8s.io/release-1.2/docs/design/resources.md#resource-specifications Limits ResourceList `json:"limits,omitempty"` // Requests describes the minimum amount of compute resources required. // If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, // otherwise to an implementation-defined value. - // More info: http://releases.k8s.io/HEAD/docs/design/resources.md#resource-specifications + // More info: http://releases.k8s.io/release-1.2/docs/design/resources.md#resource-specifications Requests ResourceList `json:"requests,omitempty"` } @@ -1050,7 +1049,7 @@ type Container struct { // Cannot be updated. Name string `json:"name"` // Docker image name. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/images.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/images.md Image string `json:"image,omitempty"` // Entrypoint array. Not executed within a shell. // The docker image's ENTRYPOINT is used if this is not provided. @@ -1059,7 +1058,7 @@ type Container struct { // can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, // regardless of whether the variable exists or not. // Cannot be updated. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/containers.md#containers-and-commands + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/containers.md#containers-and-commands Command []string `json:"command,omitempty"` // Arguments to the entrypoint. // The docker image's CMD is used if this is not provided. @@ -1068,7 +1067,7 @@ type Container struct { // can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, // regardless of whether the variable exists or not. // Cannot be updated. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/containers.md#containers-and-commands + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/containers.md#containers-and-commands Args []string `json:"args,omitempty"` // Container's working directory. // If not specified, the container runtime's default will be used, which @@ -1088,7 +1087,7 @@ type Container struct { Env []EnvVar `json:"env,omitempty" patchStrategy:"merge" patchMergeKey:"name"` // Compute Resources required by this container. // Cannot be updated. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#resources + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#resources Resources ResourceRequirements `json:"resources,omitempty"` // Pod volumes to mount into the container's filesyste. // Cannot be updated. @@ -1096,12 +1095,12 @@ type Container struct { // Periodic probe of container liveness. // Container will be restarted if the probe fails. // Cannot be updated. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#container-probes LivenessProbe *Probe `json:"livenessProbe,omitempty"` // Periodic probe of container service readiness. // Container will be removed from service endpoints if the probe fails. // Cannot be updated. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#container-probes ReadinessProbe *Probe `json:"readinessProbe,omitempty"` // Actions that the management system should take in response to container lifecycle events. // Cannot be updated. @@ -1116,10 +1115,10 @@ type Container struct { // One of Always, Never, IfNotPresent. // Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. // Cannot be updated. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/images.md#updating-images + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/images.md#updating-images ImagePullPolicy PullPolicy `json:"imagePullPolicy,omitempty"` // Security options the pod should run with. - // More info: http://releases.k8s.io/HEAD/docs/design/security_context.md + // More info: http://releases.k8s.io/release-1.2/docs/design/security_context.md SecurityContext *SecurityContext `json:"securityContext,omitempty"` // Variables for interactive containers, these have very specialized use-cases (e.g. debugging) @@ -1163,14 +1162,14 @@ type Lifecycle struct { // PostStart is called immediately after a container is created. If the handler fails, // the container is terminated and restarted according to its restart policy. // Other management of the container blocks until the hook completes. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/container-environment.md#hook-details + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/container-environment.md#hook-details PostStart *Handler `json:"postStart,omitempty"` // PreStop is called immediately before a container is terminated. // The container is terminated after the handler completes. // The reason for termination is passed to the handler. // Regardless of the outcome of the handler, the container is eventually terminated. // Other management of the container blocks until the hook completes. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/container-environment.md#hook-details + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/container-environment.md#hook-details PreStop *Handler `json:"preStop,omitempty"` } @@ -1247,13 +1246,13 @@ type ContainerStatus struct { // garbage collection. This value will get capped at 5 by GC. RestartCount int32 `json:"restartCount"` // The image the container is running. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/images.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/images.md // TODO(dchen1107): Which image the container is running with? Image string `json:"image"` // ImageID of the container's image. ImageID string `json:"imageID"` // Container's ID in the format 'docker://'. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/container-environment.md#container-information + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/container-environment.md#container-information ContainerID string `json:"containerID,omitempty"` } @@ -1294,11 +1293,11 @@ const ( type PodCondition struct { // Type is the type of the condition. // Currently only Ready. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#pod-conditions Type PodConditionType `json:"type"` // Status is the status of the condition. // Can be True, False, Unknown. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#pod-conditions Status ConditionStatus `json:"status"` // Last time we probed the condition. LastProbeTime unversioned.Time `json:"lastProbeTime,omitempty"` @@ -1428,18 +1427,18 @@ type PreferredSchedulingTerm struct { // PodSpec is a description of a pod. type PodSpec struct { // List of volumes that can be mounted by containers belonging to the pod. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md Volumes []Volume `json:"volumes,omitempty" patchStrategy:"merge" patchMergeKey:"name"` // List of containers belonging to the pod. // Containers cannot currently be added or removed. // There must be at least one container in a Pod. // Cannot be updated. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/containers.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/containers.md Containers []Container `json:"containers" patchStrategy:"merge" patchMergeKey:"name"` // Restart policy for all containers within the pod. // One of Always, OnFailure, Never. // Default to Always. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#restartpolicy + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#restartpolicy RestartPolicy RestartPolicy `json:"restartPolicy,omitempty"` // Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. // Value must be non-negative integer. The value zero indicates delete immediately. @@ -1459,11 +1458,11 @@ type PodSpec struct { DNSPolicy DNSPolicy `json:"dnsPolicy,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: http://releases.k8s.io/HEAD/docs/user-guide/node-selection/README.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/node-selection/README.md NodeSelector map[string]string `json:"nodeSelector,omitempty"` // ServiceAccountName is the name of the ServiceAccount to use to run this pod. - // More info: http://releases.k8s.io/HEAD/docs/design/service_accounts.md + // More info: http://releases.k8s.io/release-1.2/docs/design/service_accounts.md ServiceAccountName string `json:"serviceAccountName,omitempty"` // DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. // Deprecated: Use serviceAccountName instead. @@ -1489,7 +1488,7 @@ type PodSpec struct { // ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. // If specified, these secrets will be passed to individual puller implementations for them to use. For example, // in the case of docker, only DockerConfig type secrets are honored. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/images.md#specifying-imagepullsecrets-on-a-pod + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/images.md#specifying-imagepullsecrets-on-a-pod ImagePullSecrets []LocalObjectReference `json:"imagePullSecrets,omitempty" patchStrategy:"merge" patchMergeKey:"name"` } @@ -1536,10 +1535,10 @@ type PodSecurityContext struct { // state of a system. type PodStatus struct { // Current condition of the pod. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-phase + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#pod-phase Phase PodPhase `json:"phase,omitempty"` // Current service state of pod. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#pod-conditions Conditions []PodCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // A human readable message indicating details about why the pod is in this condition. Message string `json:"message,omitempty"` @@ -1559,7 +1558,7 @@ type PodStatus struct { // The list has one entry per container in the manifest. Each entry is currently the output // of `docker inspect`. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-statuses + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#container-statuses ContainerStatuses []ContainerStatus `json:"containerStatuses,omitempty"` } @@ -1567,13 +1566,13 @@ type PodStatus struct { type PodStatusResult struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Most recently observed status of the pod. // This data may not be up to date. // Populated by the system. // Read-only. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Status PodStatus `json:"status,omitempty"` } @@ -1584,18 +1583,18 @@ type PodStatusResult struct { type Pod struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Specification of the desired behavior of the pod. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Spec PodSpec `json:"spec,omitempty"` // Most recently observed status of the pod. // This data may not be up to date. // Populated by the system. // Read-only. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Status PodStatus `json:"status,omitempty"` } @@ -1603,22 +1602,22 @@ type Pod struct { type PodList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // List of pods. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/pods.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/pods.md Items []Pod `json:"items"` } // PodTemplateSpec describes the data a pod should have when created from a template type PodTemplateSpec struct { // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Specification of the desired behavior of the pod. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Spec PodSpec `json:"spec,omitempty"` } @@ -1628,11 +1627,11 @@ type PodTemplateSpec struct { type PodTemplate struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Template defines the pods that will be created from this pod template. - // http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Template PodTemplateSpec `json:"template,omitempty"` } @@ -1640,7 +1639,7 @@ type PodTemplate struct { type PodTemplateList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // List of pod templates @@ -1652,14 +1651,14 @@ type ReplicationControllerSpec struct { // Replicas is the number of desired replicas. // This is a pointer to distinguish between explicit zero and unspecified. // Defaults to 1. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md#what-is-a-replication-controller Replicas *int32 `json:"replicas,omitempty"` // Selector is a label query over pods that should match the Replicas count. // If Selector is empty, it is defaulted to the labels present on the Pod template. // Label keys and values that must match in order to be controlled by this replication // controller, if empty defaulted to labels on Pod template. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors Selector map[string]string `json:"selector,omitempty"` // TemplateRef is a reference to an object that describes the pod that will be created if @@ -1669,7 +1668,7 @@ type ReplicationControllerSpec struct { // Template is the object that describes the pod that will be created if // insufficient replicas are detected. This takes precedence over a TemplateRef. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#pod-template + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md#pod-template Template *PodTemplateSpec `json:"template,omitempty"` } @@ -1677,9 +1676,12 @@ type ReplicationControllerSpec struct { // controller. type ReplicationControllerStatus struct { // Replicas is the most recently oberved number of replicas. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md#what-is-a-replication-controller Replicas int32 `json:"replicas"` + // The number of pods that have labels matching the labels of the pod template of the replication controller. + FullyLabeledReplicas int32 `json:"fullyLabeledReplicas,omitempty"` + // ObservedGeneration reflects the generation of the most recently observed replication controller. ObservedGeneration int64 `json:"observedGeneration,omitempty"` } @@ -1692,18 +1694,18 @@ type ReplicationController struct { // If the Labels of a ReplicationController are empty, they are defaulted to // be the same as the Pod(s) that the replication controller manages. - // Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Spec defines the specification of the desired behavior of the replication controller. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Spec ReplicationControllerSpec `json:"spec,omitempty"` // Status is the most recently observed status of the replication controller. // This data may be out of date by some window of time. // Populated by the system. // Read-only. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Status ReplicationControllerStatus `json:"status,omitempty"` } @@ -1711,11 +1713,11 @@ type ReplicationController struct { type ReplicationControllerList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // List of replication controllers. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md Items []ReplicationController `json:"items"` } @@ -1777,13 +1779,13 @@ type LoadBalancerIngress struct { // ServiceSpec describes the attributes that a user creates on a service. type ServiceSpec struct { // The list of ports that are exposed by this service. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#virtual-ips-and-service-proxies + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/services.md#virtual-ips-and-service-proxies Ports []ServicePort `json:"ports"` // This service will route traffic to pods having labels matching this selector. // Label keys and values that must match in order to receive traffic for this service. // If empty, all pods are selected, if not specified, endpoints must be manually specified. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#overview + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/services.md#overview Selector map[string]string `json:"selector,omitempty"` // ClusterIP is usually assigned by the master and is the IP address of the service. @@ -1792,12 +1794,12 @@ type ServiceSpec struct { // Valid values are None, empty string (""), or a valid IP address. // 'None' can be specified for a headless service when proxying is not required. // Cannot be updated. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#virtual-ips-and-service-proxies + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/services.md#virtual-ips-and-service-proxies ClusterIP string `json:"clusterIP,omitempty"` // Type of exposed service. Must be ClusterIP, NodePort, or LoadBalancer. // Defaults to ClusterIP. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#external-services + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/services.md#external-services Type ServiceType `json:"type,omitempty"` // externalIPs is a list of IP addresses for which nodes in the cluster @@ -1820,7 +1822,7 @@ type ServiceSpec struct { // Enable client IP based session affinity. // Must be ClientIP or None. // Defaults to None. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#virtual-ips-and-service-proxies + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/services.md#virtual-ips-and-service-proxies SessionAffinity ServiceAffinity `json:"sessionAffinity,omitempty"` // Only applies to Service Type: LoadBalancer @@ -1850,16 +1852,17 @@ type ServicePort struct { // Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. // If this is a string, it will be looked up as a named port in the // target Pod's container ports. If this is not specified, the value - // of Port is used (an identity map). - // Defaults to the service port. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#defining-a-service + // of the 'port' field is used (an identity map). + // This field is ignored for services with clusterIP=None, and should be + // omitted or set equal to the 'port' field. + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/services.md#defining-a-service TargetPort intstr.IntOrString `json:"targetPort,omitempty"` // The port on each node on which this service is exposed when type=NodePort or LoadBalancer. // Usually assigned by the system. If specified, it will be allocated to the service // if unused or else creation of the service will fail. // Default is to auto-allocate a port if the ServiceType of this Service requires one. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#type--nodeport + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/services.md#type--nodeport NodePort int32 `json:"nodePort,omitempty"` } @@ -1871,17 +1874,17 @@ type ServicePort struct { type Service struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Spec defines the behavior of a service. - // http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Spec ServiceSpec `json:"spec,omitempty"` // Most recently observed status of the service. // Populated by the system. // Read-only. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Status ServiceStatus `json:"status,omitempty"` } @@ -1895,7 +1898,7 @@ const ( type ServiceList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // List of services @@ -1911,17 +1914,17 @@ type ServiceList struct { type ServiceAccount struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/secrets.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/secrets.md Secrets []ObjectReference `json:"secrets,omitempty" patchStrategy:"merge" patchMergeKey:"name"` // ImagePullSecrets is a list of references to secrets in the same namespace to use for pulling any images // in pods that reference this ServiceAccount. ImagePullSecrets are distinct from Secrets because Secrets // can be mounted in the pod, but ImagePullSecrets are only accessed by the kubelet. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/secrets.md#manually-specifying-an-imagepullsecret + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/secrets.md#manually-specifying-an-imagepullsecret ImagePullSecrets []LocalObjectReference `json:"imagePullSecrets,omitempty"` } @@ -1929,11 +1932,11 @@ type ServiceAccount struct { type ServiceAccountList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // List of ServiceAccounts. - // More info: http://releases.k8s.io/HEAD/docs/design/service_accounts.md#service-accounts + // More info: http://releases.k8s.io/release-1.2/docs/design/service_accounts.md#service-accounts Items []ServiceAccount `json:"items"` } @@ -1954,7 +1957,7 @@ type ServiceAccountList struct { type Endpoints struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // The set of all endpoints is the union of all subsets. Addresses are placed into @@ -2021,7 +2024,7 @@ type EndpointPort struct { type EndpointsList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // List of endpoints. @@ -2038,7 +2041,7 @@ type NodeSpec struct { // ID of the node assigned by the cloud provider in the format: :// ProviderID string `json:"providerID,omitempty"` // Unschedulable controls node schedulability of new pods. By default, node is schedulable. - // More info: http://releases.k8s.io/HEAD/docs/admin/node.md#manual-node-administration"` + // More info: http://releases.k8s.io/release-1.2/docs/admin/node.md#manual-node-administration"` Unschedulable bool `json:"unschedulable,omitempty"` } @@ -2077,25 +2080,25 @@ type NodeSystemInfo struct { // NodeStatus is information about the current status of a node. type NodeStatus struct { // Capacity represents the total resources of a node. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#capacity for more details. + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#capacity for more details. Capacity ResourceList `json:"capacity,omitempty"` // Allocatable represents the resources of a node that are available for scheduling. // Defaults to Capacity. Allocatable ResourceList `json:"allocatable,omitempty"` // NodePhase is the recently observed lifecycle phase of the node. - // More info: http://releases.k8s.io/HEAD/docs/admin/node.md#node-phase + // More info: http://releases.k8s.io/release-1.2/docs/admin/node.md#node-phase Phase NodePhase `json:"phase,omitempty"` // Conditions is an array of current observed node conditions. - // More info: http://releases.k8s.io/HEAD/docs/admin/node.md#node-condition + // More info: http://releases.k8s.io/release-1.2/docs/admin/node.md#node-condition Conditions []NodeCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // List of addresses reachable to the node. // Queried from cloud provider, if available. - // More info: http://releases.k8s.io/HEAD/docs/admin/node.md#node-addresses + // More info: http://releases.k8s.io/release-1.2/docs/admin/node.md#node-addresses Addresses []NodeAddress `json:"addresses,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // Endpoints of daemons running on the Node. DaemonEndpoints NodeDaemonEndpoints `json:"daemonEndpoints,omitempty"` // Set of ids/uuids to uniquely identify the node. - // More info: http://releases.k8s.io/HEAD/docs/admin/node.md#node-info + // More info: http://releases.k8s.io/release-1.2/docs/admin/node.md#node-info NodeInfo NodeSystemInfo `json:"nodeInfo,omitempty"` // List of container images on this node Images []ContainerImage `json:"images",omitempty` @@ -2190,17 +2193,17 @@ type ResourceList map[ResourceName]resource.Quantity type Node struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Spec defines the behavior of a node. - // http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Spec NodeSpec `json:"spec,omitempty"` // Most recently observed status of the node. // Populated by the system. // Read-only. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Status NodeStatus `json:"status,omitempty"` } @@ -2208,7 +2211,7 @@ type Node struct { type NodeList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // List of nodes @@ -2225,14 +2228,14 @@ const ( // NamespaceSpec describes the attributes on a Namespace. type NamespaceSpec struct { // Finalizers is an opaque list of values that must be empty to permanently remove object from storage. - // More info: http://releases.k8s.io/HEAD/docs/design/namespaces.md#finalizers + // More info: http://releases.k8s.io/release-1.2/docs/design/namespaces.md#finalizers Finalizers []FinalizerName `json:"finalizers,omitempty"` } // NamespaceStatus is information about the current status of a Namespace. type NamespaceStatus struct { // Phase is the current lifecycle phase of the namespace. - // More info: http://releases.k8s.io/HEAD/docs/design/namespaces.md#phases + // More info: http://releases.k8s.io/release-1.2/docs/design/namespaces.md#phases Phase NamespacePhase `json:"phase,omitempty"` } @@ -2253,15 +2256,15 @@ const ( type Namespace struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Spec defines the behavior of the Namespace. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Spec NamespaceSpec `json:"spec,omitempty"` // Status describes the current status of a Namespace. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Status NamespaceStatus `json:"status,omitempty"` } @@ -2269,11 +2272,11 @@ type Namespace struct { type NamespaceList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // Items is the list of Namespace objects in the list. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/namespaces.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/namespaces.md Items []Namespace `json:"items"` } @@ -2282,7 +2285,7 @@ type NamespaceList struct { type Binding struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // The target object that you want to bind to the standard object. @@ -2454,21 +2457,21 @@ type ServiceProxyOptions struct { // ObjectReference contains enough information to let you inspect or modify the referred object. type ObjectReference struct { // Kind of the referent. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds Kind string `json:"kind,omitempty"` // Namespace of the referent. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/namespaces.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/namespaces.md Namespace string `json:"namespace,omitempty"` // Name of the referent. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#names Name string `json:"name,omitempty"` // UID of the referent. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#uids + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#uids UID types.UID `json:"uid,omitempty"` // API version of the referent. APIVersion string `json:"apiVersion,omitempty"` // Specific resourceVersion to which this reference is made, if any. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#concurrency-control-and-consistency ResourceVersion string `json:"resourceVersion,omitempty"` // If referring to a piece of an object instead of an entire object, this string @@ -2486,7 +2489,7 @@ type ObjectReference struct { // referenced object inside the same namespace. type LocalObjectReference struct { // Name of the referent. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#names // TODO: Add other useful fields. apiVersion, kind, uid? Name string `json:"name,omitempty"` } @@ -2521,7 +2524,7 @@ const ( type Event struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata"` // The object that this event is about. @@ -2556,7 +2559,7 @@ type Event struct { type EventList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // List of events @@ -2567,7 +2570,7 @@ type EventList struct { type List struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // List of objects @@ -2612,11 +2615,11 @@ type LimitRangeSpec struct { type LimitRange struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Spec defines the limits enforced. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Spec LimitRangeSpec `json:"spec,omitempty"` } @@ -2624,11 +2627,11 @@ type LimitRange struct { type LimitRangeList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // Items is a list of LimitRange objects. - // More info: http://releases.k8s.io/HEAD/docs/design/admission_control_limit_range.md + // More info: http://releases.k8s.io/release-1.2/docs/design/admission_control_limit_range.md Items []LimitRange `json:"items"` } @@ -2644,21 +2647,48 @@ const ( ResourceQuotas ResourceName = "resourcequotas" // ResourceSecrets, number ResourceSecrets ResourceName = "secrets" + // ResourceConfigMaps, number + ResourceConfigMaps ResourceName = "configmaps" // ResourcePersistentVolumeClaims, number ResourcePersistentVolumeClaims ResourceName = "persistentvolumeclaims" + // CPU request, in cores. (500m = .5 cores) + ResourceCPURequest ResourceName = "cpu.request" + // CPU limit, in cores. (500m = .5 cores) + ResourceCPULimit ResourceName = "cpu.limit" + // Memory request, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + ResourceMemoryRequest ResourceName = "memory.request" + // Memory limit, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024) + ResourceMemoryLimit ResourceName = "memory.limit" +) + +// A ResourceQuotaScope defines a filter that must match each object tracked by a quota +type ResourceQuotaScope string + +const ( + // Match all pod objects where spec.activeDeadlineSeconds + ResourceQuotaScopeTerminating ResourceQuotaScope = "Terminating" + // Match all pod objects where !spec.activeDeadlineSeconds + ResourceQuotaScopeNotTerminating ResourceQuotaScope = "NotTerminating" + // Match all pod objects that have best effort quality of service + ResourceQuotaScopeBestEffort ResourceQuotaScope = "BestEffort" + // Match all pod objects that do not have best effort quality of service + ResourceQuotaScopeNotBestEffort ResourceQuotaScope = "NotBestEffort" ) // ResourceQuotaSpec defines the desired hard limits to enforce for Quota. type ResourceQuotaSpec struct { // Hard is the set of desired hard limits for each named resource. - // More info: http://releases.k8s.io/HEAD/docs/design/admission_control_resource_quota.md#admissioncontrol-plugin-resourcequota + // More info: http://releases.k8s.io/release-1.2/docs/design/admission_control_resource_quota.md#admissioncontrol-plugin-resourcequota Hard ResourceList `json:"hard,omitempty"` + // A collection of filters that must match each object tracked by a quota. + // If not specified, the quota matches all objects. + Scopes []ResourceQuotaScope `json:"scopes,omitempty"` } // ResourceQuotaStatus defines the enforced hard limits and observed use. type ResourceQuotaStatus struct { // Hard is the set of enforced hard limits for each named resource. - // More info: http://releases.k8s.io/HEAD/docs/design/admission_control_resource_quota.md#admissioncontrol-plugin-resourcequota + // More info: http://releases.k8s.io/release-1.2/docs/design/admission_control_resource_quota.md#admissioncontrol-plugin-resourcequota Hard ResourceList `json:"hard,omitempty"` // Used is the current observed total usage of the resource in the namespace. Used ResourceList `json:"used,omitempty"` @@ -2670,15 +2700,15 @@ type ResourceQuotaStatus struct { type ResourceQuota struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Spec defines the desired quota. - // http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Spec ResourceQuotaSpec `json:"spec,omitempty"` // Status defines the actual enforced quota and its current usage. - // http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Status ResourceQuotaStatus `json:"status,omitempty"` } @@ -2686,11 +2716,11 @@ type ResourceQuota struct { type ResourceQuotaList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // Items is a list of ResourceQuota objects. - // More info: http://releases.k8s.io/HEAD/docs/design/admission_control_resource_quota.md#admissioncontrol-plugin-resourcequota + // More info: http://releases.k8s.io/release-1.2/docs/design/admission_control_resource_quota.md#admissioncontrol-plugin-resourcequota Items []ResourceQuota `json:"items"` } @@ -2701,7 +2731,7 @@ type ResourceQuotaList struct { type Secret struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN @@ -2773,11 +2803,11 @@ const ( type SecretList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // Items is a list of secret objects. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/secrets.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/secrets.md Items []Secret `json:"items"` } @@ -2787,7 +2817,7 @@ type SecretList struct { type ConfigMap struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Data contains the configuration data. @@ -2799,7 +2829,7 @@ type ConfigMap struct { type ConfigMapList struct { unversioned.TypeMeta `json:",inline"` - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata unversioned.ListMeta `json:"metadata,omitempty"` // Items is the list of ConfigMaps. @@ -2836,7 +2866,7 @@ type ComponentCondition struct { type ComponentStatus struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // List of component conditions observed @@ -2847,7 +2877,7 @@ type ComponentStatus struct { type ComponentStatusList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // List of ComponentStatus objects. @@ -2918,7 +2948,7 @@ type SELinuxOptions struct { type RangeAllocation struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata ObjectMeta `json:"metadata,omitempty"` // Range is string that identifies the range represented by 'data'. diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/types_swagger_doc_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/types_swagger_doc_generated.go index 4a7bd2dba..f1878b548 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/types_swagger_doc_generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/v1/types_swagger_doc_generated.go @@ -16,7 +16,7 @@ limitations under the License. package v1 -// This file contains a collection of methods that can be used from go-resful to +// This file contains a collection of methods that can be used from go-restful to // generate Swagger API documentation for its models. Please read this PR for more // information on the implementation: https://github.com/emicklei/go-restful/pull/215 // @@ -28,11 +28,11 @@ package v1 // AUTO-GENERATED FUNCTIONS START HERE var map_AWSElasticBlockStoreVolumeSource = map[string]string{ - "": "Represents a Persistent Disk resource in AWS.\n\nAn AWS EBS disk must exist and be formatted before mounting to a container. The disk must also be in the same AWS zone as the kubelet. An AWS EBS disk can only be mounted as read/write once. AWS EBS volumes support ownership management and SELinux relabeling.", - "volumeID": "Unique ID of the persistent disk resource in AWS (Amazon EBS volume). More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#awselasticblockstore", - "fsType": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#awselasticblockstore", + "": "Represents a Persistent Disk resource in AWS.\n\nAn AWS EBS disk must exist before mounting to a container. The disk must also be in the same AWS zone as the kubelet. An AWS EBS disk can only be mounted as read/write once. AWS EBS volumes support ownership management and SELinux relabeling.", + "volumeID": "Unique ID of the persistent disk resource in AWS (Amazon EBS volume). More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#awselasticblockstore", + "fsType": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#awselasticblockstore", "partition": "The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty).", - "readOnly": "Specify \"true\" to force and set the ReadOnly property in VolumeMounts to \"true\". If omitted, the default is \"false\". More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#awselasticblockstore", + "readOnly": "Specify \"true\" to force and set the ReadOnly property in VolumeMounts to \"true\". If omitted, the default is \"false\". More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#awselasticblockstore", } func (AWSElasticBlockStoreVolumeSource) SwaggerDoc() map[string]string { @@ -61,7 +61,7 @@ func (AzureFileVolumeSource) SwaggerDoc() map[string]string { var map_Binding = map[string]string{ "": "Binding ties one object to another. For example, a pod is bound to a node by a scheduler.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", "target": "The target object that you want to bind to the standard object.", } @@ -81,12 +81,12 @@ func (Capabilities) SwaggerDoc() map[string]string { var map_CephFSVolumeSource = map[string]string{ "": "Represents a Ceph Filesystem mount that lasts the lifetime of a pod Cephfs volumes do not support ownership management or SELinux relabeling.", - "monitors": "Required: Monitors is a collection of Ceph monitors More info: http://releases.k8s.io/HEAD/examples/cephfs/README.md#how-to-use-it", + "monitors": "Required: Monitors is a collection of Ceph monitors More info: http://releases.k8s.io/release-1.2/examples/cephfs/README.md#how-to-use-it", "path": "Optional: Used as the mounted root, rather than the full Ceph tree, default is /", - "user": "Optional: User is the rados user name, default is admin More info: http://releases.k8s.io/HEAD/examples/cephfs/README.md#how-to-use-it", - "secretFile": "Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: http://releases.k8s.io/HEAD/examples/cephfs/README.md#how-to-use-it", - "secretRef": "Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: http://releases.k8s.io/HEAD/examples/cephfs/README.md#how-to-use-it", - "readOnly": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: http://releases.k8s.io/HEAD/examples/cephfs/README.md#how-to-use-it", + "user": "Optional: User is the rados user name, default is admin More info: http://releases.k8s.io/release-1.2/examples/cephfs/README.md#how-to-use-it", + "secretFile": "Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: http://releases.k8s.io/release-1.2/examples/cephfs/README.md#how-to-use-it", + "secretRef": "Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: http://releases.k8s.io/release-1.2/examples/cephfs/README.md#how-to-use-it", + "readOnly": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: http://releases.k8s.io/release-1.2/examples/cephfs/README.md#how-to-use-it", } func (CephFSVolumeSource) SwaggerDoc() map[string]string { @@ -95,9 +95,9 @@ func (CephFSVolumeSource) SwaggerDoc() map[string]string { var map_CinderVolumeSource = map[string]string{ "": "Represents a cinder volume resource in Openstack. A Cinder volume must exist before mounting to a container. The volume must also be in the same region as the kubelet. Cinder volumes support ownership management and SELinux relabeling.", - "volumeID": "volume id used to identify the volume in cinder More info: http://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md", - "fsType": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: http://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md", - "readOnly": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: http://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md", + "volumeID": "volume id used to identify the volume in cinder More info: http://releases.k8s.io/release-1.2/examples/mysql-cinder-pd/README.md", + "fsType": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: http://releases.k8s.io/release-1.2/examples/mysql-cinder-pd/README.md", + "readOnly": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: http://releases.k8s.io/release-1.2/examples/mysql-cinder-pd/README.md", } func (CinderVolumeSource) SwaggerDoc() map[string]string { @@ -118,7 +118,7 @@ func (ComponentCondition) SwaggerDoc() map[string]string { var map_ComponentStatus = map[string]string{ "": "ComponentStatus (and ComponentStatusList) holds the cluster validation info.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", "conditions": "List of component conditions observed", } @@ -128,7 +128,7 @@ func (ComponentStatus) SwaggerDoc() map[string]string { var map_ComponentStatusList = map[string]string{ "": "Status of all the conditions for the component as a list of ComponentStatus objects.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", "items": "List of ComponentStatus objects.", } @@ -138,7 +138,7 @@ func (ComponentStatusList) SwaggerDoc() map[string]string { var map_ConfigMap = map[string]string{ "": "ConfigMap holds configuration data for pods to consume.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", "data": "Data contains the configuration data. Each key must be a valid DNS_SUBDOMAIN with an optional leading dot.", } @@ -157,7 +157,7 @@ func (ConfigMapKeySelector) SwaggerDoc() map[string]string { var map_ConfigMapList = map[string]string{ "": "ConfigMapList is a resource containing a list of ConfigMap objects.", - "metadata": "More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", + "metadata": "More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", "items": "Items is the list of ConfigMaps.", } @@ -177,20 +177,20 @@ func (ConfigMapVolumeSource) SwaggerDoc() map[string]string { var map_Container = map[string]string{ "": "A single application container that you want to run within a pod.", "name": "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.", - "image": "Docker image name. More info: http://releases.k8s.io/HEAD/docs/user-guide/images.md", - "command": "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. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: http://releases.k8s.io/HEAD/docs/user-guide/containers.md#containers-and-commands", - "args": "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. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: http://releases.k8s.io/HEAD/docs/user-guide/containers.md#containers-and-commands", + "image": "Docker image name. More info: http://releases.k8s.io/release-1.2/docs/user-guide/images.md", + "command": "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. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: http://releases.k8s.io/release-1.2/docs/user-guide/containers.md#containers-and-commands", + "args": "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. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: http://releases.k8s.io/release-1.2/docs/user-guide/containers.md#containers-and-commands", "workingDir": "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.", "ports": "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.", "env": "List of environment variables to set in the container. Cannot be updated.", - "resources": "Compute Resources required by this container. Cannot be updated. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#resources", + "resources": "Compute Resources required by this container. Cannot be updated. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#resources", "volumeMounts": "Pod volumes to mount into the container's filesyste. Cannot be updated.", - "livenessProbe": "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes", - "readinessProbe": "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes", + "livenessProbe": "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#container-probes", + "readinessProbe": "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#container-probes", "lifecycle": "Actions that the management system should take in response to container lifecycle events. Cannot be updated.", "terminationMessagePath": "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. Defaults to /dev/termination-log. Cannot be updated.", - "imagePullPolicy": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: http://releases.k8s.io/HEAD/docs/user-guide/images.md#updating-images", - "securityContext": "Security options the pod should run with. More info: http://releases.k8s.io/HEAD/docs/design/security_context.md", + "imagePullPolicy": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: http://releases.k8s.io/release-1.2/docs/user-guide/images.md#updating-images", + "securityContext": "Security options the pod should run with. More info: http://releases.k8s.io/release-1.2/docs/design/security_context.md", "stdin": "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.", "stdinOnce": "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", "tty": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", @@ -275,9 +275,9 @@ var map_ContainerStatus = map[string]string{ "lastState": "Details about the container's last termination condition.", "ready": "Specifies whether the container has passed its readiness probe.", "restartCount": "The number of times the container has been restarted, currently based on the number of dead containers that have not yet been removed. Note that this is calculated from dead containers. But those containers are subject to garbage collection. This value will get capped at 5 by GC.", - "image": "The image the container is running. More info: http://releases.k8s.io/HEAD/docs/user-guide/images.md", + "image": "The image the container is running. More info: http://releases.k8s.io/release-1.2/docs/user-guide/images.md", "imageID": "ImageID of the container's image.", - "containerID": "Container's ID in the format 'docker://'. More info: http://releases.k8s.io/HEAD/docs/user-guide/container-environment.md#container-information", + "containerID": "Container's ID in the format 'docker://'. More info: http://releases.k8s.io/release-1.2/docs/user-guide/container-environment.md#container-information", } func (ContainerStatus) SwaggerDoc() map[string]string { @@ -323,7 +323,7 @@ func (DownwardAPIVolumeSource) SwaggerDoc() map[string]string { var map_EmptyDirVolumeSource = map[string]string{ "": "Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.", - "medium": "What type of storage medium should back this directory. The default is \"\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#emptydir", + "medium": "What type of storage medium should back this directory. The default is \"\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#emptydir", } func (EmptyDirVolumeSource) SwaggerDoc() map[string]string { @@ -364,7 +364,7 @@ func (EndpointSubset) SwaggerDoc() map[string]string { var map_Endpoints = map[string]string{ "": "Endpoints is a collection of endpoints that implement the actual service. Example:\n Name: \"mysvc\",\n Subsets: [\n {\n Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],\n Ports: [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]\n },\n {\n Addresses: [{\"ip\": \"10.10.3.3\"}],\n Ports: [{\"name\": \"a\", \"port\": 93}, {\"name\": \"b\", \"port\": 76}]\n },\n ]", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", "subsets": "The set of all endpoints is the union of all subsets. Addresses are placed into subsets according to the IPs they share. A single address with multiple ports, some of which are ready and some of which are not (because they come from different containers) will result in the address being displayed in different subsets for the different ports. No address will appear in both Addresses and NotReadyAddresses in the same subset. Sets of addresses and ports that comprise a service.", } @@ -374,7 +374,7 @@ func (Endpoints) SwaggerDoc() map[string]string { var map_EndpointsList = map[string]string{ "": "EndpointsList is a list of endpoints.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", "items": "List of endpoints.", } @@ -406,7 +406,7 @@ func (EnvVarSource) SwaggerDoc() map[string]string { var map_Event = map[string]string{ "": "Event is a report of an event somewhere in the cluster.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", "involvedObject": "The object that this event is about.", "reason": "This should be a short, machine understandable string that gives the reason for the transition into the object's current status.", "message": "A human-readable description of the status of this operation.", @@ -423,7 +423,7 @@ func (Event) SwaggerDoc() map[string]string { var map_EventList = map[string]string{ "": "EventList is a list of events.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", "items": "List of events", } @@ -495,11 +495,11 @@ func (FlockerVolumeSource) SwaggerDoc() map[string]string { } var map_GCEPersistentDiskVolumeSource = map[string]string{ - "": "Represents a Persistent Disk resource in Google Compute Engine.\n\nA GCE PD must exist and be formatted before mounting to a container. The disk must also be in the same GCE project and zone as the kubelet. A GCE PD can only be mounted as read/write once. GCE PDs support ownership management and SELinux relabeling.", - "pdName": "Unique name of the PD resource in GCE. Used to identify the disk in GCE. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#gcepersistentdisk", - "fsType": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#gcepersistentdisk", - "partition": "The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty). More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#gcepersistentdisk", - "readOnly": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#gcepersistentdisk", + "": "Represents a Persistent Disk resource in Google Compute Engine.\n\nA GCE PD must exist before mounting to a container. The disk must also be in the same GCE project and zone as the kubelet. A GCE PD can only be mounted as read/write once or read-only many times. GCE PDs support ownership management and SELinux relabeling.", + "pdName": "Unique name of the PD resource in GCE. Used to identify the disk in GCE. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#gcepersistentdisk", + "fsType": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#gcepersistentdisk", + "partition": "The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty). More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#gcepersistentdisk", + "readOnly": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#gcepersistentdisk", } func (GCEPersistentDiskVolumeSource) SwaggerDoc() map[string]string { @@ -519,9 +519,9 @@ func (GitRepoVolumeSource) SwaggerDoc() map[string]string { var map_GlusterfsVolumeSource = map[string]string{ "": "Represents a Glusterfs mount that lasts the lifetime of a pod. Glusterfs volumes do not support ownership management or SELinux relabeling.", - "endpoints": "EndpointsName is the endpoint name that details Glusterfs topology. More info: http://releases.k8s.io/HEAD/examples/glusterfs/README.md#create-a-pod", - "path": "Path is the Glusterfs volume path. More info: http://releases.k8s.io/HEAD/examples/glusterfs/README.md#create-a-pod", - "readOnly": "ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: http://releases.k8s.io/HEAD/examples/glusterfs/README.md#create-a-pod", + "endpoints": "EndpointsName is the endpoint name that details Glusterfs topology. More info: http://releases.k8s.io/release-1.2/examples/glusterfs/README.md#create-a-pod", + "path": "Path is the Glusterfs volume path. More info: http://releases.k8s.io/release-1.2/examples/glusterfs/README.md#create-a-pod", + "readOnly": "ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: http://releases.k8s.io/release-1.2/examples/glusterfs/README.md#create-a-pod", } func (GlusterfsVolumeSource) SwaggerDoc() map[string]string { @@ -564,7 +564,7 @@ func (Handler) SwaggerDoc() map[string]string { var map_HostPathVolumeSource = map[string]string{ "": "Represents a host path mapped into a pod. Host path volumes do not support ownership management or SELinux relabeling.", - "path": "Path of the directory on the host. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#hostpath", + "path": "Path of the directory on the host. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#hostpath", } func (HostPathVolumeSource) SwaggerDoc() map[string]string { @@ -577,7 +577,7 @@ var map_ISCSIVolumeSource = map[string]string{ "iqn": "Target iSCSI Qualified Name.", "lun": "iSCSI target lun number.", "iscsiInterface": "Optional: Defaults to 'default' (tcp). iSCSI interface name that uses an iSCSI transport.", - "fsType": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#iscsi", + "fsType": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#iscsi", "readOnly": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.", } @@ -597,8 +597,8 @@ func (KeyToPath) SwaggerDoc() map[string]string { var map_Lifecycle = map[string]string{ "": "Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.", - "postStart": "PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: http://releases.k8s.io/HEAD/docs/user-guide/container-environment.md#hook-details", - "preStop": "PreStop is called immediately before a container is terminated. The container is terminated after the handler completes. The reason for termination is passed to the handler. Regardless of the outcome of the handler, the container is eventually terminated. Other management of the container blocks until the hook completes. More info: http://releases.k8s.io/HEAD/docs/user-guide/container-environment.md#hook-details", + "postStart": "PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: http://releases.k8s.io/release-1.2/docs/user-guide/container-environment.md#hook-details", + "preStop": "PreStop is called immediately before a container is terminated. The container is terminated after the handler completes. The reason for termination is passed to the handler. Regardless of the outcome of the handler, the container is eventually terminated. Other management of the container blocks until the hook completes. More info: http://releases.k8s.io/release-1.2/docs/user-guide/container-environment.md#hook-details", } func (Lifecycle) SwaggerDoc() map[string]string { @@ -607,8 +607,8 @@ func (Lifecycle) SwaggerDoc() map[string]string { var map_LimitRange = map[string]string{ "": "LimitRange sets resource usage limits for each kind of resource in a Namespace.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "Spec defines the limits enforced. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "Spec defines the limits enforced. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", } func (LimitRange) SwaggerDoc() map[string]string { @@ -631,8 +631,8 @@ func (LimitRangeItem) SwaggerDoc() map[string]string { var map_LimitRangeList = map[string]string{ "": "LimitRangeList is a list of LimitRange items.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", - "items": "Items is a list of LimitRange objects. More info: http://releases.k8s.io/HEAD/docs/design/admission_control_limit_range.md", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", + "items": "Items is a list of LimitRange objects. More info: http://releases.k8s.io/release-1.2/docs/design/admission_control_limit_range.md", } func (LimitRangeList) SwaggerDoc() map[string]string { @@ -650,7 +650,7 @@ func (LimitRangeSpec) SwaggerDoc() map[string]string { var map_List = map[string]string{ "": "List holds a list of objects, which may not be known by the server.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", "items": "List of objects", } @@ -692,7 +692,7 @@ func (LoadBalancerStatus) SwaggerDoc() map[string]string { var map_LocalObjectReference = map[string]string{ "": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.", - "name": "Name of the referent. More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names", + "name": "Name of the referent. More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#names", } func (LocalObjectReference) SwaggerDoc() map[string]string { @@ -701,9 +701,9 @@ func (LocalObjectReference) SwaggerDoc() map[string]string { var map_NFSVolumeSource = map[string]string{ "": "Represents an NFS mount that lasts the lifetime of a pod. NFS volumes do not support ownership management or SELinux relabeling.", - "server": "Server is the hostname or IP address of the NFS server. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#nfs", - "path": "Path that is exported by the NFS server. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#nfs", - "readOnly": "ReadOnly here will force the NFS export to be mounted with read-only permissions. Defaults to false. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#nfs", + "server": "Server is the hostname or IP address of the NFS server. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#nfs", + "path": "Path that is exported by the NFS server. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#nfs", + "readOnly": "ReadOnly here will force the NFS export to be mounted with read-only permissions. Defaults to false. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#nfs", } func (NFSVolumeSource) SwaggerDoc() map[string]string { @@ -712,9 +712,9 @@ func (NFSVolumeSource) SwaggerDoc() map[string]string { var map_Namespace = map[string]string{ "": "Namespace provides a scope for Names. Use of multiple namespaces is optional.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "Spec defines the behavior of the Namespace. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", - "status": "Status describes the current status of a Namespace. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "Spec defines the behavior of the Namespace. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", + "status": "Status describes the current status of a Namespace. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", } func (Namespace) SwaggerDoc() map[string]string { @@ -723,8 +723,8 @@ func (Namespace) SwaggerDoc() map[string]string { var map_NamespaceList = map[string]string{ "": "NamespaceList is a list of Namespaces.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", - "items": "Items is the list of Namespace objects in the list. More info: http://releases.k8s.io/HEAD/docs/user-guide/namespaces.md", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", + "items": "Items is the list of Namespace objects in the list. More info: http://releases.k8s.io/release-1.2/docs/user-guide/namespaces.md", } func (NamespaceList) SwaggerDoc() map[string]string { @@ -733,7 +733,7 @@ func (NamespaceList) SwaggerDoc() map[string]string { var map_NamespaceSpec = map[string]string{ "": "NamespaceSpec describes the attributes on a Namespace.", - "finalizers": "Finalizers is an opaque list of values that must be empty to permanently remove object from storage. More info: http://releases.k8s.io/HEAD/docs/design/namespaces.md#finalizers", + "finalizers": "Finalizers is an opaque list of values that must be empty to permanently remove object from storage. More info: http://releases.k8s.io/release-1.2/docs/design/namespaces.md#finalizers", } func (NamespaceSpec) SwaggerDoc() map[string]string { @@ -742,7 +742,7 @@ func (NamespaceSpec) SwaggerDoc() map[string]string { var map_NamespaceStatus = map[string]string{ "": "NamespaceStatus is information about the current status of a Namespace.", - "phase": "Phase is the current lifecycle phase of the namespace. More info: http://releases.k8s.io/HEAD/docs/design/namespaces.md#phases", + "phase": "Phase is the current lifecycle phase of the namespace. More info: http://releases.k8s.io/release-1.2/docs/design/namespaces.md#phases", } func (NamespaceStatus) SwaggerDoc() map[string]string { @@ -751,9 +751,9 @@ func (NamespaceStatus) SwaggerDoc() map[string]string { var map_Node = map[string]string{ "": "Node is a worker node in Kubernetes, formerly known as minion. Each node will have a unique identifier in the cache (i.e. in etcd).", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "Spec defines the behavior of a node. http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", - "status": "Most recently observed status of the node. Populated by the system. Read-only. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "Spec defines the behavior of a node. http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", + "status": "Most recently observed status of the node. Populated by the system. Read-only. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", } func (Node) SwaggerDoc() map[string]string { @@ -805,7 +805,7 @@ func (NodeDaemonEndpoints) SwaggerDoc() map[string]string { var map_NodeList = map[string]string{ "": "NodeList is the whole list of all Nodes which have been registered with master.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", "items": "List of nodes", } @@ -856,7 +856,7 @@ var map_NodeSpec = map[string]string{ "podCIDR": "PodCIDR represents the pod IP range assigned to the node.", "externalID": "External ID of the node assigned by some machine database (e.g. a cloud provider). Deprecated.", "providerID": "ID of the node assigned by the cloud provider in the format: ://", - "unschedulable": "Unschedulable controls node schedulability of new pods. By default, node is schedulable. More info: http://releases.k8s.io/HEAD/docs/admin/node.md#manual-node-administration\"`", + "unschedulable": "Unschedulable controls node schedulability of new pods. By default, node is schedulable. More info: http://releases.k8s.io/release-1.2/docs/admin/node.md#manual-node-administration\"`", } func (NodeSpec) SwaggerDoc() map[string]string { @@ -865,13 +865,13 @@ func (NodeSpec) SwaggerDoc() map[string]string { var map_NodeStatus = map[string]string{ "": "NodeStatus is information about the current status of a node.", - "capacity": "Capacity represents the total resources of a node. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#capacity for more details.", + "capacity": "Capacity represents the total resources of a node. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#capacity for more details.", "allocatable": "Allocatable represents the resources of a node that are available for scheduling. Defaults to Capacity.", - "phase": "NodePhase is the recently observed lifecycle phase of the node. More info: http://releases.k8s.io/HEAD/docs/admin/node.md#node-phase", - "conditions": "Conditions is an array of current observed node conditions. More info: http://releases.k8s.io/HEAD/docs/admin/node.md#node-condition", - "addresses": "List of addresses reachable to the node. Queried from cloud provider, if available. More info: http://releases.k8s.io/HEAD/docs/admin/node.md#node-addresses", + "phase": "NodePhase is the recently observed lifecycle phase of the node. More info: http://releases.k8s.io/release-1.2/docs/admin/node.md#node-phase", + "conditions": "Conditions is an array of current observed node conditions. More info: http://releases.k8s.io/release-1.2/docs/admin/node.md#node-condition", + "addresses": "List of addresses reachable to the node. Queried from cloud provider, if available. More info: http://releases.k8s.io/release-1.2/docs/admin/node.md#node-addresses", "daemonEndpoints": "Endpoints of daemons running on the Node.", - "nodeInfo": "Set of ids/uuids to uniquely identify the node. More info: http://releases.k8s.io/HEAD/docs/admin/node.md#node-info", + "nodeInfo": "Set of ids/uuids to uniquely identify the node. More info: http://releases.k8s.io/release-1.2/docs/admin/node.md#node-info", "images": "List of container images on this node", } @@ -907,18 +907,18 @@ func (ObjectFieldSelector) SwaggerDoc() map[string]string { var map_ObjectMeta = map[string]string{ "": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.", - "name": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names", - "generateName": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\n\nApplied only if Name is not specified. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#idempotency", - "namespace": "Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: http://releases.k8s.io/HEAD/docs/user-guide/namespaces.md", + "name": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#names", + "generateName": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\n\nApplied only if Name is not specified. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#idempotency", + "namespace": "Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: http://releases.k8s.io/release-1.2/docs/user-guide/namespaces.md", "selfLink": "SelfLink is a URL representing this object. Populated by the system. Read-only.", - "uid": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#uids", - "resourceVersion": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency", - "generation": "A sequence number representing a specific generation of the desired state. Currently only implemented by replication controllers. Populated by the system. Read-only.", - "creationTimestamp": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "deletionTimestamp": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource will be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field. Once set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. Once the resource is deleted in the API, the Kubelet will send a hard termination signal to the container. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", + "uid": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#uids", + "resourceVersion": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#concurrency-control-and-consistency", + "generation": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.", + "creationTimestamp": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "deletionTimestamp": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource will be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field. Once set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. Once the resource is deleted in the API, the Kubelet will send a hard termination signal to the container. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", "deletionGracePeriodSeconds": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.", - "labels": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md", - "annotations": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://releases.k8s.io/HEAD/docs/user-guide/annotations.md", + "labels": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md", + "annotations": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://releases.k8s.io/release-1.2/docs/user-guide/annotations.md", } func (ObjectMeta) SwaggerDoc() map[string]string { @@ -927,12 +927,12 @@ func (ObjectMeta) SwaggerDoc() map[string]string { var map_ObjectReference = map[string]string{ "": "ObjectReference contains enough information to let you inspect or modify the referred object.", - "kind": "Kind of the referent. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", - "namespace": "Namespace of the referent. More info: http://releases.k8s.io/HEAD/docs/user-guide/namespaces.md", - "name": "Name of the referent. More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names", - "uid": "UID of the referent. More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#uids", + "kind": "Kind of the referent. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", + "namespace": "Namespace of the referent. More info: http://releases.k8s.io/release-1.2/docs/user-guide/namespaces.md", + "name": "Name of the referent. More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#names", + "uid": "UID of the referent. More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#uids", "apiVersion": "API version of the referent.", - "resourceVersion": "Specific resourceVersion to which this reference is made, if any. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency", + "resourceVersion": "Specific resourceVersion to which this reference is made, if any. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#concurrency-control-and-consistency", "fieldPath": "If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: \"spec.containers{name}\" (where \"name\" refers to the name of the container that triggered the event) or if no container name is specified \"spec.containers[2]\" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object.", } @@ -941,10 +941,10 @@ func (ObjectReference) SwaggerDoc() map[string]string { } var map_PersistentVolume = map[string]string{ - "": "PersistentVolume (PV) is a storage resource provisioned by an administrator. It is analogous to a node. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "Spec defines a specification of a persistent volume owned by the cluster. Provisioned by an administrator. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#persistent-volumes", - "status": "Status represents the current information/status for the persistent volume. Populated by the system. Read-only. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#persistent-volumes", + "": "PersistentVolume (PV) is a storage resource provisioned by an administrator. It is analogous to a node. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "Spec defines a specification of a persistent volume owned by the cluster. Provisioned by an administrator. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#persistent-volumes", + "status": "Status represents the current information/status for the persistent volume. Populated by the system. Read-only. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#persistent-volumes", } func (PersistentVolume) SwaggerDoc() map[string]string { @@ -953,9 +953,9 @@ func (PersistentVolume) SwaggerDoc() map[string]string { var map_PersistentVolumeClaim = map[string]string{ "": "PersistentVolumeClaim is a user's request for and claim to a persistent volume", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "Spec defines the desired characteristics of a volume requested by a pod author. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#persistentvolumeclaims", - "status": "Status represents the current information/status of a persistent volume claim. Read-only. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#persistentvolumeclaims", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "Spec defines the desired characteristics of a volume requested by a pod author. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#persistentvolumeclaims", + "status": "Status represents the current information/status of a persistent volume claim. Read-only. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#persistentvolumeclaims", } func (PersistentVolumeClaim) SwaggerDoc() map[string]string { @@ -964,8 +964,8 @@ func (PersistentVolumeClaim) SwaggerDoc() map[string]string { var map_PersistentVolumeClaimList = map[string]string{ "": "PersistentVolumeClaimList is a list of PersistentVolumeClaim items.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", - "items": "A list of persistent volume claims. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#persistentvolumeclaims", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", + "items": "A list of persistent volume claims. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#persistentvolumeclaims", } func (PersistentVolumeClaimList) SwaggerDoc() map[string]string { @@ -974,8 +974,8 @@ func (PersistentVolumeClaimList) SwaggerDoc() map[string]string { var map_PersistentVolumeClaimSpec = map[string]string{ "": "PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes", - "accessModes": "AccessModes contains the desired access modes the volume should have. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#access-modes-1", - "resources": "Resources represents the minimum resources the volume should have. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#resources", + "accessModes": "AccessModes contains the desired access modes the volume should have. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#access-modes-1", + "resources": "Resources represents the minimum resources the volume should have. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#resources", "volumeName": "VolumeName is the binding reference to the PersistentVolume backing this claim.", } @@ -986,7 +986,7 @@ func (PersistentVolumeClaimSpec) SwaggerDoc() map[string]string { var map_PersistentVolumeClaimStatus = map[string]string{ "": "PersistentVolumeClaimStatus is the current status of a persistent volume claim.", "phase": "Phase represents the current phase of PersistentVolumeClaim.", - "accessModes": "AccessModes contains the actual access modes the volume backing the PVC has. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#access-modes-1", + "accessModes": "AccessModes contains the actual access modes the volume backing the PVC has. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#access-modes-1", "capacity": "Represents the actual resources of the underlying volume.", } @@ -996,7 +996,7 @@ func (PersistentVolumeClaimStatus) SwaggerDoc() map[string]string { var map_PersistentVolumeClaimVolumeSource = map[string]string{ "": "PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace. This volume finds the bound PV and mounts that volume for the pod. A PersistentVolumeClaimVolumeSource is, essentially, a wrapper around another type of volume that is owned by someone else (the system).", - "claimName": "ClaimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#persistentvolumeclaims", + "claimName": "ClaimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#persistentvolumeclaims", "readOnly": "Will force the ReadOnly setting in VolumeMounts. Default false.", } @@ -1006,8 +1006,8 @@ func (PersistentVolumeClaimVolumeSource) SwaggerDoc() map[string]string { var map_PersistentVolumeList = map[string]string{ "": "PersistentVolumeList is a list of PersistentVolume items.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", - "items": "List of persistent volumes. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", + "items": "List of persistent volumes. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md", } func (PersistentVolumeList) SwaggerDoc() map[string]string { @@ -1016,14 +1016,14 @@ func (PersistentVolumeList) SwaggerDoc() map[string]string { var map_PersistentVolumeSource = map[string]string{ "": "PersistentVolumeSource is similar to VolumeSource but meant for the administrator who creates PVs. Exactly one of its members must be set.", - "gcePersistentDisk": "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#gcepersistentdisk", - "awsElasticBlockStore": "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#awselasticblockstore", - "hostPath": "HostPath represents a directory on the host. Provisioned by a developer or tester. This is useful for single-node development and testing only! On-host storage is not supported in any way and WILL NOT WORK in a multi-node cluster. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#hostpath", - "glusterfs": "Glusterfs represents a Glusterfs volume that is attached to a host and exposed to the pod. Provisioned by an admin. More info: http://releases.k8s.io/HEAD/examples/glusterfs/README.md", - "nfs": "NFS represents an NFS mount on the host. Provisioned by an admin. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#nfs", - "rbd": "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: http://releases.k8s.io/HEAD/examples/rbd/README.md", + "gcePersistentDisk": "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#gcepersistentdisk", + "awsElasticBlockStore": "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#awselasticblockstore", + "hostPath": "HostPath represents a directory on the host. Provisioned by a developer or tester. This is useful for single-node development and testing only! On-host storage is not supported in any way and WILL NOT WORK in a multi-node cluster. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#hostpath", + "glusterfs": "Glusterfs represents a Glusterfs volume that is attached to a host and exposed to the pod. Provisioned by an admin. More info: http://releases.k8s.io/release-1.2/examples/glusterfs/README.md", + "nfs": "NFS represents an NFS mount on the host. Provisioned by an admin. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#nfs", + "rbd": "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md", "iscsi": "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin.", - "cinder": "Cinder represents a cinder volume attached and mounted on kubelets host machine More info: http://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md", + "cinder": "Cinder represents a cinder volume attached and mounted on kubelets host machine More info: http://releases.k8s.io/release-1.2/examples/mysql-cinder-pd/README.md", "cephfs": "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime", "fc": "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.", "flocker": "Flocker represents a Flocker volume attached to a kubelet's host machine and exposed to the pod for its usage. This depends on the Flocker control service being running", @@ -1037,10 +1037,10 @@ func (PersistentVolumeSource) SwaggerDoc() map[string]string { var map_PersistentVolumeSpec = map[string]string{ "": "PersistentVolumeSpec is the specification of a persistent volume.", - "capacity": "A description of the persistent volume's resources and capacity. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#capacity", - "accessModes": "AccessModes contains all ways the volume can be mounted. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#access-modes", - "claimRef": "ClaimRef is part of a bi-directional binding between PersistentVolume and PersistentVolumeClaim. Expected to be non-nil when bound. claim.VolumeName is the authoritative bind between PV and PVC. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#binding", - "persistentVolumeReclaimPolicy": "What happens to a persistent volume when released from its claim. Valid options are Retain (default) and Recycle. Recyling must be supported by the volume plugin underlying this persistent volume. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#recycling-policy", + "capacity": "A description of the persistent volume's resources and capacity. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#capacity", + "accessModes": "AccessModes contains all ways the volume can be mounted. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#access-modes", + "claimRef": "ClaimRef is part of a bi-directional binding between PersistentVolume and PersistentVolumeClaim. Expected to be non-nil when bound. claim.VolumeName is the authoritative bind between PV and PVC. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#binding", + "persistentVolumeReclaimPolicy": "What happens to a persistent volume when released from its claim. Valid options are Retain (default) and Recycle. Recyling must be supported by the volume plugin underlying this persistent volume. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#recycling-policy", } func (PersistentVolumeSpec) SwaggerDoc() map[string]string { @@ -1049,7 +1049,7 @@ func (PersistentVolumeSpec) SwaggerDoc() map[string]string { var map_PersistentVolumeStatus = map[string]string{ "": "PersistentVolumeStatus is the current status of a persistent volume.", - "phase": "Phase indicates if a volume is available, bound to a claim, or released by a claim. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#phase", + "phase": "Phase indicates if a volume is available, bound to a claim, or released by a claim. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#phase", "message": "A human-readable message indicating details about why the volume is in this state.", "reason": "Reason is a brief CamelCase string that describes any failure and is meant for machine parsing and tidy display in the CLI.", } @@ -1060,9 +1060,9 @@ func (PersistentVolumeStatus) SwaggerDoc() map[string]string { var map_Pod = map[string]string{ "": "Pod is a collection of containers that can run on a host. This resource is created by clients and scheduled onto hosts.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "Specification of the desired behavior of the pod. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", - "status": "Most recently observed status of the pod. This data may not be up to date. Populated by the system. Read-only. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "Specification of the desired behavior of the pod. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", + "status": "Most recently observed status of the pod. This data may not be up to date. Populated by the system. Read-only. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", } func (Pod) SwaggerDoc() map[string]string { @@ -1084,8 +1084,8 @@ func (PodAttachOptions) SwaggerDoc() map[string]string { var map_PodCondition = map[string]string{ "": "PodCondition contains details for the current condition of this pod.", - "type": "Type is the type of the condition. Currently only Ready. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions", - "status": "Status is the status of the condition. Can be True, False, Unknown. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions", + "type": "Type is the type of the condition. Currently only Ready. More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#pod-conditions", + "status": "Status is the status of the condition. Can be True, False, Unknown. More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#pod-conditions", "lastProbeTime": "Last time we probed the condition.", "lastTransitionTime": "Last time the condition transitioned from one status to another.", "reason": "Unique, one-word, CamelCase reason for the condition's last transition.", @@ -1112,8 +1112,8 @@ func (PodExecOptions) SwaggerDoc() map[string]string { var map_PodList = map[string]string{ "": "PodList is a list of Pods.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", - "items": "List of pods. More info: http://releases.k8s.io/HEAD/docs/user-guide/pods.md", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", + "items": "List of pods. More info: http://releases.k8s.io/release-1.2/docs/user-guide/pods.md", } func (PodList) SwaggerDoc() map[string]string { @@ -1160,21 +1160,21 @@ func (PodSecurityContext) SwaggerDoc() map[string]string { var map_PodSpec = map[string]string{ "": "PodSpec is a description of a pod.", - "volumes": "List of volumes that can be mounted by containers belonging to the pod. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md", - "containers": "List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated. More info: http://releases.k8s.io/HEAD/docs/user-guide/containers.md", - "restartPolicy": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#restartpolicy", + "volumes": "List of volumes that can be mounted by containers belonging to the pod. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md", + "containers": "List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated. More info: http://releases.k8s.io/release-1.2/docs/user-guide/containers.md", + "restartPolicy": "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#restartpolicy", "terminationGracePeriodSeconds": "Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period will be used instead. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. Defaults to 30 seconds.", "activeDeadlineSeconds": "Optional duration in seconds the pod may be active on the node relative to StartTime before the system will actively try to mark it failed and kill associated containers. Value must be a positive integer.", "dnsPolicy": "Set DNS policy for containers within the pod. One of 'ClusterFirst' or 'Default'. Defaults to \"ClusterFirst\".", - "nodeSelector": "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: http://releases.k8s.io/HEAD/docs/user-guide/node-selection/README.md", - "serviceAccountName": "ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: http://releases.k8s.io/HEAD/docs/design/service_accounts.md", + "nodeSelector": "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: http://releases.k8s.io/release-1.2/docs/user-guide/node-selection/README.md", + "serviceAccountName": "ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: http://releases.k8s.io/release-1.2/docs/design/service_accounts.md", "serviceAccount": "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", "nodeName": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", "hostNetwork": "Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false.", "hostPID": "Use the host's pid namespace. Optional: Default to false.", "hostIPC": "Use the host's ipc namespace. Optional: Default to false.", "securityContext": "SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field.", - "imagePullSecrets": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: http://releases.k8s.io/HEAD/docs/user-guide/images.md#specifying-imagepullsecrets-on-a-pod", + "imagePullSecrets": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: http://releases.k8s.io/release-1.2/docs/user-guide/images.md#specifying-imagepullsecrets-on-a-pod", } func (PodSpec) SwaggerDoc() map[string]string { @@ -1183,14 +1183,14 @@ func (PodSpec) SwaggerDoc() map[string]string { var map_PodStatus = map[string]string{ "": "PodStatus represents information about the status of a pod. Status may trail the actual state of a system.", - "phase": "Current condition of the pod. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-phase", - "conditions": "Current service state of pod. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#pod-conditions", + "phase": "Current condition of the pod. More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#pod-phase", + "conditions": "Current service state of pod. More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#pod-conditions", "message": "A human readable message indicating details about why the pod is in this condition.", "reason": "A brief CamelCase message indicating details about why the pod is in this state. e.g. 'OutOfDisk'", "hostIP": "IP address of the host to which the pod is assigned. Empty if not yet scheduled.", "podIP": "IP address allocated to the pod. Routable at least within the cluster. Empty if not yet allocated.", "startTime": "RFC 3339 date and time at which the object was acknowledged by the Kubelet. This is before the Kubelet pulled the container image(s) for the pod.", - "containerStatuses": "The list has one entry per container in the manifest. Each entry is currently the output of `docker inspect`. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-statuses", + "containerStatuses": "The list has one entry per container in the manifest. Each entry is currently the output of `docker inspect`. More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#container-statuses", } func (PodStatus) SwaggerDoc() map[string]string { @@ -1199,8 +1199,8 @@ func (PodStatus) SwaggerDoc() map[string]string { var map_PodStatusResult = map[string]string{ "": "PodStatusResult is a wrapper for PodStatus returned by kubelet that can be encode/decoded", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "status": "Most recently observed status of the pod. This data may not be up to date. Populated by the system. Read-only. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "status": "Most recently observed status of the pod. This data may not be up to date. Populated by the system. Read-only. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", } func (PodStatusResult) SwaggerDoc() map[string]string { @@ -1209,8 +1209,8 @@ func (PodStatusResult) SwaggerDoc() map[string]string { var map_PodTemplate = map[string]string{ "": "PodTemplate describes a template for creating copies of a predefined pod.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "template": "Template defines the pods that will be created from this pod template. http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "template": "Template defines the pods that will be created from this pod template. http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", } func (PodTemplate) SwaggerDoc() map[string]string { @@ -1219,7 +1219,7 @@ func (PodTemplate) SwaggerDoc() map[string]string { var map_PodTemplateList = map[string]string{ "": "PodTemplateList is a list of PodTemplates.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", "items": "List of pod templates", } @@ -1229,8 +1229,8 @@ func (PodTemplateList) SwaggerDoc() map[string]string { var map_PodTemplateSpec = map[string]string{ "": "PodTemplateSpec describes the data a pod should have when created from a template", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "Specification of the desired behavior of the pod. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "Specification of the desired behavior of the pod. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", } func (PodTemplateSpec) SwaggerDoc() map[string]string { @@ -1249,8 +1249,8 @@ func (PreferredSchedulingTerm) SwaggerDoc() map[string]string { var map_Probe = map[string]string{ "": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.", - "initialDelaySeconds": "Number of seconds after the container has started before liveness probes are initiated. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes", - "timeoutSeconds": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes", + "initialDelaySeconds": "Number of seconds after the container has started before liveness probes are initiated. More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#container-probes", + "timeoutSeconds": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: http://releases.k8s.io/release-1.2/docs/user-guide/pod-states.md#container-probes", "periodSeconds": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.", "successThreshold": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.", "failureThreshold": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.", @@ -1262,14 +1262,14 @@ func (Probe) SwaggerDoc() map[string]string { var map_RBDVolumeSource = map[string]string{ "": "Represents a Rados Block Device mount that lasts the lifetime of a pod. RBD volumes support ownership management and SELinux relabeling.", - "monitors": "A collection of Ceph monitors. More info: http://releases.k8s.io/HEAD/examples/rbd/README.md#how-to-use-it", - "image": "The rados image name. More info: http://releases.k8s.io/HEAD/examples/rbd/README.md#how-to-use-it", - "fsType": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#rbd", - "pool": "The rados pool name. Default is rbd. More info: http://releases.k8s.io/HEAD/examples/rbd/README.md#how-to-use-it.", - "user": "The rados user name. Default is admin. More info: http://releases.k8s.io/HEAD/examples/rbd/README.md#how-to-use-it", - "keyring": "Keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: http://releases.k8s.io/HEAD/examples/rbd/README.md#how-to-use-it", - "secretRef": "SecretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is empty. More info: http://releases.k8s.io/HEAD/examples/rbd/README.md#how-to-use-it", - "readOnly": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: http://releases.k8s.io/HEAD/examples/rbd/README.md#how-to-use-it", + "monitors": "A collection of Ceph monitors. More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md#how-to-use-it", + "image": "The rados image name. More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md#how-to-use-it", + "fsType": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#rbd", + "pool": "The rados pool name. Default is rbd. More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md#how-to-use-it.", + "user": "The rados user name. Default is admin. More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md#how-to-use-it", + "keyring": "Keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md#how-to-use-it", + "secretRef": "SecretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is empty. More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md#how-to-use-it", + "readOnly": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md#how-to-use-it", } func (RBDVolumeSource) SwaggerDoc() map[string]string { @@ -1278,7 +1278,7 @@ func (RBDVolumeSource) SwaggerDoc() map[string]string { var map_RangeAllocation = map[string]string{ "": "RangeAllocation is not a public type.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", "range": "Range is string that identifies the range represented by 'data'.", "data": "Data is a bit array containing all allocated addresses in the previous segment.", } @@ -1289,9 +1289,9 @@ func (RangeAllocation) SwaggerDoc() map[string]string { var map_ReplicationController = map[string]string{ "": "ReplicationController represents the configuration of a replication controller.", - "metadata": "If the Labels of a ReplicationController are empty, they are defaulted to be the same as the Pod(s) that the replication controller manages. Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "Spec defines the specification of the desired behavior of the replication controller. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", - "status": "Status is the most recently observed status of the replication controller. This data may be out of date by some window of time. Populated by the system. Read-only. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", + "metadata": "If the Labels of a ReplicationController are empty, they are defaulted to be the same as the Pod(s) that the replication controller manages. Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "Spec defines the specification of the desired behavior of the replication controller. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", + "status": "Status is the most recently observed status of the replication controller. This data may be out of date by some window of time. Populated by the system. Read-only. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", } func (ReplicationController) SwaggerDoc() map[string]string { @@ -1300,8 +1300,8 @@ func (ReplicationController) SwaggerDoc() map[string]string { var map_ReplicationControllerList = map[string]string{ "": "ReplicationControllerList is a collection of replication controllers.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", - "items": "List of replication controllers. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", + "items": "List of replication controllers. More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md", } func (ReplicationControllerList) SwaggerDoc() map[string]string { @@ -1310,9 +1310,9 @@ func (ReplicationControllerList) SwaggerDoc() map[string]string { var map_ReplicationControllerSpec = map[string]string{ "": "ReplicationControllerSpec is the specification of a replication controller.", - "replicas": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller", - "selector": "Selector is a label query over pods that should match the Replicas count. If Selector is empty, it is defaulted to the labels present on the Pod template. Label keys and values that must match in order to be controlled by this replication controller, if empty defaulted to labels on Pod template. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors", - "template": "Template is the object that describes the pod that will be created if insufficient replicas are detected. This takes precedence over a TemplateRef. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#pod-template", + "replicas": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md#what-is-a-replication-controller", + "selector": "Selector is a label query over pods that should match the Replicas count. If Selector is empty, it is defaulted to the labels present on the Pod template. Label keys and values that must match in order to be controlled by this replication controller, if empty defaulted to labels on Pod template. More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors", + "template": "Template is the object that describes the pod that will be created if insufficient replicas are detected. This takes precedence over a TemplateRef. More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md#pod-template", } func (ReplicationControllerSpec) SwaggerDoc() map[string]string { @@ -1320,9 +1320,10 @@ func (ReplicationControllerSpec) SwaggerDoc() map[string]string { } var map_ReplicationControllerStatus = map[string]string{ - "": "ReplicationControllerStatus represents the current status of a replication controller.", - "replicas": "Replicas is the most recently oberved number of replicas. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller", - "observedGeneration": "ObservedGeneration reflects the generation of the most recently observed replication controller.", + "": "ReplicationControllerStatus represents the current status of a replication controller.", + "replicas": "Replicas is the most recently oberved number of replicas. More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md#what-is-a-replication-controller", + "fullyLabeledReplicas": "The number of pods that have labels matching the labels of the pod template of the replication controller.", + "observedGeneration": "ObservedGeneration reflects the generation of the most recently observed replication controller.", } func (ReplicationControllerStatus) SwaggerDoc() map[string]string { @@ -1331,9 +1332,9 @@ func (ReplicationControllerStatus) SwaggerDoc() map[string]string { var map_ResourceQuota = map[string]string{ "": "ResourceQuota sets aggregate quota restrictions enforced per namespace", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "Spec defines the desired quota. http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", - "status": "Status defines the actual enforced quota and its current usage. http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "Spec defines the desired quota. http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", + "status": "Status defines the actual enforced quota and its current usage. http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", } func (ResourceQuota) SwaggerDoc() map[string]string { @@ -1342,8 +1343,8 @@ func (ResourceQuota) SwaggerDoc() map[string]string { var map_ResourceQuotaList = map[string]string{ "": "ResourceQuotaList is a list of ResourceQuota items.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", - "items": "Items is a list of ResourceQuota objects. More info: http://releases.k8s.io/HEAD/docs/design/admission_control_resource_quota.md#admissioncontrol-plugin-resourcequota", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", + "items": "Items is a list of ResourceQuota objects. More info: http://releases.k8s.io/release-1.2/docs/design/admission_control_resource_quota.md#admissioncontrol-plugin-resourcequota", } func (ResourceQuotaList) SwaggerDoc() map[string]string { @@ -1351,8 +1352,9 @@ func (ResourceQuotaList) SwaggerDoc() map[string]string { } var map_ResourceQuotaSpec = map[string]string{ - "": "ResourceQuotaSpec defines the desired hard limits to enforce for Quota.", - "hard": "Hard is the set of desired hard limits for each named resource. More info: http://releases.k8s.io/HEAD/docs/design/admission_control_resource_quota.md#admissioncontrol-plugin-resourcequota", + "": "ResourceQuotaSpec defines the desired hard limits to enforce for Quota.", + "hard": "Hard is the set of desired hard limits for each named resource. More info: http://releases.k8s.io/release-1.2/docs/design/admission_control_resource_quota.md#admissioncontrol-plugin-resourcequota", + "scopes": "A collection of filters that must match each object tracked by a quota. If not specified, the quota matches all objects.", } func (ResourceQuotaSpec) SwaggerDoc() map[string]string { @@ -1361,7 +1363,7 @@ func (ResourceQuotaSpec) SwaggerDoc() map[string]string { var map_ResourceQuotaStatus = map[string]string{ "": "ResourceQuotaStatus defines the enforced hard limits and observed use.", - "hard": "Hard is the set of enforced hard limits for each named resource. More info: http://releases.k8s.io/HEAD/docs/design/admission_control_resource_quota.md#admissioncontrol-plugin-resourcequota", + "hard": "Hard is the set of enforced hard limits for each named resource. More info: http://releases.k8s.io/release-1.2/docs/design/admission_control_resource_quota.md#admissioncontrol-plugin-resourcequota", "used": "Used is the current observed total usage of the resource in the namespace.", } @@ -1371,8 +1373,8 @@ func (ResourceQuotaStatus) SwaggerDoc() map[string]string { var map_ResourceRequirements = map[string]string{ "": "ResourceRequirements describes the compute resource requirements.", - "limits": "Limits describes the maximum amount of compute resources allowed. More info: http://releases.k8s.io/HEAD/docs/design/resources.md#resource-specifications", - "requests": "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: http://releases.k8s.io/HEAD/docs/design/resources.md#resource-specifications", + "limits": "Limits describes the maximum amount of compute resources allowed. More info: http://releases.k8s.io/release-1.2/docs/design/resources.md#resource-specifications", + "requests": "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: http://releases.k8s.io/release-1.2/docs/design/resources.md#resource-specifications", } func (ResourceRequirements) SwaggerDoc() map[string]string { @@ -1393,7 +1395,7 @@ func (SELinuxOptions) SwaggerDoc() map[string]string { var map_Secret = map[string]string{ "": "Secret holds secret data of a certain type. The total bytes of the values in the Data field must be less than MaxSecretSize bytes.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", "data": "Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN or leading dot followed by valid DNS_SUBDOMAIN. The serialized form of the secret data is a base64 encoded string, representing the arbitrary (possibly non-string) data value here. Described in https://tools.ietf.org/html/rfc4648#section-4", "type": "Used to facilitate programmatic handling of secret data.", } @@ -1413,8 +1415,8 @@ func (SecretKeySelector) SwaggerDoc() map[string]string { var map_SecretList = map[string]string{ "": "SecretList is a list of Secret.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", - "items": "Items is a list of secret objects. More info: http://releases.k8s.io/HEAD/docs/user-guide/secrets.md", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", + "items": "Items is a list of secret objects. More info: http://releases.k8s.io/release-1.2/docs/user-guide/secrets.md", } func (SecretList) SwaggerDoc() map[string]string { @@ -1423,7 +1425,7 @@ func (SecretList) SwaggerDoc() map[string]string { var map_SecretVolumeSource = map[string]string{ "": "Adapts a Secret into a volume.\n\nThe contents of the target Secret's Data field will be presented in a volume as files using the keys in the Data field as the file names. Secret volumes support ownership management and SELinux relabeling.", - "secretName": "Name of the secret in the pod's namespace to use. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#secrets", + "secretName": "Name of the secret in the pod's namespace to use. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#secrets", } func (SecretVolumeSource) SwaggerDoc() map[string]string { @@ -1455,9 +1457,9 @@ func (SerializedReference) SwaggerDoc() map[string]string { var map_Service = map[string]string{ "": "Service is a named abstraction of software service (for example, mysql) consisting of local port (for example 3306) that the proxy listens on, and the selector that determines which pods will answer requests sent through the proxy.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "Spec defines the behavior of a service. http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", - "status": "Most recently observed status of the service. Populated by the system. Read-only. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "Spec defines the behavior of a service. http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", + "status": "Most recently observed status of the service. Populated by the system. Read-only. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", } func (Service) SwaggerDoc() map[string]string { @@ -1466,9 +1468,9 @@ func (Service) SwaggerDoc() map[string]string { var map_ServiceAccount = map[string]string{ "": "ServiceAccount binds together: * a name, understood by users, and perhaps by peripheral systems, for an identity * a principal that can be authenticated and authorized * a set of secrets", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "secrets": "Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount. More info: http://releases.k8s.io/HEAD/docs/user-guide/secrets.md", - "imagePullSecrets": "ImagePullSecrets is a list of references to secrets in the same namespace to use for pulling any images in pods that reference this ServiceAccount. ImagePullSecrets are distinct from Secrets because Secrets can be mounted in the pod, but ImagePullSecrets are only accessed by the kubelet. More info: http://releases.k8s.io/HEAD/docs/user-guide/secrets.md#manually-specifying-an-imagepullsecret", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "secrets": "Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount. More info: http://releases.k8s.io/release-1.2/docs/user-guide/secrets.md", + "imagePullSecrets": "ImagePullSecrets is a list of references to secrets in the same namespace to use for pulling any images in pods that reference this ServiceAccount. ImagePullSecrets are distinct from Secrets because Secrets can be mounted in the pod, but ImagePullSecrets are only accessed by the kubelet. More info: http://releases.k8s.io/release-1.2/docs/user-guide/secrets.md#manually-specifying-an-imagepullsecret", } func (ServiceAccount) SwaggerDoc() map[string]string { @@ -1477,8 +1479,8 @@ func (ServiceAccount) SwaggerDoc() map[string]string { var map_ServiceAccountList = map[string]string{ "": "ServiceAccountList is a list of ServiceAccount objects", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", - "items": "List of ServiceAccounts. More info: http://releases.k8s.io/HEAD/docs/design/service_accounts.md#service-accounts", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", + "items": "List of ServiceAccounts. More info: http://releases.k8s.io/release-1.2/docs/design/service_accounts.md#service-accounts", } func (ServiceAccountList) SwaggerDoc() map[string]string { @@ -1487,7 +1489,7 @@ func (ServiceAccountList) SwaggerDoc() map[string]string { var map_ServiceList = map[string]string{ "": "ServiceList holds a list of services.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", "items": "List of services", } @@ -1500,8 +1502,8 @@ var map_ServicePort = map[string]string{ "name": "The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. This maps to the 'Name' field in EndpointPort objects. Optional if only one ServicePort is defined on this service.", "protocol": "The IP protocol for this port. Supports \"TCP\" and \"UDP\". Default is TCP.", "port": "The port that will be exposed by this service.", - "targetPort": "Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of Port is used (an identity map). Defaults to the service port. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#defining-a-service", - "nodePort": "The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#type--nodeport", + "targetPort": "Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: http://releases.k8s.io/release-1.2/docs/user-guide/services.md#defining-a-service", + "nodePort": "The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: http://releases.k8s.io/release-1.2/docs/user-guide/services.md#type--nodeport", } func (ServicePort) SwaggerDoc() map[string]string { @@ -1519,13 +1521,13 @@ func (ServiceProxyOptions) SwaggerDoc() map[string]string { var map_ServiceSpec = map[string]string{ "": "ServiceSpec describes the attributes that a user creates on a service.", - "ports": "The list of ports that are exposed by this service. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#virtual-ips-and-service-proxies", - "selector": "This service will route traffic to pods having labels matching this selector. Label keys and values that must match in order to receive traffic for this service. If empty, all pods are selected, if not specified, endpoints must be manually specified. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#overview", - "clusterIP": "ClusterIP is usually assigned by the master and is the IP address of the service. If specified, it will be allocated to the service if it is unused or else creation of the service will fail. Valid values are None, empty string (\"\"), or a valid IP address. 'None' can be specified for a headless service when proxying is not required. Cannot be updated. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#virtual-ips-and-service-proxies", - "type": "Type of exposed service. Must be ClusterIP, NodePort, or LoadBalancer. Defaults to ClusterIP. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#external-services", + "ports": "The list of ports that are exposed by this service. More info: http://releases.k8s.io/release-1.2/docs/user-guide/services.md#virtual-ips-and-service-proxies", + "selector": "This service will route traffic to pods having labels matching this selector. Label keys and values that must match in order to receive traffic for this service. If empty, all pods are selected, if not specified, endpoints must be manually specified. More info: http://releases.k8s.io/release-1.2/docs/user-guide/services.md#overview", + "clusterIP": "ClusterIP is usually assigned by the master and is the IP address of the service. If specified, it will be allocated to the service if it is unused or else creation of the service will fail. Valid values are None, empty string (\"\"), or a valid IP address. 'None' can be specified for a headless service when proxying is not required. Cannot be updated. More info: http://releases.k8s.io/release-1.2/docs/user-guide/services.md#virtual-ips-and-service-proxies", + "type": "Type of exposed service. Must be ClusterIP, NodePort, or LoadBalancer. Defaults to ClusterIP. More info: http://releases.k8s.io/release-1.2/docs/user-guide/services.md#external-services", "externalIPs": "externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system. A previous form of this functionality exists as the deprecatedPublicIPs field. When using this field, callers should also clear the deprecatedPublicIPs field.", "deprecatedPublicIPs": "deprecatedPublicIPs is deprecated and replaced by the externalIPs field with almost the exact same semantics. This field is retained in the v1 API for compatibility until at least 8/20/2016. It will be removed from any new API revisions. If both deprecatedPublicIPs *and* externalIPs are set, deprecatedPublicIPs is used.", - "sessionAffinity": "Supports \"ClientIP\" and \"None\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#virtual-ips-and-service-proxies", + "sessionAffinity": "Supports \"ClientIP\" and \"None\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: http://releases.k8s.io/release-1.2/docs/user-guide/services.md#virtual-ips-and-service-proxies", "loadBalancerIP": "Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.", } @@ -1553,7 +1555,7 @@ func (TCPSocketAction) SwaggerDoc() map[string]string { var map_Volume = map[string]string{ "": "Volume represents a named volume in a pod that may be accessed by any container in the pod.", - "name": "Volume's name. Must be a DNS_LABEL and unique within the pod. More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names", + "name": "Volume's name. Must be a DNS_LABEL and unique within the pod. More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#names", } func (Volume) SwaggerDoc() map[string]string { @@ -1564,7 +1566,7 @@ var map_VolumeMount = map[string]string{ "": "VolumeMount describes a mounting of a Volume within a container.", "name": "This must match the Name of a Volume.", "readOnly": "Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.", - "mountPath": "Path within the container at which the volume should be mounted.", + "mountPath": "Path within the container at which the volume should be mounted. Must not contain ':'.", } func (VolumeMount) SwaggerDoc() map[string]string { @@ -1573,19 +1575,19 @@ func (VolumeMount) SwaggerDoc() map[string]string { var map_VolumeSource = map[string]string{ "": "Represents the source of a volume to mount. Only one of its members may be specified.", - "hostPath": "HostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#hostpath", - "emptyDir": "EmptyDir represents a temporary directory that shares a pod's lifetime. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#emptydir", - "gcePersistentDisk": "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#gcepersistentdisk", - "awsElasticBlockStore": "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#awselasticblockstore", + "hostPath": "HostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#hostpath", + "emptyDir": "EmptyDir represents a temporary directory that shares a pod's lifetime. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#emptydir", + "gcePersistentDisk": "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#gcepersistentdisk", + "awsElasticBlockStore": "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#awselasticblockstore", "gitRepo": "GitRepo represents a git repository at a particular revision.", - "secret": "Secret represents a secret that should populate this volume. More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#secrets", - "nfs": "NFS represents an NFS mount on the host that shares a pod's lifetime More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#nfs", - "iscsi": "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: http://releases.k8s.io/HEAD/examples/iscsi/README.md", - "glusterfs": "Glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: http://releases.k8s.io/HEAD/examples/glusterfs/README.md", - "persistentVolumeClaim": "PersistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: http://releases.k8s.io/HEAD/docs/user-guide/persistent-volumes.md#persistentvolumeclaims", - "rbd": "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: http://releases.k8s.io/HEAD/examples/rbd/README.md", + "secret": "Secret represents a secret that should populate this volume. More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#secrets", + "nfs": "NFS represents an NFS mount on the host that shares a pod's lifetime More info: http://releases.k8s.io/release-1.2/docs/user-guide/volumes.md#nfs", + "iscsi": "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: http://releases.k8s.io/release-1.2/examples/iscsi/README.md", + "glusterfs": "Glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: http://releases.k8s.io/release-1.2/examples/glusterfs/README.md", + "persistentVolumeClaim": "PersistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: http://releases.k8s.io/release-1.2/docs/user-guide/persistent-volumes.md#persistentvolumeclaims", + "rbd": "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: http://releases.k8s.io/release-1.2/examples/rbd/README.md", "flexVolume": "FlexVolume represents a generic volume resource that is provisioned/attached using a exec based plugin. This is an alpha feature and may change in future.", - "cinder": "Cinder represents a cinder volume attached and mounted on kubelets host machine More info: http://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md", + "cinder": "Cinder represents a cinder volume attached and mounted on kubelets host machine More info: http://releases.k8s.io/release-1.2/examples/mysql-cinder-pd/README.md", "cephfs": "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime", "flocker": "Flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running", "downwardAPI": "DownwardAPI represents downward API about the pod that should populate this volume", diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/events_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/events_test.go deleted file mode 100644 index a2910d064..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/events_test.go +++ /dev/null @@ -1,60 +0,0 @@ -/* -Copyright 2014 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 validation - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" -) - -func TestValidateEvent(t *testing.T) { - table := []struct { - *api.Event - valid bool - }{ - { - &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "test1", - Namespace: "foo", - }, - InvolvedObject: api.ObjectReference{ - Namespace: "bar", - }, - }, - false, - }, { - &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "test1", - Namespace: "aoeu-_-aoeu", - }, - InvolvedObject: api.ObjectReference{ - Namespace: "aoeu-_-aoeu", - }, - }, - false, - }, - } - - for _, item := range table { - if e, a := item.valid, len(ValidateEvent(item.Event)) == 0; e != a { - t.Errorf("%v: expected %v, got %v", item.Event.Name, e, a) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/name_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/name_test.go deleted file mode 100644 index b8687cdcc..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/name_test.go +++ /dev/null @@ -1,149 +0,0 @@ -/* -Copyright 2015 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 validation - -import ( - "strings" - "testing" -) - -func TestValidatePathSegmentName(t *testing.T) { - testcases := map[string]struct { - Name string - Prefix bool - ExpectedOK bool - ExpectedMsg string - }{ - "empty": { - Name: "", - Prefix: false, - ExpectedOK: true, - ExpectedMsg: "", - }, - "empty,prefix": { - Name: "", - Prefix: true, - ExpectedOK: true, - ExpectedMsg: "", - }, - - "valid": { - Name: "foo.bar.baz", - Prefix: false, - ExpectedOK: true, - ExpectedMsg: "", - }, - "valid,prefix": { - Name: "foo.bar.baz", - Prefix: true, - ExpectedOK: true, - ExpectedMsg: "", - }, - - // Make sure mixed case, non DNS subdomain characters are tolerated - "valid complex": { - Name: "sha256:ABCDEF012345@ABCDEF012345", - Prefix: false, - ExpectedOK: true, - ExpectedMsg: "", - }, - // Make sure non-ascii characters are tolerated - "valid extended charset": { - Name: "Iñtërnâtiônàlizætiøn", - Prefix: false, - ExpectedOK: true, - ExpectedMsg: "", - }, - - "dot": { - Name: ".", - Prefix: false, - ExpectedOK: false, - ExpectedMsg: ".", - }, - "dot leading": { - Name: ".test", - Prefix: false, - ExpectedOK: true, - ExpectedMsg: "", - }, - "dot,prefix": { - Name: ".", - Prefix: true, - ExpectedOK: true, // allowed because a suffix could make it valid - ExpectedMsg: "", - }, - - "dot dot": { - Name: "..", - Prefix: false, - ExpectedOK: false, - ExpectedMsg: "..", - }, - "dot dot leading": { - Name: "..test", - Prefix: false, - ExpectedOK: true, - ExpectedMsg: "", - }, - "dot dot,prefix": { - Name: "..", - Prefix: true, - ExpectedOK: true, // allowed because a suffix could make it valid - ExpectedMsg: "", - }, - - "slash": { - Name: "foo/bar", - Prefix: false, - ExpectedOK: false, - ExpectedMsg: "/", - }, - "slash,prefix": { - Name: "foo/bar", - Prefix: true, - ExpectedOK: false, - ExpectedMsg: "/", - }, - - "percent": { - Name: "foo%bar", - Prefix: false, - ExpectedOK: false, - ExpectedMsg: "%", - }, - "percent,prefix": { - Name: "foo%bar", - Prefix: true, - ExpectedOK: false, - ExpectedMsg: "%", - }, - } - - for k, tc := range testcases { - ok, msg := ValidatePathSegmentName(tc.Name, tc.Prefix) - if ok != tc.ExpectedOK { - t.Errorf("%s: expected ok=%v, got %v", k, tc.ExpectedOK, ok) - } - if len(tc.ExpectedMsg) == 0 && len(msg) > 0 { - t.Errorf("%s: expected no message, got %v", k, msg) - } - if len(tc.ExpectedMsg) > 0 && !strings.Contains(msg, tc.ExpectedMsg) { - t.Errorf("%s: expected message containing %q, got %v", k, tc.ExpectedMsg, msg) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/schema.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/schema.go index 24c1e2619..6c55bc47f 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/schema.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/schema.go @@ -44,6 +44,14 @@ func NewInvalidTypeError(expected reflect.Kind, observed reflect.Kind, fieldName return &InvalidTypeError{expected, observed, fieldName} } +// TypeNotFoundError is returned when specified type +// can not found in schema +type TypeNotFoundError string + +func (tnfe TypeNotFoundError) Error() string { + return fmt.Sprintf("couldn't find type: %s", string(tnfe)) +} + // Schema is an interface that knows how to validate an API object serialized to a byte array. type Schema interface { ValidateBytes(data []byte) error @@ -164,7 +172,7 @@ func (s *SwaggerSchema) ValidateObject(obj interface{}, fieldName, typeName stri models := s.api.Models model, ok := models.At(typeName) if !ok { - return append(allErrs, fmt.Errorf("couldn't find type: %s", typeName)) + return append(allErrs, TypeNotFoundError(typeName)) } properties := model.Properties if len(properties.List) == 0 { diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/schema_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/schema_test.go deleted file mode 100644 index be6fc83e7..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/schema_test.go +++ /dev/null @@ -1,156 +0,0 @@ -/* -Copyright 2014 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 validation - -import ( - "io/ioutil" - "math/rand" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - apitesting "k8s.io/kubernetes/pkg/api/testing" - "k8s.io/kubernetes/pkg/runtime" -) - -func readPod(filename string) (string, error) { - data, err := ioutil.ReadFile("testdata/" + testapi.Default.GroupVersion().Version + "/" + filename) - if err != nil { - return "", err - } - return string(data), nil -} - -func loadSchemaForTest() (Schema, error) { - // TODO this path is broken - pathToSwaggerSpec := "../../../api/swagger-spec/" + testapi.Default.GroupVersion().Version + ".json" - data, err := ioutil.ReadFile(pathToSwaggerSpec) - if err != nil { - return nil, err - } - return NewSwaggerSchemaFromBytes(data) -} - -func TestLoad(t *testing.T) { - _, err := loadSchemaForTest() - if err != nil { - t.Errorf("Failed to load: %v", err) - } -} - -func TestValidateOk(t *testing.T) { - schema, err := loadSchemaForTest() - if err != nil { - t.Errorf("Failed to load: %v", err) - } - tests := []struct { - obj runtime.Object - typeName string - }{ - {obj: &api.Pod{}}, - {obj: &api.Service{}}, - {obj: &api.ReplicationController{}}, - } - - seed := rand.Int63() - apiObjectFuzzer := apitesting.FuzzerFor(nil, testapi.Default.InternalGroupVersion(), rand.NewSource(seed)) - for i := 0; i < 5; i++ { - for _, test := range tests { - testObj := test.obj - apiObjectFuzzer.Fuzz(testObj) - data, err := runtime.Encode(testapi.Default.Codec(), testObj) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - err = schema.ValidateBytes(data) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - } - } -} - -func TestInvalid(t *testing.T) { - schema, err := loadSchemaForTest() - if err != nil { - t.Errorf("Failed to load: %v", err) - } - tests := []string{ - "invalidPod1.json", // command is a string, instead of []string. - "invalidPod2.json", // hostPort if of type string, instead of int. - "invalidPod3.json", // volumes is not an array of objects. - "invalidPod.yaml", // command is a string, instead of []string. - } - for _, test := range tests { - pod, err := readPod(test) - if err != nil { - t.Errorf("could not read file: %s", pod) - } - err = schema.ValidateBytes([]byte(pod)) - if err == nil { - t.Errorf("unexpected non-error, err: %s for pod: %s", err, pod) - } - } -} - -func TestValid(t *testing.T) { - schema, err := loadSchemaForTest() - if err != nil { - t.Errorf("Failed to load: %v", err) - } - tests := []string{ - "validPod.yaml", - } - for _, test := range tests { - pod, err := readPod(test) - if err != nil { - t.Errorf("could not read file: %s", test) - } - err = schema.ValidateBytes([]byte(pod)) - if err != nil { - t.Errorf("unexpected error %s, for pod %s", err, pod) - } - } -} - -func TestVersionRegex(t *testing.T) { - testCases := []struct { - typeName string - match bool - }{ - { - typeName: "v1.Binding", - match: true, - }, - { - typeName: "v1beta1.Binding", - match: true, - }, - { - typeName: "Binding", - match: false, - }, - } - for _, test := range testCases { - if versionRegexp.MatchString(test.typeName) && !test.match { - t.Errorf("unexpected error: expect %s not to match the regular expression", test.typeName) - } - if !versionRegexp.MatchString(test.typeName) && test.match { - t.Errorf("unexpected error: expect %s to match the regular expression", test.typeName) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod.yaml b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod.yaml deleted file mode 100644 index 059b2dc7b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - labels: - name: redis-master - name: name -spec: - containers: - - args: "this is a bad command" - image: redis - name: master diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod1.json b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod1.json deleted file mode 100644 index df256426c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod1.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "kind": "Pod", - "apiVersion": "v1", - "metadata": { - "name": "name", - "labels": { - "name": "redis-master" - } - }, - "spec": { - "containers": [ - { - "name": "master", - "image": "redis", - "args": "this is a bad command" - } - ] - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod2.json b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod2.json deleted file mode 100644 index 97f02ac13..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod2.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "kind": "Pod", - "apiVersion": "v1", - "metadata": { - "name": "apache-php", - "labels": { - "name": "apache-php" - } - }, - "spec": { - "volumes": [{ - "name": "shared-disk" - }], - "containers": [ - { - "name": "apache-php", - "image": "php:5.6.2-apache", - "ports": [ - { - "name": "apache", - "hostPort": "13380", - "containerPort": 80, - "protocol": "TCP" - } - ], - "volumeMounts": [ - { - "name": "shared-disk", - "mountPath": "/var/www/html" - } - ] - } - ] - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod3.json b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod3.json deleted file mode 100644 index 78bc87e8c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/invalidPod3.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "kind": "Pod", - "apiVersion": "v1", - "metadata": { - "name": "apache-php", - "labels": { - "name": "apache-php" - } - }, - "spec": { - "volumes": [ - "name": "shared-disk" - ], - "containers": [ - { - "name": "apache-php", - "image": "php:5.6.2-apache", - "ports": [ - { - "name": "apache", - "hostPort": 13380, - "containerPort": 80, - "protocol": "TCP" - } - ], - "volumeMounts": [ - { - "name": "shared-disk", - "mountPath": "/var/www/html" - } - ] - } - ] - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/validPod.yaml b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/validPod.yaml deleted file mode 100644 index b8bdbdf69..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/testdata/v1/validPod.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - labels: - name: redis-master - name: name -spec: - containers: - - args: - - this - - is - - an - - ok - - command - image: redis - name: master diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/validation.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/validation.go index 4546a2f5f..3d4d906c5 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/validation.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/validation.go @@ -27,16 +27,18 @@ import ( "regexp" "strings" + "github.com/golang/glog" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/endpoints" + utilpod "k8s.io/kubernetes/pkg/api/pod" "k8s.io/kubernetes/pkg/api/resource" + apiservice "k8s.io/kubernetes/pkg/api/service" "k8s.io/kubernetes/pkg/capabilities" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/util/intstr" "k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/util/validation" "k8s.io/kubernetes/pkg/util/validation/field" - - "github.com/golang/glog" ) // TODO: delete this global variable when we enable the validation of common @@ -44,6 +46,7 @@ import ( var RepairMalformedUpdates bool = true const isNegativeErrorMsg string = `must be greater than or equal to 0` +const isInvalidQuotaResource string = `must be a standard resource for quota` const fieldImmutableErrorMsg string = `field is immutable` const cIdentifierErrorMsg string = `must be a C identifier (matching regex ` + validation.CIdentifierFmt + `): e.g. "my_name" or "MyName"` const isNotIntegerErrorMsg string = `must be an integer` @@ -111,10 +114,34 @@ func ValidateAnnotations(annotations map[string]string, fldPath *field.Path) fie if totalSize > (int64)(totalAnnotationSizeLimitB) { allErrs = append(allErrs, field.TooLong(fldPath, "", totalAnnotationSizeLimitB)) } + return allErrs +} +func ValidatePodSpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} if annotations[api.AffinityAnnotationKey] != "" { allErrs = append(allErrs, ValidateAffinityInPodAnnotations(annotations, fldPath)...) } + + if hostname, exists := annotations[utilpod.PodHostnameAnnotation]; exists && !validation.IsDNS1123Label(hostname) { + allErrs = append(allErrs, field.Invalid(fldPath, utilpod.PodHostnameAnnotation, DNS1123LabelErrorMsg)) + } + + if subdomain, exists := annotations[utilpod.PodSubdomainAnnotation]; exists && !validation.IsDNS1123Label(subdomain) { + allErrs = append(allErrs, field.Invalid(fldPath, utilpod.PodSubdomainAnnotation, DNS1123LabelErrorMsg)) + } + + return allErrs +} + +func ValidateEndpointsSpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + hostnamesMap, exists := annotations[endpoints.PodHostnamesAnnotation] + if exists && !isValidHostnamesMap(hostnamesMap) { + allErrs = append(allErrs, field.Invalid(fldPath, endpoints.PodHostnamesAnnotation, + `must be a valid json representation of map[string(IP)][HostRecord] e.g. "{"10.245.1.6":{"HostName":"my-webserver"}}"`)) + } + return allErrs } @@ -1090,6 +1117,8 @@ func validateVolumeMounts(mounts []api.VolumeMount, volumes sets.String, fldPath } if len(mnt.MountPath) == 0 { allErrs = append(allErrs, field.Required(idxPath.Child("mountPath"), "")) + } else if strings.Contains(mnt.MountPath, ":") { + allErrs = append(allErrs, field.Invalid(idxPath.Child("mountPath"), mnt.MountPath, "must not contain ':'")) } } return allErrs @@ -1352,7 +1381,9 @@ func validateImagePullSecrets(imagePullSecrets []api.LocalObjectReference, fldPa // ValidatePod tests if required fields in the pod are set. func ValidatePod(pod *api.Pod) field.ErrorList { - allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, field.NewPath("metadata")) + fldPath := field.NewPath("metadata") + allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, fldPath) + allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, fldPath.Child("annotations"))...) allErrs = append(allErrs, ValidatePodSpec(&pod.Spec, field.NewPath("spec"))...) return allErrs } @@ -1516,8 +1547,9 @@ func ValidatePodSecurityContext(securityContext *api.PodSecurityContext, spec *a // ValidatePodUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields // that cannot be changed. func ValidatePodUpdate(newPod, oldPod *api.Pod) field.ErrorList { - allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, field.NewPath("metadata")) - + fldPath := field.NewPath("metadata") + allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath) + allErrs = append(allErrs, ValidatePodSpecificAnnotations(newPod.ObjectMeta.Annotations, fldPath.Child("annotations"))...) specPath := field.NewPath("spec") if len(newPod.Spec.Containers) != len(oldPod.Spec.Containers) { //TODO: Pinpoint the specific container that causes the invalid error after we have strategic merge diff @@ -1735,6 +1767,12 @@ func ValidateService(service *api.Service) field.ErrorList { nodePorts[key] = true } + _, err := apiservice.GetLoadBalancerSourceRanges(service.Annotations) + if err != nil { + v := service.Annotations[apiservice.AnnotationLoadBalancerSourceRangesKey] + allErrs = append(allErrs, field.Invalid(field.NewPath("metadata", "annotations").Key(apiservice.AnnotationLoadBalancerSourceRangesKey), v, "must be a comma separated list of CIDRs e.g. 192.168.0.0/16,10.0.0.0/8")) + } + return allErrs } @@ -1770,11 +1808,14 @@ func validateServicePort(sp *api.ServicePort, requireName, isHeadlessService boo allErrs = append(allErrs, field.Invalid(fldPath.Child("targetPort"), sp.TargetPort, PortNameErrorMsg)) } - if isHeadlessService { - if sp.TargetPort.Type == intstr.String || (sp.TargetPort.Type == intstr.Int && sp.Port != sp.TargetPort.IntValue()) { - allErrs = append(allErrs, field.Invalid(fldPath.Child("port"), sp.Port, "must be equal to targetPort when clusterIP = None")) - } - } + // in the v1 API, targetPorts on headless services were tolerated. + // once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility. + // + // if isHeadlessService { + // if sp.TargetPort.Type == intstr.String || (sp.TargetPort.Type == intstr.Int && sp.Port != sp.TargetPort.IntValue()) { + // allErrs = append(allErrs, field.Invalid(fldPath.Child("targetPort"), sp.TargetPort, "must be equal to the value of 'port' when clusterIP = None")) + // } + // } return allErrs } @@ -1817,6 +1858,7 @@ func ValidateReplicationControllerStatusUpdate(controller, oldController *api.Re allErrs := ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta, field.NewPath("metadata")) statusPath := field.NewPath("status") allErrs = append(allErrs, ValidateNonnegativeField(int64(controller.Status.Replicas), statusPath.Child("replicas"))...) + allErrs = append(allErrs, ValidateNonnegativeField(int64(controller.Status.FullyLabeledReplicas), statusPath.Child("fullyLabeledReplicas"))...) allErrs = append(allErrs, ValidateNonnegativeField(int64(controller.Status.ObservedGeneration), statusPath.Child("observedGeneration"))...) return allErrs } @@ -1871,6 +1913,7 @@ func ValidatePodTemplateSpec(spec *api.PodTemplateSpec, fldPath *field.Path) fie allErrs := field.ErrorList{} allErrs = append(allErrs, ValidateLabels(spec.Labels, fldPath.Child("labels"))...) allErrs = append(allErrs, ValidateAnnotations(spec.Annotations, fldPath.Child("annotations"))...) + allErrs = append(allErrs, ValidatePodSpecificAnnotations(spec.Annotations, fldPath.Child("annotations"))...) allErrs = append(allErrs, ValidatePodSpec(&spec.Spec, fldPath.Child("spec"))...) return allErrs } @@ -1968,6 +2011,57 @@ func validateResourceName(value string, fldPath *field.Path) field.ErrorList { return field.ErrorList{} } +// Validate container resource name +// Refer to docs/design/resources.md for more details. +func validateContainerResourceName(value string, fldPath *field.Path) field.ErrorList { + allErrs := validateResourceName(value, fldPath) + if len(strings.Split(value, "/")) == 1 { + if !api.IsStandardContainerResourceName(value) { + return append(allErrs, field.Invalid(fldPath, value, "must be a standard resource for containers")) + } + } + return field.ErrorList{} +} + +// Validate resource names that can go in a resource quota +// Refer to docs/design/resources.md for more details. +func validateResourceQuotaResourceName(value string, fldPath *field.Path) field.ErrorList { + allErrs := validateResourceName(value, fldPath) + if len(strings.Split(value, "/")) == 1 { + if !api.IsStandardQuotaResourceName(value) { + return append(allErrs, field.Invalid(fldPath, value, isInvalidQuotaResource)) + } + } + return field.ErrorList{} +} + +// Validate limit range types +func validateLimitRangeTypeName(value string, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + if !validation.IsQualifiedName(value) { + return append(allErrs, field.Invalid(fldPath, value, qualifiedNameErrorMsg)) + } + + if len(strings.Split(value, "/")) == 1 { + if !api.IsStandardLimitRangeType(value) { + return append(allErrs, field.Invalid(fldPath, value, "must be a standard limit type or fully qualified")) + } + } + + return allErrs +} + +// Validate limit range resource name +// limit types (other than Pod/Container) could contain storage not just cpu or memory +func validateLimitRangeResourceName(limitType api.LimitType, value string, fldPath *field.Path) field.ErrorList { + switch limitType { + case api.LimitTypePod, api.LimitTypeContainer: + return validateContainerResourceName(value, fldPath) + default: + return validateResourceName(value, fldPath) + } +} + // ValidateLimitRange tests if required fields in the LimitRange are set. func ValidateLimitRange(limitRange *api.LimitRange) field.ErrorList { allErrs := ValidateObjectMeta(&limitRange.ObjectMeta, true, ValidateLimitRangeName, field.NewPath("metadata")) @@ -1978,6 +2072,8 @@ func ValidateLimitRange(limitRange *api.LimitRange) field.ErrorList { for i := range limitRange.Spec.Limits { idxPath := fldPath.Index(i) limit := &limitRange.Spec.Limits[i] + allErrs = append(allErrs, validateLimitRangeTypeName(string(limit.Type), idxPath.Child("type"))...) + _, found := limitTypeSet[limit.Type] if found { allErrs = append(allErrs, field.Duplicate(idxPath.Child("type"), limit.Type)) @@ -1992,12 +2088,12 @@ func ValidateLimitRange(limitRange *api.LimitRange) field.ErrorList { maxLimitRequestRatios := map[string]resource.Quantity{} for k, q := range limit.Max { - allErrs = append(allErrs, validateResourceName(string(k), idxPath.Child("max").Key(string(k)))...) + allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, string(k), idxPath.Child("max").Key(string(k)))...) keys.Insert(string(k)) max[string(k)] = q } for k, q := range limit.Min { - allErrs = append(allErrs, validateResourceName(string(k), idxPath.Child("min").Key(string(k)))...) + allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, string(k), idxPath.Child("min").Key(string(k)))...) keys.Insert(string(k)) min[string(k)] = q } @@ -2011,19 +2107,19 @@ func ValidateLimitRange(limitRange *api.LimitRange) field.ErrorList { } } else { for k, q := range limit.Default { - allErrs = append(allErrs, validateResourceName(string(k), idxPath.Child("default").Key(string(k)))...) + allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, string(k), idxPath.Child("default").Key(string(k)))...) keys.Insert(string(k)) defaults[string(k)] = q } for k, q := range limit.DefaultRequest { - allErrs = append(allErrs, validateResourceName(string(k), idxPath.Child("defaultRequest").Key(string(k)))...) + allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, string(k), idxPath.Child("defaultRequest").Key(string(k)))...) keys.Insert(string(k)) defaultRequests[string(k)] = q } } for k, q := range limit.MaxLimitRequestRatio { - allErrs = append(allErrs, validateResourceName(string(k), idxPath.Child("maxLimitRequestRatio").Key(string(k)))...) + allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, string(k), idxPath.Child("maxLimitRequestRatio").Key(string(k)))...) keys.Insert(string(k)) maxLimitRequestRatios[string(k)] = q } @@ -2246,7 +2342,7 @@ func ValidateResourceRequirements(requirements *api.ResourceRequirements, fldPat for resourceName, quantity := range requirements.Limits { fldPath := limPath.Key(string(resourceName)) // Validate resource name. - allErrs = append(allErrs, validateResourceName(string(resourceName), fldPath)...) + allErrs = append(allErrs, validateContainerResourceName(string(resourceName), fldPath)...) if api.IsStandardResourceName(string(resourceName)) { allErrs = append(allErrs, validateBasicResource(quantity, fldPath.Key(string(resourceName)))...) } @@ -2262,7 +2358,7 @@ func ValidateResourceRequirements(requirements *api.ResourceRequirements, fldPat for resourceName, quantity := range requirements.Requests { fldPath := reqPath.Key(string(resourceName)) // Validate resource name. - allErrs = append(allErrs, validateResourceName(string(resourceName), fldPath)...) + allErrs = append(allErrs, validateContainerResourceName(string(resourceName), fldPath)...) if api.IsStandardResourceName(string(resourceName)) { allErrs = append(allErrs, validateBasicResource(quantity, fldPath.Key(string(resourceName)))...) } @@ -2270,6 +2366,41 @@ func ValidateResourceRequirements(requirements *api.ResourceRequirements, fldPat return allErrs } +// validateResourceQuotaScopes ensures that each enumerated hard resource constraint is valid for set of scopes +func validateResourceQuotaScopes(resourceQuota *api.ResourceQuota) field.ErrorList { + allErrs := field.ErrorList{} + if len(resourceQuota.Spec.Scopes) == 0 { + return allErrs + } + hardLimits := sets.NewString() + for k := range resourceQuota.Spec.Hard { + hardLimits.Insert(string(k)) + } + fldPath := field.NewPath("spec", "scopes") + scopeSet := sets.NewString() + for _, scope := range resourceQuota.Spec.Scopes { + if !api.IsStandardResourceQuotaScope(string(scope)) { + allErrs = append(allErrs, field.Invalid(fldPath, resourceQuota.Spec.Scopes, "unsupported scope")) + } + for _, k := range hardLimits.List() { + if api.IsStandardQuotaResourceName(k) && !api.IsResourceQuotaScopeValidForResource(scope, k) { + allErrs = append(allErrs, field.Invalid(fldPath, resourceQuota.Spec.Scopes, "unsupported scope applied to resource")) + } + } + scopeSet.Insert(string(scope)) + } + invalidScopePairs := []sets.String{ + sets.NewString(string(api.ResourceQuotaScopeBestEffort), string(api.ResourceQuotaScopeNotBestEffort)), + sets.NewString(string(api.ResourceQuotaScopeTerminating), string(api.ResourceQuotaScopeNotTerminating)), + } + for _, invalidScopePair := range invalidScopePairs { + if scopeSet.HasAll(invalidScopePair.List()...) { + allErrs = append(allErrs, field.Invalid(fldPath, resourceQuota.Spec.Scopes, "conflicting scopes")) + } + } + return allErrs +} + // ValidateResourceQuota tests if required fields in the ResourceQuota are set. func ValidateResourceQuota(resourceQuota *api.ResourceQuota) field.ErrorList { allErrs := ValidateObjectMeta(&resourceQuota.ObjectMeta, true, ValidateResourceQuotaName, field.NewPath("metadata")) @@ -2277,21 +2408,24 @@ func ValidateResourceQuota(resourceQuota *api.ResourceQuota) field.ErrorList { fldPath := field.NewPath("spec", "hard") for k, v := range resourceQuota.Spec.Hard { resPath := fldPath.Key(string(k)) - allErrs = append(allErrs, validateResourceName(string(k), resPath)...) + allErrs = append(allErrs, validateResourceQuotaResourceName(string(k), resPath)...) allErrs = append(allErrs, validateResourceQuantityValue(string(k), v, resPath)...) } + allErrs = append(allErrs, validateResourceQuotaScopes(resourceQuota)...) + fldPath = field.NewPath("status", "hard") for k, v := range resourceQuota.Status.Hard { resPath := fldPath.Key(string(k)) - allErrs = append(allErrs, validateResourceName(string(k), resPath)...) + allErrs = append(allErrs, validateResourceQuotaResourceName(string(k), resPath)...) allErrs = append(allErrs, validateResourceQuantityValue(string(k), v, resPath)...) } fldPath = field.NewPath("status", "used") for k, v := range resourceQuota.Status.Used { resPath := fldPath.Key(string(k)) - allErrs = append(allErrs, validateResourceName(string(k), resPath)...) + allErrs = append(allErrs, validateResourceQuotaResourceName(string(k), resPath)...) allErrs = append(allErrs, validateResourceQuantityValue(string(k), v, resPath)...) } + return allErrs } @@ -2314,9 +2448,25 @@ func ValidateResourceQuotaUpdate(newResourceQuota, oldResourceQuota *api.Resourc fldPath := field.NewPath("spec", "hard") for k, v := range newResourceQuota.Spec.Hard { resPath := fldPath.Key(string(k)) - allErrs = append(allErrs, validateResourceName(string(k), resPath)...) + allErrs = append(allErrs, validateResourceQuotaResourceName(string(k), resPath)...) allErrs = append(allErrs, validateResourceQuantityValue(string(k), v, resPath)...) } + + // ensure scopes cannot change, and that resources are still valid for scope + fldPath = field.NewPath("spec", "scopes") + oldScopes := sets.NewString() + newScopes := sets.NewString() + for _, scope := range newResourceQuota.Spec.Scopes { + newScopes.Insert(string(scope)) + } + for _, scope := range oldResourceQuota.Spec.Scopes { + oldScopes.Insert(string(scope)) + } + if !oldScopes.Equal(newScopes) { + allErrs = append(allErrs, field.Invalid(fldPath, newResourceQuota.Spec.Scopes, "field is immutable")) + } + allErrs = append(allErrs, validateResourceQuotaScopes(newResourceQuota)...) + newResourceQuota.Status = oldResourceQuota.Status return allErrs } @@ -2331,13 +2481,13 @@ func ValidateResourceQuotaStatusUpdate(newResourceQuota, oldResourceQuota *api.R fldPath := field.NewPath("status", "hard") for k, v := range newResourceQuota.Status.Hard { resPath := fldPath.Key(string(k)) - allErrs = append(allErrs, validateResourceName(string(k), resPath)...) + allErrs = append(allErrs, validateResourceQuotaResourceName(string(k), resPath)...) allErrs = append(allErrs, validateResourceQuantityValue(string(k), v, resPath)...) } fldPath = field.NewPath("status", "used") for k, v := range newResourceQuota.Status.Used { resPath := fldPath.Key(string(k)) - allErrs = append(allErrs, validateResourceName(string(k), resPath)...) + allErrs = append(allErrs, validateResourceQuotaResourceName(string(k), resPath)...) allErrs = append(allErrs, validateResourceQuantityValue(string(k), v, resPath)...) } newResourceQuota.Spec = oldResourceQuota.Spec @@ -2412,6 +2562,7 @@ func ValidateNamespaceFinalizeUpdate(newNamespace, oldNamespace *api.Namespace) // ValidateEndpoints tests if required fields are set. func ValidateEndpoints(endpoints *api.Endpoints) field.ErrorList { allErrs := ValidateObjectMeta(&endpoints.ObjectMeta, true, ValidateEndpointsName, field.NewPath("metadata")) + allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(endpoints.Annotations, field.NewPath("annotations"))...) allErrs = append(allErrs, validateEndpointSubsets(endpoints.Subsets, field.NewPath("subsets"))...) return allErrs } @@ -2495,6 +2646,7 @@ func validateEndpointPort(port *api.EndpointPort, requireName bool, fldPath *fie func ValidateEndpointsUpdate(newEndpoints, oldEndpoints *api.Endpoints) field.ErrorList { allErrs := ValidateObjectMetaUpdate(&newEndpoints.ObjectMeta, &oldEndpoints.ObjectMeta, field.NewPath("metadata")) allErrs = append(allErrs, validateEndpointSubsets(newEndpoints.Subsets, field.NewPath("subsets"))...) + allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(newEndpoints.Annotations, field.NewPath("annotations"))...) return allErrs } @@ -2560,3 +2712,24 @@ func ValidateLoadBalancerStatus(status *api.LoadBalancerStatus, fldPath *field.P } return allErrs } + +func isValidHostnamesMap(serializedPodHostNames string) bool { + if len(serializedPodHostNames) == 0 { + return false + } + podHostNames := map[string]endpoints.HostRecord{} + err := json.Unmarshal([]byte(serializedPodHostNames), &podHostNames) + if err != nil { + return false + } + + for ip, hostRecord := range podHostNames { + if !validation.IsDNS1123Label(hostRecord.HostName) { + return false + } + if net.ParseIP(ip) == nil { + return false + } + } + return true +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/validation_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/validation_test.go deleted file mode 100644 index 04c27126a..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/validation/validation_test.go +++ /dev/null @@ -1,5146 +0,0 @@ -/* -Copyright 2014 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 validation - -import ( - "math/rand" - "reflect" - "strings" - "testing" - "time" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/capabilities" - "k8s.io/kubernetes/pkg/util/intstr" - "k8s.io/kubernetes/pkg/util/sets" - "k8s.io/kubernetes/pkg/util/validation/field" -) - -func expectPrefix(t *testing.T, prefix string, errs field.ErrorList) { - for i := range errs { - if f, p := errs[i].Field, prefix; !strings.HasPrefix(f, p) { - t.Errorf("expected prefix '%s' for field '%s' (%v)", p, f, errs[i]) - } - } -} - -// Ensure custom name functions are allowed -func TestValidateObjectMetaCustomName(t *testing.T) { - errs := ValidateObjectMeta( - &api.ObjectMeta{Name: "test", GenerateName: "foo"}, - false, - func(s string, prefix bool) (bool, string) { - if s == "test" { - return true, "" - } - return false, "name-gen" - }, - field.NewPath("field")) - if len(errs) != 1 { - t.Fatalf("unexpected errors: %v", errs) - } - if !strings.Contains(errs[0].Error(), "name-gen") { - t.Errorf("unexpected error message: %v", errs) - } -} - -// Ensure namespace names follow dns label format -func TestValidateObjectMetaNamespaces(t *testing.T) { - errs := ValidateObjectMeta( - &api.ObjectMeta{Name: "test", Namespace: "foo.bar"}, - true, - func(s string, prefix bool) (bool, string) { - return true, "" - }, - field.NewPath("field")) - if len(errs) != 1 { - t.Fatalf("unexpected errors: %v", errs) - } - if !strings.Contains(errs[0].Error(), `Invalid value: "foo.bar"`) { - t.Errorf("unexpected error message: %v", errs) - } - maxLength := 63 - letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") - b := make([]rune, maxLength+1) - for i := range b { - b[i] = letters[rand.Intn(len(letters))] - } - errs = ValidateObjectMeta( - &api.ObjectMeta{Name: "test", Namespace: string(b)}, - true, - func(s string, prefix bool) (bool, string) { - return true, "" - }, - field.NewPath("field")) - if len(errs) != 1 { - t.Fatalf("unexpected errors: %v", errs) - } - if !strings.Contains(errs[0].Error(), "Invalid value") { - t.Errorf("unexpected error message: %v", errs) - } -} - -func TestValidateObjectMetaUpdateIgnoresCreationTimestamp(t *testing.T) { - if errs := ValidateObjectMetaUpdate( - &api.ObjectMeta{Name: "test", ResourceVersion: "1"}, - &api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: unversioned.NewTime(time.Unix(10, 0))}, - field.NewPath("field"), - ); len(errs) != 0 { - t.Fatalf("unexpected errors: %v", errs) - } - if errs := ValidateObjectMetaUpdate( - &api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: unversioned.NewTime(time.Unix(10, 0))}, - &api.ObjectMeta{Name: "test", ResourceVersion: "1"}, - field.NewPath("field"), - ); len(errs) != 0 { - t.Fatalf("unexpected errors: %v", errs) - } - if errs := ValidateObjectMetaUpdate( - &api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: unversioned.NewTime(time.Unix(10, 0))}, - &api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: unversioned.NewTime(time.Unix(11, 0))}, - field.NewPath("field"), - ); len(errs) != 0 { - t.Fatalf("unexpected errors: %v", errs) - } -} - -// Ensure trailing slash is allowed in generate name -func TestValidateObjectMetaTrimsTrailingSlash(t *testing.T) { - errs := ValidateObjectMeta( - &api.ObjectMeta{Name: "test", GenerateName: "foo-"}, - false, - NameIsDNSSubdomain, - field.NewPath("field")) - if len(errs) != 0 { - t.Fatalf("unexpected errors: %v", errs) - } -} - -func TestValidateLabels(t *testing.T) { - successCases := []map[string]string{ - {"simple": "bar"}, - {"now-with-dashes": "bar"}, - {"1-starts-with-num": "bar"}, - {"1234": "bar"}, - {"simple/simple": "bar"}, - {"now-with-dashes/simple": "bar"}, - {"now-with-dashes/now-with-dashes": "bar"}, - {"now.with.dots/simple": "bar"}, - {"now-with.dashes-and.dots/simple": "bar"}, - {"1-num.2-num/3-num": "bar"}, - {"1234/5678": "bar"}, - {"1.2.3.4/5678": "bar"}, - {"UpperCaseAreOK123": "bar"}, - {"goodvalue": "123_-.BaR"}, - } - for i := range successCases { - errs := ValidateLabels(successCases[i], field.NewPath("field")) - if len(errs) != 0 { - t.Errorf("case[%d] expected success, got %#v", i, errs) - } - } - - labelNameErrorCases := []map[string]string{ - {"nospecialchars^=@": "bar"}, - {"cantendwithadash-": "bar"}, - {"only/one/slash": "bar"}, - {strings.Repeat("a", 254): "bar"}, - } - for i := range labelNameErrorCases { - errs := ValidateLabels(labelNameErrorCases[i], field.NewPath("field")) - if len(errs) != 1 { - t.Errorf("case[%d] expected failure", i) - } else { - detail := errs[0].Detail - if detail != qualifiedNameErrorMsg { - t.Errorf("error detail %s should be equal %s", detail, qualifiedNameErrorMsg) - } - } - } - - labelValueErrorCases := []map[string]string{ - {"toolongvalue": strings.Repeat("a", 64)}, - {"backslashesinvalue": "some\\bad\\value"}, - {"nocommasallowed": "bad,value"}, - {"strangecharsinvalue": "?#$notsogood"}, - } - for i := range labelValueErrorCases { - errs := ValidateLabels(labelValueErrorCases[i], field.NewPath("field")) - if len(errs) != 1 { - t.Errorf("case[%d] expected failure", i) - } else { - detail := errs[0].Detail - if detail != labelValueErrorMsg { - t.Errorf("error detail %s should be equal %s", detail, labelValueErrorMsg) - } - } - } -} - -func TestValidateAnnotations(t *testing.T) { - successCases := []map[string]string{ - {"simple": "bar"}, - {"now-with-dashes": "bar"}, - {"1-starts-with-num": "bar"}, - {"1234": "bar"}, - {"simple/simple": "bar"}, - {"now-with-dashes/simple": "bar"}, - {"now-with-dashes/now-with-dashes": "bar"}, - {"now.with.dots/simple": "bar"}, - {"now-with.dashes-and.dots/simple": "bar"}, - {"1-num.2-num/3-num": "bar"}, - {"1234/5678": "bar"}, - {"1.2.3.4/5678": "bar"}, - {"UpperCase123": "bar"}, - {"a": strings.Repeat("b", totalAnnotationSizeLimitB-1)}, - { - "a": strings.Repeat("b", totalAnnotationSizeLimitB/2-1), - "c": strings.Repeat("d", totalAnnotationSizeLimitB/2-1), - }, - } - for i := range successCases { - errs := ValidateAnnotations(successCases[i], field.NewPath("field")) - if len(errs) != 0 { - t.Errorf("case[%d] expected success, got %#v", i, errs) - } - } - - nameErrorCases := []map[string]string{ - {"nospecialchars^=@": "bar"}, - {"cantendwithadash-": "bar"}, - {"only/one/slash": "bar"}, - {strings.Repeat("a", 254): "bar"}, - } - for i := range nameErrorCases { - errs := ValidateAnnotations(nameErrorCases[i], field.NewPath("field")) - if len(errs) != 1 { - t.Errorf("case[%d] expected failure", i) - } - detail := errs[0].Detail - if detail != qualifiedNameErrorMsg { - t.Errorf("error detail %s should be equal %s", detail, qualifiedNameErrorMsg) - } - } - totalSizeErrorCases := []map[string]string{ - {"a": strings.Repeat("b", totalAnnotationSizeLimitB)}, - { - "a": strings.Repeat("b", totalAnnotationSizeLimitB/2), - "c": strings.Repeat("d", totalAnnotationSizeLimitB/2), - }, - } - for i := range totalSizeErrorCases { - errs := ValidateAnnotations(totalSizeErrorCases[i], field.NewPath("field")) - if len(errs) != 1 { - t.Errorf("case[%d] expected failure", i) - } - } -} - -func testVolume(name string, namespace string, spec api.PersistentVolumeSpec) *api.PersistentVolume { - objMeta := api.ObjectMeta{Name: name} - if namespace != "" { - objMeta.Namespace = namespace - } - - return &api.PersistentVolume{ - ObjectMeta: objMeta, - Spec: spec, - } -} - -func TestValidatePersistentVolumes(t *testing.T) { - scenarios := map[string]struct { - isExpectedFailure bool - volume *api.PersistentVolume - }{ - "good-volume": { - isExpectedFailure: false, - volume: testVolume("foo", "", api.PersistentVolumeSpec{ - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, - PersistentVolumeSource: api.PersistentVolumeSource{ - HostPath: &api.HostPathVolumeSource{Path: "/foo"}, - }, - }), - }, - "invalid-accessmode": { - isExpectedFailure: true, - volume: testVolume("foo", "", api.PersistentVolumeSpec{ - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - AccessModes: []api.PersistentVolumeAccessMode{"fakemode"}, - PersistentVolumeSource: api.PersistentVolumeSource{ - HostPath: &api.HostPathVolumeSource{Path: "/foo"}, - }, - }), - }, - "unexpected-namespace": { - isExpectedFailure: true, - volume: testVolume("foo", "unexpected-namespace", api.PersistentVolumeSpec{ - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, - PersistentVolumeSource: api.PersistentVolumeSource{ - HostPath: &api.HostPathVolumeSource{Path: "/foo"}, - }, - }), - }, - "bad-name": { - isExpectedFailure: true, - volume: testVolume("123*Bad(Name", "unexpected-namespace", api.PersistentVolumeSpec{ - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, - PersistentVolumeSource: api.PersistentVolumeSource{ - HostPath: &api.HostPathVolumeSource{Path: "/foo"}, - }, - }), - }, - "missing-name": { - isExpectedFailure: true, - volume: testVolume("", "", api.PersistentVolumeSpec{ - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, - }), - }, - "missing-capacity": { - isExpectedFailure: true, - volume: testVolume("foo", "", api.PersistentVolumeSpec{}), - }, - "missing-accessmodes": { - isExpectedFailure: true, - volume: testVolume("goodname", "missing-accessmodes", api.PersistentVolumeSpec{ - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - PersistentVolumeSource: api.PersistentVolumeSource{ - HostPath: &api.HostPathVolumeSource{Path: "/foo"}, - }, - }), - }, - "too-many-sources": { - isExpectedFailure: true, - volume: testVolume("", "", api.PersistentVolumeSpec{ - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("5G"), - }, - PersistentVolumeSource: api.PersistentVolumeSource{ - HostPath: &api.HostPathVolumeSource{Path: "/foo"}, - GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "foo", FSType: "ext4"}, - }, - }), - }, - } - - for name, scenario := range scenarios { - errs := ValidatePersistentVolume(scenario.volume) - if len(errs) == 0 && scenario.isExpectedFailure { - t.Errorf("Unexpected success for scenario: %s", name) - } - if len(errs) > 0 && !scenario.isExpectedFailure { - t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs) - } - } - -} - -func testVolumeClaim(name string, namespace string, spec api.PersistentVolumeClaimSpec) *api.PersistentVolumeClaim { - return &api.PersistentVolumeClaim{ - ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespace}, - Spec: spec, - } -} - -func TestValidatePersistentVolumeClaim(t *testing.T) { - scenarios := map[string]struct { - isExpectedFailure bool - claim *api.PersistentVolumeClaim - }{ - "good-claim": { - isExpectedFailure: false, - claim: testVolumeClaim("foo", "ns", api.PersistentVolumeClaimSpec{ - AccessModes: []api.PersistentVolumeAccessMode{ - api.ReadWriteOnce, - api.ReadOnlyMany, - }, - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - }, - }), - }, - "invalid-accessmode": { - isExpectedFailure: true, - claim: testVolumeClaim("foo", "ns", api.PersistentVolumeClaimSpec{ - AccessModes: []api.PersistentVolumeAccessMode{"fakemode"}, - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - }, - }), - }, - "missing-namespace": { - isExpectedFailure: true, - claim: testVolumeClaim("foo", "", api.PersistentVolumeClaimSpec{ - AccessModes: []api.PersistentVolumeAccessMode{ - api.ReadWriteOnce, - api.ReadOnlyMany, - }, - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - }, - }), - }, - "no-access-modes": { - isExpectedFailure: true, - claim: testVolumeClaim("foo", "ns", api.PersistentVolumeClaimSpec{ - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - }, - }), - }, - "no-resource-requests": { - isExpectedFailure: true, - claim: testVolumeClaim("foo", "ns", api.PersistentVolumeClaimSpec{ - AccessModes: []api.PersistentVolumeAccessMode{ - api.ReadWriteOnce, - }, - }), - }, - "invalid-resource-requests": { - isExpectedFailure: true, - claim: testVolumeClaim("foo", "ns", api.PersistentVolumeClaimSpec{ - AccessModes: []api.PersistentVolumeAccessMode{ - api.ReadWriteOnce, - }, - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), - }, - }, - }), - }, - } - - for name, scenario := range scenarios { - errs := ValidatePersistentVolumeClaim(scenario.claim) - if len(errs) == 0 && scenario.isExpectedFailure { - t.Errorf("Unexpected success for scenario: %s", name) - } - if len(errs) > 0 && !scenario.isExpectedFailure { - t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs) - } - } -} - -func TestValidatePersistentVolumeClaimUpdate(t *testing.T) { - validClaim := testVolumeClaim("foo", "ns", api.PersistentVolumeClaimSpec{ - AccessModes: []api.PersistentVolumeAccessMode{ - api.ReadWriteOnce, - api.ReadOnlyMany, - }, - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - }, - }) - validUpdateClaim := testVolumeClaim("foo", "ns", api.PersistentVolumeClaimSpec{ - AccessModes: []api.PersistentVolumeAccessMode{ - api.ReadWriteOnce, - api.ReadOnlyMany, - }, - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - }, - VolumeName: "volume", - }) - invalidUpdateClaimResources := testVolumeClaim("foo", "ns", api.PersistentVolumeClaimSpec{ - AccessModes: []api.PersistentVolumeAccessMode{ - api.ReadWriteOnce, - api.ReadOnlyMany, - }, - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("20G"), - }, - }, - VolumeName: "volume", - }) - invalidUpdateClaimAccessModes := testVolumeClaim("foo", "ns", api.PersistentVolumeClaimSpec{ - AccessModes: []api.PersistentVolumeAccessMode{ - api.ReadWriteOnce, - }, - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - }, - VolumeName: "volume", - }) - scenarios := map[string]struct { - isExpectedFailure bool - oldClaim *api.PersistentVolumeClaim - newClaim *api.PersistentVolumeClaim - }{ - "valid-update": { - isExpectedFailure: false, - oldClaim: validClaim, - newClaim: validUpdateClaim, - }, - "invalid-update-change-resources-on-bound-claim": { - isExpectedFailure: true, - oldClaim: validUpdateClaim, - newClaim: invalidUpdateClaimResources, - }, - "invalid-update-change-access-modes-on-bound-claim": { - isExpectedFailure: true, - oldClaim: validUpdateClaim, - newClaim: invalidUpdateClaimAccessModes, - }, - } - - for name, scenario := range scenarios { - // ensure we have a resource version specified for updates - scenario.oldClaim.ResourceVersion = "1" - scenario.newClaim.ResourceVersion = "1" - errs := ValidatePersistentVolumeClaimUpdate(scenario.newClaim, scenario.oldClaim) - if len(errs) == 0 && scenario.isExpectedFailure { - t.Errorf("Unexpected success for scenario: %s", name) - } - if len(errs) > 0 && !scenario.isExpectedFailure { - t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs) - } - } -} - -func TestValidateVolumes(t *testing.T) { - lun := 1 - successCase := []api.Volume{ - {Name: "abc", VolumeSource: api.VolumeSource{HostPath: &api.HostPathVolumeSource{Path: "/mnt/path1"}}}, - {Name: "123", VolumeSource: api.VolumeSource{HostPath: &api.HostPathVolumeSource{Path: "/mnt/path2"}}}, - {Name: "abc-123", VolumeSource: api.VolumeSource{HostPath: &api.HostPathVolumeSource{Path: "/mnt/path3"}}}, - {Name: "empty", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}, - {Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}, - {Name: "awsebs", VolumeSource: api.VolumeSource{AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{VolumeID: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}, - {Name: "gitrepo", VolumeSource: api.VolumeSource{GitRepo: &api.GitRepoVolumeSource{Repository: "my-repo", Revision: "hashstring", Directory: "target"}}}, - {Name: "gitrepodot", VolumeSource: api.VolumeSource{GitRepo: &api.GitRepoVolumeSource{Repository: "my-repo", Directory: "."}}}, - {Name: "iscsidisk", VolumeSource: api.VolumeSource{ISCSI: &api.ISCSIVolumeSource{TargetPortal: "127.0.0.1", IQN: "iqn.2015-02.example.com:test", Lun: 1, FSType: "ext4", ReadOnly: false}}}, - {Name: "secret", VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "my-secret"}}}, - {Name: "glusterfs", VolumeSource: api.VolumeSource{Glusterfs: &api.GlusterfsVolumeSource{EndpointsName: "host1", Path: "path", ReadOnly: false}}}, - {Name: "flocker", VolumeSource: api.VolumeSource{Flocker: &api.FlockerVolumeSource{DatasetName: "datasetName"}}}, - {Name: "rbd", VolumeSource: api.VolumeSource{RBD: &api.RBDVolumeSource{CephMonitors: []string{"foo"}, RBDImage: "bar", FSType: "ext4"}}}, - {Name: "cinder", VolumeSource: api.VolumeSource{Cinder: &api.CinderVolumeSource{"29ea5088-4f60-4757-962e-dba678767887", "ext4", false}}}, - {Name: "cephfs", VolumeSource: api.VolumeSource{CephFS: &api.CephFSVolumeSource{Monitors: []string{"foo"}}}}, - {Name: "downwardapi", VolumeSource: api.VolumeSource{DownwardAPI: &api.DownwardAPIVolumeSource{Items: []api.DownwardAPIVolumeFile{ - {Path: "labels", FieldRef: api.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.labels"}}, - {Path: "annotations", FieldRef: api.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.annotations"}}, - {Path: "namespace", FieldRef: api.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.namespace"}}, - {Path: "name", FieldRef: api.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.name"}}, - {Path: "path/withslash/andslash", FieldRef: api.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.labels"}}, - {Path: "path/./withdot", FieldRef: api.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.labels"}}, - {Path: "path/with..dot", FieldRef: api.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.labels"}}, - {Path: "second-level-dirent-can-have/..dot", FieldRef: api.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.labels"}}, - }}}}, - {Name: "fc", VolumeSource: api.VolumeSource{FC: &api.FCVolumeSource{[]string{"some_wwn"}, &lun, "ext4", false}}}, - {Name: "flexvolume", VolumeSource: api.VolumeSource{FlexVolume: &api.FlexVolumeSource{Driver: "kubernetes.io/blue", FSType: "ext4"}}}, - {Name: "azure", VolumeSource: api.VolumeSource{AzureFile: &api.AzureFileVolumeSource{"key", "share", false}}}, - } - names, errs := validateVolumes(successCase, field.NewPath("field")) - if len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - if len(names) != len(successCase) || !names.HasAll("abc", "123", "abc-123", "empty", "gcepd", "gitrepo", "secret", "iscsidisk", "cinder", "cephfs", "flexvolume", "fc") { - t.Errorf("wrong names result: %v", names) - } - emptyVS := api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}} - emptyPortal := api.VolumeSource{ISCSI: &api.ISCSIVolumeSource{TargetPortal: "", IQN: "iqn.2015-02.example.com:test", Lun: 1, FSType: "ext4", ReadOnly: false}} - emptyIQN := api.VolumeSource{ISCSI: &api.ISCSIVolumeSource{TargetPortal: "127.0.0.1", IQN: "", Lun: 1, FSType: "ext4", ReadOnly: false}} - emptyHosts := api.VolumeSource{Glusterfs: &api.GlusterfsVolumeSource{EndpointsName: "", Path: "path", ReadOnly: false}} - emptyPath := api.VolumeSource{Glusterfs: &api.GlusterfsVolumeSource{EndpointsName: "host", Path: "", ReadOnly: false}} - emptyName := api.VolumeSource{Flocker: &api.FlockerVolumeSource{DatasetName: ""}} - emptyMon := api.VolumeSource{RBD: &api.RBDVolumeSource{CephMonitors: []string{}, RBDImage: "bar", FSType: "ext4"}} - emptyImage := api.VolumeSource{RBD: &api.RBDVolumeSource{CephMonitors: []string{"foo"}, RBDImage: "", FSType: "ext4"}} - emptyCephFSMon := api.VolumeSource{CephFS: &api.CephFSVolumeSource{Monitors: []string{}}} - startsWithDots := api.VolumeSource{GitRepo: &api.GitRepoVolumeSource{Repository: "foo", Directory: "..dots/bar"}} - containsDots := api.VolumeSource{GitRepo: &api.GitRepoVolumeSource{Repository: "foo", Directory: "dots/../bar"}} - absPath := api.VolumeSource{GitRepo: &api.GitRepoVolumeSource{Repository: "foo", Directory: "/abstarget"}} - emptyPathName := api.VolumeSource{DownwardAPI: &api.DownwardAPIVolumeSource{Items: []api.DownwardAPIVolumeFile{{Path: "", - FieldRef: api.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.labels"}}}, - }} - absolutePathName := api.VolumeSource{DownwardAPI: &api.DownwardAPIVolumeSource{Items: []api.DownwardAPIVolumeFile{{Path: "/absolutepath", - FieldRef: api.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.labels"}}}, - }} - dotDotInPath := api.VolumeSource{DownwardAPI: &api.DownwardAPIVolumeSource{Items: []api.DownwardAPIVolumeFile{{Path: "../../passwd", - FieldRef: api.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.labels"}}}, - }} - dotDotPathName := api.VolumeSource{DownwardAPI: &api.DownwardAPIVolumeSource{Items: []api.DownwardAPIVolumeFile{{Path: "..badFileName", - FieldRef: api.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.labels"}}}, - }} - dotDotFirstLevelDirent := api.VolumeSource{DownwardAPI: &api.DownwardAPIVolumeSource{Items: []api.DownwardAPIVolumeFile{{Path: "..badDirName/goodFileName", - FieldRef: api.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.labels"}}}, - }} - zeroWWN := api.VolumeSource{FC: &api.FCVolumeSource{[]string{}, &lun, "ext4", false}} - emptyLun := api.VolumeSource{FC: &api.FCVolumeSource{[]string{"wwn"}, nil, "ext4", false}} - slashInName := api.VolumeSource{Flocker: &api.FlockerVolumeSource{DatasetName: "foo/bar"}} - emptyAzureSecret := api.VolumeSource{AzureFile: &api.AzureFileVolumeSource{"", "share", false}} - emptyAzureShare := api.VolumeSource{AzureFile: &api.AzureFileVolumeSource{"name", "", false}} - errorCases := map[string]struct { - V []api.Volume - T field.ErrorType - F string - D string - }{ - "zero-length name": { - []api.Volume{{Name: "", VolumeSource: emptyVS}}, - field.ErrorTypeRequired, - "name", "", - }, - "name > 63 characters": { - []api.Volume{{Name: strings.Repeat("a", 64), VolumeSource: emptyVS}}, - field.ErrorTypeInvalid, - "name", "must be a DNS label", - }, - "name not a DNS label": { - []api.Volume{{Name: "a.b.c", VolumeSource: emptyVS}}, - field.ErrorTypeInvalid, - "name", "must be a DNS label", - }, - "name not unique": { - []api.Volume{{Name: "abc", VolumeSource: emptyVS}, {Name: "abc", VolumeSource: emptyVS}}, - field.ErrorTypeDuplicate, - "[1].name", "", - }, - "empty portal": { - []api.Volume{{Name: "badportal", VolumeSource: emptyPortal}}, - field.ErrorTypeRequired, - "iscsi.targetPortal", "", - }, - "empty iqn": { - []api.Volume{{Name: "badiqn", VolumeSource: emptyIQN}}, - field.ErrorTypeRequired, - "iscsi.iqn", "", - }, - "empty hosts": { - []api.Volume{{Name: "badhost", VolumeSource: emptyHosts}}, - field.ErrorTypeRequired, - "glusterfs.endpoints", "", - }, - "empty path": { - []api.Volume{{Name: "badpath", VolumeSource: emptyPath}}, - field.ErrorTypeRequired, - "glusterfs.path", "", - }, - "empty datasetName": { - []api.Volume{{Name: "badname", VolumeSource: emptyName}}, - field.ErrorTypeRequired, - "flocker.datasetName", "", - }, - "empty mon": { - []api.Volume{{Name: "badmon", VolumeSource: emptyMon}}, - field.ErrorTypeRequired, - "rbd.monitors", "", - }, - "empty image": { - []api.Volume{{Name: "badimage", VolumeSource: emptyImage}}, - field.ErrorTypeRequired, - "rbd.image", "", - }, - "empty cephfs mon": { - []api.Volume{{Name: "badmon", VolumeSource: emptyCephFSMon}}, - field.ErrorTypeRequired, - "cephfs.monitors", "", - }, - "empty metatada path": { - []api.Volume{{Name: "emptyname", VolumeSource: emptyPathName}}, - field.ErrorTypeRequired, - "downwardAPI.path", "", - }, - "absolute path": { - []api.Volume{{Name: "absolutepath", VolumeSource: absolutePathName}}, - field.ErrorTypeInvalid, - "downwardAPI.path", "", - }, - "dot dot path": { - []api.Volume{{Name: "dotdotpath", VolumeSource: dotDotInPath}}, - field.ErrorTypeInvalid, - "downwardAPI.path", `must not contain '..'`, - }, - "dot dot file name": { - []api.Volume{{Name: "dotdotfilename", VolumeSource: dotDotPathName}}, - field.ErrorTypeInvalid, - "downwardAPI.path", `must not start with '..'`, - }, - "dot dot first level dirent": { - []api.Volume{{Name: "dotdotdirfilename", VolumeSource: dotDotFirstLevelDirent}}, - field.ErrorTypeInvalid, - "downwardAPI.path", `must not start with '..'`, - }, - "empty wwn": { - []api.Volume{{Name: "badimage", VolumeSource: zeroWWN}}, - field.ErrorTypeRequired, - "fc.targetWWNs", "", - }, - "empty lun": { - []api.Volume{{Name: "badimage", VolumeSource: emptyLun}}, - field.ErrorTypeRequired, - "fc.lun", "", - }, - "slash in datasetName": { - []api.Volume{{Name: "slashinname", VolumeSource: slashInName}}, - field.ErrorTypeInvalid, - "flocker.datasetName", "must not contain '/'", - }, - "starts with '..'": { - []api.Volume{{Name: "badprefix", VolumeSource: startsWithDots}}, - field.ErrorTypeInvalid, - "gitRepo.directory", `must not start with '..'`, - }, - "contains '..'": { - []api.Volume{{Name: "containsdots", VolumeSource: containsDots}}, - field.ErrorTypeInvalid, - "gitRepo.directory", `must not contain '..'`, - }, - "absolute target": { - []api.Volume{{Name: "absolutetarget", VolumeSource: absPath}}, - field.ErrorTypeInvalid, - "gitRepo.directory", "", - }, - "empty secret": { - []api.Volume{{Name: "emptyaccount", VolumeSource: emptyAzureSecret}}, - field.ErrorTypeRequired, - "azureFile.secretName", "", - }, - "empty share": { - []api.Volume{{Name: "emptyaccount", VolumeSource: emptyAzureShare}}, - field.ErrorTypeRequired, - "azureFile.shareName", "", - }, - } - for k, v := range errorCases { - _, errs := validateVolumes(v.V, field.NewPath("field")) - if len(errs) == 0 { - t.Errorf("expected failure %s for %v", k, v.V) - continue - } - for i := range errs { - if errs[i].Type != v.T { - t.Errorf("%s: expected error to have type %q: %q", k, v.T, errs[i].Type) - } - if !strings.Contains(errs[i].Field, v.F) { - t.Errorf("%s: expected error field %q: %q", k, v.F, errs[i].Field) - } - if !strings.Contains(errs[i].Detail, v.D) { - t.Errorf("%s: expected error detail %q, got %q", k, v.D, errs[i].Detail) - } - } - } -} - -func TestValidatePorts(t *testing.T) { - successCase := []api.ContainerPort{ - {Name: "abc", ContainerPort: 80, HostPort: 80, Protocol: "TCP"}, - {Name: "easy", ContainerPort: 82, Protocol: "TCP"}, - {Name: "as", ContainerPort: 83, Protocol: "UDP"}, - {Name: "do-re-me", ContainerPort: 84, Protocol: "UDP"}, - {ContainerPort: 85, Protocol: "TCP"}, - } - if errs := validateContainerPorts(successCase, field.NewPath("field")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - - nonCanonicalCase := []api.ContainerPort{ - {ContainerPort: 80, Protocol: "TCP"}, - } - if errs := validateContainerPorts(nonCanonicalCase, field.NewPath("field")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - - errorCases := map[string]struct { - P []api.ContainerPort - T field.ErrorType - F string - D string - }{ - "name > 15 characters": { - []api.ContainerPort{{Name: strings.Repeat("a", 16), ContainerPort: 80, Protocol: "TCP"}}, - field.ErrorTypeInvalid, - "name", PortNameErrorMsg, - }, - "name not a IANA svc name ": { - []api.ContainerPort{{Name: "a.b.c", ContainerPort: 80, Protocol: "TCP"}}, - field.ErrorTypeInvalid, - "name", PortNameErrorMsg, - }, - "name not a IANA svc name (i.e. a number)": { - []api.ContainerPort{{Name: "80", ContainerPort: 80, Protocol: "TCP"}}, - field.ErrorTypeInvalid, - "name", PortNameErrorMsg, - }, - "name not unique": { - []api.ContainerPort{ - {Name: "abc", ContainerPort: 80, Protocol: "TCP"}, - {Name: "abc", ContainerPort: 81, Protocol: "TCP"}, - }, - field.ErrorTypeDuplicate, - "[1].name", "", - }, - "zero container port": { - []api.ContainerPort{{ContainerPort: 0, Protocol: "TCP"}}, - field.ErrorTypeInvalid, - "containerPort", PortRangeErrorMsg, - }, - "invalid container port": { - []api.ContainerPort{{ContainerPort: 65536, Protocol: "TCP"}}, - field.ErrorTypeInvalid, - "containerPort", PortRangeErrorMsg, - }, - "invalid host port": { - []api.ContainerPort{{ContainerPort: 80, HostPort: 65536, Protocol: "TCP"}}, - field.ErrorTypeInvalid, - "hostPort", PortRangeErrorMsg, - }, - "invalid protocol case": { - []api.ContainerPort{{ContainerPort: 80, Protocol: "tcp"}}, - field.ErrorTypeNotSupported, - "protocol", "supported values: TCP, UDP", - }, - "invalid protocol": { - []api.ContainerPort{{ContainerPort: 80, Protocol: "ICMP"}}, - field.ErrorTypeNotSupported, - "protocol", "supported values: TCP, UDP", - }, - "protocol required": { - []api.ContainerPort{{Name: "abc", ContainerPort: 80}}, - field.ErrorTypeRequired, - "protocol", "", - }, - } - for k, v := range errorCases { - errs := validateContainerPorts(v.P, field.NewPath("field")) - if len(errs) == 0 { - t.Errorf("expected failure for %s", k) - } - for i := range errs { - if errs[i].Type != v.T { - t.Errorf("%s: expected error to have type %q: %q", k, v.T, errs[i].Type) - } - if !strings.Contains(errs[i].Field, v.F) { - t.Errorf("%s: expected error field %q: %q", k, v.F, errs[i].Field) - } - if !strings.Contains(errs[i].Detail, v.D) { - t.Errorf("%s: expected error detail %q, got %q", k, v.D, errs[i].Detail) - } - } - } -} - -func TestValidateEnv(t *testing.T) { - successCase := []api.EnvVar{ - {Name: "abc", Value: "value"}, - {Name: "ABC", Value: "value"}, - {Name: "AbC_123", Value: "value"}, - {Name: "abc", Value: ""}, - { - Name: "abc", - ValueFrom: &api.EnvVarSource{ - FieldRef: &api.ObjectFieldSelector{ - APIVersion: testapi.Default.GroupVersion().String(), - FieldPath: "metadata.name", - }, - }, - }, - { - Name: "secret_value", - ValueFrom: &api.EnvVarSource{ - SecretKeyRef: &api.SecretKeySelector{ - LocalObjectReference: api.LocalObjectReference{ - Name: "some-secret", - }, - Key: "secret-key", - }, - }, - }, - { - Name: "ENV_VAR_1", - ValueFrom: &api.EnvVarSource{ - ConfigMapKeyRef: &api.ConfigMapKeySelector{ - LocalObjectReference: api.LocalObjectReference{ - Name: "some-config-map", - }, - Key: "some-key", - }, - }, - }, - } - if errs := validateEnv(successCase, field.NewPath("field")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - - errorCases := []struct { - name string - envs []api.EnvVar - expectedError string - }{ - { - name: "zero-length name", - envs: []api.EnvVar{{Name: ""}}, - expectedError: "[0].name: Required value", - }, - { - name: "name not a C identifier", - envs: []api.EnvVar{{Name: "a.b.c"}}, - expectedError: `[0].name: Invalid value: "a.b.c": must be a C identifier (matching regex [A-Za-z_][A-Za-z0-9_]*): e.g. "my_name" or "MyName"`, - }, - { - name: "value and valueFrom specified", - envs: []api.EnvVar{{ - Name: "abc", - Value: "foo", - ValueFrom: &api.EnvVarSource{ - FieldRef: &api.ObjectFieldSelector{ - APIVersion: testapi.Default.GroupVersion().String(), - FieldPath: "metadata.name", - }, - }, - }}, - expectedError: "[0].valueFrom: Invalid value: \"\": may not be specified when `value` is not empty", - }, - { - name: "valueFrom.fieldRef and valueFrom.secretKeyRef specified", - envs: []api.EnvVar{{ - Name: "abc", - ValueFrom: &api.EnvVarSource{ - FieldRef: &api.ObjectFieldSelector{ - APIVersion: testapi.Default.GroupVersion().String(), - FieldPath: "metadata.name", - }, - SecretKeyRef: &api.SecretKeySelector{ - LocalObjectReference: api.LocalObjectReference{ - Name: "a-secret", - }, - Key: "a-key", - }, - }, - }}, - expectedError: "[0].valueFrom: Invalid value: \"\": may not have more than one field specified at a time", - }, - { - name: "valueFrom.fieldRef and valueFrom.configMapKeyRef set", - envs: []api.EnvVar{{ - Name: "some_var_name", - ValueFrom: &api.EnvVarSource{ - FieldRef: &api.ObjectFieldSelector{ - APIVersion: testapi.Default.GroupVersion().String(), - FieldPath: "metadata.name", - }, - ConfigMapKeyRef: &api.ConfigMapKeySelector{ - LocalObjectReference: api.LocalObjectReference{ - Name: "some-config-map", - }, - Key: "some-key", - }, - }, - }}, - expectedError: `[0].valueFrom: Invalid value: "": may not have more than one field specified at a time`, - }, - { - name: "valueFrom.fieldRef and valueFrom.secretKeyRef specified", - envs: []api.EnvVar{{ - Name: "abc", - ValueFrom: &api.EnvVarSource{ - FieldRef: &api.ObjectFieldSelector{ - APIVersion: testapi.Default.GroupVersion().String(), - FieldPath: "metadata.name", - }, - SecretKeyRef: &api.SecretKeySelector{ - LocalObjectReference: api.LocalObjectReference{ - Name: "a-secret", - }, - Key: "a-key", - }, - ConfigMapKeyRef: &api.ConfigMapKeySelector{ - LocalObjectReference: api.LocalObjectReference{ - Name: "some-config-map", - }, - Key: "some-key", - }, - }, - }}, - expectedError: `[0].valueFrom: Invalid value: "": may not have more than one field specified at a time`, - }, - { - name: "missing FieldPath on ObjectFieldSelector", - envs: []api.EnvVar{{ - Name: "abc", - ValueFrom: &api.EnvVarSource{ - FieldRef: &api.ObjectFieldSelector{ - APIVersion: testapi.Default.GroupVersion().String(), - }, - }, - }}, - expectedError: `[0].valueFrom.fieldRef.fieldPath: Required value`, - }, - { - name: "missing APIVersion on ObjectFieldSelector", - envs: []api.EnvVar{{ - Name: "abc", - ValueFrom: &api.EnvVarSource{ - FieldRef: &api.ObjectFieldSelector{ - FieldPath: "metadata.name", - }, - }, - }}, - expectedError: `[0].valueFrom.fieldRef.apiVersion: Required value`, - }, - { - name: "invalid fieldPath", - envs: []api.EnvVar{{ - Name: "abc", - ValueFrom: &api.EnvVarSource{ - FieldRef: &api.ObjectFieldSelector{ - FieldPath: "metadata.whoops", - APIVersion: testapi.Default.GroupVersion().String(), - }, - }, - }}, - expectedError: `[0].valueFrom.fieldRef.fieldPath: Invalid value: "metadata.whoops": error converting fieldPath`, - }, - { - name: "invalid fieldPath labels", - envs: []api.EnvVar{{ - Name: "labels", - ValueFrom: &api.EnvVarSource{ - FieldRef: &api.ObjectFieldSelector{ - FieldPath: "metadata.labels", - APIVersion: "v1", - }, - }, - }}, - expectedError: `[0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.labels": supported values: metadata.name, metadata.namespace, status.podIP`, - }, - { - name: "invalid fieldPath annotations", - envs: []api.EnvVar{{ - Name: "abc", - ValueFrom: &api.EnvVarSource{ - FieldRef: &api.ObjectFieldSelector{ - FieldPath: "metadata.annotations", - APIVersion: "v1", - }, - }, - }}, - expectedError: `[0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.annotations": supported values: metadata.name, metadata.namespace, status.podIP`, - }, - { - name: "unsupported fieldPath", - envs: []api.EnvVar{{ - Name: "abc", - ValueFrom: &api.EnvVarSource{ - FieldRef: &api.ObjectFieldSelector{ - FieldPath: "status.phase", - APIVersion: testapi.Default.GroupVersion().String(), - }, - }, - }}, - expectedError: `valueFrom.fieldRef.fieldPath: Unsupported value: "status.phase": supported values: metadata.name, metadata.namespace, status.podIP`, - }, - } - for _, tc := range errorCases { - if errs := validateEnv(tc.envs, field.NewPath("field")); len(errs) == 0 { - t.Errorf("expected failure for %s", tc.name) - } else { - for i := range errs { - str := errs[i].Error() - if str != "" && !strings.Contains(str, tc.expectedError) { - t.Errorf("%s: expected error detail either empty or %q, got %q", tc.name, tc.expectedError, str) - } - } - } - } -} - -func TestValidateVolumeMounts(t *testing.T) { - volumes := sets.NewString("abc", "123", "abc-123") - - successCase := []api.VolumeMount{ - {Name: "abc", MountPath: "/foo"}, - {Name: "123", MountPath: "/foo"}, - {Name: "abc-123", MountPath: "/bar"}, - } - if errs := validateVolumeMounts(successCase, volumes, field.NewPath("field")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - - errorCases := map[string][]api.VolumeMount{ - "empty name": {{Name: "", MountPath: "/foo"}}, - "name not found": {{Name: "", MountPath: "/foo"}}, - "empty mountpath": {{Name: "abc", MountPath: ""}}, - } - for k, v := range errorCases { - if errs := validateVolumeMounts(v, volumes, field.NewPath("field")); len(errs) == 0 { - t.Errorf("expected failure for %s", k) - } - } -} - -func TestValidateProbe(t *testing.T) { - handler := api.Handler{Exec: &api.ExecAction{Command: []string{"echo"}}} - // These fields must be positive. - positiveFields := [...]string{"InitialDelaySeconds", "TimeoutSeconds", "PeriodSeconds", "SuccessThreshold", "FailureThreshold"} - successCases := []*api.Probe{nil} - for _, field := range positiveFields { - probe := &api.Probe{Handler: handler} - reflect.ValueOf(probe).Elem().FieldByName(field).SetInt(10) - successCases = append(successCases, probe) - } - - for _, p := range successCases { - if errs := validateProbe(p, field.NewPath("field")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := []*api.Probe{{TimeoutSeconds: 10, InitialDelaySeconds: 10}} - for _, field := range positiveFields { - probe := &api.Probe{Handler: handler} - reflect.ValueOf(probe).Elem().FieldByName(field).SetInt(-10) - errorCases = append(errorCases, probe) - } - for _, p := range errorCases { - if errs := validateProbe(p, field.NewPath("field")); len(errs) == 0 { - t.Errorf("expected failure for %v", p) - } - } -} - -func TestValidateHandler(t *testing.T) { - successCases := []api.Handler{ - {Exec: &api.ExecAction{Command: []string{"echo"}}}, - {HTTPGet: &api.HTTPGetAction{Path: "/", Port: intstr.FromInt(1), Host: "", Scheme: "HTTP"}}, - {HTTPGet: &api.HTTPGetAction{Path: "/foo", Port: intstr.FromInt(65535), Host: "host", Scheme: "HTTP"}}, - {HTTPGet: &api.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP"}}, - {HTTPGet: &api.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP", HTTPHeaders: []api.HTTPHeader{{"Host", "foo.example.com"}}}}, - {HTTPGet: &api.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP", HTTPHeaders: []api.HTTPHeader{{"X-Forwarded-For", "1.2.3.4"}, {"X-Forwarded-For", "5.6.7.8"}}}}, - } - for _, h := range successCases { - if errs := validateHandler(&h, field.NewPath("field")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := []api.Handler{ - {}, - {Exec: &api.ExecAction{Command: []string{}}}, - {HTTPGet: &api.HTTPGetAction{Path: "", Port: intstr.FromInt(0), Host: ""}}, - {HTTPGet: &api.HTTPGetAction{Path: "/foo", Port: intstr.FromInt(65536), Host: "host"}}, - {HTTPGet: &api.HTTPGetAction{Path: "", Port: intstr.FromString(""), Host: ""}}, - {HTTPGet: &api.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP", HTTPHeaders: []api.HTTPHeader{{"Host:", "foo.example.com"}}}}, - {HTTPGet: &api.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP", HTTPHeaders: []api.HTTPHeader{{"X_Forwarded_For", "foo.example.com"}}}}, - } - for _, h := range errorCases { - if errs := validateHandler(&h, field.NewPath("field")); len(errs) == 0 { - t.Errorf("expected failure for %#v", h) - } - } -} - -func TestValidatePullPolicy(t *testing.T) { - type T struct { - Container api.Container - ExpectedPolicy api.PullPolicy - } - testCases := map[string]T{ - "NotPresent1": { - api.Container{Name: "abc", Image: "image:latest", ImagePullPolicy: "IfNotPresent"}, - api.PullIfNotPresent, - }, - "NotPresent2": { - api.Container{Name: "abc1", Image: "image", ImagePullPolicy: "IfNotPresent"}, - api.PullIfNotPresent, - }, - "Always1": { - api.Container{Name: "123", Image: "image:latest", ImagePullPolicy: "Always"}, - api.PullAlways, - }, - "Always2": { - api.Container{Name: "1234", Image: "image", ImagePullPolicy: "Always"}, - api.PullAlways, - }, - "Never1": { - api.Container{Name: "abc-123", Image: "image:latest", ImagePullPolicy: "Never"}, - api.PullNever, - }, - "Never2": { - api.Container{Name: "abc-1234", Image: "image", ImagePullPolicy: "Never"}, - api.PullNever, - }, - } - for k, v := range testCases { - ctr := &v.Container - errs := validatePullPolicy(ctr.ImagePullPolicy, field.NewPath("field")) - if len(errs) != 0 { - t.Errorf("case[%s] expected success, got %#v", k, errs) - } - if ctr.ImagePullPolicy != v.ExpectedPolicy { - t.Errorf("case[%s] expected policy %v, got %v", k, v.ExpectedPolicy, ctr.ImagePullPolicy) - } - } -} - -func getResourceLimits(cpu, memory string) api.ResourceList { - res := api.ResourceList{} - res[api.ResourceCPU] = resource.MustParse(cpu) - res[api.ResourceMemory] = resource.MustParse(memory) - return res -} - -func TestValidateContainers(t *testing.T) { - volumes := sets.String{} - capabilities.SetForTests(capabilities.Capabilities{ - AllowPrivileged: true, - }) - - successCase := []api.Container{ - {Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}, - {Name: "123", Image: "image", ImagePullPolicy: "IfNotPresent"}, - {Name: "abc-123", Image: "image", ImagePullPolicy: "IfNotPresent"}, - { - Name: "life-123", - Image: "image", - Lifecycle: &api.Lifecycle{ - PreStop: &api.Handler{ - Exec: &api.ExecAction{Command: []string{"ls", "-l"}}, - }, - }, - ImagePullPolicy: "IfNotPresent", - }, - { - Name: "resources-test", - Image: "image", - Resources: api.ResourceRequirements{ - Limits: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("10"), - api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), - api.ResourceName("my.org/resource"): resource.MustParse("10m"), - }, - }, - ImagePullPolicy: "IfNotPresent", - }, - { - Name: "resources-request-limit-simple", - Image: "image", - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("8"), - }, - Limits: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("10"), - }, - }, - ImagePullPolicy: "IfNotPresent", - }, - { - Name: "resources-request-limit-edge", - Image: "image", - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("10"), - api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), - api.ResourceName("my.org/resource"): resource.MustParse("10m"), - }, - Limits: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("10"), - api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), - api.ResourceName("my.org/resource"): resource.MustParse("10m"), - }, - }, - ImagePullPolicy: "IfNotPresent", - }, - { - Name: "resources-request-limit-partials", - Image: "image", - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("9.5"), - api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), - }, - Limits: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("10"), - api.ResourceName("my.org/resource"): resource.MustParse("10m"), - }, - }, - ImagePullPolicy: "IfNotPresent", - }, - { - Name: "resources-request", - Image: "image", - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("9.5"), - api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), - }, - }, - ImagePullPolicy: "IfNotPresent", - }, - { - Name: "same-host-port-different-protocol", - Image: "image", - Ports: []api.ContainerPort{ - {ContainerPort: 80, HostPort: 80, Protocol: "TCP"}, - {ContainerPort: 80, HostPort: 80, Protocol: "UDP"}, - }, - ImagePullPolicy: "IfNotPresent", - }, - {Name: "abc-1234", Image: "image", ImagePullPolicy: "IfNotPresent", SecurityContext: fakeValidSecurityContext(true)}, - } - if errs := validateContainers(successCase, volumes, field.NewPath("field")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - - capabilities.SetForTests(capabilities.Capabilities{ - AllowPrivileged: false, - }) - errorCases := map[string][]api.Container{ - "zero-length name": {{Name: "", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - "name > 63 characters": {{Name: strings.Repeat("a", 64), Image: "image", ImagePullPolicy: "IfNotPresent"}}, - "name not a DNS label": {{Name: "a.b.c", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - "name not unique": { - {Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}, - {Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}, - }, - "zero-length image": {{Name: "abc", Image: "", ImagePullPolicy: "IfNotPresent"}}, - "host port not unique": { - {Name: "abc", Image: "image", Ports: []api.ContainerPort{{ContainerPort: 80, HostPort: 80, Protocol: "TCP"}}, - ImagePullPolicy: "IfNotPresent"}, - {Name: "def", Image: "image", Ports: []api.ContainerPort{{ContainerPort: 81, HostPort: 80, Protocol: "TCP"}}, - ImagePullPolicy: "IfNotPresent"}, - }, - "invalid env var name": { - {Name: "abc", Image: "image", Env: []api.EnvVar{{Name: "ev.1"}}, ImagePullPolicy: "IfNotPresent"}, - }, - "unknown volume name": { - {Name: "abc", Image: "image", VolumeMounts: []api.VolumeMount{{Name: "anything", MountPath: "/foo"}}, - ImagePullPolicy: "IfNotPresent"}, - }, - "invalid lifecycle, no exec command.": { - { - Name: "life-123", - Image: "image", - Lifecycle: &api.Lifecycle{ - PreStop: &api.Handler{ - Exec: &api.ExecAction{}, - }, - }, - ImagePullPolicy: "IfNotPresent", - }, - }, - "invalid lifecycle, no http path.": { - { - Name: "life-123", - Image: "image", - Lifecycle: &api.Lifecycle{ - PreStop: &api.Handler{ - HTTPGet: &api.HTTPGetAction{}, - }, - }, - ImagePullPolicy: "IfNotPresent", - }, - }, - "invalid lifecycle, no tcp socket port.": { - { - Name: "life-123", - Image: "image", - Lifecycle: &api.Lifecycle{ - PreStop: &api.Handler{ - TCPSocket: &api.TCPSocketAction{}, - }, - }, - ImagePullPolicy: "IfNotPresent", - }, - }, - "invalid lifecycle, zero tcp socket port.": { - { - Name: "life-123", - Image: "image", - Lifecycle: &api.Lifecycle{ - PreStop: &api.Handler{ - TCPSocket: &api.TCPSocketAction{ - Port: intstr.FromInt(0), - }, - }, - }, - ImagePullPolicy: "IfNotPresent", - }, - }, - "invalid lifecycle, no action.": { - { - Name: "life-123", - Image: "image", - Lifecycle: &api.Lifecycle{ - PreStop: &api.Handler{}, - }, - ImagePullPolicy: "IfNotPresent", - }, - }, - "invalid liveness probe, no tcp socket port.": { - { - Name: "life-123", - Image: "image", - LivenessProbe: &api.Probe{ - Handler: api.Handler{ - TCPSocket: &api.TCPSocketAction{}, - }, - }, - ImagePullPolicy: "IfNotPresent", - }, - }, - "invalid liveness probe, no action.": { - { - Name: "life-123", - Image: "image", - LivenessProbe: &api.Probe{ - Handler: api.Handler{}, - }, - ImagePullPolicy: "IfNotPresent", - }, - }, - "privilege disabled": { - {Name: "abc", Image: "image", SecurityContext: fakeValidSecurityContext(true)}, - }, - "invalid compute resource": { - { - Name: "abc-123", - Image: "image", - Resources: api.ResourceRequirements{ - Limits: api.ResourceList{ - "disk": resource.MustParse("10G"), - }, - }, - ImagePullPolicy: "IfNotPresent", - }, - }, - "Resource CPU invalid": { - { - Name: "abc-123", - Image: "image", - Resources: api.ResourceRequirements{ - Limits: getResourceLimits("-10", "0"), - }, - ImagePullPolicy: "IfNotPresent", - }, - }, - "Resource Requests CPU invalid": { - { - Name: "abc-123", - Image: "image", - Resources: api.ResourceRequirements{ - Requests: getResourceLimits("-10", "0"), - }, - ImagePullPolicy: "IfNotPresent", - }, - }, - "Resource Memory invalid": { - { - Name: "abc-123", - Image: "image", - Resources: api.ResourceRequirements{ - Limits: getResourceLimits("0", "-10"), - }, - ImagePullPolicy: "IfNotPresent", - }, - }, - "Request limit simple invalid": { - { - Name: "abc-123", - Image: "image", - Resources: api.ResourceRequirements{ - Limits: getResourceLimits("5", "3"), - Requests: getResourceLimits("6", "3"), - }, - ImagePullPolicy: "IfNotPresent", - }, - }, - "Request limit multiple invalid": { - { - Name: "abc-123", - Image: "image", - Resources: api.ResourceRequirements{ - Limits: getResourceLimits("5", "3"), - Requests: getResourceLimits("6", "4"), - }, - ImagePullPolicy: "IfNotPresent", - }, - }, - } - for k, v := range errorCases { - if errs := validateContainers(v, volumes, field.NewPath("field")); len(errs) == 0 { - t.Errorf("expected failure for %s", k) - } - } -} - -func TestValidateRestartPolicy(t *testing.T) { - successCases := []api.RestartPolicy{ - api.RestartPolicyAlways, - api.RestartPolicyOnFailure, - api.RestartPolicyNever, - } - for _, policy := range successCases { - if errs := validateRestartPolicy(&policy, field.NewPath("field")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := []api.RestartPolicy{"", "newpolicy"} - - for k, policy := range errorCases { - if errs := validateRestartPolicy(&policy, field.NewPath("field")); len(errs) == 0 { - t.Errorf("expected failure for %d", k) - } - } -} - -func TestValidateDNSPolicy(t *testing.T) { - successCases := []api.DNSPolicy{api.DNSClusterFirst, api.DNSDefault, api.DNSPolicy(api.DNSClusterFirst)} - for _, policy := range successCases { - if errs := validateDNSPolicy(&policy, field.NewPath("field")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := []api.DNSPolicy{api.DNSPolicy("invalid")} - for _, policy := range errorCases { - if errs := validateDNSPolicy(&policy, field.NewPath("field")); len(errs) == 0 { - t.Errorf("expected failure for %v", policy) - } - } -} - -func TestValidatePodSpec(t *testing.T) { - activeDeadlineSeconds := int64(30) - minID := int64(0) - maxID := int64(2147483647) - successCases := []api.PodSpec{ - { // Populate basic fields, leave defaults for most. - Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}}, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - { // Populate all fields. - Volumes: []api.Volume{ - {Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}, - }, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - NodeSelector: map[string]string{ - "key": "value", - }, - NodeName: "foobar", - DNSPolicy: api.DNSClusterFirst, - ActiveDeadlineSeconds: &activeDeadlineSeconds, - ServiceAccountName: "acct", - }, - { // Populate HostNetwork. - Containers: []api.Container{ - {Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", Ports: []api.ContainerPort{ - {HostPort: 8080, ContainerPort: 8080, Protocol: "TCP"}}, - }, - }, - SecurityContext: &api.PodSecurityContext{ - HostNetwork: true, - }, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - { // Populate RunAsUser SupplementalGroups FSGroup with minID 0 - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - SecurityContext: &api.PodSecurityContext{ - SupplementalGroups: []int64{minID}, - RunAsUser: &minID, - FSGroup: &minID, - }, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - { // Populate RunAsUser SupplementalGroups FSGroup with maxID 2147483647 - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - SecurityContext: &api.PodSecurityContext{ - SupplementalGroups: []int64{maxID}, - RunAsUser: &maxID, - FSGroup: &maxID, - }, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - { // Populate HostIPC. - SecurityContext: &api.PodSecurityContext{ - HostIPC: true, - }, - Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}}, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - { // Populate HostPID. - SecurityContext: &api.PodSecurityContext{ - HostPID: true, - }, - Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}}, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - { // Populate Affinity. - Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}}, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - } - for i := range successCases { - if errs := ValidatePodSpec(&successCases[i], field.NewPath("field")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - activeDeadlineSeconds = int64(0) - minID = int64(-1) - maxID = int64(2147483648) - failureCases := map[string]api.PodSpec{ - "bad volume": { - Volumes: []api.Volume{{}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - "no containers": { - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - "bad container": { - Containers: []api.Container{{}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - "bad DNS policy": { - DNSPolicy: api.DNSPolicy("invalid"), - RestartPolicy: api.RestartPolicyAlways, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - "bad service account name": { - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - ServiceAccountName: "invalidName", - }, - "bad restart policy": { - RestartPolicy: "UnknowPolicy", - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - "with hostNetwork hostPort not equal to containerPort": { - Containers: []api.Container{ - {Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", Ports: []api.ContainerPort{ - {HostPort: 8080, ContainerPort: 2600, Protocol: "TCP"}}, - }, - }, - SecurityContext: &api.PodSecurityContext{ - HostNetwork: true, - }, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - "bad supplementalGroups large than math.MaxInt32": { - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - SecurityContext: &api.PodSecurityContext{ - HostNetwork: false, - SupplementalGroups: []int64{maxID, 1234}, - }, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - "bad supplementalGroups less than 0": { - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - SecurityContext: &api.PodSecurityContext{ - HostNetwork: false, - SupplementalGroups: []int64{minID, 1234}, - }, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - "bad runAsUser large than math.MaxInt32": { - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - SecurityContext: &api.PodSecurityContext{ - HostNetwork: false, - RunAsUser: &maxID, - }, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - "bad runAsUser less than 0": { - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - SecurityContext: &api.PodSecurityContext{ - HostNetwork: false, - RunAsUser: &minID, - }, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - "bad fsGroup large than math.MaxInt32": { - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - SecurityContext: &api.PodSecurityContext{ - HostNetwork: false, - FSGroup: &maxID, - }, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - "bad fsGroup less than 0": { - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - SecurityContext: &api.PodSecurityContext{ - HostNetwork: false, - FSGroup: &minID, - }, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - "bad-active-deadline-seconds": { - Volumes: []api.Volume{ - {Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}, - }, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - NodeSelector: map[string]string{ - "key": "value", - }, - NodeName: "foobar", - DNSPolicy: api.DNSClusterFirst, - ActiveDeadlineSeconds: &activeDeadlineSeconds, - }, - "bad nodeName": { - NodeName: "node name", - Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}}, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - } - for k, v := range failureCases { - if errs := ValidatePodSpec(&v, field.NewPath("field")); len(errs) == 0 { - t.Errorf("expected failure for %q", k) - } - } -} - -func TestValidatePod(t *testing.T) { - successCases := []api.Pod{ - { // Basic fields. - ObjectMeta: api.ObjectMeta{Name: "123", Namespace: "ns"}, - Spec: api.PodSpec{ - Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}}, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - }, - { // Just about everything. - ObjectMeta: api.ObjectMeta{Name: "abc.123.do-re-mi", Namespace: "ns"}, - Spec: api.PodSpec{ - Volumes: []api.Volume{ - {Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}, - }, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - NodeSelector: map[string]string{ - "key": "value", - }, - NodeName: "foobar", - }, - }, - { // Serialized affinity requirements in annotations. - ObjectMeta: api.ObjectMeta{ - Name: "123", - Namespace: "ns", - // TODO: Uncomment and move this block into Annotations map once - // RequiredDuringSchedulingRequiredDuringExecution is implemented - // "requiredDuringSchedulingRequiredDuringExecution": { - // "nodeSelectorTerms": [{ - // "matchExpressions": [{ - // "key": "key1", - // "operator": "Exists" - // }] - // }] - // }, - Annotations: map[string]string{ - api.AffinityAnnotationKey: ` - {"nodeAffinity": { - "requiredDuringSchedulingIgnoredDuringExecution": { - "nodeSelectorTerms": [{ - "matchExpressions": [{ - "key": "key2", - "operator": "In", - "values": ["value1", "value2"] - }] - }] - }, - "preferredDuringSchedulingIgnoredDuringExecution": [ - { - "weight": 10, - "preference": {"matchExpressions": [ - { - "key": "foo", - "operator": "In", "values": ["bar"] - } - ]} - } - ] - }}`, - }, - }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - }, - } - for _, pod := range successCases { - if errs := ValidatePod(&pod); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := map[string]api.Pod{ - "bad name": { - ObjectMeta: api.ObjectMeta{Name: "", Namespace: "ns"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - }, - "bad namespace": { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: ""}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - }, - "bad spec": { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "ns"}, - Spec: api.PodSpec{ - Containers: []api.Container{{}}, - }, - }, - "bad label": { - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: "ns", - Labels: map[string]string{ - "NoUppercaseOrSpecialCharsLike=Equals": "bar", - }, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - }, - "invalid json of affinity in pod annotations": { - ObjectMeta: api.ObjectMeta{ - Name: "123", - Namespace: "ns", - Annotations: map[string]string{ - api.AffinityAnnotationKey: ` - {"nodeAffinity": { - "requiredDuringSchedulingIgnoredDuringExecution": { - "nodeSelectorTerms": [{ - `, - }, - }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - }, - "invalid node selector requirement in affinity in pod annotations, operator can't be null": { - ObjectMeta: api.ObjectMeta{ - Name: "123", - Namespace: "ns", - Annotations: map[string]string{ - api.AffinityAnnotationKey: ` - {"nodeAffinity": {"requiredDuringSchedulingIgnoredDuringExecution": { - "nodeSelectorTerms": [{ - "matchExpressions": [{ - "key": "key1", - }] - }] - }}}`, - }, - }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - }, - "invalid preferredSchedulingTerm in affinity in pod annotations, weight should be in range 1-100": { - ObjectMeta: api.ObjectMeta{ - Name: "123", - Namespace: "ns", - Annotations: map[string]string{ - api.AffinityAnnotationKey: ` - {"nodeAffinity": {"preferredDuringSchedulingIgnoredDuringExecution": [ - { - "weight": 199, - "preference": {"matchExpressions": [ - { - "key": "foo", - "operator": "In", - "values": ["bar"] - } - ]} - } - ]}}`, - }, - }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - }, - "invalid requiredDuringSchedulingIgnoredDuringExecution node selector, nodeSelectorTerms must have at least one term": { - ObjectMeta: api.ObjectMeta{ - Name: "123", - Namespace: "ns", - Annotations: map[string]string{ - api.AffinityAnnotationKey: ` - {"nodeAffinity": { - "requiredDuringSchedulingIgnoredDuringExecution": { - "nodeSelectorTerms": [] - }, - }}`, - }, - }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - }, - "invalid requiredDuringSchedulingIgnoredDuringExecution node selector term, matchExpressions must have at least one node selector requirement": { - ObjectMeta: api.ObjectMeta{ - Name: "123", - Namespace: "ns", - Annotations: map[string]string{ - api.AffinityAnnotationKey: ` - {"nodeAffinity": { - "requiredDuringSchedulingIgnoredDuringExecution": { - "nodeSelectorTerms": [{ - "matchExpressions": [] - }] - }, - }}`, - }, - }, - Spec: api.PodSpec{ - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - }, - } - for k, v := range errorCases { - if errs := ValidatePod(&v); len(errs) == 0 { - t.Errorf("expected failure for %q", k) - } - } -} - -func TestValidatePodUpdate(t *testing.T) { - var ( - activeDeadlineSecondsZero = int64(0) - activeDeadlineSecondsNegative = int64(-30) - activeDeadlineSecondsPositive = int64(30) - activeDeadlineSecondsLarger = int64(31) - - now = unversioned.Now() - grace = int64(30) - grace2 = int64(31) - ) - - tests := []struct { - a api.Pod - b api.Pod - isValid bool - test string - }{ - {api.Pod{}, api.Pod{}, true, "nothing"}, - { - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - }, - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "bar"}, - }, - false, - "ids", - }, - { - api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "bar": "foo", - }, - }, - }, - true, - "labels", - }, - { - api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Annotations: map[string]string{ - "foo": "bar", - }, - }, - }, - api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Annotations: map[string]string{ - "bar": "foo", - }, - }, - }, - true, - "annotations", - }, - { - api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Image: "foo:V1", - }, - }, - }, - }, - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Image: "foo:V2", - }, - { - Image: "bar:V2", - }, - }, - }, - }, - false, - "more containers", - }, - { - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo", DeletionTimestamp: &now}, - Spec: api.PodSpec{Containers: []api.Container{{Image: "foo:V1"}}}, - }, - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: api.PodSpec{Containers: []api.Container{{Image: "foo:V1"}}}, - }, - true, - "deletion timestamp filled out", - }, - { - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &grace}, - Spec: api.PodSpec{Containers: []api.Container{{Image: "foo:V1"}}}, - }, - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &grace2}, - Spec: api.PodSpec{Containers: []api.Container{{Image: "foo:V1"}}}, - }, - false, - "deletion grace period seconds cleared", - }, - { - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Image: "foo:V1", - }, - }, - }, - }, - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Image: "foo:V2", - }, - }, - }, - }, - true, - "image change", - }, - { - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: api.PodSpec{ - Containers: []api.Container{ - {}, - }, - }, - }, - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Image: "foo:V2", - }, - }, - }, - }, - false, - "image change to empty", - }, - { - api.Pod{ - Spec: api.PodSpec{}, - }, - api.Pod{ - Spec: api.PodSpec{}, - }, - true, - "activeDeadlineSeconds no change, nil", - }, - { - api.Pod{ - Spec: api.PodSpec{ - ActiveDeadlineSeconds: &activeDeadlineSecondsPositive, - }, - }, - api.Pod{ - Spec: api.PodSpec{ - ActiveDeadlineSeconds: &activeDeadlineSecondsPositive, - }, - }, - true, - "activeDeadlineSeconds no change, set", - }, - { - api.Pod{ - Spec: api.PodSpec{ - ActiveDeadlineSeconds: &activeDeadlineSecondsPositive, - }, - }, - api.Pod{}, - true, - "activeDeadlineSeconds change to positive from nil", - }, - { - api.Pod{ - Spec: api.PodSpec{ - ActiveDeadlineSeconds: &activeDeadlineSecondsPositive, - }, - }, - api.Pod{ - Spec: api.PodSpec{ - ActiveDeadlineSeconds: &activeDeadlineSecondsLarger, - }, - }, - true, - "activeDeadlineSeconds change to smaller positive", - }, - { - api.Pod{ - Spec: api.PodSpec{ - ActiveDeadlineSeconds: &activeDeadlineSecondsLarger, - }, - }, - api.Pod{ - Spec: api.PodSpec{ - ActiveDeadlineSeconds: &activeDeadlineSecondsPositive, - }, - }, - false, - "activeDeadlineSeconds change to larger positive", - }, - - { - api.Pod{ - Spec: api.PodSpec{ - ActiveDeadlineSeconds: &activeDeadlineSecondsNegative, - }, - }, - api.Pod{}, - false, - "activeDeadlineSeconds change to negative from nil", - }, - { - api.Pod{ - Spec: api.PodSpec{ - ActiveDeadlineSeconds: &activeDeadlineSecondsNegative, - }, - }, - api.Pod{ - Spec: api.PodSpec{ - ActiveDeadlineSeconds: &activeDeadlineSecondsPositive, - }, - }, - false, - "activeDeadlineSeconds change to negative from positive", - }, - { - api.Pod{ - Spec: api.PodSpec{ - ActiveDeadlineSeconds: &activeDeadlineSecondsZero, - }, - }, - api.Pod{ - Spec: api.PodSpec{ - ActiveDeadlineSeconds: &activeDeadlineSecondsPositive, - }, - }, - true, - "activeDeadlineSeconds change to zero from positive", - }, - { - api.Pod{ - Spec: api.PodSpec{ - ActiveDeadlineSeconds: &activeDeadlineSecondsZero, - }, - }, - api.Pod{}, - true, - "activeDeadlineSeconds change to zero from nil", - }, - { - api.Pod{}, - api.Pod{ - Spec: api.PodSpec{ - ActiveDeadlineSeconds: &activeDeadlineSecondsPositive, - }, - }, - false, - "activeDeadlineSeconds change to nil from positive", - }, - - { - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Image: "foo:V1", - Resources: api.ResourceRequirements{ - Limits: getResourceLimits("100m", "0"), - }, - }, - }, - }, - }, - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Image: "foo:V2", - Resources: api.ResourceRequirements{ - Limits: getResourceLimits("1000m", "0"), - }, - }, - }, - }, - }, - false, - "cpu change", - }, - { - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Image: "foo:V1", - Ports: []api.ContainerPort{ - {HostPort: 8080, ContainerPort: 80}, - }, - }, - }, - }, - }, - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Image: "foo:V2", - Ports: []api.ContainerPort{ - {HostPort: 8000, ContainerPort: 80}, - }, - }, - }, - }, - }, - false, - "port change", - }, - { - api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "Bar": "foo", - }, - }, - }, - true, - "bad label change", - }, - } - - for _, test := range tests { - test.a.ObjectMeta.ResourceVersion = "1" - test.b.ObjectMeta.ResourceVersion = "1" - errs := ValidatePodUpdate(&test.a, &test.b) - if test.isValid { - if len(errs) != 0 { - t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.a, test.b) - } - } else { - if len(errs) == 0 { - t.Errorf("unexpected valid: %s\nA: %+v\nB: %+v", test.test, test.a, test.b) - } - } - } -} - -func makeValidService() api.Service { - return api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "valid", - Namespace: "valid", - Labels: map[string]string{}, - Annotations: map[string]string{}, - ResourceVersion: "1", - }, - Spec: api.ServiceSpec{ - Selector: map[string]string{"key": "val"}, - SessionAffinity: "None", - Type: api.ServiceTypeClusterIP, - Ports: []api.ServicePort{{Name: "p", Protocol: "TCP", Port: 8675, TargetPort: intstr.FromInt(8675)}}, - }, - } -} - -func TestValidateService(t *testing.T) { - testCases := []struct { - name string - tweakSvc func(svc *api.Service) // given a basic valid service, each test case can customize it - numErrs int - }{ - { - name: "missing namespace", - tweakSvc: func(s *api.Service) { - s.Namespace = "" - }, - numErrs: 1, - }, - { - name: "invalid namespace", - tweakSvc: func(s *api.Service) { - s.Namespace = "-123" - }, - numErrs: 1, - }, - { - name: "missing name", - tweakSvc: func(s *api.Service) { - s.Name = "" - }, - numErrs: 1, - }, - { - name: "invalid name", - tweakSvc: func(s *api.Service) { - s.Name = "-123" - }, - numErrs: 1, - }, - { - name: "too long name", - tweakSvc: func(s *api.Service) { - s.Name = strings.Repeat("a", 25) - }, - numErrs: 1, - }, - { - name: "invalid generateName", - tweakSvc: func(s *api.Service) { - s.GenerateName = "-123" - }, - numErrs: 1, - }, - { - name: "too long generateName", - tweakSvc: func(s *api.Service) { - s.GenerateName = strings.Repeat("a", 25) - }, - numErrs: 1, - }, - { - name: "invalid label", - tweakSvc: func(s *api.Service) { - s.Labels["NoUppercaseOrSpecialCharsLike=Equals"] = "bar" - }, - numErrs: 1, - }, - { - name: "invalid annotation", - tweakSvc: func(s *api.Service) { - s.Annotations["NoSpecialCharsLike=Equals"] = "bar" - }, - numErrs: 1, - }, - { - name: "nil selector", - tweakSvc: func(s *api.Service) { - s.Spec.Selector = nil - }, - numErrs: 0, - }, - { - name: "invalid selector", - tweakSvc: func(s *api.Service) { - s.Spec.Selector["NoSpecialCharsLike=Equals"] = "bar" - }, - numErrs: 1, - }, - { - name: "missing session affinity", - tweakSvc: func(s *api.Service) { - s.Spec.SessionAffinity = "" - }, - numErrs: 1, - }, - { - name: "missing type", - tweakSvc: func(s *api.Service) { - s.Spec.Type = "" - }, - numErrs: 1, - }, - { - name: "missing ports", - tweakSvc: func(s *api.Service) { - s.Spec.Ports = nil - }, - numErrs: 1, - }, - { - name: "missing ports but headless", - tweakSvc: func(s *api.Service) { - s.Spec.Ports = nil - s.Spec.ClusterIP = api.ClusterIPNone - }, - numErrs: 0, - }, - { - name: "empty port[0] name", - tweakSvc: func(s *api.Service) { - s.Spec.Ports[0].Name = "" - }, - numErrs: 0, - }, - { - name: "empty port[1] name", - tweakSvc: func(s *api.Service) { - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "", Protocol: "TCP", Port: 12345, TargetPort: intstr.FromInt(12345)}) - }, - numErrs: 1, - }, - { - name: "empty multi-port port[0] name", - tweakSvc: func(s *api.Service) { - s.Spec.Ports[0].Name = "" - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "p", Protocol: "TCP", Port: 12345, TargetPort: intstr.FromInt(12345)}) - }, - numErrs: 1, - }, - { - name: "invalid port name", - tweakSvc: func(s *api.Service) { - s.Spec.Ports[0].Name = "INVALID" - }, - numErrs: 1, - }, - { - name: "missing protocol", - tweakSvc: func(s *api.Service) { - s.Spec.Ports[0].Protocol = "" - }, - numErrs: 1, - }, - { - name: "invalid protocol", - tweakSvc: func(s *api.Service) { - s.Spec.Ports[0].Protocol = "INVALID" - }, - numErrs: 1, - }, - { - name: "invalid cluster ip", - tweakSvc: func(s *api.Service) { - s.Spec.ClusterIP = "invalid" - }, - numErrs: 1, - }, - { - name: "missing port", - tweakSvc: func(s *api.Service) { - s.Spec.Ports[0].Port = 0 - }, - numErrs: 1, - }, - { - name: "invalid port", - tweakSvc: func(s *api.Service) { - s.Spec.Ports[0].Port = 65536 - }, - numErrs: 1, - }, - { - name: "invalid TargetPort int", - tweakSvc: func(s *api.Service) { - s.Spec.Ports[0].TargetPort = intstr.FromInt(65536) - }, - numErrs: 1, - }, - { - name: "valid port headless", - tweakSvc: func(s *api.Service) { - s.Spec.Ports[0].Port = 11722 - s.Spec.Ports[0].TargetPort = intstr.FromInt(11722) - s.Spec.ClusterIP = api.ClusterIPNone - }, - numErrs: 0, - }, - { - name: "invalid port headless", - tweakSvc: func(s *api.Service) { - s.Spec.Ports[0].Port = 11722 - s.Spec.Ports[0].TargetPort = intstr.FromInt(11721) - s.Spec.ClusterIP = api.ClusterIPNone - }, - numErrs: 1, - }, - { - name: "invalid port headless", - tweakSvc: func(s *api.Service) { - s.Spec.Ports[0].Port = 11722 - s.Spec.Ports[0].TargetPort = intstr.FromString("target") - s.Spec.ClusterIP = api.ClusterIPNone - }, - numErrs: 1, - }, - { - name: "invalid publicIPs localhost", - tweakSvc: func(s *api.Service) { - s.Spec.ExternalIPs = []string{"127.0.0.1"} - }, - numErrs: 1, - }, - { - name: "invalid publicIPs", - tweakSvc: func(s *api.Service) { - s.Spec.ExternalIPs = []string{"0.0.0.0"} - }, - numErrs: 1, - }, - { - name: "invalid publicIPs host", - tweakSvc: func(s *api.Service) { - s.Spec.ExternalIPs = []string{"myhost.mydomain"} - }, - numErrs: 1, - }, - { - name: "dup port name", - tweakSvc: func(s *api.Service) { - s.Spec.Ports[0].Name = "p" - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "p", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)}) - }, - numErrs: 1, - }, - { - name: "valid load balancer protocol UDP 1", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeLoadBalancer - s.Spec.Ports[0].Protocol = "UDP" - }, - numErrs: 0, - }, - { - name: "valid load balancer protocol UDP 2", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeLoadBalancer - s.Spec.Ports[0] = api.ServicePort{Name: "q", Port: 12345, Protocol: "UDP", TargetPort: intstr.FromInt(12345)} - }, - numErrs: 0, - }, - { - name: "invalid load balancer with mix protocol", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeLoadBalancer - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 12345, Protocol: "UDP", TargetPort: intstr.FromInt(12345)}) - }, - numErrs: 1, - }, - { - name: "valid 1", - tweakSvc: func(s *api.Service) { - // do nothing - }, - numErrs: 0, - }, - { - name: "valid 2", - tweakSvc: func(s *api.Service) { - s.Spec.Ports[0].Protocol = "UDP" - s.Spec.Ports[0].TargetPort = intstr.FromInt(12345) - }, - numErrs: 0, - }, - { - name: "valid 3", - tweakSvc: func(s *api.Service) { - s.Spec.Ports[0].TargetPort = intstr.FromString("http") - }, - numErrs: 0, - }, - { - name: "valid cluster ip - none ", - tweakSvc: func(s *api.Service) { - s.Spec.ClusterIP = "None" - }, - numErrs: 0, - }, - { - name: "valid cluster ip - empty", - tweakSvc: func(s *api.Service) { - s.Spec.ClusterIP = "" - s.Spec.Ports[0].TargetPort = intstr.FromString("http") - }, - numErrs: 0, - }, - { - name: "valid type - cluster", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeClusterIP - }, - numErrs: 0, - }, - { - name: "valid type - loadbalancer", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeLoadBalancer - }, - numErrs: 0, - }, - { - name: "valid type loadbalancer 2 ports", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeLoadBalancer - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)}) - }, - numErrs: 0, - }, - { - name: "valid external load balancer 2 ports", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeLoadBalancer - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)}) - }, - numErrs: 0, - }, - { - name: "duplicate nodeports", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeNodePort - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)}) - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "r", Port: 2, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(2)}) - }, - numErrs: 1, - }, - { - name: "duplicate nodeports (different protocols)", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeNodePort - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)}) - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "r", Port: 2, Protocol: "UDP", NodePort: 1, TargetPort: intstr.FromInt(2)}) - }, - numErrs: 0, - }, - { - name: "valid type - cluster", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeClusterIP - }, - numErrs: 0, - }, - { - name: "valid type - nodeport", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeNodePort - }, - numErrs: 0, - }, - { - name: "valid type - loadbalancer", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeLoadBalancer - }, - numErrs: 0, - }, - { - name: "valid type loadbalancer 2 ports", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeLoadBalancer - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)}) - }, - numErrs: 0, - }, - { - name: "valid type loadbalancer with NodePort", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeLoadBalancer - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", NodePort: 12345, TargetPort: intstr.FromInt(12345)}) - }, - numErrs: 0, - }, - { - name: "valid type=NodePort service with NodePort", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeNodePort - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", NodePort: 12345, TargetPort: intstr.FromInt(12345)}) - }, - numErrs: 0, - }, - { - name: "valid type=NodePort service without NodePort", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeNodePort - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)}) - }, - numErrs: 0, - }, - { - name: "valid cluster service without NodePort", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeClusterIP - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)}) - }, - numErrs: 0, - }, - { - name: "invalid cluster service with NodePort", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeClusterIP - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", NodePort: 12345, TargetPort: intstr.FromInt(12345)}) - }, - numErrs: 1, - }, - { - name: "invalid public service with duplicate NodePort", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeNodePort - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "p1", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)}) - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "p2", Port: 2, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(2)}) - }, - numErrs: 1, - }, - { - name: "valid type=LoadBalancer", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeLoadBalancer - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(12345)}) - }, - numErrs: 0, - }, - { - // For now we open firewalls, and its insecure if we open 10250, remove this - // when we have better protections in place. - name: "invalid port type=LoadBalancer", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeLoadBalancer - s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "kubelet", Port: 10250, Protocol: "TCP", TargetPort: intstr.FromInt(12345)}) - }, - numErrs: 1, - }, - } - - for _, tc := range testCases { - svc := makeValidService() - tc.tweakSvc(&svc) - errs := ValidateService(&svc) - if len(errs) != tc.numErrs { - t.Errorf("Unexpected error list for case %q: %v", tc.name, errs.ToAggregate()) - } - } -} - -func TestValidateReplicationControllerStatusUpdate(t *testing.T) { - validSelector := map[string]string{"a": "b"} - validPodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validSelector, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - }, - } - type rcUpdateTest struct { - old api.ReplicationController - update api.ReplicationController - } - successCases := []rcUpdateTest{ - { - old: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - Status: api.ReplicationControllerStatus{ - Replicas: 2, - }, - }, - update: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Replicas: 3, - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - Status: api.ReplicationControllerStatus{ - Replicas: 4, - }, - }, - }, - } - for _, successCase := range successCases { - successCase.old.ObjectMeta.ResourceVersion = "1" - successCase.update.ObjectMeta.ResourceVersion = "1" - if errs := ValidateReplicationControllerStatusUpdate(&successCase.update, &successCase.old); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - errorCases := map[string]rcUpdateTest{ - "negative replicas": { - old: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - Status: api.ReplicationControllerStatus{ - Replicas: 3, - }, - }, - update: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Replicas: 2, - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - Status: api.ReplicationControllerStatus{ - Replicas: -3, - }, - }, - }, - } - for testName, errorCase := range errorCases { - if errs := ValidateReplicationControllerStatusUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 { - t.Errorf("expected failure: %s", testName) - } - } - -} - -func TestValidateReplicationControllerUpdate(t *testing.T) { - validSelector := map[string]string{"a": "b"} - validPodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validSelector, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - }, - } - readWriteVolumePodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validSelector, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}}, - }, - }, - } - invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} - invalidPodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - ObjectMeta: api.ObjectMeta{ - Labels: invalidSelector, - }, - }, - } - type rcUpdateTest struct { - old api.ReplicationController - update api.ReplicationController - } - successCases := []rcUpdateTest{ - { - old: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - }, - update: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Replicas: 3, - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - }, - }, - { - old: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - }, - update: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Replicas: 1, - Selector: validSelector, - Template: &readWriteVolumePodTemplate.Template, - }, - }, - }, - } - for _, successCase := range successCases { - successCase.old.ObjectMeta.ResourceVersion = "1" - successCase.update.ObjectMeta.ResourceVersion = "1" - if errs := ValidateReplicationControllerUpdate(&successCase.update, &successCase.old); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - errorCases := map[string]rcUpdateTest{ - "more than one read/write": { - old: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - }, - update: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Replicas: 2, - Selector: validSelector, - Template: &readWriteVolumePodTemplate.Template, - }, - }, - }, - "invalid selector": { - old: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - }, - update: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Replicas: 2, - Selector: invalidSelector, - Template: &validPodTemplate.Template, - }, - }, - }, - "invalid pod": { - old: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - }, - update: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Replicas: 2, - Selector: validSelector, - Template: &invalidPodTemplate.Template, - }, - }, - }, - "negative replicas": { - old: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - }, - update: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Replicas: -1, - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - }, - }, - } - for testName, errorCase := range errorCases { - if errs := ValidateReplicationControllerUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 { - t.Errorf("expected failure: %s", testName) - } - } -} - -func TestValidateReplicationController(t *testing.T) { - validSelector := map[string]string{"a": "b"} - validPodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validSelector, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - }, - } - readWriteVolumePodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validSelector, - }, - Spec: api.PodSpec{ - Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - }, - } - invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} - invalidPodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - ObjectMeta: api.ObjectMeta{ - Labels: invalidSelector, - }, - }, - } - successCases := []api.ReplicationController{ - { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - }, - { - ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - }, - { - ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Replicas: 1, - Selector: validSelector, - Template: &readWriteVolumePodTemplate.Template, - }, - }, - } - for _, successCase := range successCases { - if errs := ValidateReplicationController(&successCase); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := map[string]api.ReplicationController{ - "zero-length ID": { - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - }, - "missing-namespace": { - ObjectMeta: api.ObjectMeta{Name: "abc-123"}, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - }, - "empty selector": { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Template: &validPodTemplate.Template, - }, - }, - "selector_doesnt_match": { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Selector: map[string]string{"foo": "bar"}, - Template: &validPodTemplate.Template, - }, - }, - "invalid manifest": { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - }, - }, - "read-write persistent disk with > 1 pod": { - ObjectMeta: api.ObjectMeta{Name: "abc"}, - Spec: api.ReplicationControllerSpec{ - Replicas: 2, - Selector: validSelector, - Template: &readWriteVolumePodTemplate.Template, - }, - }, - "negative_replicas": { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: api.ReplicationControllerSpec{ - Replicas: -1, - Selector: validSelector, - }, - }, - "invalid_label": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Namespace: api.NamespaceDefault, - Labels: map[string]string{ - "NoUppercaseOrSpecialCharsLike=Equals": "bar", - }, - }, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - }, - "invalid_label 2": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Namespace: api.NamespaceDefault, - Labels: map[string]string{ - "NoUppercaseOrSpecialCharsLike=Equals": "bar", - }, - }, - Spec: api.ReplicationControllerSpec{ - Template: &invalidPodTemplate.Template, - }, - }, - "invalid_annotation": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Namespace: api.NamespaceDefault, - Annotations: map[string]string{ - "NoUppercaseOrSpecialCharsLike=Equals": "bar", - }, - }, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, - }, - }, - "invalid restart policy 1": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Namespace: api.NamespaceDefault, - }, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &api.PodTemplateSpec{ - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyOnFailure, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - ObjectMeta: api.ObjectMeta{ - Labels: validSelector, - }, - }, - }, - }, - "invalid restart policy 2": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Namespace: api.NamespaceDefault, - }, - Spec: api.ReplicationControllerSpec{ - Selector: validSelector, - Template: &api.PodTemplateSpec{ - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyNever, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - ObjectMeta: api.ObjectMeta{ - Labels: validSelector, - }, - }, - }, - }, - } - for k, v := range errorCases { - errs := ValidateReplicationController(&v) - if len(errs) == 0 { - t.Errorf("expected failure for %s", k) - } - for i := range errs { - field := errs[i].Field - if !strings.HasPrefix(field, "spec.template.") && - field != "metadata.name" && - field != "metadata.namespace" && - field != "spec.selector" && - field != "spec.template" && - field != "GCEPersistentDisk.ReadOnly" && - field != "spec.replicas" && - field != "spec.template.labels" && - field != "metadata.annotations" && - field != "metadata.labels" && - field != "status.replicas" { - t.Errorf("%s: missing prefix for: %v", k, errs[i]) - } - } - } -} - -func TestValidateNode(t *testing.T) { - validSelector := map[string]string{"a": "b"} - invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} - successCases := []api.Node{ - { - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Labels: validSelector, - }, - Status: api.NodeStatus{ - Addresses: []api.NodeAddress{ - {Type: api.NodeLegacyHostIP, Address: "something"}, - }, - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("10"), - api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), - api.ResourceName("my.org/gpu"): resource.MustParse("10"), - }, - }, - Spec: api.NodeSpec{ - ExternalID: "external", - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "abc", - }, - Status: api.NodeStatus{ - Addresses: []api.NodeAddress{ - {Type: api.NodeLegacyHostIP, Address: "something"}, - }, - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("10"), - api.ResourceName(api.ResourceMemory): resource.MustParse("0"), - }, - }, - Spec: api.NodeSpec{ - ExternalID: "external", - }, - }, - } - for _, successCase := range successCases { - if errs := ValidateNode(&successCase); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := map[string]api.Node{ - "zero-length Name": { - ObjectMeta: api.ObjectMeta{ - Name: "", - Labels: validSelector, - }, - Status: api.NodeStatus{ - Addresses: []api.NodeAddress{}, - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("10"), - api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), - }, - }, - Spec: api.NodeSpec{ - ExternalID: "external", - }, - }, - "invalid-labels": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Labels: invalidSelector, - }, - Status: api.NodeStatus{ - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("10"), - api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), - }, - }, - Spec: api.NodeSpec{ - ExternalID: "external", - }, - }, - "missing-external-id": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Labels: validSelector, - }, - Status: api.NodeStatus{ - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("10"), - api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), - }, - }, - }, - } - for k, v := range errorCases { - errs := ValidateNode(&v) - if len(errs) == 0 { - t.Errorf("expected failure for %s", k) - } - for i := range errs { - field := errs[i].Field - expectedFields := map[string]bool{ - "metadata.name": true, - "metadata.labels": true, - "metadata.annotations": true, - "metadata.namespace": true, - "spec.externalID": true, - } - if expectedFields[field] == false { - t.Errorf("%s: missing prefix for: %v", k, errs[i]) - } - } - } -} - -func TestValidateNodeUpdate(t *testing.T) { - tests := []struct { - oldNode api.Node - node api.Node - valid bool - }{ - {api.Node{}, api.Node{}, true}, - {api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo"}}, - api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "bar"}, - }, false}, - {api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "bar"}, - }, - }, api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "baz"}, - }, - }, true}, - {api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - }, api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "baz"}, - }, - }, true}, - {api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"bar": "foo"}, - }, - }, api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "baz"}, - }, - }, true}, - {api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.NodeSpec{ - PodCIDR: "", - }, - }, api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.NodeSpec{ - PodCIDR: "192.168.0.0/16", - }, - }, true}, - {api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.NodeSpec{ - PodCIDR: "192.123.0.0/16", - }, - }, api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.NodeSpec{ - PodCIDR: "192.168.0.0/16", - }, - }, false}, - {api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Status: api.NodeStatus{ - Capacity: api.ResourceList{ - api.ResourceCPU: resource.MustParse("10000"), - api.ResourceMemory: resource.MustParse("100"), - }, - }, - }, api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Status: api.NodeStatus{ - Capacity: api.ResourceList{ - api.ResourceCPU: resource.MustParse("100"), - api.ResourceMemory: resource.MustParse("10000"), - }, - }, - }, true}, - {api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"bar": "foo"}, - }, - Status: api.NodeStatus{ - Capacity: api.ResourceList{ - api.ResourceCPU: resource.MustParse("10000"), - api.ResourceMemory: resource.MustParse("100"), - }, - }, - }, api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"bar": "fooobaz"}, - }, - Status: api.NodeStatus{ - Capacity: api.ResourceList{ - api.ResourceCPU: resource.MustParse("100"), - api.ResourceMemory: resource.MustParse("10000"), - }, - }, - }, true}, - {api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"bar": "foo"}, - }, - Status: api.NodeStatus{ - Addresses: []api.NodeAddress{ - {Type: api.NodeLegacyHostIP, Address: "1.2.3.4"}, - }, - }, - }, api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"bar": "fooobaz"}, - }, - }, true}, - {api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "baz"}, - }, - }, api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"Foo": "baz"}, - }, - }, true}, - {api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.NodeSpec{ - Unschedulable: false, - }, - }, api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.NodeSpec{ - Unschedulable: true, - }, - }, true}, - {api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.NodeSpec{ - Unschedulable: false, - }, - }, api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Status: api.NodeStatus{ - Addresses: []api.NodeAddress{ - {Type: api.NodeExternalIP, Address: "1.1.1.1"}, - {Type: api.NodeExternalIP, Address: "1.1.1.1"}, - }, - }, - }, false}, - {api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.NodeSpec{ - Unschedulable: false, - }, - }, api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Status: api.NodeStatus{ - Addresses: []api.NodeAddress{ - {Type: api.NodeExternalIP, Address: "1.1.1.1"}, - {Type: api.NodeInternalIP, Address: "10.1.1.1"}, - }, - }, - }, true}, - } - for i, test := range tests { - test.oldNode.ObjectMeta.ResourceVersion = "1" - test.node.ObjectMeta.ResourceVersion = "1" - errs := ValidateNodeUpdate(&test.node, &test.oldNode) - if test.valid && len(errs) > 0 { - t.Errorf("%d: Unexpected error: %v", i, errs) - t.Logf("%#v vs %#v", test.oldNode.ObjectMeta, test.node.ObjectMeta) - } - if !test.valid && len(errs) == 0 { - t.Errorf("%d: Unexpected non-error", i) - } - } -} - -func TestValidateServiceUpdate(t *testing.T) { - testCases := []struct { - name string - tweakSvc func(oldSvc, newSvc *api.Service) // given basic valid services, each test case can customize them - numErrs int - }{ - { - name: "no change", - tweakSvc: func(oldSvc, newSvc *api.Service) { - // do nothing - }, - numErrs: 0, - }, - { - name: "change name", - tweakSvc: func(oldSvc, newSvc *api.Service) { - newSvc.Name += "2" - }, - numErrs: 1, - }, - { - name: "change namespace", - tweakSvc: func(oldSvc, newSvc *api.Service) { - newSvc.Namespace += "2" - }, - numErrs: 1, - }, - { - name: "change label valid", - tweakSvc: func(oldSvc, newSvc *api.Service) { - newSvc.Labels["key"] = "other-value" - }, - numErrs: 0, - }, - { - name: "add label", - tweakSvc: func(oldSvc, newSvc *api.Service) { - newSvc.Labels["key2"] = "value2" - }, - numErrs: 0, - }, - { - name: "change cluster IP", - tweakSvc: func(oldSvc, newSvc *api.Service) { - oldSvc.Spec.ClusterIP = "1.2.3.4" - newSvc.Spec.ClusterIP = "8.6.7.5" - }, - numErrs: 1, - }, - { - name: "remove cluster IP", - tweakSvc: func(oldSvc, newSvc *api.Service) { - oldSvc.Spec.ClusterIP = "1.2.3.4" - newSvc.Spec.ClusterIP = "" - }, - numErrs: 1, - }, - { - name: "change affinity", - tweakSvc: func(oldSvc, newSvc *api.Service) { - newSvc.Spec.SessionAffinity = "ClientIP" - }, - numErrs: 0, - }, - { - name: "remove affinity", - tweakSvc: func(oldSvc, newSvc *api.Service) { - newSvc.Spec.SessionAffinity = "" - }, - numErrs: 1, - }, - { - name: "change type", - tweakSvc: func(oldSvc, newSvc *api.Service) { - newSvc.Spec.Type = api.ServiceTypeLoadBalancer - }, - numErrs: 0, - }, - { - name: "remove type", - tweakSvc: func(oldSvc, newSvc *api.Service) { - newSvc.Spec.Type = "" - }, - numErrs: 1, - }, - { - name: "change type -> nodeport", - tweakSvc: func(oldSvc, newSvc *api.Service) { - newSvc.Spec.Type = api.ServiceTypeNodePort - }, - numErrs: 0, - }, - } - - for _, tc := range testCases { - oldSvc := makeValidService() - newSvc := makeValidService() - tc.tweakSvc(&oldSvc, &newSvc) - errs := ValidateServiceUpdate(&newSvc, &oldSvc) - if len(errs) != tc.numErrs { - t.Errorf("Unexpected error list for case %q: %v", tc.name, errs.ToAggregate()) - } - } -} - -func TestValidateResourceNames(t *testing.T) { - table := []struct { - input string - success bool - }{ - {"memory", true}, - {"cpu", true}, - {"network", false}, - {"disk", false}, - {"", false}, - {".", false}, - {"..", false}, - {"my.favorite.app.co/12345", true}, - {"my.favorite.app.co/_12345", false}, - {"my.favorite.app.co/12345_", false}, - {"kubernetes.io/..", false}, - {"kubernetes.io/" + strings.Repeat("a", 63), true}, - {"kubernetes.io/" + strings.Repeat("a", 64), false}, - {"kubernetes.io//", false}, - {"kubernetes.io", false}, - {"kubernetes.io/will/not/work/", false}, - } - for k, item := range table { - err := validateResourceName(item.input, field.NewPath("field")) - if len(err) != 0 && item.success { - t.Errorf("expected no failure for input %q", item.input) - } else if len(err) == 0 && !item.success { - t.Errorf("expected failure for input %q", item.input) - for i := range err { - detail := err[i].Detail - if detail != "" && detail != qualifiedNameErrorMsg { - t.Errorf("%d: expected error detail either empty or %s, got %s", k, qualifiedNameErrorMsg, detail) - } - } - } - } -} - -func getResourceList(cpu, memory string) api.ResourceList { - res := api.ResourceList{} - if cpu != "" { - res[api.ResourceCPU] = resource.MustParse(cpu) - } - if memory != "" { - res[api.ResourceMemory] = resource.MustParse(memory) - } - return res -} - -func TestValidateLimitRange(t *testing.T) { - successCases := []struct { - name string - spec api.LimitRangeSpec - }{ - { - name: "all-fields-valid", - spec: api.LimitRangeSpec{ - Limits: []api.LimitRangeItem{ - { - Type: api.LimitTypePod, - Max: getResourceList("100m", "10000Mi"), - Min: getResourceList("5m", "100Mi"), - MaxLimitRequestRatio: getResourceList("10", ""), - }, - { - Type: api.LimitTypeContainer, - Max: getResourceList("100m", "10000Mi"), - Min: getResourceList("5m", "100Mi"), - Default: getResourceList("50m", "500Mi"), - DefaultRequest: getResourceList("10m", "200Mi"), - MaxLimitRequestRatio: getResourceList("10", ""), - }, - }, - }, - }, - { - name: "all-fields-valid-big-numbers", - spec: api.LimitRangeSpec{ - Limits: []api.LimitRangeItem{ - { - Type: api.LimitTypeContainer, - Max: getResourceList("100m", "10000T"), - Min: getResourceList("5m", "100Mi"), - Default: getResourceList("50m", "500Mi"), - DefaultRequest: getResourceList("10m", "200Mi"), - MaxLimitRequestRatio: getResourceList("10", ""), - }, - }, - }, - }, - } - - for _, successCase := range successCases { - limitRange := &api.LimitRange{ObjectMeta: api.ObjectMeta{Name: successCase.name, Namespace: "foo"}, Spec: successCase.spec} - if errs := ValidateLimitRange(limitRange); len(errs) != 0 { - t.Errorf("Case %v, unexpected error: %v", successCase.name, errs) - } - } - - errorCases := map[string]struct { - R api.LimitRange - D string - }{ - "zero-length-name": { - api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "", Namespace: "foo"}, Spec: api.LimitRangeSpec{}}, - "name or generateName is required", - }, - "zero-length-namespace": { - api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: ""}, Spec: api.LimitRangeSpec{}}, - "", - }, - "invalid-name": { - api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "^Invalid", Namespace: "foo"}, Spec: api.LimitRangeSpec{}}, - DNSSubdomainErrorMsg, - }, - "invalid-namespace": { - api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "^Invalid"}, Spec: api.LimitRangeSpec{}}, - DNS1123LabelErrorMsg, - }, - "duplicate-limit-type": { - api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: api.LimitRangeSpec{ - Limits: []api.LimitRangeItem{ - { - Type: api.LimitTypePod, - Max: getResourceList("100m", "10000m"), - Min: getResourceList("0m", "100m"), - }, - { - Type: api.LimitTypePod, - Min: getResourceList("0m", "100m"), - }, - }, - }}, - "", - }, - "default-limit-type-pod": { - api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: api.LimitRangeSpec{ - Limits: []api.LimitRangeItem{ - { - Type: api.LimitTypePod, - Max: getResourceList("100m", "10000m"), - Min: getResourceList("0m", "100m"), - Default: getResourceList("10m", "100m"), - }, - }, - }}, - "may not be specified when `type` is 'Pod'", - }, - "default-request-limit-type-pod": { - api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: api.LimitRangeSpec{ - Limits: []api.LimitRangeItem{ - { - Type: api.LimitTypePod, - Max: getResourceList("100m", "10000m"), - Min: getResourceList("0m", "100m"), - DefaultRequest: getResourceList("10m", "100m"), - }, - }, - }}, - "may not be specified when `type` is 'Pod'", - }, - "min value 100m is greater than max value 10m": { - api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: api.LimitRangeSpec{ - Limits: []api.LimitRangeItem{ - { - Type: api.LimitTypePod, - Max: getResourceList("10m", ""), - Min: getResourceList("100m", ""), - }, - }, - }}, - "min value 100m is greater than max value 10m", - }, - "invalid spec default outside range": { - api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: api.LimitRangeSpec{ - Limits: []api.LimitRangeItem{ - { - Type: api.LimitTypeContainer, - Max: getResourceList("1", ""), - Min: getResourceList("100m", ""), - Default: getResourceList("2000m", ""), - }, - }, - }}, - "default value 2 is greater than max value 1", - }, - "invalid spec defaultrequest outside range": { - api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: api.LimitRangeSpec{ - Limits: []api.LimitRangeItem{ - { - Type: api.LimitTypeContainer, - Max: getResourceList("1", ""), - Min: getResourceList("100m", ""), - DefaultRequest: getResourceList("2000m", ""), - }, - }, - }}, - "default request value 2 is greater than max value 1", - }, - "invalid spec defaultrequest more than default": { - api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: api.LimitRangeSpec{ - Limits: []api.LimitRangeItem{ - { - Type: api.LimitTypeContainer, - Max: getResourceList("2", ""), - Min: getResourceList("100m", ""), - Default: getResourceList("500m", ""), - DefaultRequest: getResourceList("800m", ""), - }, - }, - }}, - "default request value 800m is greater than default limit value 500m", - }, - "invalid spec maxLimitRequestRatio less than 1": { - api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: api.LimitRangeSpec{ - Limits: []api.LimitRangeItem{ - { - Type: api.LimitTypePod, - MaxLimitRequestRatio: getResourceList("800m", ""), - }, - }, - }}, - "ratio 800m is less than 1", - }, - "invalid spec maxLimitRequestRatio greater than max/min": { - api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: api.LimitRangeSpec{ - Limits: []api.LimitRangeItem{ - { - Type: api.LimitTypeContainer, - Max: getResourceList("", "2Gi"), - Min: getResourceList("", "512Mi"), - MaxLimitRequestRatio: getResourceList("", "10"), - }, - }, - }}, - "ratio 10 is greater than max/min = 4.000000", - }, - } - - for k, v := range errorCases { - errs := ValidateLimitRange(&v.R) - if len(errs) == 0 { - t.Errorf("expected failure for %s", k) - } - for i := range errs { - detail := errs[i].Detail - if detail != v.D { - t.Errorf("[%s]: expected error detail either empty or %q, got %q", k, v.D, detail) - } - } - } - -} - -func TestValidateResourceQuota(t *testing.T) { - spec := api.ResourceQuotaSpec{ - Hard: api.ResourceList{ - api.ResourceCPU: resource.MustParse("100"), - api.ResourceMemory: resource.MustParse("10000"), - api.ResourcePods: resource.MustParse("10"), - api.ResourceServices: resource.MustParse("0"), - api.ResourceReplicationControllers: resource.MustParse("10"), - api.ResourceQuotas: resource.MustParse("10"), - }, - } - - negativeSpec := api.ResourceQuotaSpec{ - Hard: api.ResourceList{ - api.ResourceCPU: resource.MustParse("-100"), - api.ResourceMemory: resource.MustParse("-10000"), - api.ResourcePods: resource.MustParse("-10"), - api.ResourceServices: resource.MustParse("-10"), - api.ResourceReplicationControllers: resource.MustParse("-10"), - api.ResourceQuotas: resource.MustParse("-10"), - }, - } - - fractionalComputeSpec := api.ResourceQuotaSpec{ - Hard: api.ResourceList{ - api.ResourceCPU: resource.MustParse("100m"), - }, - } - - fractionalPodSpec := api.ResourceQuotaSpec{ - Hard: api.ResourceList{ - api.ResourcePods: resource.MustParse(".1"), - api.ResourceServices: resource.MustParse(".5"), - api.ResourceReplicationControllers: resource.MustParse("1.25"), - api.ResourceQuotas: resource.MustParse("2.5"), - }, - } - - successCases := []api.ResourceQuota{ - { - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: "foo", - }, - Spec: spec, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: "foo", - }, - Spec: fractionalComputeSpec, - }, - } - - for _, successCase := range successCases { - if errs := ValidateResourceQuota(&successCase); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := map[string]struct { - R api.ResourceQuota - D string - }{ - "zero-length Name": { - api.ResourceQuota{ObjectMeta: api.ObjectMeta{Name: "", Namespace: "foo"}, Spec: spec}, - "name or generateName is required", - }, - "zero-length Namespace": { - api.ResourceQuota{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: ""}, Spec: spec}, - "", - }, - "invalid Name": { - api.ResourceQuota{ObjectMeta: api.ObjectMeta{Name: "^Invalid", Namespace: "foo"}, Spec: spec}, - DNSSubdomainErrorMsg, - }, - "invalid Namespace": { - api.ResourceQuota{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "^Invalid"}, Spec: spec}, - DNS1123LabelErrorMsg, - }, - "negative-limits": { - api.ResourceQuota{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: negativeSpec}, - isNegativeErrorMsg, - }, - "fractional-api-resource": { - api.ResourceQuota{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: fractionalPodSpec}, - isNotIntegerErrorMsg, - }, - } - for k, v := range errorCases { - errs := ValidateResourceQuota(&v.R) - if len(errs) == 0 { - t.Errorf("expected failure for %s", k) - } - for i := range errs { - if errs[i].Detail != v.D { - t.Errorf("[%s]: expected error detail either empty or %s, got %s", k, v.D, errs[i].Detail) - } - } - } -} - -func TestValidateNamespace(t *testing.T) { - validLabels := map[string]string{"a": "b"} - invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} - successCases := []api.Namespace{ - { - ObjectMeta: api.ObjectMeta{Name: "abc", Labels: validLabels}, - }, - { - ObjectMeta: api.ObjectMeta{Name: "abc-123"}, - Spec: api.NamespaceSpec{ - Finalizers: []api.FinalizerName{"example.com/something", "example.com/other"}, - }, - }, - } - for _, successCase := range successCases { - if errs := ValidateNamespace(&successCase); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - errorCases := map[string]struct { - R api.Namespace - D string - }{ - "zero-length name": { - api.Namespace{ObjectMeta: api.ObjectMeta{Name: ""}}, - "", - }, - "defined-namespace": { - api.Namespace{ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: "makesnosense"}}, - "", - }, - "invalid-labels": { - api.Namespace{ObjectMeta: api.ObjectMeta{Name: "abc", Labels: invalidLabels}}, - "", - }, - } - for k, v := range errorCases { - errs := ValidateNamespace(&v.R) - if len(errs) == 0 { - t.Errorf("expected failure for %s", k) - } - } -} - -func TestValidateNamespaceFinalizeUpdate(t *testing.T) { - tests := []struct { - oldNamespace api.Namespace - namespace api.Namespace - valid bool - }{ - {api.Namespace{}, api.Namespace{}, true}, - {api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo"}}, - api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo"}, - Spec: api.NamespaceSpec{ - Finalizers: []api.FinalizerName{"Foo"}, - }, - }, false}, - {api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo"}, - Spec: api.NamespaceSpec{ - Finalizers: []api.FinalizerName{"foo.com/bar"}, - }, - }, - api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo"}, - Spec: api.NamespaceSpec{ - Finalizers: []api.FinalizerName{"foo.com/bar", "what.com/bar"}, - }, - }, true}, - {api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "fooemptyfinalizer"}, - Spec: api.NamespaceSpec{ - Finalizers: []api.FinalizerName{"foo.com/bar"}, - }, - }, - api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "fooemptyfinalizer"}, - Spec: api.NamespaceSpec{ - Finalizers: []api.FinalizerName{"", "foo.com/bar", "what.com/bar"}, - }, - }, false}, - } - for i, test := range tests { - test.namespace.ObjectMeta.ResourceVersion = "1" - test.oldNamespace.ObjectMeta.ResourceVersion = "1" - errs := ValidateNamespaceFinalizeUpdate(&test.namespace, &test.oldNamespace) - if test.valid && len(errs) > 0 { - t.Errorf("%d: Unexpected error: %v", i, errs) - t.Logf("%#v vs %#v", test.oldNamespace, test.namespace) - } - if !test.valid && len(errs) == 0 { - t.Errorf("%d: Unexpected non-error", i) - } - } -} - -func TestValidateNamespaceStatusUpdate(t *testing.T) { - now := unversioned.Now() - - tests := []struct { - oldNamespace api.Namespace - namespace api.Namespace - valid bool - }{ - {api.Namespace{}, api.Namespace{ - Status: api.NamespaceStatus{ - Phase: api.NamespaceActive, - }, - }, true}, - {api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo"}}, - api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - DeletionTimestamp: &now}, - Status: api.NamespaceStatus{ - Phase: api.NamespaceTerminating, - }, - }, true}, - {api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo"}}, - api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo"}, - Status: api.NamespaceStatus{ - Phase: api.NamespaceTerminating, - }, - }, false}, - {api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo"}}, - api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "bar"}, - Status: api.NamespaceStatus{ - Phase: api.NamespaceTerminating, - }, - }, false}, - } - for i, test := range tests { - test.namespace.ObjectMeta.ResourceVersion = "1" - test.oldNamespace.ObjectMeta.ResourceVersion = "1" - errs := ValidateNamespaceStatusUpdate(&test.namespace, &test.oldNamespace) - if test.valid && len(errs) > 0 { - t.Errorf("%d: Unexpected error: %v", i, errs) - t.Logf("%#v vs %#v", test.oldNamespace.ObjectMeta, test.namespace.ObjectMeta) - } - if !test.valid && len(errs) == 0 { - t.Errorf("%d: Unexpected non-error", i) - } - } -} - -func TestValidateNamespaceUpdate(t *testing.T) { - tests := []struct { - oldNamespace api.Namespace - namespace api.Namespace - valid bool - }{ - {api.Namespace{}, api.Namespace{}, true}, - {api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo1"}}, - api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "bar1"}, - }, false}, - {api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo2", - Labels: map[string]string{"foo": "bar"}, - }, - }, api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo2", - Labels: map[string]string{"foo": "baz"}, - }, - }, true}, - {api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo3", - }, - }, api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo3", - Labels: map[string]string{"foo": "baz"}, - }, - }, true}, - {api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo4", - Labels: map[string]string{"bar": "foo"}, - }, - }, api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo4", - Labels: map[string]string{"foo": "baz"}, - }, - }, true}, - {api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo5", - Labels: map[string]string{"foo": "baz"}, - }, - }, api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo5", - Labels: map[string]string{"Foo": "baz"}, - }, - }, true}, - {api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo6", - Labels: map[string]string{"foo": "baz"}, - }, - }, api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo6", - Labels: map[string]string{"Foo": "baz"}, - }, - Spec: api.NamespaceSpec{ - Finalizers: []api.FinalizerName{"kubernetes"}, - }, - Status: api.NamespaceStatus{ - Phase: api.NamespaceTerminating, - }, - }, true}, - } - for i, test := range tests { - test.namespace.ObjectMeta.ResourceVersion = "1" - test.oldNamespace.ObjectMeta.ResourceVersion = "1" - errs := ValidateNamespaceUpdate(&test.namespace, &test.oldNamespace) - if test.valid && len(errs) > 0 { - t.Errorf("%d: Unexpected error: %v", i, errs) - t.Logf("%#v vs %#v", test.oldNamespace.ObjectMeta, test.namespace.ObjectMeta) - } - if !test.valid && len(errs) == 0 { - t.Errorf("%d: Unexpected non-error", i) - } - } -} - -func TestValidateSecret(t *testing.T) { - // Opaque secret validation - validSecret := func() api.Secret { - return api.Secret{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"}, - Data: map[string][]byte{ - "data-1": []byte("bar"), - }, - } - } - - var ( - emptyName = validSecret() - invalidName = validSecret() - emptyNs = validSecret() - invalidNs = validSecret() - overMaxSize = validSecret() - invalidKey = validSecret() - leadingDotKey = validSecret() - dotKey = validSecret() - doubleDotKey = validSecret() - ) - - emptyName.Name = "" - invalidName.Name = "NoUppercaseOrSpecialCharsLike=Equals" - emptyNs.Namespace = "" - invalidNs.Namespace = "NoUppercaseOrSpecialCharsLike=Equals" - overMaxSize.Data = map[string][]byte{ - "over": make([]byte, api.MaxSecretSize+1), - } - invalidKey.Data["a..b"] = []byte("whoops") - leadingDotKey.Data[".key"] = []byte("bar") - dotKey.Data["."] = []byte("bar") - doubleDotKey.Data[".."] = []byte("bar") - - // kubernetes.io/service-account-token secret validation - validServiceAccountTokenSecret := func() api.Secret { - return api.Secret{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "bar", - Annotations: map[string]string{ - api.ServiceAccountNameKey: "foo", - }, - }, - Type: api.SecretTypeServiceAccountToken, - Data: map[string][]byte{ - "data-1": []byte("bar"), - }, - } - } - - var ( - emptyTokenAnnotation = validServiceAccountTokenSecret() - missingTokenAnnotation = validServiceAccountTokenSecret() - missingTokenAnnotations = validServiceAccountTokenSecret() - ) - emptyTokenAnnotation.Annotations[api.ServiceAccountNameKey] = "" - delete(missingTokenAnnotation.Annotations, api.ServiceAccountNameKey) - missingTokenAnnotations.Annotations = nil - - tests := map[string]struct { - secret api.Secret - valid bool - }{ - "valid": {validSecret(), true}, - "empty name": {emptyName, false}, - "invalid name": {invalidName, false}, - "empty namespace": {emptyNs, false}, - "invalid namespace": {invalidNs, false}, - "over max size": {overMaxSize, false}, - "invalid key": {invalidKey, false}, - "valid service-account-token secret": {validServiceAccountTokenSecret(), true}, - "empty service-account-token annotation": {emptyTokenAnnotation, false}, - "missing service-account-token annotation": {missingTokenAnnotation, false}, - "missing service-account-token annotations": {missingTokenAnnotations, false}, - "leading dot key": {leadingDotKey, true}, - "dot key": {dotKey, false}, - "double dot key": {doubleDotKey, false}, - } - - for name, tc := range tests { - errs := ValidateSecret(&tc.secret) - if tc.valid && len(errs) > 0 { - t.Errorf("%v: Unexpected error: %v", name, errs) - } - if !tc.valid && len(errs) == 0 { - t.Errorf("%v: Unexpected non-error", name) - } - } -} - -func TestValidateDockerConfigSecret(t *testing.T) { - validDockerSecret := func() api.Secret { - return api.Secret{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"}, - Type: api.SecretTypeDockercfg, - Data: map[string][]byte{ - api.DockerConfigKey: []byte(`{"https://index.docker.io/v1/": {"auth": "Y2x1ZWRyb29sZXIwMDAxOnBhc3N3b3Jk","email": "fake@example.com"}}`), - }, - } - } - validDockerSecret2 := func() api.Secret { - return api.Secret{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"}, - Type: api.SecretTypeDockerConfigJson, - Data: map[string][]byte{ - api.DockerConfigJsonKey: []byte(`{"auths":{"https://index.docker.io/v1/": {"auth": "Y2x1ZWRyb29sZXIwMDAxOnBhc3N3b3Jk","email": "fake@example.com"}}}`), - }, - } - } - - var ( - missingDockerConfigKey = validDockerSecret() - emptyDockerConfigKey = validDockerSecret() - invalidDockerConfigKey = validDockerSecret() - missingDockerConfigKey2 = validDockerSecret2() - emptyDockerConfigKey2 = validDockerSecret2() - invalidDockerConfigKey2 = validDockerSecret2() - ) - - delete(missingDockerConfigKey.Data, api.DockerConfigKey) - emptyDockerConfigKey.Data[api.DockerConfigKey] = []byte("") - invalidDockerConfigKey.Data[api.DockerConfigKey] = []byte("bad") - delete(missingDockerConfigKey2.Data, api.DockerConfigJsonKey) - emptyDockerConfigKey2.Data[api.DockerConfigJsonKey] = []byte("") - invalidDockerConfigKey2.Data[api.DockerConfigJsonKey] = []byte("bad") - - tests := map[string]struct { - secret api.Secret - valid bool - }{ - "valid dockercfg": {validDockerSecret(), true}, - "missing dockercfg": {missingDockerConfigKey, false}, - "empty dockercfg": {emptyDockerConfigKey, false}, - "invalid dockercfg": {invalidDockerConfigKey, false}, - "valid config.json": {validDockerSecret2(), true}, - "missing config.json": {missingDockerConfigKey2, false}, - "empty config.json": {emptyDockerConfigKey2, false}, - "invalid config.json": {invalidDockerConfigKey2, false}, - } - - for name, tc := range tests { - errs := ValidateSecret(&tc.secret) - if tc.valid && len(errs) > 0 { - t.Errorf("%v: Unexpected error: %v", name, errs) - } - if !tc.valid && len(errs) == 0 { - t.Errorf("%v: Unexpected non-error", name) - } - } -} - -func TestValidateBasicAuthSecret(t *testing.T) { - validBasicAuthSecret := func() api.Secret { - return api.Secret{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"}, - Type: api.SecretTypeBasicAuth, - Data: map[string][]byte{ - api.BasicAuthUsernameKey: []byte("username"), - api.BasicAuthPasswordKey: []byte("password"), - }, - } - } - - var ( - missingBasicAuthUsernamePasswordKeys = validBasicAuthSecret() - // invalidBasicAuthUsernamePasswordKey = validBasicAuthSecret() - // emptyBasicAuthUsernameKey = validBasicAuthSecret() - // emptyBasicAuthPasswordKey = validBasicAuthSecret() - ) - - delete(missingBasicAuthUsernamePasswordKeys.Data, api.BasicAuthUsernameKey) - delete(missingBasicAuthUsernamePasswordKeys.Data, api.BasicAuthPasswordKey) - - // invalidBasicAuthUsernamePasswordKey.Data[api.BasicAuthUsernameKey] = []byte("bad") - // invalidBasicAuthUsernamePasswordKey.Data[api.BasicAuthPasswordKey] = []byte("bad") - - // emptyBasicAuthUsernameKey.Data[api.BasicAuthUsernameKey] = []byte("") - // emptyBasicAuthPasswordKey.Data[api.BasicAuthPasswordKey] = []byte("") - - tests := map[string]struct { - secret api.Secret - valid bool - }{ - "valid": {validBasicAuthSecret(), true}, - "missing username and password": {missingBasicAuthUsernamePasswordKeys, false}, - // "invalid username and password": {invalidBasicAuthUsernamePasswordKey, false}, - // "empty username": {emptyBasicAuthUsernameKey, false}, - // "empty password": {emptyBasicAuthPasswordKey, false}, - } - - for name, tc := range tests { - errs := ValidateSecret(&tc.secret) - if tc.valid && len(errs) > 0 { - t.Errorf("%v: Unexpected error: %v", name, errs) - } - if !tc.valid && len(errs) == 0 { - t.Errorf("%v: Unexpected non-error", name) - } - } -} - -func TestValidateSSHAuthSecret(t *testing.T) { - validSSHAuthSecret := func() api.Secret { - return api.Secret{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"}, - Type: api.SecretTypeSSHAuth, - Data: map[string][]byte{ - api.SSHAuthPrivateKey: []byte("foo-bar-baz"), - }, - } - } - - missingSSHAuthPrivateKey := validSSHAuthSecret() - - delete(missingSSHAuthPrivateKey.Data, api.SSHAuthPrivateKey) - - tests := map[string]struct { - secret api.Secret - valid bool - }{ - "valid": {validSSHAuthSecret(), true}, - "missing private key": {missingSSHAuthPrivateKey, false}, - } - - for name, tc := range tests { - errs := ValidateSecret(&tc.secret) - if tc.valid && len(errs) > 0 { - t.Errorf("%v: Unexpected error: %v", name, errs) - } - if !tc.valid && len(errs) == 0 { - t.Errorf("%v: Unexpected non-error", name) - } - } -} - -func TestValidateEndpoints(t *testing.T) { - successCases := map[string]api.Endpoints{ - "simple endpoint": { - ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, - Subsets: []api.EndpointSubset{ - { - Addresses: []api.EndpointAddress{{IP: "10.10.1.1"}, {IP: "10.10.2.2"}}, - Ports: []api.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP"}, {Name: "b", Port: 309, Protocol: "TCP"}}, - }, - { - Addresses: []api.EndpointAddress{{IP: "10.10.3.3"}}, - Ports: []api.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}, {Name: "b", Port: 76, Protocol: "TCP"}}, - }, - }, - }, - "empty subsets": { - ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, - }, - "no name required for singleton port": { - ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, - Subsets: []api.EndpointSubset{ - { - Addresses: []api.EndpointAddress{{IP: "10.10.1.1"}}, - Ports: []api.EndpointPort{{Port: 8675, Protocol: "TCP"}}, - }, - }, - }, - } - - for k, v := range successCases { - if errs := ValidateEndpoints(&v); len(errs) != 0 { - t.Errorf("Expected success for %s, got %v", k, errs) - } - } - - errorCases := map[string]struct { - endpoints api.Endpoints - errorType field.ErrorType - errorDetail string - }{ - "missing namespace": { - endpoints: api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "mysvc"}}, - errorType: "FieldValueRequired", - }, - "missing name": { - endpoints: api.Endpoints{ObjectMeta: api.ObjectMeta{Namespace: "namespace"}}, - errorType: "FieldValueRequired", - }, - "invalid namespace": { - endpoints: api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "no@#invalid.;chars\"allowed"}}, - errorType: "FieldValueInvalid", - errorDetail: DNS1123LabelErrorMsg, - }, - "invalid name": { - endpoints: api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "-_Invliad^&Characters", Namespace: "namespace"}}, - errorType: "FieldValueInvalid", - errorDetail: DNSSubdomainErrorMsg, - }, - "empty addresses": { - endpoints: api.Endpoints{ - ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, - Subsets: []api.EndpointSubset{ - { - Ports: []api.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}}, - }, - }, - }, - errorType: "FieldValueRequired", - }, - "empty ports": { - endpoints: api.Endpoints{ - ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, - Subsets: []api.EndpointSubset{ - { - Addresses: []api.EndpointAddress{{IP: "10.10.3.3"}}, - }, - }, - }, - errorType: "FieldValueRequired", - }, - "invalid IP": { - endpoints: api.Endpoints{ - ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, - Subsets: []api.EndpointSubset{ - { - Addresses: []api.EndpointAddress{{IP: "2001:0db8:85a3:0042:1000:8a2e:0370:7334"}}, - Ports: []api.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}}, - }, - }, - }, - errorType: "FieldValueInvalid", - errorDetail: "must be a valid IPv4 address", - }, - "Multiple ports, one without name": { - endpoints: api.Endpoints{ - ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, - Subsets: []api.EndpointSubset{ - { - Addresses: []api.EndpointAddress{{IP: "10.10.1.1"}}, - Ports: []api.EndpointPort{{Port: 8675, Protocol: "TCP"}, {Name: "b", Port: 309, Protocol: "TCP"}}, - }, - }, - }, - errorType: "FieldValueRequired", - }, - "Invalid port number": { - endpoints: api.Endpoints{ - ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, - Subsets: []api.EndpointSubset{ - { - Addresses: []api.EndpointAddress{{IP: "10.10.1.1"}}, - Ports: []api.EndpointPort{{Name: "a", Port: 66000, Protocol: "TCP"}}, - }, - }, - }, - errorType: "FieldValueInvalid", - errorDetail: PortRangeErrorMsg, - }, - "Invalid protocol": { - endpoints: api.Endpoints{ - ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, - Subsets: []api.EndpointSubset{ - { - Addresses: []api.EndpointAddress{{IP: "10.10.1.1"}}, - Ports: []api.EndpointPort{{Name: "a", Port: 93, Protocol: "Protocol"}}, - }, - }, - }, - errorType: "FieldValueNotSupported", - }, - "Address missing IP": { - endpoints: api.Endpoints{ - ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, - Subsets: []api.EndpointSubset{ - { - Addresses: []api.EndpointAddress{{}}, - Ports: []api.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}}, - }, - }, - }, - errorType: "FieldValueInvalid", - errorDetail: "must be a valid IPv4 address", - }, - "Port missing number": { - endpoints: api.Endpoints{ - ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, - Subsets: []api.EndpointSubset{ - { - Addresses: []api.EndpointAddress{{IP: "10.10.1.1"}}, - Ports: []api.EndpointPort{{Name: "a", Protocol: "TCP"}}, - }, - }, - }, - errorType: "FieldValueInvalid", - errorDetail: PortRangeErrorMsg, - }, - "Port missing protocol": { - endpoints: api.Endpoints{ - ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, - Subsets: []api.EndpointSubset{ - { - Addresses: []api.EndpointAddress{{IP: "10.10.1.1"}}, - Ports: []api.EndpointPort{{Name: "a", Port: 93}}, - }, - }, - }, - errorType: "FieldValueRequired", - }, - "Address is loopback": { - endpoints: api.Endpoints{ - ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, - Subsets: []api.EndpointSubset{ - { - Addresses: []api.EndpointAddress{{IP: "127.0.0.1"}}, - Ports: []api.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}}, - }, - }, - }, - errorType: "FieldValueInvalid", - errorDetail: "loopback", - }, - "Address is link-local": { - endpoints: api.Endpoints{ - ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, - Subsets: []api.EndpointSubset{ - { - Addresses: []api.EndpointAddress{{IP: "169.254.169.254"}}, - Ports: []api.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}}, - }, - }, - }, - errorType: "FieldValueInvalid", - errorDetail: "link-local", - }, - "Address is link-local multicast": { - endpoints: api.Endpoints{ - ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "namespace"}, - Subsets: []api.EndpointSubset{ - { - Addresses: []api.EndpointAddress{{IP: "224.0.0.1"}}, - Ports: []api.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}}, - }, - }, - }, - errorType: "FieldValueInvalid", - errorDetail: "link-local multicast", - }, - } - - for k, v := range errorCases { - if errs := ValidateEndpoints(&v.endpoints); len(errs) == 0 || errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) { - t.Errorf("[%s] Expected error type %s with detail %q, got %v", k, v.errorType, v.errorDetail, errs) - } - } -} - -func TestValidateTLSSecret(t *testing.T) { - successCases := map[string]api.Secret{ - "emtpy certificate chain": { - ObjectMeta: api.ObjectMeta{Name: "tls-cert", Namespace: "namespace"}, - Data: map[string][]byte{ - api.TLSCertKey: []byte("public key"), - api.TLSPrivateKeyKey: []byte("private key"), - }, - }, - } - for k, v := range successCases { - if errs := ValidateSecret(&v); len(errs) != 0 { - t.Errorf("Expected success for %s, got %v", k, errs) - } - } - errorCases := map[string]struct { - secrets api.Secret - errorType field.ErrorType - errorDetail string - }{ - "missing public key": { - secrets: api.Secret{ - ObjectMeta: api.ObjectMeta{Name: "tls-cert"}, - Data: map[string][]byte{ - api.TLSCertKey: []byte("public key"), - }, - }, - errorType: "FieldValueRequired", - }, - "missing private key": { - secrets: api.Secret{ - ObjectMeta: api.ObjectMeta{Name: "tls-cert"}, - Data: map[string][]byte{ - api.TLSCertKey: []byte("public key"), - }, - }, - errorType: "FieldValueRequired", - }, - } - for k, v := range errorCases { - if errs := ValidateSecret(&v.secrets); len(errs) == 0 || errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) { - t.Errorf("[%s] Expected error type %s with detail %q, got %v", k, v.errorType, v.errorDetail, errs) - } - } -} - -func TestValidateSecurityContext(t *testing.T) { - priv := false - var runAsUser int64 = 1 - fullValidSC := func() *api.SecurityContext { - return &api.SecurityContext{ - Privileged: &priv, - Capabilities: &api.Capabilities{ - Add: []api.Capability{"foo"}, - Drop: []api.Capability{"bar"}, - }, - SELinuxOptions: &api.SELinuxOptions{ - User: "user", - Role: "role", - Type: "type", - Level: "level", - }, - RunAsUser: &runAsUser, - } - } - - //setup data - allSettings := fullValidSC() - noCaps := fullValidSC() - noCaps.Capabilities = nil - - noSELinux := fullValidSC() - noSELinux.SELinuxOptions = nil - - noPrivRequest := fullValidSC() - noPrivRequest.Privileged = nil - - noRunAsUser := fullValidSC() - noRunAsUser.RunAsUser = nil - - successCases := map[string]struct { - sc *api.SecurityContext - }{ - "all settings": {allSettings}, - "no capabilities": {noCaps}, - "no selinux": {noSELinux}, - "no priv request": {noPrivRequest}, - "no run as user": {noRunAsUser}, - } - for k, v := range successCases { - if errs := ValidateSecurityContext(v.sc, field.NewPath("field")); len(errs) != 0 { - t.Errorf("[%s] Expected success, got %v", k, errs) - } - } - - privRequestWithGlobalDeny := fullValidSC() - requestPrivileged := true - privRequestWithGlobalDeny.Privileged = &requestPrivileged - - negativeRunAsUser := fullValidSC() - var negativeUser int64 = -1 - negativeRunAsUser.RunAsUser = &negativeUser - - errorCases := map[string]struct { - sc *api.SecurityContext - errorType field.ErrorType - errorDetail string - }{ - "request privileged when capabilities forbids": { - sc: privRequestWithGlobalDeny, - errorType: "FieldValueForbidden", - errorDetail: "disallowed by policy", - }, - "negative RunAsUser": { - sc: negativeRunAsUser, - errorType: "FieldValueInvalid", - errorDetail: isNegativeErrorMsg, - }, - } - for k, v := range errorCases { - if errs := ValidateSecurityContext(v.sc, field.NewPath("field")); len(errs) == 0 || errs[0].Type != v.errorType || !strings.Contains(errs[0].Detail, v.errorDetail) { - t.Errorf("[%s] Expected error type %q with detail %q, got %v", k, v.errorType, v.errorDetail, errs) - } - } -} - -func fakeValidSecurityContext(priv bool) *api.SecurityContext { - return &api.SecurityContext{ - Privileged: &priv, - } -} - -func TestValidPodLogOptions(t *testing.T) { - now := unversioned.Now() - negative := int64(-1) - zero := int64(0) - positive := int64(1) - tests := []struct { - opt api.PodLogOptions - errs int - }{ - {api.PodLogOptions{}, 0}, - {api.PodLogOptions{Previous: true}, 0}, - {api.PodLogOptions{Follow: true}, 0}, - {api.PodLogOptions{TailLines: &zero}, 0}, - {api.PodLogOptions{TailLines: &negative}, 1}, - {api.PodLogOptions{TailLines: &positive}, 0}, - {api.PodLogOptions{LimitBytes: &zero}, 1}, - {api.PodLogOptions{LimitBytes: &negative}, 1}, - {api.PodLogOptions{LimitBytes: &positive}, 0}, - {api.PodLogOptions{SinceSeconds: &negative}, 1}, - {api.PodLogOptions{SinceSeconds: &positive}, 0}, - {api.PodLogOptions{SinceSeconds: &zero}, 1}, - {api.PodLogOptions{SinceTime: &now}, 0}, - } - for i, test := range tests { - errs := ValidatePodLogOptions(&test.opt) - if test.errs != len(errs) { - t.Errorf("%d: Unexpected errors: %v", i, errs) - } - } -} - -func TestValidateConfigMap(t *testing.T) { - newConfigMap := func(name, namespace string, data map[string]string) api.ConfigMap { - return api.ConfigMap{ - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Data: data, - } - } - - var ( - validConfigMap = newConfigMap("validname", "validns", map[string]string{"key": "value"}) - maxKeyLength = newConfigMap("validname", "validns", map[string]string{strings.Repeat("a", 253): "value"}) - - emptyName = newConfigMap("", "validns", nil) - invalidName = newConfigMap("NoUppercaseOrSpecialCharsLike=Equals", "validns", nil) - emptyNs = newConfigMap("validname", "", nil) - invalidNs = newConfigMap("validname", "NoUppercaseOrSpecialCharsLike=Equals", nil) - invalidKey = newConfigMap("validname", "validns", map[string]string{"a..b": "value"}) - leadingDotKey = newConfigMap("validname", "validns", map[string]string{".ab": "value"}) - dotKey = newConfigMap("validname", "validns", map[string]string{".": "value"}) - doubleDotKey = newConfigMap("validname", "validns", map[string]string{"..": "value"}) - overMaxKeyLength = newConfigMap("validname", "validns", map[string]string{strings.Repeat("a", 254): "value"}) - overMaxSize = newConfigMap("validname", "validns", map[string]string{"key": strings.Repeat("a", api.MaxSecretSize+1)}) - ) - - tests := map[string]struct { - cfg api.ConfigMap - isValid bool - }{ - "valid": {validConfigMap, true}, - "max key length": {maxKeyLength, true}, - "leading dot key": {leadingDotKey, true}, - "empty name": {emptyName, false}, - "invalid name": {invalidName, false}, - "invalid key": {invalidKey, false}, - "empty namespace": {emptyNs, false}, - "invalid namespace": {invalidNs, false}, - "dot key": {dotKey, false}, - "double dot key": {doubleDotKey, false}, - "over max key length": {overMaxKeyLength, false}, - "over max size": {overMaxSize, false}, - } - - for name, tc := range tests { - errs := ValidateConfigMap(&tc.cfg) - if tc.isValid && len(errs) > 0 { - t.Errorf("%v: unexpected error: %v", name, errs) - } - if !tc.isValid && len(errs) == 0 { - t.Errorf("%v: unexpected non-error", name) - } - } -} - -func TestValidateConfigMapUpdate(t *testing.T) { - newConfigMap := func(version, name, namespace string, data map[string]string) api.ConfigMap { - return api.ConfigMap{ - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: namespace, - ResourceVersion: version, - }, - Data: data, - } - } - - var ( - validConfigMap = newConfigMap("1", "validname", "validns", map[string]string{"key": "value"}) - noVersion = newConfigMap("", "validname", "validns", map[string]string{"key": "value"}) - ) - - cases := []struct { - name string - newCfg api.ConfigMap - oldCfg api.ConfigMap - isValid bool - }{ - { - name: "valid", - newCfg: validConfigMap, - oldCfg: validConfigMap, - isValid: true, - }, - { - name: "invalid", - newCfg: noVersion, - oldCfg: validConfigMap, - isValid: false, - }, - } - - for _, tc := range cases { - errs := ValidateConfigMapUpdate(&tc.newCfg, &tc.oldCfg) - if tc.isValid && len(errs) > 0 { - t.Errorf("%v: unexpected error: %v", tc.name, errs) - } - if !tc.isValid && len(errs) == 0 { - t.Errorf("%v: unexpected non-error", tc.name) - } - } -} - -func TestValidateHasLabel(t *testing.T) { - successCase := api.ObjectMeta{ - Name: "123", - Namespace: "ns", - Labels: map[string]string{ - "other": "blah", - "foo": "bar", - }, - } - if errs := ValidateHasLabel(successCase, field.NewPath("field"), "foo", "bar"); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - - missingCase := api.ObjectMeta{ - Name: "123", - Namespace: "ns", - Labels: map[string]string{ - "other": "blah", - }, - } - if errs := ValidateHasLabel(missingCase, field.NewPath("field"), "foo", "bar"); len(errs) == 0 { - t.Errorf("expected failure") - } - - wrongValueCase := api.ObjectMeta{ - Name: "123", - Namespace: "ns", - Labels: map[string]string{ - "other": "blah", - "foo": "notbar", - }, - } - if errs := ValidateHasLabel(wrongValueCase, field.NewPath("field"), "foo", "bar"); len(errs) == 0 { - t.Errorf("expected failure") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apimachinery/registered/registered.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apimachinery/registered/registered.go index 6baef8f91..8903fd3a5 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apimachinery/registered/registered.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apimachinery/registered/registered.go @@ -24,8 +24,11 @@ import ( "strings" "github.com/golang/glog" + + "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery" + "k8s.io/kubernetes/pkg/util/sets" ) var ( @@ -43,8 +46,8 @@ var ( // envRequestedVersions represents the versions requested via the // KUBE_API_VERSIONS environment variable. The install package of each group // checks this list before add their versions to the latest package and - // Scheme. - envRequestedVersions = map[unversioned.GroupVersion]struct{}{} + // Scheme. This list is small and order matters, so represent as a slice + envRequestedVersions = []unversioned.GroupVersion{} ) func init() { @@ -58,7 +61,7 @@ func init() { glog.Fatalf("invalid api version: %s in KUBE_API_VERSIONS: %s.", version, os.Getenv("KUBE_API_VERSIONS")) } - envRequestedVersions[gv] = struct{}{} + envRequestedVersions = append(envRequestedVersions, gv) } } } @@ -104,8 +107,12 @@ func IsAllowedVersion(v unversioned.GroupVersion) bool { if len(envRequestedVersions) == 0 { return true } - _, found := envRequestedVersions[v] - return found + for _, envGV := range envRequestedVersions { + if v == envGV { + return true + } + } + return false } // IsEnabledVersion returns if a version is enabled. @@ -167,6 +174,80 @@ func GroupOrDie(group string) *apimachinery.GroupMeta { return &groupMetaCopy } +// RESTMapper returns a union RESTMapper of all known types with priorities chosen in the following order: +// 1. if KUBE_API_VERSIONS is specified, then KUBE_API_VERSIONS in order, OR +// 1. legacy kube group preferred version, extensions preferred version, metrics perferred version, legacy +// kube any version, extensions any version, metrics any version, all other groups alphabetical preferred version, +// all other groups alphabetical. +func RESTMapper(versionPatterns ...unversioned.GroupVersion) meta.RESTMapper { + unionMapper := meta.MultiRESTMapper{} + for enabledVersion := range enabledVersions { + groupMeta := groupMetaMap[enabledVersion.Group] + unionMapper = append(unionMapper, groupMeta.RESTMapper) + } + + if len(versionPatterns) != 0 { + resourcePriority := []unversioned.GroupVersionResource{} + kindPriority := []unversioned.GroupVersionKind{} + for _, versionPriority := range versionPatterns { + resourcePriority = append(resourcePriority, versionPriority.WithResource(meta.AnyResource)) + kindPriority = append(kindPriority, versionPriority.WithKind(meta.AnyKind)) + } + + return meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority} + } + + if len(envRequestedVersions) != 0 { + resourcePriority := []unversioned.GroupVersionResource{} + kindPriority := []unversioned.GroupVersionKind{} + + for _, versionPriority := range envRequestedVersions { + resourcePriority = append(resourcePriority, versionPriority.WithResource(meta.AnyResource)) + kindPriority = append(kindPriority, versionPriority.WithKind(meta.AnyKind)) + } + + return meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority} + } + + prioritizedGroups := []string{"", "extensions", "metrics"} + resourcePriority, kindPriority := prioritiesForGroups(prioritizedGroups...) + + prioritizedGroupsSet := sets.NewString(prioritizedGroups...) + remainingGroups := sets.String{} + for enabledVersion := range enabledVersions { + if !prioritizedGroupsSet.Has(enabledVersion.Group) { + remainingGroups.Insert(enabledVersion.Group) + } + } + + remainingResourcePriority, remainingKindPriority := prioritiesForGroups(remainingGroups.List()...) + resourcePriority = append(resourcePriority, remainingResourcePriority...) + kindPriority = append(kindPriority, remainingKindPriority...) + + return meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority} +} + +// prioritiesForGroups returns the resource and kind priorities for a PriorityRESTMapper, preferring the preferred version of each group first, +// then any non-preferred version of the group second. +func prioritiesForGroups(groups ...string) ([]unversioned.GroupVersionResource, []unversioned.GroupVersionKind) { + resourcePriority := []unversioned.GroupVersionResource{} + kindPriority := []unversioned.GroupVersionKind{} + + for _, group := range groups { + availableVersions := EnabledVersionsForGroup(group) + if len(availableVersions) > 0 { + resourcePriority = append(resourcePriority, availableVersions[0].WithResource(meta.AnyResource)) + kindPriority = append(kindPriority, availableVersions[0].WithKind(meta.AnyKind)) + } + } + for _, group := range groups { + resourcePriority = append(resourcePriority, unversioned.GroupVersionResource{Group: group, Version: meta.AnyVersion, Resource: meta.AnyResource}) + kindPriority = append(kindPriority, unversioned.GroupVersionKind{Group: group, Version: meta.AnyVersion, Kind: meta.AnyKind}) + } + + return resourcePriority, kindPriority +} + // AllPreferredGroupVersions returns the preferred versions of all registered // groups in the form of "group1/version1,group2/version2,..." func AllPreferredGroupVersions() string { @@ -185,7 +266,7 @@ func AllPreferredGroupVersions() string { // the KUBE_API_VERSIONS environment variable, but not enabled. func ValidateEnvRequestedVersions() []unversioned.GroupVersion { var missingVersions []unversioned.GroupVersion - for v := range envRequestedVersions { + for _, v := range envRequestedVersions { if _, found := enabledVersions[v]; !found { missingVersions = append(missingVersions, v) } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apimachinery/registered/registered_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apimachinery/registered/registered_test.go deleted file mode 100644 index e7466fcc2..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apimachinery/registered/registered_test.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright 2015 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 registered - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apimachinery" -) - -func TestAllPreferredGroupVersions(t *testing.T) { - testCases := []struct { - groupMetas []apimachinery.GroupMeta - expect string - }{ - { - groupMetas: []apimachinery.GroupMeta{ - { - GroupVersion: unversioned.GroupVersion{"group1", "v1"}, - }, - { - GroupVersion: unversioned.GroupVersion{"group2", "v2"}, - }, - { - GroupVersion: unversioned.GroupVersion{"", "v1"}, - }, - }, - expect: "group1/v1,group2/v2,v1", - }, - { - groupMetas: []apimachinery.GroupMeta{ - { - GroupVersion: unversioned.GroupVersion{"", "v1"}, - }, - }, - expect: "v1", - }, - { - groupMetas: []apimachinery.GroupMeta{}, - expect: "", - }, - } - for _, testCase := range testCases { - for _, groupMeta := range testCase.groupMetas { - RegisterGroup(groupMeta) - } - output := AllPreferredGroupVersions() - if testCase.expect != output { - t.Errorf("Error. expect: %s, got: %s", testCase.expect, output) - } - reset() - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/authorization/v1beta1/types_swagger_doc_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/authorization/v1beta1/types_swagger_doc_generated.go index 681e962ed..d9910ef14 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/authorization/v1beta1/types_swagger_doc_generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/authorization/v1beta1/types_swagger_doc_generated.go @@ -16,7 +16,7 @@ limitations under the License. package v1beta1 -// This file contains a collection of methods that can be used from go-resful to +// This file contains a collection of methods that can be used from go-restful to // generate Swagger API documentation for its models. Please read this PR for more // information on the implementation: https://github.com/emicklei/go-restful/pull/215 // diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/authorization/validation/validation.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/authorization/validation/validation.go deleted file mode 100644 index 11a548f41..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/authorization/validation/validation.go +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright 2015 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 validation - -import ( - authorizationapi "k8s.io/kubernetes/pkg/apis/authorization" - "k8s.io/kubernetes/pkg/util/validation/field" -) - -func ValidateSubjectAccessReviewSpec(spec authorizationapi.SubjectAccessReviewSpec, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - if spec.ResourceAttributes != nil && spec.NonResourceAttributes != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("nonResourceAttributes"), spec.NonResourceAttributes, `cannot be specified in combination with resourceAttributes`)) - } - if spec.ResourceAttributes == nil && spec.NonResourceAttributes == nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceAttributes"), spec.NonResourceAttributes, `exactly one of nonResourceAttributes or resourceAttributes must be specified`)) - } - if len(spec.User) == 0 && len(spec.Groups) == 0 { - allErrs = append(allErrs, field.Invalid(fldPath.Child("user"), spec.User, `at least one of user or group must be specified`)) - } - - return allErrs -} - -func ValidateSelfSubjectAccessReviewSpec(spec authorizationapi.SelfSubjectAccessReviewSpec, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - if spec.ResourceAttributes != nil && spec.NonResourceAttributes != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("nonResourceAttributes"), spec.NonResourceAttributes, `cannot be specified in combination with resourceAttributes`)) - } - if spec.ResourceAttributes == nil && spec.NonResourceAttributes == nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceAttributes"), spec.NonResourceAttributes, `exactly one of nonResourceAttributes or resourceAttributes must be specified`)) - } - - return allErrs -} - -func ValidateSubjectAccessReview(sar *authorizationapi.SubjectAccessReview) field.ErrorList { - allErrs := ValidateSubjectAccessReviewSpec(sar.Spec, field.NewPath("spec")) - return allErrs -} - -func ValidateSelfSubjectAccessReview(sar *authorizationapi.SelfSubjectAccessReview) field.ErrorList { - allErrs := ValidateSelfSubjectAccessReviewSpec(sar.Spec, field.NewPath("spec")) - return allErrs -} - -func ValidateLocalSubjectAccessReview(sar *authorizationapi.LocalSubjectAccessReview) field.ErrorList { - allErrs := ValidateSubjectAccessReviewSpec(sar.Spec, field.NewPath("spec")) - return allErrs -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/authorization/validation/validation_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/authorization/validation/validation_test.go deleted file mode 100644 index c2776c404..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/authorization/validation/validation_test.go +++ /dev/null @@ -1,135 +0,0 @@ -/* -Copyright 2014 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 validation - -import ( - "strings" - "testing" - - authorizationapi "k8s.io/kubernetes/pkg/apis/authorization" - "k8s.io/kubernetes/pkg/util/validation/field" -) - -func TestValidateSARSpec(t *testing.T) { - successCases := []authorizationapi.SubjectAccessReviewSpec{ - {ResourceAttributes: &authorizationapi.ResourceAttributes{}, User: "me"}, - {NonResourceAttributes: &authorizationapi.NonResourceAttributes{}, Groups: []string{"my-group"}}, - } - for _, successCase := range successCases { - if errs := ValidateSubjectAccessReviewSpec(successCase, field.NewPath("spec")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := []struct { - name string - obj authorizationapi.SubjectAccessReviewSpec - msg string - }{ - { - name: "neither request", - obj: authorizationapi.SubjectAccessReviewSpec{User: "me"}, - msg: "exactly one of nonResourceAttributes or resourceAttributes must be specified", - }, - { - name: "both requests", - obj: authorizationapi.SubjectAccessReviewSpec{ - ResourceAttributes: &authorizationapi.ResourceAttributes{}, - NonResourceAttributes: &authorizationapi.NonResourceAttributes{}, - User: "me", - }, - msg: "cannot be specified in combination with resourceAttributes", - }, - { - name: "no subject", - obj: authorizationapi.SubjectAccessReviewSpec{ - ResourceAttributes: &authorizationapi.ResourceAttributes{}, - }, - msg: `spec.user: Invalid value: "": at least one of user or group must be specified`, - }, - } - - for _, c := range errorCases { - errs := ValidateSubjectAccessReviewSpec(c.obj, field.NewPath("spec")) - if len(errs) == 0 { - t.Errorf("%s: expected failure for %q", c.name, c.msg) - } else if !strings.Contains(errs[0].Error(), c.msg) { - t.Errorf("%s: unexpected error: %q, expected: %q", c.name, errs[0], c.msg) - } - - errs = ValidateSubjectAccessReview(&authorizationapi.SubjectAccessReview{Spec: c.obj}) - if len(errs) == 0 { - t.Errorf("%s: expected failure for %q", c.name, c.msg) - } else if !strings.Contains(errs[0].Error(), c.msg) { - t.Errorf("%s: unexpected error: %q, expected: %q", c.name, errs[0], c.msg) - } - errs = ValidateLocalSubjectAccessReview(&authorizationapi.LocalSubjectAccessReview{Spec: c.obj}) - if len(errs) == 0 { - t.Errorf("%s: expected failure for %q", c.name, c.msg) - } else if !strings.Contains(errs[0].Error(), c.msg) { - t.Errorf("%s: unexpected error: %q, expected: %q", c.name, errs[0], c.msg) - } - - } -} - -func TestValidateSelfSAR(t *testing.T) { - successCases := []authorizationapi.SelfSubjectAccessReviewSpec{ - {ResourceAttributes: &authorizationapi.ResourceAttributes{}}, - } - for _, successCase := range successCases { - if errs := ValidateSelfSubjectAccessReviewSpec(successCase, field.NewPath("spec")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := []struct { - name string - obj authorizationapi.SelfSubjectAccessReviewSpec - msg string - }{ - { - name: "neither request", - obj: authorizationapi.SelfSubjectAccessReviewSpec{}, - msg: "exactly one of nonResourceAttributes or resourceAttributes must be specified", - }, - { - name: "both requests", - obj: authorizationapi.SelfSubjectAccessReviewSpec{ - ResourceAttributes: &authorizationapi.ResourceAttributes{}, - NonResourceAttributes: &authorizationapi.NonResourceAttributes{}, - }, - msg: "cannot be specified in combination with resourceAttributes", - }, - } - - for _, c := range errorCases { - errs := ValidateSelfSubjectAccessReviewSpec(c.obj, field.NewPath("spec")) - if len(errs) == 0 { - t.Errorf("%s: expected failure for %q", c.name, c.msg) - } else if !strings.Contains(errs[0].Error(), c.msg) { - t.Errorf("%s: unexpected error: %q, expected: %q", c.name, errs[0], c.msg) - } - - errs = ValidateSelfSubjectAccessReview(&authorizationapi.SelfSubjectAccessReview{Spec: c.obj}) - if len(errs) == 0 { - t.Errorf("%s: expected failure for %q", c.name, c.msg) - } else if !strings.Contains(errs[0].Error(), c.msg) { - t.Errorf("%s: unexpected error: %q, expected: %q", c.name, errs[0], c.msg) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/register.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/register.go index 519e8f6be..dfc86f24d 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/register.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/register.go @@ -47,8 +47,11 @@ func AddToScheme(scheme *runtime.Scheme) { // Adds the list of known types to api.Scheme. func addKnownTypes(scheme *runtime.Scheme) { scheme.AddKnownTypes(SchemeGroupVersion, + &Scale{}, &extensions.HorizontalPodAutoscaler{}, &extensions.HorizontalPodAutoscalerList{}, &api.ListOptions{}, ) } + +func (obj *Scale) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/types.generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/types.generated.go new file mode 100644 index 000000000..7302ebca6 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/types.generated.go @@ -0,0 +1,794 @@ +/* +Copyright 2015 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. +*/ + +// ************************************************************ +// DO NOT EDIT. +// THIS FILE IS AUTO-GENERATED BY codecgen. +// ************************************************************ + +package autoscaling + +import ( + "errors" + "fmt" + codec1978 "github.com/ugorji/go/codec" + pkg2_api "k8s.io/kubernetes/pkg/api" + pkg1_unversioned "k8s.io/kubernetes/pkg/api/unversioned" + pkg3_types "k8s.io/kubernetes/pkg/types" + "reflect" + "runtime" + time "time" +) + +const ( + // ----- content types ---- + codecSelferC_UTF81234 = 1 + codecSelferC_RAW1234 = 0 + // ----- value types used ---- + codecSelferValueTypeArray1234 = 10 + codecSelferValueTypeMap1234 = 9 + // ----- containerStateValues ---- + codecSelfer_containerMapKey1234 = 2 + codecSelfer_containerMapValue1234 = 3 + codecSelfer_containerMapEnd1234 = 4 + codecSelfer_containerArrayElem1234 = 6 + codecSelfer_containerArrayEnd1234 = 7 +) + +var ( + codecSelferBitsize1234 = uint8(reflect.TypeOf(uint(0)).Bits()) + codecSelferOnlyMapOrArrayEncodeToStructErr1234 = errors.New(`only encoded map or array can be decoded into a struct`) +) + +type codecSelfer1234 struct{} + +func init() { + if codec1978.GenVersion != 5 { + _, file, _, _ := runtime.Caller(0) + err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v", + 5, codec1978.GenVersion, file) + panic(err) + } + if false { // reference the types, but skip this branch at build/run time + var v0 pkg2_api.ObjectMeta + var v1 pkg1_unversioned.TypeMeta + var v2 pkg3_types.UID + var v3 time.Time + _, _, _, _ = v0, v1, v2, v3 + } +} + +func (x *Scale) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [5]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = true + yyq2[1] = true + yyq2[2] = true + yyq2[3] = x.Kind != "" + yyq2[4] = x.APIVersion != "" + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(5) + } else { + yynn2 = 0 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + yy4 := &x.ObjectMeta + yy4.CodecEncodeSelf(e) + } else { + r.EncodeNil() + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("metadata")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy6 := &x.ObjectMeta + yy6.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + yy9 := &x.Spec + yy9.CodecEncodeSelf(e) + } else { + r.EncodeNil() + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("spec")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy11 := &x.Spec + yy11.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yy14 := &x.Status + yy14.CodecEncodeSelf(e) + } else { + r.EncodeNil() + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("status")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy16 := &x.Status + yy16.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[3] { + yym19 := z.EncBinary() + _ = yym19 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[3] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("kind")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym20 := z.EncBinary() + _ = yym20 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[4] { + yym22 := z.EncBinary() + _ = yym22 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[4] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym23 := z.EncBinary() + _ = yym23 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *Scale) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *Scale) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "metadata": + if r.TryDecodeAsNil() { + x.ObjectMeta = pkg2_api.ObjectMeta{} + } else { + yyv4 := &x.ObjectMeta + yyv4.CodecDecodeSelf(d) + } + case "spec": + if r.TryDecodeAsNil() { + x.Spec = ScaleSpec{} + } else { + yyv5 := &x.Spec + yyv5.CodecDecodeSelf(d) + } + case "status": + if r.TryDecodeAsNil() { + x.Status = ScaleStatus{} + } else { + yyv6 := &x.Status + yyv6.CodecDecodeSelf(d) + } + case "kind": + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + case "apiVersion": + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *Scale) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj9 int + var yyb9 bool + var yyhl9 bool = l >= 0 + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.ObjectMeta = pkg2_api.ObjectMeta{} + } else { + yyv10 := &x.ObjectMeta + yyv10.CodecDecodeSelf(d) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Spec = ScaleSpec{} + } else { + yyv11 := &x.Spec + yyv11.CodecDecodeSelf(d) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Status = ScaleStatus{} + } else { + yyv12 := &x.Status + yyv12.CodecDecodeSelf(d) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + for { + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj9-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x *ScaleSpec) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [1]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = x.Replicas != 0 + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(1) + } else { + yynn2 = 0 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + yym4 := z.EncBinary() + _ = yym4 + if false { + } else { + r.EncodeInt(int64(x.Replicas)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("replicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym5 := z.EncBinary() + _ = yym5 + if false { + } else { + r.EncodeInt(int64(x.Replicas)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *ScaleSpec) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *ScaleSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "replicas": + if r.TryDecodeAsNil() { + x.Replicas = 0 + } else { + x.Replicas = int(r.DecodeInt(codecSelferBitsize1234)) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *ScaleSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj5 int + var yyb5 bool + var yyhl5 bool = l >= 0 + yyj5++ + if yyhl5 { + yyb5 = yyj5 > l + } else { + yyb5 = r.CheckBreak() + } + if yyb5 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Replicas = 0 + } else { + x.Replicas = int(r.DecodeInt(codecSelferBitsize1234)) + } + for { + yyj5++ + if yyhl5 { + yyb5 = yyj5 > l + } else { + yyb5 = r.CheckBreak() + } + if yyb5 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj5-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x *ScaleStatus) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [2]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[1] = x.Selector != "" + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(2) + } else { + yynn2 = 1 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym4 := z.EncBinary() + _ = yym4 + if false { + } else { + r.EncodeInt(int64(x.Replicas)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("replicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym5 := z.EncBinary() + _ = yym5 + if false { + } else { + r.EncodeInt(int64(x.Replicas)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + yym7 := z.EncBinary() + _ = yym7 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Selector)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("selector")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym8 := z.EncBinary() + _ = yym8 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Selector)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *ScaleStatus) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *ScaleStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "replicas": + if r.TryDecodeAsNil() { + x.Replicas = 0 + } else { + x.Replicas = int(r.DecodeInt(codecSelferBitsize1234)) + } + case "selector": + if r.TryDecodeAsNil() { + x.Selector = "" + } else { + x.Selector = string(r.DecodeString()) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *ScaleStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj6 int + var yyb6 bool + var yyhl6 bool = l >= 0 + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Replicas = 0 + } else { + x.Replicas = int(r.DecodeInt(codecSelferBitsize1234)) + } + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Selector = "" + } else { + x.Selector = string(r.DecodeString()) + } + for { + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj6-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/types.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/types.go new file mode 100644 index 000000000..99080a8a6 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/types.go @@ -0,0 +1,53 @@ +/* +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 autoscaling + +import ( + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" +) + +// Scale represents a scaling request for a resource. +type Scale struct { + unversioned.TypeMeta `json:",inline"` + // Standard object metadata; More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata. + api.ObjectMeta `json:"metadata,omitempty"` + + // defines the behavior of the scale. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status. + Spec ScaleSpec `json:"spec,omitempty"` + + // current status of the scale. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status. Read-only. + Status ScaleStatus `json:"status,omitempty"` +} + +// ScaleSpec describes the attributes of a scale subresource. +type ScaleSpec struct { + // desired number of instances for the scaled object. + Replicas int `json:"replicas,omitempty"` +} + +// ScaleStatus represents the current status of a scale subresource. +type ScaleStatus struct { + // actual number of observed instances of the scaled object. + Replicas int `json:"replicas"` + + // label query over pods that should match the replicas count. This is same + // as the label selector but in the string format to avoid introspection + // by clients. The string will be in the same format as the query-param syntax. + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors + Selector string `json:"selector,omitempty"` +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/conversion_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/conversion_generated.go index 40eed3525..f55d38fd9 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/conversion_generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/conversion_generated.go @@ -24,6 +24,7 @@ import ( api "k8s.io/kubernetes/pkg/api" unversioned "k8s.io/kubernetes/pkg/api/unversioned" v1 "k8s.io/kubernetes/pkg/api/v1" + autoscaling "k8s.io/kubernetes/pkg/apis/autoscaling" extensions "k8s.io/kubernetes/pkg/apis/extensions" conversion "k8s.io/kubernetes/pkg/conversion" ) @@ -132,6 +133,54 @@ func Convert_v1_ObjectMeta_To_api_ObjectMeta(in *v1.ObjectMeta, out *api.ObjectM return autoConvert_v1_ObjectMeta_To_api_ObjectMeta(in, out, s) } +func autoConvert_autoscaling_Scale_To_v1_Scale(in *autoscaling.Scale, out *Scale, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*autoscaling.Scale))(in) + } + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_autoscaling_ScaleSpec_To_v1_ScaleSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_autoscaling_ScaleStatus_To_v1_ScaleStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +func Convert_autoscaling_Scale_To_v1_Scale(in *autoscaling.Scale, out *Scale, s conversion.Scope) error { + return autoConvert_autoscaling_Scale_To_v1_Scale(in, out, s) +} + +func autoConvert_autoscaling_ScaleSpec_To_v1_ScaleSpec(in *autoscaling.ScaleSpec, out *ScaleSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*autoscaling.ScaleSpec))(in) + } + out.Replicas = int32(in.Replicas) + return nil +} + +func Convert_autoscaling_ScaleSpec_To_v1_ScaleSpec(in *autoscaling.ScaleSpec, out *ScaleSpec, s conversion.Scope) error { + return autoConvert_autoscaling_ScaleSpec_To_v1_ScaleSpec(in, out, s) +} + +func autoConvert_autoscaling_ScaleStatus_To_v1_ScaleStatus(in *autoscaling.ScaleStatus, out *ScaleStatus, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*autoscaling.ScaleStatus))(in) + } + out.Replicas = int32(in.Replicas) + out.Selector = in.Selector + return nil +} + +func Convert_autoscaling_ScaleStatus_To_v1_ScaleStatus(in *autoscaling.ScaleStatus, out *ScaleStatus, s conversion.Scope) error { + return autoConvert_autoscaling_ScaleStatus_To_v1_ScaleStatus(in, out, s) +} + func autoConvert_v1_HorizontalPodAutoscaler_To_extensions_HorizontalPodAutoscaler(in *HorizontalPodAutoscaler, out *extensions.HorizontalPodAutoscaler, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*HorizontalPodAutoscaler))(in) @@ -232,6 +281,54 @@ func Convert_v1_HorizontalPodAutoscalerStatus_To_extensions_HorizontalPodAutosca return autoConvert_v1_HorizontalPodAutoscalerStatus_To_extensions_HorizontalPodAutoscalerStatus(in, out, s) } +func autoConvert_v1_Scale_To_autoscaling_Scale(in *Scale, out *autoscaling.Scale, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*Scale))(in) + } + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1_ScaleSpec_To_autoscaling_ScaleSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1_ScaleStatus_To_autoscaling_ScaleStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +func Convert_v1_Scale_To_autoscaling_Scale(in *Scale, out *autoscaling.Scale, s conversion.Scope) error { + return autoConvert_v1_Scale_To_autoscaling_Scale(in, out, s) +} + +func autoConvert_v1_ScaleSpec_To_autoscaling_ScaleSpec(in *ScaleSpec, out *autoscaling.ScaleSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*ScaleSpec))(in) + } + out.Replicas = int(in.Replicas) + return nil +} + +func Convert_v1_ScaleSpec_To_autoscaling_ScaleSpec(in *ScaleSpec, out *autoscaling.ScaleSpec, s conversion.Scope) error { + return autoConvert_v1_ScaleSpec_To_autoscaling_ScaleSpec(in, out, s) +} + +func autoConvert_v1_ScaleStatus_To_autoscaling_ScaleStatus(in *ScaleStatus, out *autoscaling.ScaleStatus, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*ScaleStatus))(in) + } + out.Replicas = int(in.Replicas) + out.Selector = in.Selector + return nil +} + +func Convert_v1_ScaleStatus_To_autoscaling_ScaleStatus(in *ScaleStatus, out *autoscaling.ScaleStatus, s conversion.Scope) error { + return autoConvert_v1_ScaleStatus_To_autoscaling_ScaleStatus(in, out, s) +} + func autoConvert_extensions_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler(in *extensions.HorizontalPodAutoscaler, out *HorizontalPodAutoscaler, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*extensions.HorizontalPodAutoscaler))(in) @@ -335,6 +432,9 @@ func Convert_extensions_HorizontalPodAutoscalerStatus_To_v1_HorizontalPodAutosca func init() { err := api.Scheme.AddGeneratedConversionFuncs( autoConvert_api_ObjectMeta_To_v1_ObjectMeta, + autoConvert_autoscaling_ScaleSpec_To_v1_ScaleSpec, + autoConvert_autoscaling_ScaleStatus_To_v1_ScaleStatus, + autoConvert_autoscaling_Scale_To_v1_Scale, autoConvert_extensions_HorizontalPodAutoscalerList_To_v1_HorizontalPodAutoscalerList, autoConvert_extensions_HorizontalPodAutoscalerSpec_To_v1_HorizontalPodAutoscalerSpec, autoConvert_extensions_HorizontalPodAutoscalerStatus_To_v1_HorizontalPodAutoscalerStatus, @@ -344,6 +444,9 @@ func init() { autoConvert_v1_HorizontalPodAutoscalerStatus_To_extensions_HorizontalPodAutoscalerStatus, autoConvert_v1_HorizontalPodAutoscaler_To_extensions_HorizontalPodAutoscaler, autoConvert_v1_ObjectMeta_To_api_ObjectMeta, + autoConvert_v1_ScaleSpec_To_autoscaling_ScaleSpec, + autoConvert_v1_ScaleStatus_To_autoscaling_ScaleStatus, + autoConvert_v1_Scale_To_autoscaling_Scale, ) if err != nil { // If one of the conversion functions is malformed, detect it immediately. diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/deep_copy_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/deep_copy_generated.go index e2c8d6fb4..a71749abb 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/deep_copy_generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/deep_copy_generated.go @@ -181,6 +181,33 @@ func deepCopy_v1_HorizontalPodAutoscalerStatus(in HorizontalPodAutoscalerStatus, return nil } +func deepCopy_v1_Scale(in Scale, out *Scale, c *conversion.Cloner) error { + if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { + return err + } + if err := deepCopy_v1_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { + return err + } + if err := deepCopy_v1_ScaleSpec(in.Spec, &out.Spec, c); err != nil { + return err + } + if err := deepCopy_v1_ScaleStatus(in.Status, &out.Status, c); err != nil { + return err + } + return nil +} + +func deepCopy_v1_ScaleSpec(in ScaleSpec, out *ScaleSpec, c *conversion.Cloner) error { + out.Replicas = in.Replicas + return nil +} + +func deepCopy_v1_ScaleStatus(in ScaleStatus, out *ScaleStatus, c *conversion.Cloner) error { + out.Replicas = in.Replicas + out.Selector = in.Selector + return nil +} + func init() { err := api.Scheme.AddGeneratedDeepCopyFuncs( deepCopy_unversioned_ListMeta, @@ -192,6 +219,9 @@ func init() { deepCopy_v1_HorizontalPodAutoscalerList, deepCopy_v1_HorizontalPodAutoscalerSpec, deepCopy_v1_HorizontalPodAutoscalerStatus, + deepCopy_v1_Scale, + deepCopy_v1_ScaleSpec, + deepCopy_v1_ScaleStatus, ) if err != nil { // if one of the deep copy functions is malformed, detect it immediately. diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/register.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/register.go index 1209a4f9b..5af7611c5 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/register.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/register.go @@ -39,9 +39,11 @@ func addKnownTypes(scheme *runtime.Scheme) { scheme.AddKnownTypes(SchemeGroupVersion, &HorizontalPodAutoscaler{}, &HorizontalPodAutoscalerList{}, + &Scale{}, &v1.ListOptions{}, ) } func (obj *HorizontalPodAutoscaler) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } func (obj *HorizontalPodAutoscalerList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *Scale) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/types.generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/types.generated.go index 43d2dbc5a..1e5a195c2 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/types.generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/types.generated.go @@ -1817,6 +1817,728 @@ func (x *HorizontalPodAutoscalerList) codecDecodeSelfFromArray(l int, d *codec19 z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } +func (x *Scale) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [5]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = true + yyq2[1] = true + yyq2[2] = true + yyq2[3] = x.Kind != "" + yyq2[4] = x.APIVersion != "" + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(5) + } else { + yynn2 = 0 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + yy4 := &x.ObjectMeta + yy4.CodecEncodeSelf(e) + } else { + r.EncodeNil() + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("metadata")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy6 := &x.ObjectMeta + yy6.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + yy9 := &x.Spec + yy9.CodecEncodeSelf(e) + } else { + r.EncodeNil() + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("spec")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy11 := &x.Spec + yy11.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yy14 := &x.Status + yy14.CodecEncodeSelf(e) + } else { + r.EncodeNil() + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("status")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy16 := &x.Status + yy16.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[3] { + yym19 := z.EncBinary() + _ = yym19 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[3] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("kind")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym20 := z.EncBinary() + _ = yym20 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[4] { + yym22 := z.EncBinary() + _ = yym22 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[4] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym23 := z.EncBinary() + _ = yym23 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *Scale) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *Scale) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "metadata": + if r.TryDecodeAsNil() { + x.ObjectMeta = pkg2_v1.ObjectMeta{} + } else { + yyv4 := &x.ObjectMeta + yyv4.CodecDecodeSelf(d) + } + case "spec": + if r.TryDecodeAsNil() { + x.Spec = ScaleSpec{} + } else { + yyv5 := &x.Spec + yyv5.CodecDecodeSelf(d) + } + case "status": + if r.TryDecodeAsNil() { + x.Status = ScaleStatus{} + } else { + yyv6 := &x.Status + yyv6.CodecDecodeSelf(d) + } + case "kind": + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + case "apiVersion": + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *Scale) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj9 int + var yyb9 bool + var yyhl9 bool = l >= 0 + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.ObjectMeta = pkg2_v1.ObjectMeta{} + } else { + yyv10 := &x.ObjectMeta + yyv10.CodecDecodeSelf(d) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Spec = ScaleSpec{} + } else { + yyv11 := &x.Spec + yyv11.CodecDecodeSelf(d) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Status = ScaleStatus{} + } else { + yyv12 := &x.Status + yyv12.CodecDecodeSelf(d) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + for { + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj9-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x *ScaleSpec) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [1]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = x.Replicas != 0 + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(1) + } else { + yynn2 = 0 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + yym4 := z.EncBinary() + _ = yym4 + if false { + } else { + r.EncodeInt(int64(x.Replicas)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("replicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym5 := z.EncBinary() + _ = yym5 + if false { + } else { + r.EncodeInt(int64(x.Replicas)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *ScaleSpec) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *ScaleSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "replicas": + if r.TryDecodeAsNil() { + x.Replicas = 0 + } else { + x.Replicas = int32(r.DecodeInt(32)) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *ScaleSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj5 int + var yyb5 bool + var yyhl5 bool = l >= 0 + yyj5++ + if yyhl5 { + yyb5 = yyj5 > l + } else { + yyb5 = r.CheckBreak() + } + if yyb5 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Replicas = 0 + } else { + x.Replicas = int32(r.DecodeInt(32)) + } + for { + yyj5++ + if yyhl5 { + yyb5 = yyj5 > l + } else { + yyb5 = r.CheckBreak() + } + if yyb5 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj5-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x *ScaleStatus) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [2]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[1] = x.Selector != "" + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(2) + } else { + yynn2 = 1 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym4 := z.EncBinary() + _ = yym4 + if false { + } else { + r.EncodeInt(int64(x.Replicas)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("replicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym5 := z.EncBinary() + _ = yym5 + if false { + } else { + r.EncodeInt(int64(x.Replicas)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + yym7 := z.EncBinary() + _ = yym7 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Selector)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("selector")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym8 := z.EncBinary() + _ = yym8 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Selector)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *ScaleStatus) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *ScaleStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "replicas": + if r.TryDecodeAsNil() { + x.Replicas = 0 + } else { + x.Replicas = int32(r.DecodeInt(32)) + } + case "selector": + if r.TryDecodeAsNil() { + x.Selector = "" + } else { + x.Selector = string(r.DecodeString()) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *ScaleStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj6 int + var yyb6 bool + var yyhl6 bool = l >= 0 + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Replicas = 0 + } else { + x.Replicas = int32(r.DecodeInt(32)) + } + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Selector = "" + } else { + x.Selector = string(r.DecodeString()) + } + for { + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj6-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + func (x codecSelfer1234) encSliceHorizontalPodAutoscaler(v []HorizontalPodAutoscaler, e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/types.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/types.go index 6ad1d61af..20b96728d 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/types.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/types.go @@ -23,9 +23,9 @@ import ( // CrossVersionObjectReference contains enough information to let you identify the referred resource. type CrossVersionObjectReference struct { - // Kind of the referent; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds" + // Kind of the referent; More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds" Kind string `json:"kind"` - // Name of the referent; More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names + // Name of the referent; More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#names Name string `json:"name"` // API version of the referent APIVersion string `json:"apiVersion,omitempty"` @@ -41,6 +41,7 @@ type HorizontalPodAutoscalerSpec struct { // upper limit for the number of pods that can be set by the autoscaler; cannot be smaller than MinReplicas. MaxReplicas int32 `json:"maxReplicas"` // target average CPU utilization (represented as a percentage of requested CPU) over all the pods; + // if not specified the default autoscaling policy will be used. TargetCPUUtilizationPercentage *int32 `json:"targetCPUUtilizationPercentage,omitempty"` } @@ -67,10 +68,10 @@ type HorizontalPodAutoscalerStatus struct { // configuration of a horizontal pod autoscaler. type HorizontalPodAutoscaler struct { unversioned.TypeMeta `json:",inline"` - // Standard object metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // Standard object metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata v1.ObjectMeta `json:"metadata,omitempty"` - // behaviour of autoscaler. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status. + // behaviour of autoscaler. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status. Spec HorizontalPodAutoscalerSpec `json:"spec,omitempty"` // current information about the autoscaler. @@ -86,3 +87,34 @@ type HorizontalPodAutoscalerList struct { // list of horizontal pod autoscaler objects. Items []HorizontalPodAutoscaler `json:"items"` } + +// Scale represents a scaling request for a resource. +type Scale struct { + unversioned.TypeMeta `json:",inline"` + // Standard object metadata; More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata. + v1.ObjectMeta `json:"metadata,omitempty"` + + // defines the behavior of the scale. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status. + Spec ScaleSpec `json:"spec,omitempty"` + + // current status of the scale. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status. Read-only. + Status ScaleStatus `json:"status,omitempty"` +} + +// ScaleSpec describes the attributes of a scale subresource. +type ScaleSpec struct { + // desired number of instances for the scaled object. + Replicas int32 `json:"replicas,omitempty"` +} + +// ScaleStatus represents the current status of a scale subresource. +type ScaleStatus struct { + // actual number of observed instances of the scaled object. + Replicas int32 `json:"replicas"` + + // label query over pods that should match the replicas count. This is same + // as the label selector but in the string format to avoid introspection + // by clients. The string will be in the same format as the query-param syntax. + // More info about label selectors: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors + Selector string `json:"selector,omitempty"` +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/types_swagger_doc_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/types_swagger_doc_generated.go index ccec779f2..537dafba4 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/types_swagger_doc_generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/autoscaling/v1/types_swagger_doc_generated.go @@ -16,7 +16,7 @@ limitations under the License. package v1 -// This file contains a collection of methods that can be used from go-resful to +// This file contains a collection of methods that can be used from go-restful to // generate Swagger API documentation for its models. Please read this PR for more // information on the implementation: https://github.com/emicklei/go-restful/pull/215 // @@ -29,8 +29,8 @@ package v1 // AUTO-GENERATED FUNCTIONS START HERE var map_CrossVersionObjectReference = map[string]string{ "": "CrossVersionObjectReference contains enough information to let you identify the referred resource.", - "kind": "Kind of the referent; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds\"", - "name": "Name of the referent; More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names", + "kind": "Kind of the referent; More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds\"", + "name": "Name of the referent; More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#names", "apiVersion": "API version of the referent", } @@ -40,8 +40,8 @@ func (CrossVersionObjectReference) SwaggerDoc() map[string]string { var map_HorizontalPodAutoscaler = map[string]string{ "": "configuration of a horizontal pod autoscaler.", - "metadata": "Standard object metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "behaviour of autoscaler. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status.", + "metadata": "Standard object metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "behaviour of autoscaler. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status.", "status": "current information about the autoscaler.", } @@ -64,7 +64,7 @@ var map_HorizontalPodAutoscalerSpec = map[string]string{ "scaleTargetRef": "reference to scaled resource; horizontal pod autoscaler will learn the current resource consumption and will set the desired number of pods by using its Scale subresource.", "minReplicas": "lower limit for the number of pods that can be set by the autoscaler, default 1.", "maxReplicas": "upper limit for the number of pods that can be set by the autoscaler; cannot be smaller than MinReplicas.", - "targetCPUUtilizationPercentage": "target average CPU utilization (represented as a percentage of requested CPU) over all the pods;", + "targetCPUUtilizationPercentage": "target average CPU utilization (represented as a percentage of requested CPU) over all the pods; if not specified the default autoscaling policy will be used.", } func (HorizontalPodAutoscalerSpec) SwaggerDoc() map[string]string { @@ -84,4 +84,34 @@ func (HorizontalPodAutoscalerStatus) SwaggerDoc() map[string]string { return map_HorizontalPodAutoscalerStatus } +var map_Scale = map[string]string{ + "": "Scale represents a scaling request for a resource.", + "metadata": "Standard object metadata; More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata.", + "spec": "defines the behavior of the scale. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status.", + "status": "current status of the scale. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status. Read-only.", +} + +func (Scale) SwaggerDoc() map[string]string { + return map_Scale +} + +var map_ScaleSpec = map[string]string{ + "": "ScaleSpec describes the attributes of a scale subresource.", + "replicas": "desired number of instances for the scaled object.", +} + +func (ScaleSpec) SwaggerDoc() map[string]string { + return map_ScaleSpec +} + +var map_ScaleStatus = map[string]string{ + "": "ScaleStatus represents the current status of a scale subresource.", + "replicas": "actual number of observed instances of the scaled object.", + "selector": "label query over pods that should match the replicas count. This is same as the label selector but in the string format to avoid introspection by clients. The string will be in the same format as the query-param syntax. More info about label selectors: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors", +} + +func (ScaleStatus) SwaggerDoc() map[string]string { + return map_ScaleStatus +} + // AUTO-GENERATED FUNCTIONS END HERE diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/term.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/deep_copy_generated.go similarity index 64% rename from Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/term.go rename to Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/deep_copy_generated.go index 9db85fe4b..55d346a61 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/term.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/deep_copy_generated.go @@ -1,5 +1,3 @@ -// +build !windows - /* Copyright 2015 The Kubernetes Authors All rights reserved. @@ -16,12 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -package editor +// DO NOT EDIT. THIS FILE IS AUTO-GENERATED BY $KUBEROOT/hack/update-generated-deep-copies.sh. -import ( - "os" - "syscall" -) +package batch -// childSignals are the allowed signals that can be sent to children in Unix variant OS's -var childSignals = []os.Signal{syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT} +import api "k8s.io/kubernetes/pkg/api" + +func init() { + err := api.Scheme.AddGeneratedDeepCopyFuncs() + if err != nil { + // if one of the deep copy functions is malformed, detect it immediately. + panic(err) + } +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/install/install.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/install/install.go new file mode 100644 index 000000000..830020a93 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/install/install.go @@ -0,0 +1,129 @@ +/* +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 install installs the batch API group, making it available as +// an option to all of the API encoding/decoding machinery. +package install + +import ( + "fmt" + + "github.com/golang/glog" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apimachinery" + "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/apis/batch" + "k8s.io/kubernetes/pkg/apis/batch/v1" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/sets" +) + +const importPrefix = "k8s.io/kubernetes/pkg/apis/batch" + +var accessor = meta.NewAccessor() + +// availableVersions lists all known external versions for this group from most preferred to least preferred +var availableVersions = []unversioned.GroupVersion{v1.SchemeGroupVersion} + +func init() { + registered.RegisterVersions(availableVersions) + externalVersions := []unversioned.GroupVersion{} + for _, v := range availableVersions { + if registered.IsAllowedVersion(v) { + externalVersions = append(externalVersions, v) + } + } + if len(externalVersions) == 0 { + glog.V(4).Infof("No version is registered for group %v", batch.GroupName) + return + } + + if err := registered.EnableVersions(externalVersions...); err != nil { + glog.V(4).Infof("%v", err) + return + } + if err := enableVersions(externalVersions); err != nil { + glog.V(4).Infof("%v", err) + return + } +} + +// TODO: enableVersions should be centralized rather than spread in each API +// group. +// We can combine registered.RegisterVersions, registered.EnableVersions and +// registered.RegisterGroup once we have moved enableVersions there. +func enableVersions(externalVersions []unversioned.GroupVersion) error { + addVersionsToScheme(externalVersions...) + preferredExternalVersion := externalVersions[0] + + groupMeta := apimachinery.GroupMeta{ + GroupVersion: preferredExternalVersion, + GroupVersions: externalVersions, + RESTMapper: newRESTMapper(externalVersions), + SelfLinker: runtime.SelfLinker(accessor), + InterfacesFor: interfacesFor, + } + + if err := registered.RegisterGroup(groupMeta); err != nil { + return err + } + api.RegisterRESTMapper(groupMeta.RESTMapper) + return nil +} + +func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper { + // the list of kinds that are scoped at the root of the api hierarchy + // if a kind is not enumerated here, it is assumed to have a namespace scope + rootScoped := sets.NewString() + + ignoredKinds := sets.NewString() + + return api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped) +} + +// interfacesFor returns the default Codec and ResourceVersioner for a given version +// string, or an error if the version is not known. +func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) { + switch version { + case v1.SchemeGroupVersion: + return &meta.VersionInterfaces{ + ObjectConvertor: api.Scheme, + MetadataAccessor: accessor, + }, nil + default: + g, _ := registered.Group(batch.GroupName) + return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, g.GroupVersions) + } +} + +func addVersionsToScheme(externalVersions ...unversioned.GroupVersion) { + // add the internal version to Scheme + batch.AddToScheme(api.Scheme) + // add the enabled external versions to Scheme + for _, v := range externalVersions { + if !registered.IsEnabledVersion(v) { + glog.Errorf("Version %s is not enabled, so it will not be added to the Scheme.", v) + continue + } + switch v { + case v1.SchemeGroupVersion: + v1.AddToScheme(api.Scheme) + } + } +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/register.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/register.go new file mode 100644 index 000000000..a302fe751 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/register.go @@ -0,0 +1,54 @@ +/* +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 batch + +import ( + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/runtime" +) + +// GroupName is the group name use in this package +const GroupName = "batch" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) unversioned.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns back a Group qualified GroupResource +func Resource(resource string) unversioned.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +func AddToScheme(scheme *runtime.Scheme) { + // Add the API to Scheme. + addKnownTypes(scheme) +} + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) { + scheme.AddKnownTypes(SchemeGroupVersion, + &extensions.Job{}, + &extensions.JobList{}, + &api.ListOptions{}, + ) +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/conversion.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/conversion.go new file mode 100644 index 000000000..c1a77ee8e --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/conversion.go @@ -0,0 +1,63 @@ +/* +Copyright 2015 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 v1 + +import ( + "fmt" + + "k8s.io/kubernetes/pkg/api" + v1 "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/conversion" + "k8s.io/kubernetes/pkg/runtime" +) + +func addConversionFuncs(scheme *runtime.Scheme) { + // Add non-generated conversion functions + err := scheme.AddConversionFuncs( + Convert_api_PodSpec_To_v1_PodSpec, + Convert_v1_PodSpec_To_api_PodSpec, + ) + if err != nil { + // If one of the conversion functions is malformed, detect it immediately. + panic(err) + } + + err = api.Scheme.AddFieldLabelConversionFunc("batch/v1", "Job", + func(label, value string) (string, string, error) { + switch label { + case "metadata.name", "metadata.namespace", "status.successful": + return label, value, nil + default: + return "", "", fmt.Errorf("field label not supported: %s", label) + } + }) + if err != nil { + // If one of the conversion functions is malformed, detect it immediately. + panic(err) + } +} + +// The following two PodSpec conversions functions where copied from pkg/api/conversion.go +// for the generated functions to work properly. +// This should be fixed: https://github.com/kubernetes/kubernetes/issues/12977 +func Convert_api_PodSpec_To_v1_PodSpec(in *api.PodSpec, out *v1.PodSpec, s conversion.Scope) error { + return v1.Convert_api_PodSpec_To_v1_PodSpec(in, out, s) +} + +func Convert_v1_PodSpec_To_api_PodSpec(in *v1.PodSpec, out *api.PodSpec, s conversion.Scope) error { + return v1.Convert_v1_PodSpec_To_api_PodSpec(in, out, s) +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/conversion_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/conversion_generated.go new file mode 100644 index 000000000..9a7b4876f --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/conversion_generated.go @@ -0,0 +1,3065 @@ +/* +Copyright 2015 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. +*/ + +// DO NOT EDIT. THIS FILE IS AUTO-GENERATED BY $KUBEROOT/hack/update-generated-conversions.sh + +package v1 + +import ( + reflect "reflect" + + api "k8s.io/kubernetes/pkg/api" + resource "k8s.io/kubernetes/pkg/api/resource" + unversioned "k8s.io/kubernetes/pkg/api/unversioned" + v1 "k8s.io/kubernetes/pkg/api/v1" + extensions "k8s.io/kubernetes/pkg/apis/extensions" + conversion "k8s.io/kubernetes/pkg/conversion" +) + +func autoConvert_api_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolumeSource(in *api.AWSElasticBlockStoreVolumeSource, out *v1.AWSElasticBlockStoreVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.AWSElasticBlockStoreVolumeSource))(in) + } + out.VolumeID = in.VolumeID + out.FSType = in.FSType + out.Partition = int32(in.Partition) + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_api_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolumeSource(in *api.AWSElasticBlockStoreVolumeSource, out *v1.AWSElasticBlockStoreVolumeSource, s conversion.Scope) error { + return autoConvert_api_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolumeSource(in, out, s) +} + +func autoConvert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource(in *api.AzureFileVolumeSource, out *v1.AzureFileVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.AzureFileVolumeSource))(in) + } + out.SecretName = in.SecretName + out.ShareName = in.ShareName + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource(in *api.AzureFileVolumeSource, out *v1.AzureFileVolumeSource, s conversion.Scope) error { + return autoConvert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource(in, out, s) +} + +func autoConvert_api_Capabilities_To_v1_Capabilities(in *api.Capabilities, out *v1.Capabilities, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.Capabilities))(in) + } + if in.Add != nil { + out.Add = make([]v1.Capability, len(in.Add)) + for i := range in.Add { + out.Add[i] = v1.Capability(in.Add[i]) + } + } else { + out.Add = nil + } + if in.Drop != nil { + out.Drop = make([]v1.Capability, len(in.Drop)) + for i := range in.Drop { + out.Drop[i] = v1.Capability(in.Drop[i]) + } + } else { + out.Drop = nil + } + return nil +} + +func Convert_api_Capabilities_To_v1_Capabilities(in *api.Capabilities, out *v1.Capabilities, s conversion.Scope) error { + return autoConvert_api_Capabilities_To_v1_Capabilities(in, out, s) +} + +func autoConvert_api_CephFSVolumeSource_To_v1_CephFSVolumeSource(in *api.CephFSVolumeSource, out *v1.CephFSVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.CephFSVolumeSource))(in) + } + if in.Monitors != nil { + out.Monitors = make([]string, len(in.Monitors)) + for i := range in.Monitors { + out.Monitors[i] = in.Monitors[i] + } + } else { + out.Monitors = nil + } + out.Path = in.Path + out.User = in.User + out.SecretFile = in.SecretFile + // unable to generate simple pointer conversion for api.LocalObjectReference -> v1.LocalObjectReference + if in.SecretRef != nil { + out.SecretRef = new(v1.LocalObjectReference) + if err := Convert_api_LocalObjectReference_To_v1_LocalObjectReference(in.SecretRef, out.SecretRef, s); err != nil { + return err + } + } else { + out.SecretRef = nil + } + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_api_CephFSVolumeSource_To_v1_CephFSVolumeSource(in *api.CephFSVolumeSource, out *v1.CephFSVolumeSource, s conversion.Scope) error { + return autoConvert_api_CephFSVolumeSource_To_v1_CephFSVolumeSource(in, out, s) +} + +func autoConvert_api_CinderVolumeSource_To_v1_CinderVolumeSource(in *api.CinderVolumeSource, out *v1.CinderVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.CinderVolumeSource))(in) + } + out.VolumeID = in.VolumeID + out.FSType = in.FSType + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_api_CinderVolumeSource_To_v1_CinderVolumeSource(in *api.CinderVolumeSource, out *v1.CinderVolumeSource, s conversion.Scope) error { + return autoConvert_api_CinderVolumeSource_To_v1_CinderVolumeSource(in, out, s) +} + +func autoConvert_api_ConfigMapKeySelector_To_v1_ConfigMapKeySelector(in *api.ConfigMapKeySelector, out *v1.ConfigMapKeySelector, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.ConfigMapKeySelector))(in) + } + if err := Convert_api_LocalObjectReference_To_v1_LocalObjectReference(&in.LocalObjectReference, &out.LocalObjectReference, s); err != nil { + return err + } + out.Key = in.Key + return nil +} + +func Convert_api_ConfigMapKeySelector_To_v1_ConfigMapKeySelector(in *api.ConfigMapKeySelector, out *v1.ConfigMapKeySelector, s conversion.Scope) error { + return autoConvert_api_ConfigMapKeySelector_To_v1_ConfigMapKeySelector(in, out, s) +} + +func autoConvert_api_ConfigMapVolumeSource_To_v1_ConfigMapVolumeSource(in *api.ConfigMapVolumeSource, out *v1.ConfigMapVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.ConfigMapVolumeSource))(in) + } + if err := Convert_api_LocalObjectReference_To_v1_LocalObjectReference(&in.LocalObjectReference, &out.LocalObjectReference, s); err != nil { + return err + } + if in.Items != nil { + out.Items = make([]v1.KeyToPath, len(in.Items)) + for i := range in.Items { + if err := Convert_api_KeyToPath_To_v1_KeyToPath(&in.Items[i], &out.Items[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func Convert_api_ConfigMapVolumeSource_To_v1_ConfigMapVolumeSource(in *api.ConfigMapVolumeSource, out *v1.ConfigMapVolumeSource, s conversion.Scope) error { + return autoConvert_api_ConfigMapVolumeSource_To_v1_ConfigMapVolumeSource(in, out, s) +} + +func autoConvert_api_Container_To_v1_Container(in *api.Container, out *v1.Container, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.Container))(in) + } + out.Name = in.Name + out.Image = in.Image + if in.Command != nil { + out.Command = make([]string, len(in.Command)) + for i := range in.Command { + out.Command[i] = in.Command[i] + } + } else { + out.Command = nil + } + if in.Args != nil { + out.Args = make([]string, len(in.Args)) + for i := range in.Args { + out.Args[i] = in.Args[i] + } + } else { + out.Args = nil + } + out.WorkingDir = in.WorkingDir + if in.Ports != nil { + out.Ports = make([]v1.ContainerPort, len(in.Ports)) + for i := range in.Ports { + if err := Convert_api_ContainerPort_To_v1_ContainerPort(&in.Ports[i], &out.Ports[i], s); err != nil { + return err + } + } + } else { + out.Ports = nil + } + if in.Env != nil { + out.Env = make([]v1.EnvVar, len(in.Env)) + for i := range in.Env { + if err := Convert_api_EnvVar_To_v1_EnvVar(&in.Env[i], &out.Env[i], s); err != nil { + return err + } + } + } else { + out.Env = nil + } + if err := Convert_api_ResourceRequirements_To_v1_ResourceRequirements(&in.Resources, &out.Resources, s); err != nil { + return err + } + if in.VolumeMounts != nil { + out.VolumeMounts = make([]v1.VolumeMount, len(in.VolumeMounts)) + for i := range in.VolumeMounts { + if err := Convert_api_VolumeMount_To_v1_VolumeMount(&in.VolumeMounts[i], &out.VolumeMounts[i], s); err != nil { + return err + } + } + } else { + out.VolumeMounts = nil + } + // unable to generate simple pointer conversion for api.Probe -> v1.Probe + if in.LivenessProbe != nil { + out.LivenessProbe = new(v1.Probe) + if err := Convert_api_Probe_To_v1_Probe(in.LivenessProbe, out.LivenessProbe, s); err != nil { + return err + } + } else { + out.LivenessProbe = nil + } + // unable to generate simple pointer conversion for api.Probe -> v1.Probe + if in.ReadinessProbe != nil { + out.ReadinessProbe = new(v1.Probe) + if err := Convert_api_Probe_To_v1_Probe(in.ReadinessProbe, out.ReadinessProbe, s); err != nil { + return err + } + } else { + out.ReadinessProbe = nil + } + // unable to generate simple pointer conversion for api.Lifecycle -> v1.Lifecycle + if in.Lifecycle != nil { + out.Lifecycle = new(v1.Lifecycle) + if err := Convert_api_Lifecycle_To_v1_Lifecycle(in.Lifecycle, out.Lifecycle, s); err != nil { + return err + } + } else { + out.Lifecycle = nil + } + out.TerminationMessagePath = in.TerminationMessagePath + out.ImagePullPolicy = v1.PullPolicy(in.ImagePullPolicy) + // unable to generate simple pointer conversion for api.SecurityContext -> v1.SecurityContext + if in.SecurityContext != nil { + out.SecurityContext = new(v1.SecurityContext) + if err := Convert_api_SecurityContext_To_v1_SecurityContext(in.SecurityContext, out.SecurityContext, s); err != nil { + return err + } + } else { + out.SecurityContext = nil + } + out.Stdin = in.Stdin + out.StdinOnce = in.StdinOnce + out.TTY = in.TTY + return nil +} + +func Convert_api_Container_To_v1_Container(in *api.Container, out *v1.Container, s conversion.Scope) error { + return autoConvert_api_Container_To_v1_Container(in, out, s) +} + +func autoConvert_api_ContainerPort_To_v1_ContainerPort(in *api.ContainerPort, out *v1.ContainerPort, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.ContainerPort))(in) + } + out.Name = in.Name + out.HostPort = int32(in.HostPort) + out.ContainerPort = int32(in.ContainerPort) + out.Protocol = v1.Protocol(in.Protocol) + out.HostIP = in.HostIP + return nil +} + +func Convert_api_ContainerPort_To_v1_ContainerPort(in *api.ContainerPort, out *v1.ContainerPort, s conversion.Scope) error { + return autoConvert_api_ContainerPort_To_v1_ContainerPort(in, out, s) +} + +func autoConvert_api_DownwardAPIVolumeFile_To_v1_DownwardAPIVolumeFile(in *api.DownwardAPIVolumeFile, out *v1.DownwardAPIVolumeFile, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.DownwardAPIVolumeFile))(in) + } + out.Path = in.Path + if err := Convert_api_ObjectFieldSelector_To_v1_ObjectFieldSelector(&in.FieldRef, &out.FieldRef, s); err != nil { + return err + } + return nil +} + +func Convert_api_DownwardAPIVolumeFile_To_v1_DownwardAPIVolumeFile(in *api.DownwardAPIVolumeFile, out *v1.DownwardAPIVolumeFile, s conversion.Scope) error { + return autoConvert_api_DownwardAPIVolumeFile_To_v1_DownwardAPIVolumeFile(in, out, s) +} + +func autoConvert_api_DownwardAPIVolumeSource_To_v1_DownwardAPIVolumeSource(in *api.DownwardAPIVolumeSource, out *v1.DownwardAPIVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.DownwardAPIVolumeSource))(in) + } + if in.Items != nil { + out.Items = make([]v1.DownwardAPIVolumeFile, len(in.Items)) + for i := range in.Items { + if err := Convert_api_DownwardAPIVolumeFile_To_v1_DownwardAPIVolumeFile(&in.Items[i], &out.Items[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func Convert_api_DownwardAPIVolumeSource_To_v1_DownwardAPIVolumeSource(in *api.DownwardAPIVolumeSource, out *v1.DownwardAPIVolumeSource, s conversion.Scope) error { + return autoConvert_api_DownwardAPIVolumeSource_To_v1_DownwardAPIVolumeSource(in, out, s) +} + +func autoConvert_api_EmptyDirVolumeSource_To_v1_EmptyDirVolumeSource(in *api.EmptyDirVolumeSource, out *v1.EmptyDirVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.EmptyDirVolumeSource))(in) + } + out.Medium = v1.StorageMedium(in.Medium) + return nil +} + +func Convert_api_EmptyDirVolumeSource_To_v1_EmptyDirVolumeSource(in *api.EmptyDirVolumeSource, out *v1.EmptyDirVolumeSource, s conversion.Scope) error { + return autoConvert_api_EmptyDirVolumeSource_To_v1_EmptyDirVolumeSource(in, out, s) +} + +func autoConvert_api_EnvVar_To_v1_EnvVar(in *api.EnvVar, out *v1.EnvVar, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.EnvVar))(in) + } + out.Name = in.Name + out.Value = in.Value + // unable to generate simple pointer conversion for api.EnvVarSource -> v1.EnvVarSource + if in.ValueFrom != nil { + out.ValueFrom = new(v1.EnvVarSource) + if err := Convert_api_EnvVarSource_To_v1_EnvVarSource(in.ValueFrom, out.ValueFrom, s); err != nil { + return err + } + } else { + out.ValueFrom = nil + } + return nil +} + +func Convert_api_EnvVar_To_v1_EnvVar(in *api.EnvVar, out *v1.EnvVar, s conversion.Scope) error { + return autoConvert_api_EnvVar_To_v1_EnvVar(in, out, s) +} + +func autoConvert_api_EnvVarSource_To_v1_EnvVarSource(in *api.EnvVarSource, out *v1.EnvVarSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.EnvVarSource))(in) + } + // unable to generate simple pointer conversion for api.ObjectFieldSelector -> v1.ObjectFieldSelector + if in.FieldRef != nil { + out.FieldRef = new(v1.ObjectFieldSelector) + if err := Convert_api_ObjectFieldSelector_To_v1_ObjectFieldSelector(in.FieldRef, out.FieldRef, s); err != nil { + return err + } + } else { + out.FieldRef = nil + } + // unable to generate simple pointer conversion for api.ConfigMapKeySelector -> v1.ConfigMapKeySelector + if in.ConfigMapKeyRef != nil { + out.ConfigMapKeyRef = new(v1.ConfigMapKeySelector) + if err := Convert_api_ConfigMapKeySelector_To_v1_ConfigMapKeySelector(in.ConfigMapKeyRef, out.ConfigMapKeyRef, s); err != nil { + return err + } + } else { + out.ConfigMapKeyRef = nil + } + // unable to generate simple pointer conversion for api.SecretKeySelector -> v1.SecretKeySelector + if in.SecretKeyRef != nil { + out.SecretKeyRef = new(v1.SecretKeySelector) + if err := Convert_api_SecretKeySelector_To_v1_SecretKeySelector(in.SecretKeyRef, out.SecretKeyRef, s); err != nil { + return err + } + } else { + out.SecretKeyRef = nil + } + return nil +} + +func Convert_api_EnvVarSource_To_v1_EnvVarSource(in *api.EnvVarSource, out *v1.EnvVarSource, s conversion.Scope) error { + return autoConvert_api_EnvVarSource_To_v1_EnvVarSource(in, out, s) +} + +func autoConvert_api_ExecAction_To_v1_ExecAction(in *api.ExecAction, out *v1.ExecAction, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.ExecAction))(in) + } + if in.Command != nil { + out.Command = make([]string, len(in.Command)) + for i := range in.Command { + out.Command[i] = in.Command[i] + } + } else { + out.Command = nil + } + return nil +} + +func Convert_api_ExecAction_To_v1_ExecAction(in *api.ExecAction, out *v1.ExecAction, s conversion.Scope) error { + return autoConvert_api_ExecAction_To_v1_ExecAction(in, out, s) +} + +func autoConvert_api_FCVolumeSource_To_v1_FCVolumeSource(in *api.FCVolumeSource, out *v1.FCVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.FCVolumeSource))(in) + } + if in.TargetWWNs != nil { + out.TargetWWNs = make([]string, len(in.TargetWWNs)) + for i := range in.TargetWWNs { + out.TargetWWNs[i] = in.TargetWWNs[i] + } + } else { + out.TargetWWNs = nil + } + if in.Lun != nil { + out.Lun = new(int32) + *out.Lun = int32(*in.Lun) + } else { + out.Lun = nil + } + out.FSType = in.FSType + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_api_FCVolumeSource_To_v1_FCVolumeSource(in *api.FCVolumeSource, out *v1.FCVolumeSource, s conversion.Scope) error { + return autoConvert_api_FCVolumeSource_To_v1_FCVolumeSource(in, out, s) +} + +func autoConvert_api_FlexVolumeSource_To_v1_FlexVolumeSource(in *api.FlexVolumeSource, out *v1.FlexVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.FlexVolumeSource))(in) + } + out.Driver = in.Driver + out.FSType = in.FSType + // unable to generate simple pointer conversion for api.LocalObjectReference -> v1.LocalObjectReference + if in.SecretRef != nil { + out.SecretRef = new(v1.LocalObjectReference) + if err := Convert_api_LocalObjectReference_To_v1_LocalObjectReference(in.SecretRef, out.SecretRef, s); err != nil { + return err + } + } else { + out.SecretRef = nil + } + out.ReadOnly = in.ReadOnly + if in.Options != nil { + out.Options = make(map[string]string) + for key, val := range in.Options { + out.Options[key] = val + } + } else { + out.Options = nil + } + return nil +} + +func Convert_api_FlexVolumeSource_To_v1_FlexVolumeSource(in *api.FlexVolumeSource, out *v1.FlexVolumeSource, s conversion.Scope) error { + return autoConvert_api_FlexVolumeSource_To_v1_FlexVolumeSource(in, out, s) +} + +func autoConvert_api_FlockerVolumeSource_To_v1_FlockerVolumeSource(in *api.FlockerVolumeSource, out *v1.FlockerVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.FlockerVolumeSource))(in) + } + out.DatasetName = in.DatasetName + return nil +} + +func Convert_api_FlockerVolumeSource_To_v1_FlockerVolumeSource(in *api.FlockerVolumeSource, out *v1.FlockerVolumeSource, s conversion.Scope) error { + return autoConvert_api_FlockerVolumeSource_To_v1_FlockerVolumeSource(in, out, s) +} + +func autoConvert_api_GCEPersistentDiskVolumeSource_To_v1_GCEPersistentDiskVolumeSource(in *api.GCEPersistentDiskVolumeSource, out *v1.GCEPersistentDiskVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.GCEPersistentDiskVolumeSource))(in) + } + out.PDName = in.PDName + out.FSType = in.FSType + out.Partition = int32(in.Partition) + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_api_GCEPersistentDiskVolumeSource_To_v1_GCEPersistentDiskVolumeSource(in *api.GCEPersistentDiskVolumeSource, out *v1.GCEPersistentDiskVolumeSource, s conversion.Scope) error { + return autoConvert_api_GCEPersistentDiskVolumeSource_To_v1_GCEPersistentDiskVolumeSource(in, out, s) +} + +func autoConvert_api_GitRepoVolumeSource_To_v1_GitRepoVolumeSource(in *api.GitRepoVolumeSource, out *v1.GitRepoVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.GitRepoVolumeSource))(in) + } + out.Repository = in.Repository + out.Revision = in.Revision + out.Directory = in.Directory + return nil +} + +func Convert_api_GitRepoVolumeSource_To_v1_GitRepoVolumeSource(in *api.GitRepoVolumeSource, out *v1.GitRepoVolumeSource, s conversion.Scope) error { + return autoConvert_api_GitRepoVolumeSource_To_v1_GitRepoVolumeSource(in, out, s) +} + +func autoConvert_api_GlusterfsVolumeSource_To_v1_GlusterfsVolumeSource(in *api.GlusterfsVolumeSource, out *v1.GlusterfsVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.GlusterfsVolumeSource))(in) + } + out.EndpointsName = in.EndpointsName + out.Path = in.Path + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_api_GlusterfsVolumeSource_To_v1_GlusterfsVolumeSource(in *api.GlusterfsVolumeSource, out *v1.GlusterfsVolumeSource, s conversion.Scope) error { + return autoConvert_api_GlusterfsVolumeSource_To_v1_GlusterfsVolumeSource(in, out, s) +} + +func autoConvert_api_HTTPGetAction_To_v1_HTTPGetAction(in *api.HTTPGetAction, out *v1.HTTPGetAction, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.HTTPGetAction))(in) + } + out.Path = in.Path + if err := api.Convert_intstr_IntOrString_To_intstr_IntOrString(&in.Port, &out.Port, s); err != nil { + return err + } + out.Host = in.Host + out.Scheme = v1.URIScheme(in.Scheme) + if in.HTTPHeaders != nil { + out.HTTPHeaders = make([]v1.HTTPHeader, len(in.HTTPHeaders)) + for i := range in.HTTPHeaders { + if err := Convert_api_HTTPHeader_To_v1_HTTPHeader(&in.HTTPHeaders[i], &out.HTTPHeaders[i], s); err != nil { + return err + } + } + } else { + out.HTTPHeaders = nil + } + return nil +} + +func Convert_api_HTTPGetAction_To_v1_HTTPGetAction(in *api.HTTPGetAction, out *v1.HTTPGetAction, s conversion.Scope) error { + return autoConvert_api_HTTPGetAction_To_v1_HTTPGetAction(in, out, s) +} + +func autoConvert_api_HTTPHeader_To_v1_HTTPHeader(in *api.HTTPHeader, out *v1.HTTPHeader, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.HTTPHeader))(in) + } + out.Name = in.Name + out.Value = in.Value + return nil +} + +func Convert_api_HTTPHeader_To_v1_HTTPHeader(in *api.HTTPHeader, out *v1.HTTPHeader, s conversion.Scope) error { + return autoConvert_api_HTTPHeader_To_v1_HTTPHeader(in, out, s) +} + +func autoConvert_api_Handler_To_v1_Handler(in *api.Handler, out *v1.Handler, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.Handler))(in) + } + // unable to generate simple pointer conversion for api.ExecAction -> v1.ExecAction + if in.Exec != nil { + out.Exec = new(v1.ExecAction) + if err := Convert_api_ExecAction_To_v1_ExecAction(in.Exec, out.Exec, s); err != nil { + return err + } + } else { + out.Exec = nil + } + // unable to generate simple pointer conversion for api.HTTPGetAction -> v1.HTTPGetAction + if in.HTTPGet != nil { + out.HTTPGet = new(v1.HTTPGetAction) + if err := Convert_api_HTTPGetAction_To_v1_HTTPGetAction(in.HTTPGet, out.HTTPGet, s); err != nil { + return err + } + } else { + out.HTTPGet = nil + } + // unable to generate simple pointer conversion for api.TCPSocketAction -> v1.TCPSocketAction + if in.TCPSocket != nil { + out.TCPSocket = new(v1.TCPSocketAction) + if err := Convert_api_TCPSocketAction_To_v1_TCPSocketAction(in.TCPSocket, out.TCPSocket, s); err != nil { + return err + } + } else { + out.TCPSocket = nil + } + return nil +} + +func Convert_api_Handler_To_v1_Handler(in *api.Handler, out *v1.Handler, s conversion.Scope) error { + return autoConvert_api_Handler_To_v1_Handler(in, out, s) +} + +func autoConvert_api_HostPathVolumeSource_To_v1_HostPathVolumeSource(in *api.HostPathVolumeSource, out *v1.HostPathVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.HostPathVolumeSource))(in) + } + out.Path = in.Path + return nil +} + +func Convert_api_HostPathVolumeSource_To_v1_HostPathVolumeSource(in *api.HostPathVolumeSource, out *v1.HostPathVolumeSource, s conversion.Scope) error { + return autoConvert_api_HostPathVolumeSource_To_v1_HostPathVolumeSource(in, out, s) +} + +func autoConvert_api_ISCSIVolumeSource_To_v1_ISCSIVolumeSource(in *api.ISCSIVolumeSource, out *v1.ISCSIVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.ISCSIVolumeSource))(in) + } + out.TargetPortal = in.TargetPortal + out.IQN = in.IQN + out.Lun = int32(in.Lun) + out.ISCSIInterface = in.ISCSIInterface + out.FSType = in.FSType + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_api_ISCSIVolumeSource_To_v1_ISCSIVolumeSource(in *api.ISCSIVolumeSource, out *v1.ISCSIVolumeSource, s conversion.Scope) error { + return autoConvert_api_ISCSIVolumeSource_To_v1_ISCSIVolumeSource(in, out, s) +} + +func autoConvert_api_KeyToPath_To_v1_KeyToPath(in *api.KeyToPath, out *v1.KeyToPath, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.KeyToPath))(in) + } + out.Key = in.Key + out.Path = in.Path + return nil +} + +func Convert_api_KeyToPath_To_v1_KeyToPath(in *api.KeyToPath, out *v1.KeyToPath, s conversion.Scope) error { + return autoConvert_api_KeyToPath_To_v1_KeyToPath(in, out, s) +} + +func autoConvert_api_Lifecycle_To_v1_Lifecycle(in *api.Lifecycle, out *v1.Lifecycle, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.Lifecycle))(in) + } + // unable to generate simple pointer conversion for api.Handler -> v1.Handler + if in.PostStart != nil { + out.PostStart = new(v1.Handler) + if err := Convert_api_Handler_To_v1_Handler(in.PostStart, out.PostStart, s); err != nil { + return err + } + } else { + out.PostStart = nil + } + // unable to generate simple pointer conversion for api.Handler -> v1.Handler + if in.PreStop != nil { + out.PreStop = new(v1.Handler) + if err := Convert_api_Handler_To_v1_Handler(in.PreStop, out.PreStop, s); err != nil { + return err + } + } else { + out.PreStop = nil + } + return nil +} + +func Convert_api_Lifecycle_To_v1_Lifecycle(in *api.Lifecycle, out *v1.Lifecycle, s conversion.Scope) error { + return autoConvert_api_Lifecycle_To_v1_Lifecycle(in, out, s) +} + +func autoConvert_api_LocalObjectReference_To_v1_LocalObjectReference(in *api.LocalObjectReference, out *v1.LocalObjectReference, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.LocalObjectReference))(in) + } + out.Name = in.Name + return nil +} + +func Convert_api_LocalObjectReference_To_v1_LocalObjectReference(in *api.LocalObjectReference, out *v1.LocalObjectReference, s conversion.Scope) error { + return autoConvert_api_LocalObjectReference_To_v1_LocalObjectReference(in, out, s) +} + +func autoConvert_api_NFSVolumeSource_To_v1_NFSVolumeSource(in *api.NFSVolumeSource, out *v1.NFSVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.NFSVolumeSource))(in) + } + out.Server = in.Server + out.Path = in.Path + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_api_NFSVolumeSource_To_v1_NFSVolumeSource(in *api.NFSVolumeSource, out *v1.NFSVolumeSource, s conversion.Scope) error { + return autoConvert_api_NFSVolumeSource_To_v1_NFSVolumeSource(in, out, s) +} + +func autoConvert_api_ObjectFieldSelector_To_v1_ObjectFieldSelector(in *api.ObjectFieldSelector, out *v1.ObjectFieldSelector, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.ObjectFieldSelector))(in) + } + out.APIVersion = in.APIVersion + out.FieldPath = in.FieldPath + return nil +} + +func Convert_api_ObjectFieldSelector_To_v1_ObjectFieldSelector(in *api.ObjectFieldSelector, out *v1.ObjectFieldSelector, s conversion.Scope) error { + return autoConvert_api_ObjectFieldSelector_To_v1_ObjectFieldSelector(in, out, s) +} + +func autoConvert_api_ObjectMeta_To_v1_ObjectMeta(in *api.ObjectMeta, out *v1.ObjectMeta, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.ObjectMeta))(in) + } + out.Name = in.Name + out.GenerateName = in.GenerateName + out.Namespace = in.Namespace + out.SelfLink = in.SelfLink + out.UID = in.UID + out.ResourceVersion = in.ResourceVersion + out.Generation = in.Generation + if err := api.Convert_unversioned_Time_To_unversioned_Time(&in.CreationTimestamp, &out.CreationTimestamp, s); err != nil { + return err + } + // unable to generate simple pointer conversion for unversioned.Time -> unversioned.Time + if in.DeletionTimestamp != nil { + out.DeletionTimestamp = new(unversioned.Time) + if err := api.Convert_unversioned_Time_To_unversioned_Time(in.DeletionTimestamp, out.DeletionTimestamp, s); err != nil { + return err + } + } else { + out.DeletionTimestamp = nil + } + if in.DeletionGracePeriodSeconds != nil { + out.DeletionGracePeriodSeconds = new(int64) + *out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds + } else { + out.DeletionGracePeriodSeconds = nil + } + if in.Labels != nil { + out.Labels = make(map[string]string) + for key, val := range in.Labels { + out.Labels[key] = val + } + } else { + out.Labels = nil + } + if in.Annotations != nil { + out.Annotations = make(map[string]string) + for key, val := range in.Annotations { + out.Annotations[key] = val + } + } else { + out.Annotations = nil + } + return nil +} + +func Convert_api_ObjectMeta_To_v1_ObjectMeta(in *api.ObjectMeta, out *v1.ObjectMeta, s conversion.Scope) error { + return autoConvert_api_ObjectMeta_To_v1_ObjectMeta(in, out, s) +} + +func autoConvert_api_PersistentVolumeClaimVolumeSource_To_v1_PersistentVolumeClaimVolumeSource(in *api.PersistentVolumeClaimVolumeSource, out *v1.PersistentVolumeClaimVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.PersistentVolumeClaimVolumeSource))(in) + } + out.ClaimName = in.ClaimName + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_api_PersistentVolumeClaimVolumeSource_To_v1_PersistentVolumeClaimVolumeSource(in *api.PersistentVolumeClaimVolumeSource, out *v1.PersistentVolumeClaimVolumeSource, s conversion.Scope) error { + return autoConvert_api_PersistentVolumeClaimVolumeSource_To_v1_PersistentVolumeClaimVolumeSource(in, out, s) +} + +func autoConvert_api_PodSpec_To_v1_PodSpec(in *api.PodSpec, out *v1.PodSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.PodSpec))(in) + } + if in.Volumes != nil { + out.Volumes = make([]v1.Volume, len(in.Volumes)) + for i := range in.Volumes { + if err := Convert_api_Volume_To_v1_Volume(&in.Volumes[i], &out.Volumes[i], s); err != nil { + return err + } + } + } else { + out.Volumes = nil + } + if in.Containers != nil { + out.Containers = make([]v1.Container, len(in.Containers)) + for i := range in.Containers { + if err := Convert_api_Container_To_v1_Container(&in.Containers[i], &out.Containers[i], s); err != nil { + return err + } + } + } else { + out.Containers = nil + } + out.RestartPolicy = v1.RestartPolicy(in.RestartPolicy) + if in.TerminationGracePeriodSeconds != nil { + out.TerminationGracePeriodSeconds = new(int64) + *out.TerminationGracePeriodSeconds = *in.TerminationGracePeriodSeconds + } else { + out.TerminationGracePeriodSeconds = nil + } + if in.ActiveDeadlineSeconds != nil { + out.ActiveDeadlineSeconds = new(int64) + *out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds + } else { + out.ActiveDeadlineSeconds = nil + } + out.DNSPolicy = v1.DNSPolicy(in.DNSPolicy) + if in.NodeSelector != nil { + out.NodeSelector = make(map[string]string) + for key, val := range in.NodeSelector { + out.NodeSelector[key] = val + } + } else { + out.NodeSelector = nil + } + out.ServiceAccountName = in.ServiceAccountName + out.NodeName = in.NodeName + // unable to generate simple pointer conversion for api.PodSecurityContext -> v1.PodSecurityContext + if in.SecurityContext != nil { + if err := s.Convert(&in.SecurityContext, &out.SecurityContext, 0); err != nil { + return err + } + } else { + out.SecurityContext = nil + } + if in.ImagePullSecrets != nil { + out.ImagePullSecrets = make([]v1.LocalObjectReference, len(in.ImagePullSecrets)) + for i := range in.ImagePullSecrets { + if err := Convert_api_LocalObjectReference_To_v1_LocalObjectReference(&in.ImagePullSecrets[i], &out.ImagePullSecrets[i], s); err != nil { + return err + } + } + } else { + out.ImagePullSecrets = nil + } + return nil +} + +func autoConvert_api_PodTemplateSpec_To_v1_PodTemplateSpec(in *api.PodTemplateSpec, out *v1.PodTemplateSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.PodTemplateSpec))(in) + } + if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_api_PodSpec_To_v1_PodSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +func Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(in *api.PodTemplateSpec, out *v1.PodTemplateSpec, s conversion.Scope) error { + return autoConvert_api_PodTemplateSpec_To_v1_PodTemplateSpec(in, out, s) +} + +func autoConvert_api_Probe_To_v1_Probe(in *api.Probe, out *v1.Probe, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.Probe))(in) + } + if err := Convert_api_Handler_To_v1_Handler(&in.Handler, &out.Handler, s); err != nil { + return err + } + out.InitialDelaySeconds = int32(in.InitialDelaySeconds) + out.TimeoutSeconds = int32(in.TimeoutSeconds) + out.PeriodSeconds = int32(in.PeriodSeconds) + out.SuccessThreshold = int32(in.SuccessThreshold) + out.FailureThreshold = int32(in.FailureThreshold) + return nil +} + +func Convert_api_Probe_To_v1_Probe(in *api.Probe, out *v1.Probe, s conversion.Scope) error { + return autoConvert_api_Probe_To_v1_Probe(in, out, s) +} + +func autoConvert_api_RBDVolumeSource_To_v1_RBDVolumeSource(in *api.RBDVolumeSource, out *v1.RBDVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.RBDVolumeSource))(in) + } + if in.CephMonitors != nil { + out.CephMonitors = make([]string, len(in.CephMonitors)) + for i := range in.CephMonitors { + out.CephMonitors[i] = in.CephMonitors[i] + } + } else { + out.CephMonitors = nil + } + out.RBDImage = in.RBDImage + out.FSType = in.FSType + out.RBDPool = in.RBDPool + out.RadosUser = in.RadosUser + out.Keyring = in.Keyring + // unable to generate simple pointer conversion for api.LocalObjectReference -> v1.LocalObjectReference + if in.SecretRef != nil { + out.SecretRef = new(v1.LocalObjectReference) + if err := Convert_api_LocalObjectReference_To_v1_LocalObjectReference(in.SecretRef, out.SecretRef, s); err != nil { + return err + } + } else { + out.SecretRef = nil + } + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_api_RBDVolumeSource_To_v1_RBDVolumeSource(in *api.RBDVolumeSource, out *v1.RBDVolumeSource, s conversion.Scope) error { + return autoConvert_api_RBDVolumeSource_To_v1_RBDVolumeSource(in, out, s) +} + +func autoConvert_api_ResourceRequirements_To_v1_ResourceRequirements(in *api.ResourceRequirements, out *v1.ResourceRequirements, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.ResourceRequirements))(in) + } + if in.Limits != nil { + out.Limits = make(v1.ResourceList) + for key, val := range in.Limits { + newVal := resource.Quantity{} + if err := api.Convert_resource_Quantity_To_resource_Quantity(&val, &newVal, s); err != nil { + return err + } + out.Limits[v1.ResourceName(key)] = newVal + } + } else { + out.Limits = nil + } + if in.Requests != nil { + out.Requests = make(v1.ResourceList) + for key, val := range in.Requests { + newVal := resource.Quantity{} + if err := api.Convert_resource_Quantity_To_resource_Quantity(&val, &newVal, s); err != nil { + return err + } + out.Requests[v1.ResourceName(key)] = newVal + } + } else { + out.Requests = nil + } + return nil +} + +func Convert_api_ResourceRequirements_To_v1_ResourceRequirements(in *api.ResourceRequirements, out *v1.ResourceRequirements, s conversion.Scope) error { + return autoConvert_api_ResourceRequirements_To_v1_ResourceRequirements(in, out, s) +} + +func autoConvert_api_SELinuxOptions_To_v1_SELinuxOptions(in *api.SELinuxOptions, out *v1.SELinuxOptions, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.SELinuxOptions))(in) + } + out.User = in.User + out.Role = in.Role + out.Type = in.Type + out.Level = in.Level + return nil +} + +func Convert_api_SELinuxOptions_To_v1_SELinuxOptions(in *api.SELinuxOptions, out *v1.SELinuxOptions, s conversion.Scope) error { + return autoConvert_api_SELinuxOptions_To_v1_SELinuxOptions(in, out, s) +} + +func autoConvert_api_SecretKeySelector_To_v1_SecretKeySelector(in *api.SecretKeySelector, out *v1.SecretKeySelector, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.SecretKeySelector))(in) + } + if err := Convert_api_LocalObjectReference_To_v1_LocalObjectReference(&in.LocalObjectReference, &out.LocalObjectReference, s); err != nil { + return err + } + out.Key = in.Key + return nil +} + +func Convert_api_SecretKeySelector_To_v1_SecretKeySelector(in *api.SecretKeySelector, out *v1.SecretKeySelector, s conversion.Scope) error { + return autoConvert_api_SecretKeySelector_To_v1_SecretKeySelector(in, out, s) +} + +func autoConvert_api_SecretVolumeSource_To_v1_SecretVolumeSource(in *api.SecretVolumeSource, out *v1.SecretVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.SecretVolumeSource))(in) + } + out.SecretName = in.SecretName + return nil +} + +func Convert_api_SecretVolumeSource_To_v1_SecretVolumeSource(in *api.SecretVolumeSource, out *v1.SecretVolumeSource, s conversion.Scope) error { + return autoConvert_api_SecretVolumeSource_To_v1_SecretVolumeSource(in, out, s) +} + +func autoConvert_api_SecurityContext_To_v1_SecurityContext(in *api.SecurityContext, out *v1.SecurityContext, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.SecurityContext))(in) + } + // unable to generate simple pointer conversion for api.Capabilities -> v1.Capabilities + if in.Capabilities != nil { + out.Capabilities = new(v1.Capabilities) + if err := Convert_api_Capabilities_To_v1_Capabilities(in.Capabilities, out.Capabilities, s); err != nil { + return err + } + } else { + out.Capabilities = nil + } + if in.Privileged != nil { + out.Privileged = new(bool) + *out.Privileged = *in.Privileged + } else { + out.Privileged = nil + } + // unable to generate simple pointer conversion for api.SELinuxOptions -> v1.SELinuxOptions + if in.SELinuxOptions != nil { + out.SELinuxOptions = new(v1.SELinuxOptions) + if err := Convert_api_SELinuxOptions_To_v1_SELinuxOptions(in.SELinuxOptions, out.SELinuxOptions, s); err != nil { + return err + } + } else { + out.SELinuxOptions = nil + } + if in.RunAsUser != nil { + out.RunAsUser = new(int64) + *out.RunAsUser = *in.RunAsUser + } else { + out.RunAsUser = nil + } + if in.RunAsNonRoot != nil { + out.RunAsNonRoot = new(bool) + *out.RunAsNonRoot = *in.RunAsNonRoot + } else { + out.RunAsNonRoot = nil + } + if in.ReadOnlyRootFilesystem != nil { + out.ReadOnlyRootFilesystem = new(bool) + *out.ReadOnlyRootFilesystem = *in.ReadOnlyRootFilesystem + } else { + out.ReadOnlyRootFilesystem = nil + } + return nil +} + +func Convert_api_SecurityContext_To_v1_SecurityContext(in *api.SecurityContext, out *v1.SecurityContext, s conversion.Scope) error { + return autoConvert_api_SecurityContext_To_v1_SecurityContext(in, out, s) +} + +func autoConvert_api_TCPSocketAction_To_v1_TCPSocketAction(in *api.TCPSocketAction, out *v1.TCPSocketAction, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.TCPSocketAction))(in) + } + if err := api.Convert_intstr_IntOrString_To_intstr_IntOrString(&in.Port, &out.Port, s); err != nil { + return err + } + return nil +} + +func Convert_api_TCPSocketAction_To_v1_TCPSocketAction(in *api.TCPSocketAction, out *v1.TCPSocketAction, s conversion.Scope) error { + return autoConvert_api_TCPSocketAction_To_v1_TCPSocketAction(in, out, s) +} + +func autoConvert_api_Volume_To_v1_Volume(in *api.Volume, out *v1.Volume, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.Volume))(in) + } + out.Name = in.Name + if err := Convert_api_VolumeSource_To_v1_VolumeSource(&in.VolumeSource, &out.VolumeSource, s); err != nil { + return err + } + return nil +} + +func Convert_api_Volume_To_v1_Volume(in *api.Volume, out *v1.Volume, s conversion.Scope) error { + return autoConvert_api_Volume_To_v1_Volume(in, out, s) +} + +func autoConvert_api_VolumeMount_To_v1_VolumeMount(in *api.VolumeMount, out *v1.VolumeMount, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.VolumeMount))(in) + } + out.Name = in.Name + out.ReadOnly = in.ReadOnly + out.MountPath = in.MountPath + return nil +} + +func Convert_api_VolumeMount_To_v1_VolumeMount(in *api.VolumeMount, out *v1.VolumeMount, s conversion.Scope) error { + return autoConvert_api_VolumeMount_To_v1_VolumeMount(in, out, s) +} + +func autoConvert_api_VolumeSource_To_v1_VolumeSource(in *api.VolumeSource, out *v1.VolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*api.VolumeSource))(in) + } + // unable to generate simple pointer conversion for api.HostPathVolumeSource -> v1.HostPathVolumeSource + if in.HostPath != nil { + out.HostPath = new(v1.HostPathVolumeSource) + if err := Convert_api_HostPathVolumeSource_To_v1_HostPathVolumeSource(in.HostPath, out.HostPath, s); err != nil { + return err + } + } else { + out.HostPath = nil + } + // unable to generate simple pointer conversion for api.EmptyDirVolumeSource -> v1.EmptyDirVolumeSource + if in.EmptyDir != nil { + out.EmptyDir = new(v1.EmptyDirVolumeSource) + if err := Convert_api_EmptyDirVolumeSource_To_v1_EmptyDirVolumeSource(in.EmptyDir, out.EmptyDir, s); err != nil { + return err + } + } else { + out.EmptyDir = nil + } + // unable to generate simple pointer conversion for api.GCEPersistentDiskVolumeSource -> v1.GCEPersistentDiskVolumeSource + if in.GCEPersistentDisk != nil { + out.GCEPersistentDisk = new(v1.GCEPersistentDiskVolumeSource) + if err := Convert_api_GCEPersistentDiskVolumeSource_To_v1_GCEPersistentDiskVolumeSource(in.GCEPersistentDisk, out.GCEPersistentDisk, s); err != nil { + return err + } + } else { + out.GCEPersistentDisk = nil + } + // unable to generate simple pointer conversion for api.AWSElasticBlockStoreVolumeSource -> v1.AWSElasticBlockStoreVolumeSource + if in.AWSElasticBlockStore != nil { + out.AWSElasticBlockStore = new(v1.AWSElasticBlockStoreVolumeSource) + if err := Convert_api_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolumeSource(in.AWSElasticBlockStore, out.AWSElasticBlockStore, s); err != nil { + return err + } + } else { + out.AWSElasticBlockStore = nil + } + // unable to generate simple pointer conversion for api.GitRepoVolumeSource -> v1.GitRepoVolumeSource + if in.GitRepo != nil { + out.GitRepo = new(v1.GitRepoVolumeSource) + if err := Convert_api_GitRepoVolumeSource_To_v1_GitRepoVolumeSource(in.GitRepo, out.GitRepo, s); err != nil { + return err + } + } else { + out.GitRepo = nil + } + // unable to generate simple pointer conversion for api.SecretVolumeSource -> v1.SecretVolumeSource + if in.Secret != nil { + out.Secret = new(v1.SecretVolumeSource) + if err := Convert_api_SecretVolumeSource_To_v1_SecretVolumeSource(in.Secret, out.Secret, s); err != nil { + return err + } + } else { + out.Secret = nil + } + // unable to generate simple pointer conversion for api.NFSVolumeSource -> v1.NFSVolumeSource + if in.NFS != nil { + out.NFS = new(v1.NFSVolumeSource) + if err := Convert_api_NFSVolumeSource_To_v1_NFSVolumeSource(in.NFS, out.NFS, s); err != nil { + return err + } + } else { + out.NFS = nil + } + // unable to generate simple pointer conversion for api.ISCSIVolumeSource -> v1.ISCSIVolumeSource + if in.ISCSI != nil { + out.ISCSI = new(v1.ISCSIVolumeSource) + if err := Convert_api_ISCSIVolumeSource_To_v1_ISCSIVolumeSource(in.ISCSI, out.ISCSI, s); err != nil { + return err + } + } else { + out.ISCSI = nil + } + // unable to generate simple pointer conversion for api.GlusterfsVolumeSource -> v1.GlusterfsVolumeSource + if in.Glusterfs != nil { + out.Glusterfs = new(v1.GlusterfsVolumeSource) + if err := Convert_api_GlusterfsVolumeSource_To_v1_GlusterfsVolumeSource(in.Glusterfs, out.Glusterfs, s); err != nil { + return err + } + } else { + out.Glusterfs = nil + } + // unable to generate simple pointer conversion for api.PersistentVolumeClaimVolumeSource -> v1.PersistentVolumeClaimVolumeSource + if in.PersistentVolumeClaim != nil { + out.PersistentVolumeClaim = new(v1.PersistentVolumeClaimVolumeSource) + if err := Convert_api_PersistentVolumeClaimVolumeSource_To_v1_PersistentVolumeClaimVolumeSource(in.PersistentVolumeClaim, out.PersistentVolumeClaim, s); err != nil { + return err + } + } else { + out.PersistentVolumeClaim = nil + } + // unable to generate simple pointer conversion for api.RBDVolumeSource -> v1.RBDVolumeSource + if in.RBD != nil { + out.RBD = new(v1.RBDVolumeSource) + if err := Convert_api_RBDVolumeSource_To_v1_RBDVolumeSource(in.RBD, out.RBD, s); err != nil { + return err + } + } else { + out.RBD = nil + } + // unable to generate simple pointer conversion for api.FlexVolumeSource -> v1.FlexVolumeSource + if in.FlexVolume != nil { + out.FlexVolume = new(v1.FlexVolumeSource) + if err := Convert_api_FlexVolumeSource_To_v1_FlexVolumeSource(in.FlexVolume, out.FlexVolume, s); err != nil { + return err + } + } else { + out.FlexVolume = nil + } + // unable to generate simple pointer conversion for api.CinderVolumeSource -> v1.CinderVolumeSource + if in.Cinder != nil { + out.Cinder = new(v1.CinderVolumeSource) + if err := Convert_api_CinderVolumeSource_To_v1_CinderVolumeSource(in.Cinder, out.Cinder, s); err != nil { + return err + } + } else { + out.Cinder = nil + } + // unable to generate simple pointer conversion for api.CephFSVolumeSource -> v1.CephFSVolumeSource + if in.CephFS != nil { + out.CephFS = new(v1.CephFSVolumeSource) + if err := Convert_api_CephFSVolumeSource_To_v1_CephFSVolumeSource(in.CephFS, out.CephFS, s); err != nil { + return err + } + } else { + out.CephFS = nil + } + // unable to generate simple pointer conversion for api.FlockerVolumeSource -> v1.FlockerVolumeSource + if in.Flocker != nil { + out.Flocker = new(v1.FlockerVolumeSource) + if err := Convert_api_FlockerVolumeSource_To_v1_FlockerVolumeSource(in.Flocker, out.Flocker, s); err != nil { + return err + } + } else { + out.Flocker = nil + } + // unable to generate simple pointer conversion for api.DownwardAPIVolumeSource -> v1.DownwardAPIVolumeSource + if in.DownwardAPI != nil { + out.DownwardAPI = new(v1.DownwardAPIVolumeSource) + if err := Convert_api_DownwardAPIVolumeSource_To_v1_DownwardAPIVolumeSource(in.DownwardAPI, out.DownwardAPI, s); err != nil { + return err + } + } else { + out.DownwardAPI = nil + } + // unable to generate simple pointer conversion for api.FCVolumeSource -> v1.FCVolumeSource + if in.FC != nil { + out.FC = new(v1.FCVolumeSource) + if err := Convert_api_FCVolumeSource_To_v1_FCVolumeSource(in.FC, out.FC, s); err != nil { + return err + } + } else { + out.FC = nil + } + // unable to generate simple pointer conversion for api.AzureFileVolumeSource -> v1.AzureFileVolumeSource + if in.AzureFile != nil { + out.AzureFile = new(v1.AzureFileVolumeSource) + if err := Convert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource(in.AzureFile, out.AzureFile, s); err != nil { + return err + } + } else { + out.AzureFile = nil + } + // unable to generate simple pointer conversion for api.ConfigMapVolumeSource -> v1.ConfigMapVolumeSource + if in.ConfigMap != nil { + out.ConfigMap = new(v1.ConfigMapVolumeSource) + if err := Convert_api_ConfigMapVolumeSource_To_v1_ConfigMapVolumeSource(in.ConfigMap, out.ConfigMap, s); err != nil { + return err + } + } else { + out.ConfigMap = nil + } + return nil +} + +func Convert_api_VolumeSource_To_v1_VolumeSource(in *api.VolumeSource, out *v1.VolumeSource, s conversion.Scope) error { + return autoConvert_api_VolumeSource_To_v1_VolumeSource(in, out, s) +} + +func autoConvert_unversioned_LabelSelector_To_v1_LabelSelector(in *unversioned.LabelSelector, out *LabelSelector, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*unversioned.LabelSelector))(in) + } + if in.MatchLabels != nil { + out.MatchLabels = make(map[string]string) + for key, val := range in.MatchLabels { + out.MatchLabels[key] = val + } + } else { + out.MatchLabels = nil + } + if in.MatchExpressions != nil { + out.MatchExpressions = make([]LabelSelectorRequirement, len(in.MatchExpressions)) + for i := range in.MatchExpressions { + if err := Convert_unversioned_LabelSelectorRequirement_To_v1_LabelSelectorRequirement(&in.MatchExpressions[i], &out.MatchExpressions[i], s); err != nil { + return err + } + } + } else { + out.MatchExpressions = nil + } + return nil +} + +func Convert_unversioned_LabelSelector_To_v1_LabelSelector(in *unversioned.LabelSelector, out *LabelSelector, s conversion.Scope) error { + return autoConvert_unversioned_LabelSelector_To_v1_LabelSelector(in, out, s) +} + +func autoConvert_unversioned_LabelSelectorRequirement_To_v1_LabelSelectorRequirement(in *unversioned.LabelSelectorRequirement, out *LabelSelectorRequirement, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*unversioned.LabelSelectorRequirement))(in) + } + out.Key = in.Key + out.Operator = LabelSelectorOperator(in.Operator) + if in.Values != nil { + out.Values = make([]string, len(in.Values)) + for i := range in.Values { + out.Values[i] = in.Values[i] + } + } else { + out.Values = nil + } + return nil +} + +func Convert_unversioned_LabelSelectorRequirement_To_v1_LabelSelectorRequirement(in *unversioned.LabelSelectorRequirement, out *LabelSelectorRequirement, s conversion.Scope) error { + return autoConvert_unversioned_LabelSelectorRequirement_To_v1_LabelSelectorRequirement(in, out, s) +} + +func autoConvert_v1_AWSElasticBlockStoreVolumeSource_To_api_AWSElasticBlockStoreVolumeSource(in *v1.AWSElasticBlockStoreVolumeSource, out *api.AWSElasticBlockStoreVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.AWSElasticBlockStoreVolumeSource))(in) + } + out.VolumeID = in.VolumeID + out.FSType = in.FSType + out.Partition = int(in.Partition) + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_v1_AWSElasticBlockStoreVolumeSource_To_api_AWSElasticBlockStoreVolumeSource(in *v1.AWSElasticBlockStoreVolumeSource, out *api.AWSElasticBlockStoreVolumeSource, s conversion.Scope) error { + return autoConvert_v1_AWSElasticBlockStoreVolumeSource_To_api_AWSElasticBlockStoreVolumeSource(in, out, s) +} + +func autoConvert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource(in *v1.AzureFileVolumeSource, out *api.AzureFileVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.AzureFileVolumeSource))(in) + } + out.SecretName = in.SecretName + out.ShareName = in.ShareName + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource(in *v1.AzureFileVolumeSource, out *api.AzureFileVolumeSource, s conversion.Scope) error { + return autoConvert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource(in, out, s) +} + +func autoConvert_v1_Capabilities_To_api_Capabilities(in *v1.Capabilities, out *api.Capabilities, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.Capabilities))(in) + } + if in.Add != nil { + out.Add = make([]api.Capability, len(in.Add)) + for i := range in.Add { + out.Add[i] = api.Capability(in.Add[i]) + } + } else { + out.Add = nil + } + if in.Drop != nil { + out.Drop = make([]api.Capability, len(in.Drop)) + for i := range in.Drop { + out.Drop[i] = api.Capability(in.Drop[i]) + } + } else { + out.Drop = nil + } + return nil +} + +func Convert_v1_Capabilities_To_api_Capabilities(in *v1.Capabilities, out *api.Capabilities, s conversion.Scope) error { + return autoConvert_v1_Capabilities_To_api_Capabilities(in, out, s) +} + +func autoConvert_v1_CephFSVolumeSource_To_api_CephFSVolumeSource(in *v1.CephFSVolumeSource, out *api.CephFSVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.CephFSVolumeSource))(in) + } + if in.Monitors != nil { + out.Monitors = make([]string, len(in.Monitors)) + for i := range in.Monitors { + out.Monitors[i] = in.Monitors[i] + } + } else { + out.Monitors = nil + } + out.Path = in.Path + out.User = in.User + out.SecretFile = in.SecretFile + // unable to generate simple pointer conversion for v1.LocalObjectReference -> api.LocalObjectReference + if in.SecretRef != nil { + out.SecretRef = new(api.LocalObjectReference) + if err := Convert_v1_LocalObjectReference_To_api_LocalObjectReference(in.SecretRef, out.SecretRef, s); err != nil { + return err + } + } else { + out.SecretRef = nil + } + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_v1_CephFSVolumeSource_To_api_CephFSVolumeSource(in *v1.CephFSVolumeSource, out *api.CephFSVolumeSource, s conversion.Scope) error { + return autoConvert_v1_CephFSVolumeSource_To_api_CephFSVolumeSource(in, out, s) +} + +func autoConvert_v1_CinderVolumeSource_To_api_CinderVolumeSource(in *v1.CinderVolumeSource, out *api.CinderVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.CinderVolumeSource))(in) + } + out.VolumeID = in.VolumeID + out.FSType = in.FSType + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_v1_CinderVolumeSource_To_api_CinderVolumeSource(in *v1.CinderVolumeSource, out *api.CinderVolumeSource, s conversion.Scope) error { + return autoConvert_v1_CinderVolumeSource_To_api_CinderVolumeSource(in, out, s) +} + +func autoConvert_v1_ConfigMapKeySelector_To_api_ConfigMapKeySelector(in *v1.ConfigMapKeySelector, out *api.ConfigMapKeySelector, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.ConfigMapKeySelector))(in) + } + if err := Convert_v1_LocalObjectReference_To_api_LocalObjectReference(&in.LocalObjectReference, &out.LocalObjectReference, s); err != nil { + return err + } + out.Key = in.Key + return nil +} + +func Convert_v1_ConfigMapKeySelector_To_api_ConfigMapKeySelector(in *v1.ConfigMapKeySelector, out *api.ConfigMapKeySelector, s conversion.Scope) error { + return autoConvert_v1_ConfigMapKeySelector_To_api_ConfigMapKeySelector(in, out, s) +} + +func autoConvert_v1_ConfigMapVolumeSource_To_api_ConfigMapVolumeSource(in *v1.ConfigMapVolumeSource, out *api.ConfigMapVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.ConfigMapVolumeSource))(in) + } + if err := Convert_v1_LocalObjectReference_To_api_LocalObjectReference(&in.LocalObjectReference, &out.LocalObjectReference, s); err != nil { + return err + } + if in.Items != nil { + out.Items = make([]api.KeyToPath, len(in.Items)) + for i := range in.Items { + if err := Convert_v1_KeyToPath_To_api_KeyToPath(&in.Items[i], &out.Items[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func Convert_v1_ConfigMapVolumeSource_To_api_ConfigMapVolumeSource(in *v1.ConfigMapVolumeSource, out *api.ConfigMapVolumeSource, s conversion.Scope) error { + return autoConvert_v1_ConfigMapVolumeSource_To_api_ConfigMapVolumeSource(in, out, s) +} + +func autoConvert_v1_Container_To_api_Container(in *v1.Container, out *api.Container, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.Container))(in) + } + out.Name = in.Name + out.Image = in.Image + if in.Command != nil { + out.Command = make([]string, len(in.Command)) + for i := range in.Command { + out.Command[i] = in.Command[i] + } + } else { + out.Command = nil + } + if in.Args != nil { + out.Args = make([]string, len(in.Args)) + for i := range in.Args { + out.Args[i] = in.Args[i] + } + } else { + out.Args = nil + } + out.WorkingDir = in.WorkingDir + if in.Ports != nil { + out.Ports = make([]api.ContainerPort, len(in.Ports)) + for i := range in.Ports { + if err := Convert_v1_ContainerPort_To_api_ContainerPort(&in.Ports[i], &out.Ports[i], s); err != nil { + return err + } + } + } else { + out.Ports = nil + } + if in.Env != nil { + out.Env = make([]api.EnvVar, len(in.Env)) + for i := range in.Env { + if err := Convert_v1_EnvVar_To_api_EnvVar(&in.Env[i], &out.Env[i], s); err != nil { + return err + } + } + } else { + out.Env = nil + } + if err := Convert_v1_ResourceRequirements_To_api_ResourceRequirements(&in.Resources, &out.Resources, s); err != nil { + return err + } + if in.VolumeMounts != nil { + out.VolumeMounts = make([]api.VolumeMount, len(in.VolumeMounts)) + for i := range in.VolumeMounts { + if err := Convert_v1_VolumeMount_To_api_VolumeMount(&in.VolumeMounts[i], &out.VolumeMounts[i], s); err != nil { + return err + } + } + } else { + out.VolumeMounts = nil + } + // unable to generate simple pointer conversion for v1.Probe -> api.Probe + if in.LivenessProbe != nil { + out.LivenessProbe = new(api.Probe) + if err := Convert_v1_Probe_To_api_Probe(in.LivenessProbe, out.LivenessProbe, s); err != nil { + return err + } + } else { + out.LivenessProbe = nil + } + // unable to generate simple pointer conversion for v1.Probe -> api.Probe + if in.ReadinessProbe != nil { + out.ReadinessProbe = new(api.Probe) + if err := Convert_v1_Probe_To_api_Probe(in.ReadinessProbe, out.ReadinessProbe, s); err != nil { + return err + } + } else { + out.ReadinessProbe = nil + } + // unable to generate simple pointer conversion for v1.Lifecycle -> api.Lifecycle + if in.Lifecycle != nil { + out.Lifecycle = new(api.Lifecycle) + if err := Convert_v1_Lifecycle_To_api_Lifecycle(in.Lifecycle, out.Lifecycle, s); err != nil { + return err + } + } else { + out.Lifecycle = nil + } + out.TerminationMessagePath = in.TerminationMessagePath + out.ImagePullPolicy = api.PullPolicy(in.ImagePullPolicy) + // unable to generate simple pointer conversion for v1.SecurityContext -> api.SecurityContext + if in.SecurityContext != nil { + out.SecurityContext = new(api.SecurityContext) + if err := Convert_v1_SecurityContext_To_api_SecurityContext(in.SecurityContext, out.SecurityContext, s); err != nil { + return err + } + } else { + out.SecurityContext = nil + } + out.Stdin = in.Stdin + out.StdinOnce = in.StdinOnce + out.TTY = in.TTY + return nil +} + +func Convert_v1_Container_To_api_Container(in *v1.Container, out *api.Container, s conversion.Scope) error { + return autoConvert_v1_Container_To_api_Container(in, out, s) +} + +func autoConvert_v1_ContainerPort_To_api_ContainerPort(in *v1.ContainerPort, out *api.ContainerPort, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.ContainerPort))(in) + } + out.Name = in.Name + out.HostPort = int(in.HostPort) + out.ContainerPort = int(in.ContainerPort) + out.Protocol = api.Protocol(in.Protocol) + out.HostIP = in.HostIP + return nil +} + +func Convert_v1_ContainerPort_To_api_ContainerPort(in *v1.ContainerPort, out *api.ContainerPort, s conversion.Scope) error { + return autoConvert_v1_ContainerPort_To_api_ContainerPort(in, out, s) +} + +func autoConvert_v1_DownwardAPIVolumeFile_To_api_DownwardAPIVolumeFile(in *v1.DownwardAPIVolumeFile, out *api.DownwardAPIVolumeFile, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.DownwardAPIVolumeFile))(in) + } + out.Path = in.Path + if err := Convert_v1_ObjectFieldSelector_To_api_ObjectFieldSelector(&in.FieldRef, &out.FieldRef, s); err != nil { + return err + } + return nil +} + +func Convert_v1_DownwardAPIVolumeFile_To_api_DownwardAPIVolumeFile(in *v1.DownwardAPIVolumeFile, out *api.DownwardAPIVolumeFile, s conversion.Scope) error { + return autoConvert_v1_DownwardAPIVolumeFile_To_api_DownwardAPIVolumeFile(in, out, s) +} + +func autoConvert_v1_DownwardAPIVolumeSource_To_api_DownwardAPIVolumeSource(in *v1.DownwardAPIVolumeSource, out *api.DownwardAPIVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.DownwardAPIVolumeSource))(in) + } + if in.Items != nil { + out.Items = make([]api.DownwardAPIVolumeFile, len(in.Items)) + for i := range in.Items { + if err := Convert_v1_DownwardAPIVolumeFile_To_api_DownwardAPIVolumeFile(&in.Items[i], &out.Items[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func Convert_v1_DownwardAPIVolumeSource_To_api_DownwardAPIVolumeSource(in *v1.DownwardAPIVolumeSource, out *api.DownwardAPIVolumeSource, s conversion.Scope) error { + return autoConvert_v1_DownwardAPIVolumeSource_To_api_DownwardAPIVolumeSource(in, out, s) +} + +func autoConvert_v1_EmptyDirVolumeSource_To_api_EmptyDirVolumeSource(in *v1.EmptyDirVolumeSource, out *api.EmptyDirVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.EmptyDirVolumeSource))(in) + } + out.Medium = api.StorageMedium(in.Medium) + return nil +} + +func Convert_v1_EmptyDirVolumeSource_To_api_EmptyDirVolumeSource(in *v1.EmptyDirVolumeSource, out *api.EmptyDirVolumeSource, s conversion.Scope) error { + return autoConvert_v1_EmptyDirVolumeSource_To_api_EmptyDirVolumeSource(in, out, s) +} + +func autoConvert_v1_EnvVar_To_api_EnvVar(in *v1.EnvVar, out *api.EnvVar, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.EnvVar))(in) + } + out.Name = in.Name + out.Value = in.Value + // unable to generate simple pointer conversion for v1.EnvVarSource -> api.EnvVarSource + if in.ValueFrom != nil { + out.ValueFrom = new(api.EnvVarSource) + if err := Convert_v1_EnvVarSource_To_api_EnvVarSource(in.ValueFrom, out.ValueFrom, s); err != nil { + return err + } + } else { + out.ValueFrom = nil + } + return nil +} + +func Convert_v1_EnvVar_To_api_EnvVar(in *v1.EnvVar, out *api.EnvVar, s conversion.Scope) error { + return autoConvert_v1_EnvVar_To_api_EnvVar(in, out, s) +} + +func autoConvert_v1_EnvVarSource_To_api_EnvVarSource(in *v1.EnvVarSource, out *api.EnvVarSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.EnvVarSource))(in) + } + // unable to generate simple pointer conversion for v1.ObjectFieldSelector -> api.ObjectFieldSelector + if in.FieldRef != nil { + out.FieldRef = new(api.ObjectFieldSelector) + if err := Convert_v1_ObjectFieldSelector_To_api_ObjectFieldSelector(in.FieldRef, out.FieldRef, s); err != nil { + return err + } + } else { + out.FieldRef = nil + } + // unable to generate simple pointer conversion for v1.ConfigMapKeySelector -> api.ConfigMapKeySelector + if in.ConfigMapKeyRef != nil { + out.ConfigMapKeyRef = new(api.ConfigMapKeySelector) + if err := Convert_v1_ConfigMapKeySelector_To_api_ConfigMapKeySelector(in.ConfigMapKeyRef, out.ConfigMapKeyRef, s); err != nil { + return err + } + } else { + out.ConfigMapKeyRef = nil + } + // unable to generate simple pointer conversion for v1.SecretKeySelector -> api.SecretKeySelector + if in.SecretKeyRef != nil { + out.SecretKeyRef = new(api.SecretKeySelector) + if err := Convert_v1_SecretKeySelector_To_api_SecretKeySelector(in.SecretKeyRef, out.SecretKeyRef, s); err != nil { + return err + } + } else { + out.SecretKeyRef = nil + } + return nil +} + +func Convert_v1_EnvVarSource_To_api_EnvVarSource(in *v1.EnvVarSource, out *api.EnvVarSource, s conversion.Scope) error { + return autoConvert_v1_EnvVarSource_To_api_EnvVarSource(in, out, s) +} + +func autoConvert_v1_ExecAction_To_api_ExecAction(in *v1.ExecAction, out *api.ExecAction, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.ExecAction))(in) + } + if in.Command != nil { + out.Command = make([]string, len(in.Command)) + for i := range in.Command { + out.Command[i] = in.Command[i] + } + } else { + out.Command = nil + } + return nil +} + +func Convert_v1_ExecAction_To_api_ExecAction(in *v1.ExecAction, out *api.ExecAction, s conversion.Scope) error { + return autoConvert_v1_ExecAction_To_api_ExecAction(in, out, s) +} + +func autoConvert_v1_FCVolumeSource_To_api_FCVolumeSource(in *v1.FCVolumeSource, out *api.FCVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.FCVolumeSource))(in) + } + if in.TargetWWNs != nil { + out.TargetWWNs = make([]string, len(in.TargetWWNs)) + for i := range in.TargetWWNs { + out.TargetWWNs[i] = in.TargetWWNs[i] + } + } else { + out.TargetWWNs = nil + } + if in.Lun != nil { + out.Lun = new(int) + *out.Lun = int(*in.Lun) + } else { + out.Lun = nil + } + out.FSType = in.FSType + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_v1_FCVolumeSource_To_api_FCVolumeSource(in *v1.FCVolumeSource, out *api.FCVolumeSource, s conversion.Scope) error { + return autoConvert_v1_FCVolumeSource_To_api_FCVolumeSource(in, out, s) +} + +func autoConvert_v1_FlexVolumeSource_To_api_FlexVolumeSource(in *v1.FlexVolumeSource, out *api.FlexVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.FlexVolumeSource))(in) + } + out.Driver = in.Driver + out.FSType = in.FSType + // unable to generate simple pointer conversion for v1.LocalObjectReference -> api.LocalObjectReference + if in.SecretRef != nil { + out.SecretRef = new(api.LocalObjectReference) + if err := Convert_v1_LocalObjectReference_To_api_LocalObjectReference(in.SecretRef, out.SecretRef, s); err != nil { + return err + } + } else { + out.SecretRef = nil + } + out.ReadOnly = in.ReadOnly + if in.Options != nil { + out.Options = make(map[string]string) + for key, val := range in.Options { + out.Options[key] = val + } + } else { + out.Options = nil + } + return nil +} + +func Convert_v1_FlexVolumeSource_To_api_FlexVolumeSource(in *v1.FlexVolumeSource, out *api.FlexVolumeSource, s conversion.Scope) error { + return autoConvert_v1_FlexVolumeSource_To_api_FlexVolumeSource(in, out, s) +} + +func autoConvert_v1_FlockerVolumeSource_To_api_FlockerVolumeSource(in *v1.FlockerVolumeSource, out *api.FlockerVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.FlockerVolumeSource))(in) + } + out.DatasetName = in.DatasetName + return nil +} + +func Convert_v1_FlockerVolumeSource_To_api_FlockerVolumeSource(in *v1.FlockerVolumeSource, out *api.FlockerVolumeSource, s conversion.Scope) error { + return autoConvert_v1_FlockerVolumeSource_To_api_FlockerVolumeSource(in, out, s) +} + +func autoConvert_v1_GCEPersistentDiskVolumeSource_To_api_GCEPersistentDiskVolumeSource(in *v1.GCEPersistentDiskVolumeSource, out *api.GCEPersistentDiskVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.GCEPersistentDiskVolumeSource))(in) + } + out.PDName = in.PDName + out.FSType = in.FSType + out.Partition = int(in.Partition) + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_v1_GCEPersistentDiskVolumeSource_To_api_GCEPersistentDiskVolumeSource(in *v1.GCEPersistentDiskVolumeSource, out *api.GCEPersistentDiskVolumeSource, s conversion.Scope) error { + return autoConvert_v1_GCEPersistentDiskVolumeSource_To_api_GCEPersistentDiskVolumeSource(in, out, s) +} + +func autoConvert_v1_GitRepoVolumeSource_To_api_GitRepoVolumeSource(in *v1.GitRepoVolumeSource, out *api.GitRepoVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.GitRepoVolumeSource))(in) + } + out.Repository = in.Repository + out.Revision = in.Revision + out.Directory = in.Directory + return nil +} + +func Convert_v1_GitRepoVolumeSource_To_api_GitRepoVolumeSource(in *v1.GitRepoVolumeSource, out *api.GitRepoVolumeSource, s conversion.Scope) error { + return autoConvert_v1_GitRepoVolumeSource_To_api_GitRepoVolumeSource(in, out, s) +} + +func autoConvert_v1_GlusterfsVolumeSource_To_api_GlusterfsVolumeSource(in *v1.GlusterfsVolumeSource, out *api.GlusterfsVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.GlusterfsVolumeSource))(in) + } + out.EndpointsName = in.EndpointsName + out.Path = in.Path + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_v1_GlusterfsVolumeSource_To_api_GlusterfsVolumeSource(in *v1.GlusterfsVolumeSource, out *api.GlusterfsVolumeSource, s conversion.Scope) error { + return autoConvert_v1_GlusterfsVolumeSource_To_api_GlusterfsVolumeSource(in, out, s) +} + +func autoConvert_v1_HTTPGetAction_To_api_HTTPGetAction(in *v1.HTTPGetAction, out *api.HTTPGetAction, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.HTTPGetAction))(in) + } + out.Path = in.Path + if err := api.Convert_intstr_IntOrString_To_intstr_IntOrString(&in.Port, &out.Port, s); err != nil { + return err + } + out.Host = in.Host + out.Scheme = api.URIScheme(in.Scheme) + if in.HTTPHeaders != nil { + out.HTTPHeaders = make([]api.HTTPHeader, len(in.HTTPHeaders)) + for i := range in.HTTPHeaders { + if err := Convert_v1_HTTPHeader_To_api_HTTPHeader(&in.HTTPHeaders[i], &out.HTTPHeaders[i], s); err != nil { + return err + } + } + } else { + out.HTTPHeaders = nil + } + return nil +} + +func Convert_v1_HTTPGetAction_To_api_HTTPGetAction(in *v1.HTTPGetAction, out *api.HTTPGetAction, s conversion.Scope) error { + return autoConvert_v1_HTTPGetAction_To_api_HTTPGetAction(in, out, s) +} + +func autoConvert_v1_HTTPHeader_To_api_HTTPHeader(in *v1.HTTPHeader, out *api.HTTPHeader, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.HTTPHeader))(in) + } + out.Name = in.Name + out.Value = in.Value + return nil +} + +func Convert_v1_HTTPHeader_To_api_HTTPHeader(in *v1.HTTPHeader, out *api.HTTPHeader, s conversion.Scope) error { + return autoConvert_v1_HTTPHeader_To_api_HTTPHeader(in, out, s) +} + +func autoConvert_v1_Handler_To_api_Handler(in *v1.Handler, out *api.Handler, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.Handler))(in) + } + // unable to generate simple pointer conversion for v1.ExecAction -> api.ExecAction + if in.Exec != nil { + out.Exec = new(api.ExecAction) + if err := Convert_v1_ExecAction_To_api_ExecAction(in.Exec, out.Exec, s); err != nil { + return err + } + } else { + out.Exec = nil + } + // unable to generate simple pointer conversion for v1.HTTPGetAction -> api.HTTPGetAction + if in.HTTPGet != nil { + out.HTTPGet = new(api.HTTPGetAction) + if err := Convert_v1_HTTPGetAction_To_api_HTTPGetAction(in.HTTPGet, out.HTTPGet, s); err != nil { + return err + } + } else { + out.HTTPGet = nil + } + // unable to generate simple pointer conversion for v1.TCPSocketAction -> api.TCPSocketAction + if in.TCPSocket != nil { + out.TCPSocket = new(api.TCPSocketAction) + if err := Convert_v1_TCPSocketAction_To_api_TCPSocketAction(in.TCPSocket, out.TCPSocket, s); err != nil { + return err + } + } else { + out.TCPSocket = nil + } + return nil +} + +func Convert_v1_Handler_To_api_Handler(in *v1.Handler, out *api.Handler, s conversion.Scope) error { + return autoConvert_v1_Handler_To_api_Handler(in, out, s) +} + +func autoConvert_v1_HostPathVolumeSource_To_api_HostPathVolumeSource(in *v1.HostPathVolumeSource, out *api.HostPathVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.HostPathVolumeSource))(in) + } + out.Path = in.Path + return nil +} + +func Convert_v1_HostPathVolumeSource_To_api_HostPathVolumeSource(in *v1.HostPathVolumeSource, out *api.HostPathVolumeSource, s conversion.Scope) error { + return autoConvert_v1_HostPathVolumeSource_To_api_HostPathVolumeSource(in, out, s) +} + +func autoConvert_v1_ISCSIVolumeSource_To_api_ISCSIVolumeSource(in *v1.ISCSIVolumeSource, out *api.ISCSIVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.ISCSIVolumeSource))(in) + } + out.TargetPortal = in.TargetPortal + out.IQN = in.IQN + out.Lun = int(in.Lun) + out.ISCSIInterface = in.ISCSIInterface + out.FSType = in.FSType + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_v1_ISCSIVolumeSource_To_api_ISCSIVolumeSource(in *v1.ISCSIVolumeSource, out *api.ISCSIVolumeSource, s conversion.Scope) error { + return autoConvert_v1_ISCSIVolumeSource_To_api_ISCSIVolumeSource(in, out, s) +} + +func autoConvert_v1_KeyToPath_To_api_KeyToPath(in *v1.KeyToPath, out *api.KeyToPath, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.KeyToPath))(in) + } + out.Key = in.Key + out.Path = in.Path + return nil +} + +func Convert_v1_KeyToPath_To_api_KeyToPath(in *v1.KeyToPath, out *api.KeyToPath, s conversion.Scope) error { + return autoConvert_v1_KeyToPath_To_api_KeyToPath(in, out, s) +} + +func autoConvert_v1_Lifecycle_To_api_Lifecycle(in *v1.Lifecycle, out *api.Lifecycle, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.Lifecycle))(in) + } + // unable to generate simple pointer conversion for v1.Handler -> api.Handler + if in.PostStart != nil { + out.PostStart = new(api.Handler) + if err := Convert_v1_Handler_To_api_Handler(in.PostStart, out.PostStart, s); err != nil { + return err + } + } else { + out.PostStart = nil + } + // unable to generate simple pointer conversion for v1.Handler -> api.Handler + if in.PreStop != nil { + out.PreStop = new(api.Handler) + if err := Convert_v1_Handler_To_api_Handler(in.PreStop, out.PreStop, s); err != nil { + return err + } + } else { + out.PreStop = nil + } + return nil +} + +func Convert_v1_Lifecycle_To_api_Lifecycle(in *v1.Lifecycle, out *api.Lifecycle, s conversion.Scope) error { + return autoConvert_v1_Lifecycle_To_api_Lifecycle(in, out, s) +} + +func autoConvert_v1_LocalObjectReference_To_api_LocalObjectReference(in *v1.LocalObjectReference, out *api.LocalObjectReference, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.LocalObjectReference))(in) + } + out.Name = in.Name + return nil +} + +func Convert_v1_LocalObjectReference_To_api_LocalObjectReference(in *v1.LocalObjectReference, out *api.LocalObjectReference, s conversion.Scope) error { + return autoConvert_v1_LocalObjectReference_To_api_LocalObjectReference(in, out, s) +} + +func autoConvert_v1_NFSVolumeSource_To_api_NFSVolumeSource(in *v1.NFSVolumeSource, out *api.NFSVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.NFSVolumeSource))(in) + } + out.Server = in.Server + out.Path = in.Path + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_v1_NFSVolumeSource_To_api_NFSVolumeSource(in *v1.NFSVolumeSource, out *api.NFSVolumeSource, s conversion.Scope) error { + return autoConvert_v1_NFSVolumeSource_To_api_NFSVolumeSource(in, out, s) +} + +func autoConvert_v1_ObjectFieldSelector_To_api_ObjectFieldSelector(in *v1.ObjectFieldSelector, out *api.ObjectFieldSelector, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.ObjectFieldSelector))(in) + } + out.APIVersion = in.APIVersion + out.FieldPath = in.FieldPath + return nil +} + +func Convert_v1_ObjectFieldSelector_To_api_ObjectFieldSelector(in *v1.ObjectFieldSelector, out *api.ObjectFieldSelector, s conversion.Scope) error { + return autoConvert_v1_ObjectFieldSelector_To_api_ObjectFieldSelector(in, out, s) +} + +func autoConvert_v1_ObjectMeta_To_api_ObjectMeta(in *v1.ObjectMeta, out *api.ObjectMeta, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.ObjectMeta))(in) + } + out.Name = in.Name + out.GenerateName = in.GenerateName + out.Namespace = in.Namespace + out.SelfLink = in.SelfLink + out.UID = in.UID + out.ResourceVersion = in.ResourceVersion + out.Generation = in.Generation + if err := api.Convert_unversioned_Time_To_unversioned_Time(&in.CreationTimestamp, &out.CreationTimestamp, s); err != nil { + return err + } + // unable to generate simple pointer conversion for unversioned.Time -> unversioned.Time + if in.DeletionTimestamp != nil { + out.DeletionTimestamp = new(unversioned.Time) + if err := api.Convert_unversioned_Time_To_unversioned_Time(in.DeletionTimestamp, out.DeletionTimestamp, s); err != nil { + return err + } + } else { + out.DeletionTimestamp = nil + } + if in.DeletionGracePeriodSeconds != nil { + out.DeletionGracePeriodSeconds = new(int64) + *out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds + } else { + out.DeletionGracePeriodSeconds = nil + } + if in.Labels != nil { + out.Labels = make(map[string]string) + for key, val := range in.Labels { + out.Labels[key] = val + } + } else { + out.Labels = nil + } + if in.Annotations != nil { + out.Annotations = make(map[string]string) + for key, val := range in.Annotations { + out.Annotations[key] = val + } + } else { + out.Annotations = nil + } + return nil +} + +func Convert_v1_ObjectMeta_To_api_ObjectMeta(in *v1.ObjectMeta, out *api.ObjectMeta, s conversion.Scope) error { + return autoConvert_v1_ObjectMeta_To_api_ObjectMeta(in, out, s) +} + +func autoConvert_v1_PersistentVolumeClaimVolumeSource_To_api_PersistentVolumeClaimVolumeSource(in *v1.PersistentVolumeClaimVolumeSource, out *api.PersistentVolumeClaimVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.PersistentVolumeClaimVolumeSource))(in) + } + out.ClaimName = in.ClaimName + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_v1_PersistentVolumeClaimVolumeSource_To_api_PersistentVolumeClaimVolumeSource(in *v1.PersistentVolumeClaimVolumeSource, out *api.PersistentVolumeClaimVolumeSource, s conversion.Scope) error { + return autoConvert_v1_PersistentVolumeClaimVolumeSource_To_api_PersistentVolumeClaimVolumeSource(in, out, s) +} + +func autoConvert_v1_PodSpec_To_api_PodSpec(in *v1.PodSpec, out *api.PodSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.PodSpec))(in) + } + if in.Volumes != nil { + out.Volumes = make([]api.Volume, len(in.Volumes)) + for i := range in.Volumes { + if err := Convert_v1_Volume_To_api_Volume(&in.Volumes[i], &out.Volumes[i], s); err != nil { + return err + } + } + } else { + out.Volumes = nil + } + if in.Containers != nil { + out.Containers = make([]api.Container, len(in.Containers)) + for i := range in.Containers { + if err := Convert_v1_Container_To_api_Container(&in.Containers[i], &out.Containers[i], s); err != nil { + return err + } + } + } else { + out.Containers = nil + } + out.RestartPolicy = api.RestartPolicy(in.RestartPolicy) + if in.TerminationGracePeriodSeconds != nil { + out.TerminationGracePeriodSeconds = new(int64) + *out.TerminationGracePeriodSeconds = *in.TerminationGracePeriodSeconds + } else { + out.TerminationGracePeriodSeconds = nil + } + if in.ActiveDeadlineSeconds != nil { + out.ActiveDeadlineSeconds = new(int64) + *out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds + } else { + out.ActiveDeadlineSeconds = nil + } + out.DNSPolicy = api.DNSPolicy(in.DNSPolicy) + if in.NodeSelector != nil { + out.NodeSelector = make(map[string]string) + for key, val := range in.NodeSelector { + out.NodeSelector[key] = val + } + } else { + out.NodeSelector = nil + } + out.ServiceAccountName = in.ServiceAccountName + // in.DeprecatedServiceAccount has no peer in out + out.NodeName = in.NodeName + // in.HostNetwork has no peer in out + // in.HostPID has no peer in out + // in.HostIPC has no peer in out + // unable to generate simple pointer conversion for v1.PodSecurityContext -> api.PodSecurityContext + if in.SecurityContext != nil { + if err := s.Convert(&in.SecurityContext, &out.SecurityContext, 0); err != nil { + return err + } + } else { + out.SecurityContext = nil + } + if in.ImagePullSecrets != nil { + out.ImagePullSecrets = make([]api.LocalObjectReference, len(in.ImagePullSecrets)) + for i := range in.ImagePullSecrets { + if err := Convert_v1_LocalObjectReference_To_api_LocalObjectReference(&in.ImagePullSecrets[i], &out.ImagePullSecrets[i], s); err != nil { + return err + } + } + } else { + out.ImagePullSecrets = nil + } + return nil +} + +func autoConvert_v1_PodTemplateSpec_To_api_PodTemplateSpec(in *v1.PodTemplateSpec, out *api.PodTemplateSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.PodTemplateSpec))(in) + } + if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1_PodSpec_To_api_PodSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +func Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(in *v1.PodTemplateSpec, out *api.PodTemplateSpec, s conversion.Scope) error { + return autoConvert_v1_PodTemplateSpec_To_api_PodTemplateSpec(in, out, s) +} + +func autoConvert_v1_Probe_To_api_Probe(in *v1.Probe, out *api.Probe, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.Probe))(in) + } + if err := Convert_v1_Handler_To_api_Handler(&in.Handler, &out.Handler, s); err != nil { + return err + } + out.InitialDelaySeconds = int(in.InitialDelaySeconds) + out.TimeoutSeconds = int(in.TimeoutSeconds) + out.PeriodSeconds = int(in.PeriodSeconds) + out.SuccessThreshold = int(in.SuccessThreshold) + out.FailureThreshold = int(in.FailureThreshold) + return nil +} + +func Convert_v1_Probe_To_api_Probe(in *v1.Probe, out *api.Probe, s conversion.Scope) error { + return autoConvert_v1_Probe_To_api_Probe(in, out, s) +} + +func autoConvert_v1_RBDVolumeSource_To_api_RBDVolumeSource(in *v1.RBDVolumeSource, out *api.RBDVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.RBDVolumeSource))(in) + } + if in.CephMonitors != nil { + out.CephMonitors = make([]string, len(in.CephMonitors)) + for i := range in.CephMonitors { + out.CephMonitors[i] = in.CephMonitors[i] + } + } else { + out.CephMonitors = nil + } + out.RBDImage = in.RBDImage + out.FSType = in.FSType + out.RBDPool = in.RBDPool + out.RadosUser = in.RadosUser + out.Keyring = in.Keyring + // unable to generate simple pointer conversion for v1.LocalObjectReference -> api.LocalObjectReference + if in.SecretRef != nil { + out.SecretRef = new(api.LocalObjectReference) + if err := Convert_v1_LocalObjectReference_To_api_LocalObjectReference(in.SecretRef, out.SecretRef, s); err != nil { + return err + } + } else { + out.SecretRef = nil + } + out.ReadOnly = in.ReadOnly + return nil +} + +func Convert_v1_RBDVolumeSource_To_api_RBDVolumeSource(in *v1.RBDVolumeSource, out *api.RBDVolumeSource, s conversion.Scope) error { + return autoConvert_v1_RBDVolumeSource_To_api_RBDVolumeSource(in, out, s) +} + +func autoConvert_v1_ResourceRequirements_To_api_ResourceRequirements(in *v1.ResourceRequirements, out *api.ResourceRequirements, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.ResourceRequirements))(in) + } + if in.Limits != nil { + out.Limits = make(api.ResourceList) + for key, val := range in.Limits { + newVal := resource.Quantity{} + if err := api.Convert_resource_Quantity_To_resource_Quantity(&val, &newVal, s); err != nil { + return err + } + out.Limits[api.ResourceName(key)] = newVal + } + } else { + out.Limits = nil + } + if in.Requests != nil { + out.Requests = make(api.ResourceList) + for key, val := range in.Requests { + newVal := resource.Quantity{} + if err := api.Convert_resource_Quantity_To_resource_Quantity(&val, &newVal, s); err != nil { + return err + } + out.Requests[api.ResourceName(key)] = newVal + } + } else { + out.Requests = nil + } + return nil +} + +func Convert_v1_ResourceRequirements_To_api_ResourceRequirements(in *v1.ResourceRequirements, out *api.ResourceRequirements, s conversion.Scope) error { + return autoConvert_v1_ResourceRequirements_To_api_ResourceRequirements(in, out, s) +} + +func autoConvert_v1_SELinuxOptions_To_api_SELinuxOptions(in *v1.SELinuxOptions, out *api.SELinuxOptions, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.SELinuxOptions))(in) + } + out.User = in.User + out.Role = in.Role + out.Type = in.Type + out.Level = in.Level + return nil +} + +func Convert_v1_SELinuxOptions_To_api_SELinuxOptions(in *v1.SELinuxOptions, out *api.SELinuxOptions, s conversion.Scope) error { + return autoConvert_v1_SELinuxOptions_To_api_SELinuxOptions(in, out, s) +} + +func autoConvert_v1_SecretKeySelector_To_api_SecretKeySelector(in *v1.SecretKeySelector, out *api.SecretKeySelector, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.SecretKeySelector))(in) + } + if err := Convert_v1_LocalObjectReference_To_api_LocalObjectReference(&in.LocalObjectReference, &out.LocalObjectReference, s); err != nil { + return err + } + out.Key = in.Key + return nil +} + +func Convert_v1_SecretKeySelector_To_api_SecretKeySelector(in *v1.SecretKeySelector, out *api.SecretKeySelector, s conversion.Scope) error { + return autoConvert_v1_SecretKeySelector_To_api_SecretKeySelector(in, out, s) +} + +func autoConvert_v1_SecretVolumeSource_To_api_SecretVolumeSource(in *v1.SecretVolumeSource, out *api.SecretVolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.SecretVolumeSource))(in) + } + out.SecretName = in.SecretName + return nil +} + +func Convert_v1_SecretVolumeSource_To_api_SecretVolumeSource(in *v1.SecretVolumeSource, out *api.SecretVolumeSource, s conversion.Scope) error { + return autoConvert_v1_SecretVolumeSource_To_api_SecretVolumeSource(in, out, s) +} + +func autoConvert_v1_SecurityContext_To_api_SecurityContext(in *v1.SecurityContext, out *api.SecurityContext, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.SecurityContext))(in) + } + // unable to generate simple pointer conversion for v1.Capabilities -> api.Capabilities + if in.Capabilities != nil { + out.Capabilities = new(api.Capabilities) + if err := Convert_v1_Capabilities_To_api_Capabilities(in.Capabilities, out.Capabilities, s); err != nil { + return err + } + } else { + out.Capabilities = nil + } + if in.Privileged != nil { + out.Privileged = new(bool) + *out.Privileged = *in.Privileged + } else { + out.Privileged = nil + } + // unable to generate simple pointer conversion for v1.SELinuxOptions -> api.SELinuxOptions + if in.SELinuxOptions != nil { + out.SELinuxOptions = new(api.SELinuxOptions) + if err := Convert_v1_SELinuxOptions_To_api_SELinuxOptions(in.SELinuxOptions, out.SELinuxOptions, s); err != nil { + return err + } + } else { + out.SELinuxOptions = nil + } + if in.RunAsUser != nil { + out.RunAsUser = new(int64) + *out.RunAsUser = *in.RunAsUser + } else { + out.RunAsUser = nil + } + if in.RunAsNonRoot != nil { + out.RunAsNonRoot = new(bool) + *out.RunAsNonRoot = *in.RunAsNonRoot + } else { + out.RunAsNonRoot = nil + } + if in.ReadOnlyRootFilesystem != nil { + out.ReadOnlyRootFilesystem = new(bool) + *out.ReadOnlyRootFilesystem = *in.ReadOnlyRootFilesystem + } else { + out.ReadOnlyRootFilesystem = nil + } + return nil +} + +func Convert_v1_SecurityContext_To_api_SecurityContext(in *v1.SecurityContext, out *api.SecurityContext, s conversion.Scope) error { + return autoConvert_v1_SecurityContext_To_api_SecurityContext(in, out, s) +} + +func autoConvert_v1_TCPSocketAction_To_api_TCPSocketAction(in *v1.TCPSocketAction, out *api.TCPSocketAction, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.TCPSocketAction))(in) + } + if err := api.Convert_intstr_IntOrString_To_intstr_IntOrString(&in.Port, &out.Port, s); err != nil { + return err + } + return nil +} + +func Convert_v1_TCPSocketAction_To_api_TCPSocketAction(in *v1.TCPSocketAction, out *api.TCPSocketAction, s conversion.Scope) error { + return autoConvert_v1_TCPSocketAction_To_api_TCPSocketAction(in, out, s) +} + +func autoConvert_v1_Volume_To_api_Volume(in *v1.Volume, out *api.Volume, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.Volume))(in) + } + out.Name = in.Name + if err := Convert_v1_VolumeSource_To_api_VolumeSource(&in.VolumeSource, &out.VolumeSource, s); err != nil { + return err + } + return nil +} + +func Convert_v1_Volume_To_api_Volume(in *v1.Volume, out *api.Volume, s conversion.Scope) error { + return autoConvert_v1_Volume_To_api_Volume(in, out, s) +} + +func autoConvert_v1_VolumeMount_To_api_VolumeMount(in *v1.VolumeMount, out *api.VolumeMount, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.VolumeMount))(in) + } + out.Name = in.Name + out.ReadOnly = in.ReadOnly + out.MountPath = in.MountPath + return nil +} + +func Convert_v1_VolumeMount_To_api_VolumeMount(in *v1.VolumeMount, out *api.VolumeMount, s conversion.Scope) error { + return autoConvert_v1_VolumeMount_To_api_VolumeMount(in, out, s) +} + +func autoConvert_v1_VolumeSource_To_api_VolumeSource(in *v1.VolumeSource, out *api.VolumeSource, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*v1.VolumeSource))(in) + } + // unable to generate simple pointer conversion for v1.HostPathVolumeSource -> api.HostPathVolumeSource + if in.HostPath != nil { + out.HostPath = new(api.HostPathVolumeSource) + if err := Convert_v1_HostPathVolumeSource_To_api_HostPathVolumeSource(in.HostPath, out.HostPath, s); err != nil { + return err + } + } else { + out.HostPath = nil + } + // unable to generate simple pointer conversion for v1.EmptyDirVolumeSource -> api.EmptyDirVolumeSource + if in.EmptyDir != nil { + out.EmptyDir = new(api.EmptyDirVolumeSource) + if err := Convert_v1_EmptyDirVolumeSource_To_api_EmptyDirVolumeSource(in.EmptyDir, out.EmptyDir, s); err != nil { + return err + } + } else { + out.EmptyDir = nil + } + // unable to generate simple pointer conversion for v1.GCEPersistentDiskVolumeSource -> api.GCEPersistentDiskVolumeSource + if in.GCEPersistentDisk != nil { + out.GCEPersistentDisk = new(api.GCEPersistentDiskVolumeSource) + if err := Convert_v1_GCEPersistentDiskVolumeSource_To_api_GCEPersistentDiskVolumeSource(in.GCEPersistentDisk, out.GCEPersistentDisk, s); err != nil { + return err + } + } else { + out.GCEPersistentDisk = nil + } + // unable to generate simple pointer conversion for v1.AWSElasticBlockStoreVolumeSource -> api.AWSElasticBlockStoreVolumeSource + if in.AWSElasticBlockStore != nil { + out.AWSElasticBlockStore = new(api.AWSElasticBlockStoreVolumeSource) + if err := Convert_v1_AWSElasticBlockStoreVolumeSource_To_api_AWSElasticBlockStoreVolumeSource(in.AWSElasticBlockStore, out.AWSElasticBlockStore, s); err != nil { + return err + } + } else { + out.AWSElasticBlockStore = nil + } + // unable to generate simple pointer conversion for v1.GitRepoVolumeSource -> api.GitRepoVolumeSource + if in.GitRepo != nil { + out.GitRepo = new(api.GitRepoVolumeSource) + if err := Convert_v1_GitRepoVolumeSource_To_api_GitRepoVolumeSource(in.GitRepo, out.GitRepo, s); err != nil { + return err + } + } else { + out.GitRepo = nil + } + // unable to generate simple pointer conversion for v1.SecretVolumeSource -> api.SecretVolumeSource + if in.Secret != nil { + out.Secret = new(api.SecretVolumeSource) + if err := Convert_v1_SecretVolumeSource_To_api_SecretVolumeSource(in.Secret, out.Secret, s); err != nil { + return err + } + } else { + out.Secret = nil + } + // unable to generate simple pointer conversion for v1.NFSVolumeSource -> api.NFSVolumeSource + if in.NFS != nil { + out.NFS = new(api.NFSVolumeSource) + if err := Convert_v1_NFSVolumeSource_To_api_NFSVolumeSource(in.NFS, out.NFS, s); err != nil { + return err + } + } else { + out.NFS = nil + } + // unable to generate simple pointer conversion for v1.ISCSIVolumeSource -> api.ISCSIVolumeSource + if in.ISCSI != nil { + out.ISCSI = new(api.ISCSIVolumeSource) + if err := Convert_v1_ISCSIVolumeSource_To_api_ISCSIVolumeSource(in.ISCSI, out.ISCSI, s); err != nil { + return err + } + } else { + out.ISCSI = nil + } + // unable to generate simple pointer conversion for v1.GlusterfsVolumeSource -> api.GlusterfsVolumeSource + if in.Glusterfs != nil { + out.Glusterfs = new(api.GlusterfsVolumeSource) + if err := Convert_v1_GlusterfsVolumeSource_To_api_GlusterfsVolumeSource(in.Glusterfs, out.Glusterfs, s); err != nil { + return err + } + } else { + out.Glusterfs = nil + } + // unable to generate simple pointer conversion for v1.PersistentVolumeClaimVolumeSource -> api.PersistentVolumeClaimVolumeSource + if in.PersistentVolumeClaim != nil { + out.PersistentVolumeClaim = new(api.PersistentVolumeClaimVolumeSource) + if err := Convert_v1_PersistentVolumeClaimVolumeSource_To_api_PersistentVolumeClaimVolumeSource(in.PersistentVolumeClaim, out.PersistentVolumeClaim, s); err != nil { + return err + } + } else { + out.PersistentVolumeClaim = nil + } + // unable to generate simple pointer conversion for v1.RBDVolumeSource -> api.RBDVolumeSource + if in.RBD != nil { + out.RBD = new(api.RBDVolumeSource) + if err := Convert_v1_RBDVolumeSource_To_api_RBDVolumeSource(in.RBD, out.RBD, s); err != nil { + return err + } + } else { + out.RBD = nil + } + // unable to generate simple pointer conversion for v1.FlexVolumeSource -> api.FlexVolumeSource + if in.FlexVolume != nil { + out.FlexVolume = new(api.FlexVolumeSource) + if err := Convert_v1_FlexVolumeSource_To_api_FlexVolumeSource(in.FlexVolume, out.FlexVolume, s); err != nil { + return err + } + } else { + out.FlexVolume = nil + } + // unable to generate simple pointer conversion for v1.CinderVolumeSource -> api.CinderVolumeSource + if in.Cinder != nil { + out.Cinder = new(api.CinderVolumeSource) + if err := Convert_v1_CinderVolumeSource_To_api_CinderVolumeSource(in.Cinder, out.Cinder, s); err != nil { + return err + } + } else { + out.Cinder = nil + } + // unable to generate simple pointer conversion for v1.CephFSVolumeSource -> api.CephFSVolumeSource + if in.CephFS != nil { + out.CephFS = new(api.CephFSVolumeSource) + if err := Convert_v1_CephFSVolumeSource_To_api_CephFSVolumeSource(in.CephFS, out.CephFS, s); err != nil { + return err + } + } else { + out.CephFS = nil + } + // unable to generate simple pointer conversion for v1.FlockerVolumeSource -> api.FlockerVolumeSource + if in.Flocker != nil { + out.Flocker = new(api.FlockerVolumeSource) + if err := Convert_v1_FlockerVolumeSource_To_api_FlockerVolumeSource(in.Flocker, out.Flocker, s); err != nil { + return err + } + } else { + out.Flocker = nil + } + // unable to generate simple pointer conversion for v1.DownwardAPIVolumeSource -> api.DownwardAPIVolumeSource + if in.DownwardAPI != nil { + out.DownwardAPI = new(api.DownwardAPIVolumeSource) + if err := Convert_v1_DownwardAPIVolumeSource_To_api_DownwardAPIVolumeSource(in.DownwardAPI, out.DownwardAPI, s); err != nil { + return err + } + } else { + out.DownwardAPI = nil + } + // unable to generate simple pointer conversion for v1.FCVolumeSource -> api.FCVolumeSource + if in.FC != nil { + out.FC = new(api.FCVolumeSource) + if err := Convert_v1_FCVolumeSource_To_api_FCVolumeSource(in.FC, out.FC, s); err != nil { + return err + } + } else { + out.FC = nil + } + // unable to generate simple pointer conversion for v1.AzureFileVolumeSource -> api.AzureFileVolumeSource + if in.AzureFile != nil { + out.AzureFile = new(api.AzureFileVolumeSource) + if err := Convert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource(in.AzureFile, out.AzureFile, s); err != nil { + return err + } + } else { + out.AzureFile = nil + } + // unable to generate simple pointer conversion for v1.ConfigMapVolumeSource -> api.ConfigMapVolumeSource + if in.ConfigMap != nil { + out.ConfigMap = new(api.ConfigMapVolumeSource) + if err := Convert_v1_ConfigMapVolumeSource_To_api_ConfigMapVolumeSource(in.ConfigMap, out.ConfigMap, s); err != nil { + return err + } + } else { + out.ConfigMap = nil + } + return nil +} + +func Convert_v1_VolumeSource_To_api_VolumeSource(in *v1.VolumeSource, out *api.VolumeSource, s conversion.Scope) error { + return autoConvert_v1_VolumeSource_To_api_VolumeSource(in, out, s) +} + +func autoConvert_v1_Job_To_extensions_Job(in *Job, out *extensions.Job, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*Job))(in) + } + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1_JobSpec_To_extensions_JobSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1_JobStatus_To_extensions_JobStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +func Convert_v1_Job_To_extensions_Job(in *Job, out *extensions.Job, s conversion.Scope) error { + return autoConvert_v1_Job_To_extensions_Job(in, out, s) +} + +func autoConvert_v1_JobCondition_To_extensions_JobCondition(in *JobCondition, out *extensions.JobCondition, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*JobCondition))(in) + } + out.Type = extensions.JobConditionType(in.Type) + out.Status = api.ConditionStatus(in.Status) + if err := api.Convert_unversioned_Time_To_unversioned_Time(&in.LastProbeTime, &out.LastProbeTime, s); err != nil { + return err + } + if err := api.Convert_unversioned_Time_To_unversioned_Time(&in.LastTransitionTime, &out.LastTransitionTime, s); err != nil { + return err + } + out.Reason = in.Reason + out.Message = in.Message + return nil +} + +func Convert_v1_JobCondition_To_extensions_JobCondition(in *JobCondition, out *extensions.JobCondition, s conversion.Scope) error { + return autoConvert_v1_JobCondition_To_extensions_JobCondition(in, out, s) +} + +func autoConvert_v1_JobList_To_extensions_JobList(in *JobList, out *extensions.JobList, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*JobList))(in) + } + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { + return err + } + if in.Items != nil { + out.Items = make([]extensions.Job, len(in.Items)) + for i := range in.Items { + if err := Convert_v1_Job_To_extensions_Job(&in.Items[i], &out.Items[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func Convert_v1_JobList_To_extensions_JobList(in *JobList, out *extensions.JobList, s conversion.Scope) error { + return autoConvert_v1_JobList_To_extensions_JobList(in, out, s) +} + +func autoConvert_v1_JobSpec_To_extensions_JobSpec(in *JobSpec, out *extensions.JobSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*JobSpec))(in) + } + if in.Parallelism != nil { + out.Parallelism = new(int) + *out.Parallelism = int(*in.Parallelism) + } else { + out.Parallelism = nil + } + if in.Completions != nil { + out.Completions = new(int) + *out.Completions = int(*in.Completions) + } else { + out.Completions = nil + } + if in.ActiveDeadlineSeconds != nil { + out.ActiveDeadlineSeconds = new(int64) + *out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds + } else { + out.ActiveDeadlineSeconds = nil + } + // unable to generate simple pointer conversion for v1.LabelSelector -> unversioned.LabelSelector + if in.Selector != nil { + out.Selector = new(unversioned.LabelSelector) + if err := Convert_v1_LabelSelector_To_unversioned_LabelSelector(in.Selector, out.Selector, s); err != nil { + return err + } + } else { + out.Selector = nil + } + if in.ManualSelector != nil { + out.ManualSelector = new(bool) + *out.ManualSelector = *in.ManualSelector + } else { + out.ManualSelector = nil + } + if err := Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err + } + return nil +} + +func Convert_v1_JobSpec_To_extensions_JobSpec(in *JobSpec, out *extensions.JobSpec, s conversion.Scope) error { + return autoConvert_v1_JobSpec_To_extensions_JobSpec(in, out, s) +} + +func autoConvert_v1_JobStatus_To_extensions_JobStatus(in *JobStatus, out *extensions.JobStatus, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*JobStatus))(in) + } + if in.Conditions != nil { + out.Conditions = make([]extensions.JobCondition, len(in.Conditions)) + for i := range in.Conditions { + if err := Convert_v1_JobCondition_To_extensions_JobCondition(&in.Conditions[i], &out.Conditions[i], s); err != nil { + return err + } + } + } else { + out.Conditions = nil + } + // unable to generate simple pointer conversion for unversioned.Time -> unversioned.Time + if in.StartTime != nil { + out.StartTime = new(unversioned.Time) + if err := api.Convert_unversioned_Time_To_unversioned_Time(in.StartTime, out.StartTime, s); err != nil { + return err + } + } else { + out.StartTime = nil + } + // unable to generate simple pointer conversion for unversioned.Time -> unversioned.Time + if in.CompletionTime != nil { + out.CompletionTime = new(unversioned.Time) + if err := api.Convert_unversioned_Time_To_unversioned_Time(in.CompletionTime, out.CompletionTime, s); err != nil { + return err + } + } else { + out.CompletionTime = nil + } + out.Active = int(in.Active) + out.Succeeded = int(in.Succeeded) + out.Failed = int(in.Failed) + return nil +} + +func Convert_v1_JobStatus_To_extensions_JobStatus(in *JobStatus, out *extensions.JobStatus, s conversion.Scope) error { + return autoConvert_v1_JobStatus_To_extensions_JobStatus(in, out, s) +} + +func autoConvert_v1_LabelSelector_To_unversioned_LabelSelector(in *LabelSelector, out *unversioned.LabelSelector, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*LabelSelector))(in) + } + if in.MatchLabels != nil { + out.MatchLabels = make(map[string]string) + for key, val := range in.MatchLabels { + out.MatchLabels[key] = val + } + } else { + out.MatchLabels = nil + } + if in.MatchExpressions != nil { + out.MatchExpressions = make([]unversioned.LabelSelectorRequirement, len(in.MatchExpressions)) + for i := range in.MatchExpressions { + if err := Convert_v1_LabelSelectorRequirement_To_unversioned_LabelSelectorRequirement(&in.MatchExpressions[i], &out.MatchExpressions[i], s); err != nil { + return err + } + } + } else { + out.MatchExpressions = nil + } + return nil +} + +func Convert_v1_LabelSelector_To_unversioned_LabelSelector(in *LabelSelector, out *unversioned.LabelSelector, s conversion.Scope) error { + return autoConvert_v1_LabelSelector_To_unversioned_LabelSelector(in, out, s) +} + +func autoConvert_v1_LabelSelectorRequirement_To_unversioned_LabelSelectorRequirement(in *LabelSelectorRequirement, out *unversioned.LabelSelectorRequirement, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*LabelSelectorRequirement))(in) + } + out.Key = in.Key + out.Operator = unversioned.LabelSelectorOperator(in.Operator) + if in.Values != nil { + out.Values = make([]string, len(in.Values)) + for i := range in.Values { + out.Values[i] = in.Values[i] + } + } else { + out.Values = nil + } + return nil +} + +func Convert_v1_LabelSelectorRequirement_To_unversioned_LabelSelectorRequirement(in *LabelSelectorRequirement, out *unversioned.LabelSelectorRequirement, s conversion.Scope) error { + return autoConvert_v1_LabelSelectorRequirement_To_unversioned_LabelSelectorRequirement(in, out, s) +} + +func autoConvert_extensions_Job_To_v1_Job(in *extensions.Job, out *Job, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.Job))(in) + } + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_extensions_JobSpec_To_v1_JobSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_extensions_JobStatus_To_v1_JobStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +func Convert_extensions_Job_To_v1_Job(in *extensions.Job, out *Job, s conversion.Scope) error { + return autoConvert_extensions_Job_To_v1_Job(in, out, s) +} + +func autoConvert_extensions_JobCondition_To_v1_JobCondition(in *extensions.JobCondition, out *JobCondition, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.JobCondition))(in) + } + out.Type = JobConditionType(in.Type) + out.Status = v1.ConditionStatus(in.Status) + if err := api.Convert_unversioned_Time_To_unversioned_Time(&in.LastProbeTime, &out.LastProbeTime, s); err != nil { + return err + } + if err := api.Convert_unversioned_Time_To_unversioned_Time(&in.LastTransitionTime, &out.LastTransitionTime, s); err != nil { + return err + } + out.Reason = in.Reason + out.Message = in.Message + return nil +} + +func Convert_extensions_JobCondition_To_v1_JobCondition(in *extensions.JobCondition, out *JobCondition, s conversion.Scope) error { + return autoConvert_extensions_JobCondition_To_v1_JobCondition(in, out, s) +} + +func autoConvert_extensions_JobList_To_v1_JobList(in *extensions.JobList, out *JobList, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.JobList))(in) + } + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { + return err + } + if in.Items != nil { + out.Items = make([]Job, len(in.Items)) + for i := range in.Items { + if err := Convert_extensions_Job_To_v1_Job(&in.Items[i], &out.Items[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func Convert_extensions_JobList_To_v1_JobList(in *extensions.JobList, out *JobList, s conversion.Scope) error { + return autoConvert_extensions_JobList_To_v1_JobList(in, out, s) +} + +func autoConvert_extensions_JobSpec_To_v1_JobSpec(in *extensions.JobSpec, out *JobSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.JobSpec))(in) + } + if in.Parallelism != nil { + out.Parallelism = new(int32) + *out.Parallelism = int32(*in.Parallelism) + } else { + out.Parallelism = nil + } + if in.Completions != nil { + out.Completions = new(int32) + *out.Completions = int32(*in.Completions) + } else { + out.Completions = nil + } + if in.ActiveDeadlineSeconds != nil { + out.ActiveDeadlineSeconds = new(int64) + *out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds + } else { + out.ActiveDeadlineSeconds = nil + } + // unable to generate simple pointer conversion for unversioned.LabelSelector -> v1.LabelSelector + if in.Selector != nil { + out.Selector = new(LabelSelector) + if err := Convert_unversioned_LabelSelector_To_v1_LabelSelector(in.Selector, out.Selector, s); err != nil { + return err + } + } else { + out.Selector = nil + } + if in.ManualSelector != nil { + out.ManualSelector = new(bool) + *out.ManualSelector = *in.ManualSelector + } else { + out.ManualSelector = nil + } + if err := Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err + } + return nil +} + +func Convert_extensions_JobSpec_To_v1_JobSpec(in *extensions.JobSpec, out *JobSpec, s conversion.Scope) error { + return autoConvert_extensions_JobSpec_To_v1_JobSpec(in, out, s) +} + +func autoConvert_extensions_JobStatus_To_v1_JobStatus(in *extensions.JobStatus, out *JobStatus, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.JobStatus))(in) + } + if in.Conditions != nil { + out.Conditions = make([]JobCondition, len(in.Conditions)) + for i := range in.Conditions { + if err := Convert_extensions_JobCondition_To_v1_JobCondition(&in.Conditions[i], &out.Conditions[i], s); err != nil { + return err + } + } + } else { + out.Conditions = nil + } + // unable to generate simple pointer conversion for unversioned.Time -> unversioned.Time + if in.StartTime != nil { + out.StartTime = new(unversioned.Time) + if err := api.Convert_unversioned_Time_To_unversioned_Time(in.StartTime, out.StartTime, s); err != nil { + return err + } + } else { + out.StartTime = nil + } + // unable to generate simple pointer conversion for unversioned.Time -> unversioned.Time + if in.CompletionTime != nil { + out.CompletionTime = new(unversioned.Time) + if err := api.Convert_unversioned_Time_To_unversioned_Time(in.CompletionTime, out.CompletionTime, s); err != nil { + return err + } + } else { + out.CompletionTime = nil + } + out.Active = int32(in.Active) + out.Succeeded = int32(in.Succeeded) + out.Failed = int32(in.Failed) + return nil +} + +func Convert_extensions_JobStatus_To_v1_JobStatus(in *extensions.JobStatus, out *JobStatus, s conversion.Scope) error { + return autoConvert_extensions_JobStatus_To_v1_JobStatus(in, out, s) +} + +func init() { + err := api.Scheme.AddGeneratedConversionFuncs( + autoConvert_api_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolumeSource, + autoConvert_api_AzureFileVolumeSource_To_v1_AzureFileVolumeSource, + autoConvert_api_Capabilities_To_v1_Capabilities, + autoConvert_api_CephFSVolumeSource_To_v1_CephFSVolumeSource, + autoConvert_api_CinderVolumeSource_To_v1_CinderVolumeSource, + autoConvert_api_ConfigMapKeySelector_To_v1_ConfigMapKeySelector, + autoConvert_api_ConfigMapVolumeSource_To_v1_ConfigMapVolumeSource, + autoConvert_api_ContainerPort_To_v1_ContainerPort, + autoConvert_api_Container_To_v1_Container, + autoConvert_api_DownwardAPIVolumeFile_To_v1_DownwardAPIVolumeFile, + autoConvert_api_DownwardAPIVolumeSource_To_v1_DownwardAPIVolumeSource, + autoConvert_api_EmptyDirVolumeSource_To_v1_EmptyDirVolumeSource, + autoConvert_api_EnvVarSource_To_v1_EnvVarSource, + autoConvert_api_EnvVar_To_v1_EnvVar, + autoConvert_api_ExecAction_To_v1_ExecAction, + autoConvert_api_FCVolumeSource_To_v1_FCVolumeSource, + autoConvert_api_FlexVolumeSource_To_v1_FlexVolumeSource, + autoConvert_api_FlockerVolumeSource_To_v1_FlockerVolumeSource, + autoConvert_api_GCEPersistentDiskVolumeSource_To_v1_GCEPersistentDiskVolumeSource, + autoConvert_api_GitRepoVolumeSource_To_v1_GitRepoVolumeSource, + autoConvert_api_GlusterfsVolumeSource_To_v1_GlusterfsVolumeSource, + autoConvert_api_HTTPGetAction_To_v1_HTTPGetAction, + autoConvert_api_HTTPHeader_To_v1_HTTPHeader, + autoConvert_api_Handler_To_v1_Handler, + autoConvert_api_HostPathVolumeSource_To_v1_HostPathVolumeSource, + autoConvert_api_ISCSIVolumeSource_To_v1_ISCSIVolumeSource, + autoConvert_api_KeyToPath_To_v1_KeyToPath, + autoConvert_api_Lifecycle_To_v1_Lifecycle, + autoConvert_api_LocalObjectReference_To_v1_LocalObjectReference, + autoConvert_api_NFSVolumeSource_To_v1_NFSVolumeSource, + autoConvert_api_ObjectFieldSelector_To_v1_ObjectFieldSelector, + autoConvert_api_ObjectMeta_To_v1_ObjectMeta, + autoConvert_api_PersistentVolumeClaimVolumeSource_To_v1_PersistentVolumeClaimVolumeSource, + autoConvert_api_PodSpec_To_v1_PodSpec, + autoConvert_api_PodTemplateSpec_To_v1_PodTemplateSpec, + autoConvert_api_Probe_To_v1_Probe, + autoConvert_api_RBDVolumeSource_To_v1_RBDVolumeSource, + autoConvert_api_ResourceRequirements_To_v1_ResourceRequirements, + autoConvert_api_SELinuxOptions_To_v1_SELinuxOptions, + autoConvert_api_SecretKeySelector_To_v1_SecretKeySelector, + autoConvert_api_SecretVolumeSource_To_v1_SecretVolumeSource, + autoConvert_api_SecurityContext_To_v1_SecurityContext, + autoConvert_api_TCPSocketAction_To_v1_TCPSocketAction, + autoConvert_api_VolumeMount_To_v1_VolumeMount, + autoConvert_api_VolumeSource_To_v1_VolumeSource, + autoConvert_api_Volume_To_v1_Volume, + autoConvert_extensions_JobCondition_To_v1_JobCondition, + autoConvert_extensions_JobList_To_v1_JobList, + autoConvert_extensions_JobSpec_To_v1_JobSpec, + autoConvert_extensions_JobStatus_To_v1_JobStatus, + autoConvert_extensions_Job_To_v1_Job, + autoConvert_unversioned_LabelSelectorRequirement_To_v1_LabelSelectorRequirement, + autoConvert_unversioned_LabelSelector_To_v1_LabelSelector, + autoConvert_v1_AWSElasticBlockStoreVolumeSource_To_api_AWSElasticBlockStoreVolumeSource, + autoConvert_v1_AzureFileVolumeSource_To_api_AzureFileVolumeSource, + autoConvert_v1_Capabilities_To_api_Capabilities, + autoConvert_v1_CephFSVolumeSource_To_api_CephFSVolumeSource, + autoConvert_v1_CinderVolumeSource_To_api_CinderVolumeSource, + autoConvert_v1_ConfigMapKeySelector_To_api_ConfigMapKeySelector, + autoConvert_v1_ConfigMapVolumeSource_To_api_ConfigMapVolumeSource, + autoConvert_v1_ContainerPort_To_api_ContainerPort, + autoConvert_v1_Container_To_api_Container, + autoConvert_v1_DownwardAPIVolumeFile_To_api_DownwardAPIVolumeFile, + autoConvert_v1_DownwardAPIVolumeSource_To_api_DownwardAPIVolumeSource, + autoConvert_v1_EmptyDirVolumeSource_To_api_EmptyDirVolumeSource, + autoConvert_v1_EnvVarSource_To_api_EnvVarSource, + autoConvert_v1_EnvVar_To_api_EnvVar, + autoConvert_v1_ExecAction_To_api_ExecAction, + autoConvert_v1_FCVolumeSource_To_api_FCVolumeSource, + autoConvert_v1_FlexVolumeSource_To_api_FlexVolumeSource, + autoConvert_v1_FlockerVolumeSource_To_api_FlockerVolumeSource, + autoConvert_v1_GCEPersistentDiskVolumeSource_To_api_GCEPersistentDiskVolumeSource, + autoConvert_v1_GitRepoVolumeSource_To_api_GitRepoVolumeSource, + autoConvert_v1_GlusterfsVolumeSource_To_api_GlusterfsVolumeSource, + autoConvert_v1_HTTPGetAction_To_api_HTTPGetAction, + autoConvert_v1_HTTPHeader_To_api_HTTPHeader, + autoConvert_v1_Handler_To_api_Handler, + autoConvert_v1_HostPathVolumeSource_To_api_HostPathVolumeSource, + autoConvert_v1_ISCSIVolumeSource_To_api_ISCSIVolumeSource, + autoConvert_v1_JobCondition_To_extensions_JobCondition, + autoConvert_v1_JobList_To_extensions_JobList, + autoConvert_v1_JobSpec_To_extensions_JobSpec, + autoConvert_v1_JobStatus_To_extensions_JobStatus, + autoConvert_v1_Job_To_extensions_Job, + autoConvert_v1_KeyToPath_To_api_KeyToPath, + autoConvert_v1_LabelSelectorRequirement_To_unversioned_LabelSelectorRequirement, + autoConvert_v1_LabelSelector_To_unversioned_LabelSelector, + autoConvert_v1_Lifecycle_To_api_Lifecycle, + autoConvert_v1_LocalObjectReference_To_api_LocalObjectReference, + autoConvert_v1_NFSVolumeSource_To_api_NFSVolumeSource, + autoConvert_v1_ObjectFieldSelector_To_api_ObjectFieldSelector, + autoConvert_v1_ObjectMeta_To_api_ObjectMeta, + autoConvert_v1_PersistentVolumeClaimVolumeSource_To_api_PersistentVolumeClaimVolumeSource, + autoConvert_v1_PodSpec_To_api_PodSpec, + autoConvert_v1_PodTemplateSpec_To_api_PodTemplateSpec, + autoConvert_v1_Probe_To_api_Probe, + autoConvert_v1_RBDVolumeSource_To_api_RBDVolumeSource, + autoConvert_v1_ResourceRequirements_To_api_ResourceRequirements, + autoConvert_v1_SELinuxOptions_To_api_SELinuxOptions, + autoConvert_v1_SecretKeySelector_To_api_SecretKeySelector, + autoConvert_v1_SecretVolumeSource_To_api_SecretVolumeSource, + autoConvert_v1_SecurityContext_To_api_SecurityContext, + autoConvert_v1_TCPSocketAction_To_api_TCPSocketAction, + autoConvert_v1_VolumeMount_To_api_VolumeMount, + autoConvert_v1_VolumeSource_To_api_VolumeSource, + autoConvert_v1_Volume_To_api_Volume, + ) + if err != nil { + // If one of the conversion functions is malformed, detect it immediately. + panic(err) + } +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/deep_copy_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/deep_copy_generated.go new file mode 100644 index 000000000..3ddbd6d2b --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/deep_copy_generated.go @@ -0,0 +1,1233 @@ +/* +Copyright 2015 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. +*/ + +// DO NOT EDIT. THIS FILE IS AUTO-GENERATED BY $KUBEROOT/hack/update-generated-deep-copies.sh. + +package v1 + +import ( + time "time" + + api "k8s.io/kubernetes/pkg/api" + resource "k8s.io/kubernetes/pkg/api/resource" + unversioned "k8s.io/kubernetes/pkg/api/unversioned" + v1 "k8s.io/kubernetes/pkg/api/v1" + conversion "k8s.io/kubernetes/pkg/conversion" + intstr "k8s.io/kubernetes/pkg/util/intstr" + inf "speter.net/go/exp/math/dec/inf" +) + +func deepCopy_resource_Quantity(in resource.Quantity, out *resource.Quantity, c *conversion.Cloner) error { + if in.Amount != nil { + if newVal, err := c.DeepCopy(in.Amount); err != nil { + return err + } else { + out.Amount = newVal.(*inf.Dec) + } + } else { + out.Amount = nil + } + out.Format = in.Format + return nil +} + +func deepCopy_unversioned_ListMeta(in unversioned.ListMeta, out *unversioned.ListMeta, c *conversion.Cloner) error { + out.SelfLink = in.SelfLink + out.ResourceVersion = in.ResourceVersion + return nil +} + +func deepCopy_unversioned_Time(in unversioned.Time, out *unversioned.Time, c *conversion.Cloner) error { + if newVal, err := c.DeepCopy(in.Time); err != nil { + return err + } else { + out.Time = newVal.(time.Time) + } + return nil +} + +func deepCopy_unversioned_TypeMeta(in unversioned.TypeMeta, out *unversioned.TypeMeta, c *conversion.Cloner) error { + out.Kind = in.Kind + out.APIVersion = in.APIVersion + return nil +} + +func deepCopy_v1_AWSElasticBlockStoreVolumeSource(in v1.AWSElasticBlockStoreVolumeSource, out *v1.AWSElasticBlockStoreVolumeSource, c *conversion.Cloner) error { + out.VolumeID = in.VolumeID + out.FSType = in.FSType + out.Partition = in.Partition + out.ReadOnly = in.ReadOnly + return nil +} + +func deepCopy_v1_AzureFileVolumeSource(in v1.AzureFileVolumeSource, out *v1.AzureFileVolumeSource, c *conversion.Cloner) error { + out.SecretName = in.SecretName + out.ShareName = in.ShareName + out.ReadOnly = in.ReadOnly + return nil +} + +func deepCopy_v1_Capabilities(in v1.Capabilities, out *v1.Capabilities, c *conversion.Cloner) error { + if in.Add != nil { + out.Add = make([]v1.Capability, len(in.Add)) + for i := range in.Add { + out.Add[i] = in.Add[i] + } + } else { + out.Add = nil + } + if in.Drop != nil { + out.Drop = make([]v1.Capability, len(in.Drop)) + for i := range in.Drop { + out.Drop[i] = in.Drop[i] + } + } else { + out.Drop = nil + } + return nil +} + +func deepCopy_v1_CephFSVolumeSource(in v1.CephFSVolumeSource, out *v1.CephFSVolumeSource, c *conversion.Cloner) error { + if in.Monitors != nil { + out.Monitors = make([]string, len(in.Monitors)) + for i := range in.Monitors { + out.Monitors[i] = in.Monitors[i] + } + } else { + out.Monitors = nil + } + out.Path = in.Path + out.User = in.User + out.SecretFile = in.SecretFile + if in.SecretRef != nil { + out.SecretRef = new(v1.LocalObjectReference) + if err := deepCopy_v1_LocalObjectReference(*in.SecretRef, out.SecretRef, c); err != nil { + return err + } + } else { + out.SecretRef = nil + } + out.ReadOnly = in.ReadOnly + return nil +} + +func deepCopy_v1_CinderVolumeSource(in v1.CinderVolumeSource, out *v1.CinderVolumeSource, c *conversion.Cloner) error { + out.VolumeID = in.VolumeID + out.FSType = in.FSType + out.ReadOnly = in.ReadOnly + return nil +} + +func deepCopy_v1_ConfigMapKeySelector(in v1.ConfigMapKeySelector, out *v1.ConfigMapKeySelector, c *conversion.Cloner) error { + if err := deepCopy_v1_LocalObjectReference(in.LocalObjectReference, &out.LocalObjectReference, c); err != nil { + return err + } + out.Key = in.Key + return nil +} + +func deepCopy_v1_ConfigMapVolumeSource(in v1.ConfigMapVolumeSource, out *v1.ConfigMapVolumeSource, c *conversion.Cloner) error { + if err := deepCopy_v1_LocalObjectReference(in.LocalObjectReference, &out.LocalObjectReference, c); err != nil { + return err + } + if in.Items != nil { + out.Items = make([]v1.KeyToPath, len(in.Items)) + for i := range in.Items { + if err := deepCopy_v1_KeyToPath(in.Items[i], &out.Items[i], c); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func deepCopy_v1_Container(in v1.Container, out *v1.Container, c *conversion.Cloner) error { + out.Name = in.Name + out.Image = in.Image + if in.Command != nil { + out.Command = make([]string, len(in.Command)) + for i := range in.Command { + out.Command[i] = in.Command[i] + } + } else { + out.Command = nil + } + if in.Args != nil { + out.Args = make([]string, len(in.Args)) + for i := range in.Args { + out.Args[i] = in.Args[i] + } + } else { + out.Args = nil + } + out.WorkingDir = in.WorkingDir + if in.Ports != nil { + out.Ports = make([]v1.ContainerPort, len(in.Ports)) + for i := range in.Ports { + if err := deepCopy_v1_ContainerPort(in.Ports[i], &out.Ports[i], c); err != nil { + return err + } + } + } else { + out.Ports = nil + } + if in.Env != nil { + out.Env = make([]v1.EnvVar, len(in.Env)) + for i := range in.Env { + if err := deepCopy_v1_EnvVar(in.Env[i], &out.Env[i], c); err != nil { + return err + } + } + } else { + out.Env = nil + } + if err := deepCopy_v1_ResourceRequirements(in.Resources, &out.Resources, c); err != nil { + return err + } + if in.VolumeMounts != nil { + out.VolumeMounts = make([]v1.VolumeMount, len(in.VolumeMounts)) + for i := range in.VolumeMounts { + if err := deepCopy_v1_VolumeMount(in.VolumeMounts[i], &out.VolumeMounts[i], c); err != nil { + return err + } + } + } else { + out.VolumeMounts = nil + } + if in.LivenessProbe != nil { + out.LivenessProbe = new(v1.Probe) + if err := deepCopy_v1_Probe(*in.LivenessProbe, out.LivenessProbe, c); err != nil { + return err + } + } else { + out.LivenessProbe = nil + } + if in.ReadinessProbe != nil { + out.ReadinessProbe = new(v1.Probe) + if err := deepCopy_v1_Probe(*in.ReadinessProbe, out.ReadinessProbe, c); err != nil { + return err + } + } else { + out.ReadinessProbe = nil + } + if in.Lifecycle != nil { + out.Lifecycle = new(v1.Lifecycle) + if err := deepCopy_v1_Lifecycle(*in.Lifecycle, out.Lifecycle, c); err != nil { + return err + } + } else { + out.Lifecycle = nil + } + out.TerminationMessagePath = in.TerminationMessagePath + out.ImagePullPolicy = in.ImagePullPolicy + if in.SecurityContext != nil { + out.SecurityContext = new(v1.SecurityContext) + if err := deepCopy_v1_SecurityContext(*in.SecurityContext, out.SecurityContext, c); err != nil { + return err + } + } else { + out.SecurityContext = nil + } + out.Stdin = in.Stdin + out.StdinOnce = in.StdinOnce + out.TTY = in.TTY + return nil +} + +func deepCopy_v1_ContainerPort(in v1.ContainerPort, out *v1.ContainerPort, c *conversion.Cloner) error { + out.Name = in.Name + out.HostPort = in.HostPort + out.ContainerPort = in.ContainerPort + out.Protocol = in.Protocol + out.HostIP = in.HostIP + return nil +} + +func deepCopy_v1_DownwardAPIVolumeFile(in v1.DownwardAPIVolumeFile, out *v1.DownwardAPIVolumeFile, c *conversion.Cloner) error { + out.Path = in.Path + if err := deepCopy_v1_ObjectFieldSelector(in.FieldRef, &out.FieldRef, c); err != nil { + return err + } + return nil +} + +func deepCopy_v1_DownwardAPIVolumeSource(in v1.DownwardAPIVolumeSource, out *v1.DownwardAPIVolumeSource, c *conversion.Cloner) error { + if in.Items != nil { + out.Items = make([]v1.DownwardAPIVolumeFile, len(in.Items)) + for i := range in.Items { + if err := deepCopy_v1_DownwardAPIVolumeFile(in.Items[i], &out.Items[i], c); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func deepCopy_v1_EmptyDirVolumeSource(in v1.EmptyDirVolumeSource, out *v1.EmptyDirVolumeSource, c *conversion.Cloner) error { + out.Medium = in.Medium + return nil +} + +func deepCopy_v1_EnvVar(in v1.EnvVar, out *v1.EnvVar, c *conversion.Cloner) error { + out.Name = in.Name + out.Value = in.Value + if in.ValueFrom != nil { + out.ValueFrom = new(v1.EnvVarSource) + if err := deepCopy_v1_EnvVarSource(*in.ValueFrom, out.ValueFrom, c); err != nil { + return err + } + } else { + out.ValueFrom = nil + } + return nil +} + +func deepCopy_v1_EnvVarSource(in v1.EnvVarSource, out *v1.EnvVarSource, c *conversion.Cloner) error { + if in.FieldRef != nil { + out.FieldRef = new(v1.ObjectFieldSelector) + if err := deepCopy_v1_ObjectFieldSelector(*in.FieldRef, out.FieldRef, c); err != nil { + return err + } + } else { + out.FieldRef = nil + } + if in.ConfigMapKeyRef != nil { + out.ConfigMapKeyRef = new(v1.ConfigMapKeySelector) + if err := deepCopy_v1_ConfigMapKeySelector(*in.ConfigMapKeyRef, out.ConfigMapKeyRef, c); err != nil { + return err + } + } else { + out.ConfigMapKeyRef = nil + } + if in.SecretKeyRef != nil { + out.SecretKeyRef = new(v1.SecretKeySelector) + if err := deepCopy_v1_SecretKeySelector(*in.SecretKeyRef, out.SecretKeyRef, c); err != nil { + return err + } + } else { + out.SecretKeyRef = nil + } + return nil +} + +func deepCopy_v1_ExecAction(in v1.ExecAction, out *v1.ExecAction, c *conversion.Cloner) error { + if in.Command != nil { + out.Command = make([]string, len(in.Command)) + for i := range in.Command { + out.Command[i] = in.Command[i] + } + } else { + out.Command = nil + } + return nil +} + +func deepCopy_v1_FCVolumeSource(in v1.FCVolumeSource, out *v1.FCVolumeSource, c *conversion.Cloner) error { + if in.TargetWWNs != nil { + out.TargetWWNs = make([]string, len(in.TargetWWNs)) + for i := range in.TargetWWNs { + out.TargetWWNs[i] = in.TargetWWNs[i] + } + } else { + out.TargetWWNs = nil + } + if in.Lun != nil { + out.Lun = new(int32) + *out.Lun = *in.Lun + } else { + out.Lun = nil + } + out.FSType = in.FSType + out.ReadOnly = in.ReadOnly + return nil +} + +func deepCopy_v1_FlexVolumeSource(in v1.FlexVolumeSource, out *v1.FlexVolumeSource, c *conversion.Cloner) error { + out.Driver = in.Driver + out.FSType = in.FSType + if in.SecretRef != nil { + out.SecretRef = new(v1.LocalObjectReference) + if err := deepCopy_v1_LocalObjectReference(*in.SecretRef, out.SecretRef, c); err != nil { + return err + } + } else { + out.SecretRef = nil + } + out.ReadOnly = in.ReadOnly + if in.Options != nil { + out.Options = make(map[string]string) + for key, val := range in.Options { + out.Options[key] = val + } + } else { + out.Options = nil + } + return nil +} + +func deepCopy_v1_FlockerVolumeSource(in v1.FlockerVolumeSource, out *v1.FlockerVolumeSource, c *conversion.Cloner) error { + out.DatasetName = in.DatasetName + return nil +} + +func deepCopy_v1_GCEPersistentDiskVolumeSource(in v1.GCEPersistentDiskVolumeSource, out *v1.GCEPersistentDiskVolumeSource, c *conversion.Cloner) error { + out.PDName = in.PDName + out.FSType = in.FSType + out.Partition = in.Partition + out.ReadOnly = in.ReadOnly + return nil +} + +func deepCopy_v1_GitRepoVolumeSource(in v1.GitRepoVolumeSource, out *v1.GitRepoVolumeSource, c *conversion.Cloner) error { + out.Repository = in.Repository + out.Revision = in.Revision + out.Directory = in.Directory + return nil +} + +func deepCopy_v1_GlusterfsVolumeSource(in v1.GlusterfsVolumeSource, out *v1.GlusterfsVolumeSource, c *conversion.Cloner) error { + out.EndpointsName = in.EndpointsName + out.Path = in.Path + out.ReadOnly = in.ReadOnly + return nil +} + +func deepCopy_v1_HTTPGetAction(in v1.HTTPGetAction, out *v1.HTTPGetAction, c *conversion.Cloner) error { + out.Path = in.Path + if err := deepCopy_intstr_IntOrString(in.Port, &out.Port, c); err != nil { + return err + } + out.Host = in.Host + out.Scheme = in.Scheme + if in.HTTPHeaders != nil { + out.HTTPHeaders = make([]v1.HTTPHeader, len(in.HTTPHeaders)) + for i := range in.HTTPHeaders { + if err := deepCopy_v1_HTTPHeader(in.HTTPHeaders[i], &out.HTTPHeaders[i], c); err != nil { + return err + } + } + } else { + out.HTTPHeaders = nil + } + return nil +} + +func deepCopy_v1_HTTPHeader(in v1.HTTPHeader, out *v1.HTTPHeader, c *conversion.Cloner) error { + out.Name = in.Name + out.Value = in.Value + return nil +} + +func deepCopy_v1_Handler(in v1.Handler, out *v1.Handler, c *conversion.Cloner) error { + if in.Exec != nil { + out.Exec = new(v1.ExecAction) + if err := deepCopy_v1_ExecAction(*in.Exec, out.Exec, c); err != nil { + return err + } + } else { + out.Exec = nil + } + if in.HTTPGet != nil { + out.HTTPGet = new(v1.HTTPGetAction) + if err := deepCopy_v1_HTTPGetAction(*in.HTTPGet, out.HTTPGet, c); err != nil { + return err + } + } else { + out.HTTPGet = nil + } + if in.TCPSocket != nil { + out.TCPSocket = new(v1.TCPSocketAction) + if err := deepCopy_v1_TCPSocketAction(*in.TCPSocket, out.TCPSocket, c); err != nil { + return err + } + } else { + out.TCPSocket = nil + } + return nil +} + +func deepCopy_v1_HostPathVolumeSource(in v1.HostPathVolumeSource, out *v1.HostPathVolumeSource, c *conversion.Cloner) error { + out.Path = in.Path + return nil +} + +func deepCopy_v1_ISCSIVolumeSource(in v1.ISCSIVolumeSource, out *v1.ISCSIVolumeSource, c *conversion.Cloner) error { + out.TargetPortal = in.TargetPortal + out.IQN = in.IQN + out.Lun = in.Lun + out.ISCSIInterface = in.ISCSIInterface + out.FSType = in.FSType + out.ReadOnly = in.ReadOnly + return nil +} + +func deepCopy_v1_KeyToPath(in v1.KeyToPath, out *v1.KeyToPath, c *conversion.Cloner) error { + out.Key = in.Key + out.Path = in.Path + return nil +} + +func deepCopy_v1_Lifecycle(in v1.Lifecycle, out *v1.Lifecycle, c *conversion.Cloner) error { + if in.PostStart != nil { + out.PostStart = new(v1.Handler) + if err := deepCopy_v1_Handler(*in.PostStart, out.PostStart, c); err != nil { + return err + } + } else { + out.PostStart = nil + } + if in.PreStop != nil { + out.PreStop = new(v1.Handler) + if err := deepCopy_v1_Handler(*in.PreStop, out.PreStop, c); err != nil { + return err + } + } else { + out.PreStop = nil + } + return nil +} + +func deepCopy_v1_LocalObjectReference(in v1.LocalObjectReference, out *v1.LocalObjectReference, c *conversion.Cloner) error { + out.Name = in.Name + return nil +} + +func deepCopy_v1_NFSVolumeSource(in v1.NFSVolumeSource, out *v1.NFSVolumeSource, c *conversion.Cloner) error { + out.Server = in.Server + out.Path = in.Path + out.ReadOnly = in.ReadOnly + return nil +} + +func deepCopy_v1_ObjectFieldSelector(in v1.ObjectFieldSelector, out *v1.ObjectFieldSelector, c *conversion.Cloner) error { + out.APIVersion = in.APIVersion + out.FieldPath = in.FieldPath + return nil +} + +func deepCopy_v1_ObjectMeta(in v1.ObjectMeta, out *v1.ObjectMeta, c *conversion.Cloner) error { + out.Name = in.Name + out.GenerateName = in.GenerateName + out.Namespace = in.Namespace + out.SelfLink = in.SelfLink + out.UID = in.UID + out.ResourceVersion = in.ResourceVersion + out.Generation = in.Generation + if err := deepCopy_unversioned_Time(in.CreationTimestamp, &out.CreationTimestamp, c); err != nil { + return err + } + if in.DeletionTimestamp != nil { + out.DeletionTimestamp = new(unversioned.Time) + if err := deepCopy_unversioned_Time(*in.DeletionTimestamp, out.DeletionTimestamp, c); err != nil { + return err + } + } else { + out.DeletionTimestamp = nil + } + if in.DeletionGracePeriodSeconds != nil { + out.DeletionGracePeriodSeconds = new(int64) + *out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds + } else { + out.DeletionGracePeriodSeconds = nil + } + if in.Labels != nil { + out.Labels = make(map[string]string) + for key, val := range in.Labels { + out.Labels[key] = val + } + } else { + out.Labels = nil + } + if in.Annotations != nil { + out.Annotations = make(map[string]string) + for key, val := range in.Annotations { + out.Annotations[key] = val + } + } else { + out.Annotations = nil + } + return nil +} + +func deepCopy_v1_PersistentVolumeClaimVolumeSource(in v1.PersistentVolumeClaimVolumeSource, out *v1.PersistentVolumeClaimVolumeSource, c *conversion.Cloner) error { + out.ClaimName = in.ClaimName + out.ReadOnly = in.ReadOnly + return nil +} + +func deepCopy_v1_PodSecurityContext(in v1.PodSecurityContext, out *v1.PodSecurityContext, c *conversion.Cloner) error { + if in.SELinuxOptions != nil { + out.SELinuxOptions = new(v1.SELinuxOptions) + if err := deepCopy_v1_SELinuxOptions(*in.SELinuxOptions, out.SELinuxOptions, c); err != nil { + return err + } + } else { + out.SELinuxOptions = nil + } + if in.RunAsUser != nil { + out.RunAsUser = new(int64) + *out.RunAsUser = *in.RunAsUser + } else { + out.RunAsUser = nil + } + if in.RunAsNonRoot != nil { + out.RunAsNonRoot = new(bool) + *out.RunAsNonRoot = *in.RunAsNonRoot + } else { + out.RunAsNonRoot = nil + } + if in.SupplementalGroups != nil { + out.SupplementalGroups = make([]int64, len(in.SupplementalGroups)) + for i := range in.SupplementalGroups { + out.SupplementalGroups[i] = in.SupplementalGroups[i] + } + } else { + out.SupplementalGroups = nil + } + if in.FSGroup != nil { + out.FSGroup = new(int64) + *out.FSGroup = *in.FSGroup + } else { + out.FSGroup = nil + } + return nil +} + +func deepCopy_v1_PodSpec(in v1.PodSpec, out *v1.PodSpec, c *conversion.Cloner) error { + if in.Volumes != nil { + out.Volumes = make([]v1.Volume, len(in.Volumes)) + for i := range in.Volumes { + if err := deepCopy_v1_Volume(in.Volumes[i], &out.Volumes[i], c); err != nil { + return err + } + } + } else { + out.Volumes = nil + } + if in.Containers != nil { + out.Containers = make([]v1.Container, len(in.Containers)) + for i := range in.Containers { + if err := deepCopy_v1_Container(in.Containers[i], &out.Containers[i], c); err != nil { + return err + } + } + } else { + out.Containers = nil + } + out.RestartPolicy = in.RestartPolicy + if in.TerminationGracePeriodSeconds != nil { + out.TerminationGracePeriodSeconds = new(int64) + *out.TerminationGracePeriodSeconds = *in.TerminationGracePeriodSeconds + } else { + out.TerminationGracePeriodSeconds = nil + } + if in.ActiveDeadlineSeconds != nil { + out.ActiveDeadlineSeconds = new(int64) + *out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds + } else { + out.ActiveDeadlineSeconds = nil + } + out.DNSPolicy = in.DNSPolicy + if in.NodeSelector != nil { + out.NodeSelector = make(map[string]string) + for key, val := range in.NodeSelector { + out.NodeSelector[key] = val + } + } else { + out.NodeSelector = nil + } + out.ServiceAccountName = in.ServiceAccountName + out.DeprecatedServiceAccount = in.DeprecatedServiceAccount + out.NodeName = in.NodeName + out.HostNetwork = in.HostNetwork + out.HostPID = in.HostPID + out.HostIPC = in.HostIPC + if in.SecurityContext != nil { + out.SecurityContext = new(v1.PodSecurityContext) + if err := deepCopy_v1_PodSecurityContext(*in.SecurityContext, out.SecurityContext, c); err != nil { + return err + } + } else { + out.SecurityContext = nil + } + if in.ImagePullSecrets != nil { + out.ImagePullSecrets = make([]v1.LocalObjectReference, len(in.ImagePullSecrets)) + for i := range in.ImagePullSecrets { + if err := deepCopy_v1_LocalObjectReference(in.ImagePullSecrets[i], &out.ImagePullSecrets[i], c); err != nil { + return err + } + } + } else { + out.ImagePullSecrets = nil + } + return nil +} + +func deepCopy_v1_PodTemplateSpec(in v1.PodTemplateSpec, out *v1.PodTemplateSpec, c *conversion.Cloner) error { + if err := deepCopy_v1_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { + return err + } + if err := deepCopy_v1_PodSpec(in.Spec, &out.Spec, c); err != nil { + return err + } + return nil +} + +func deepCopy_v1_Probe(in v1.Probe, out *v1.Probe, c *conversion.Cloner) error { + if err := deepCopy_v1_Handler(in.Handler, &out.Handler, c); err != nil { + return err + } + out.InitialDelaySeconds = in.InitialDelaySeconds + out.TimeoutSeconds = in.TimeoutSeconds + out.PeriodSeconds = in.PeriodSeconds + out.SuccessThreshold = in.SuccessThreshold + out.FailureThreshold = in.FailureThreshold + return nil +} + +func deepCopy_v1_RBDVolumeSource(in v1.RBDVolumeSource, out *v1.RBDVolumeSource, c *conversion.Cloner) error { + if in.CephMonitors != nil { + out.CephMonitors = make([]string, len(in.CephMonitors)) + for i := range in.CephMonitors { + out.CephMonitors[i] = in.CephMonitors[i] + } + } else { + out.CephMonitors = nil + } + out.RBDImage = in.RBDImage + out.FSType = in.FSType + out.RBDPool = in.RBDPool + out.RadosUser = in.RadosUser + out.Keyring = in.Keyring + if in.SecretRef != nil { + out.SecretRef = new(v1.LocalObjectReference) + if err := deepCopy_v1_LocalObjectReference(*in.SecretRef, out.SecretRef, c); err != nil { + return err + } + } else { + out.SecretRef = nil + } + out.ReadOnly = in.ReadOnly + return nil +} + +func deepCopy_v1_ResourceRequirements(in v1.ResourceRequirements, out *v1.ResourceRequirements, c *conversion.Cloner) error { + if in.Limits != nil { + out.Limits = make(v1.ResourceList) + for key, val := range in.Limits { + newVal := new(resource.Quantity) + if err := deepCopy_resource_Quantity(val, newVal, c); err != nil { + return err + } + out.Limits[key] = *newVal + } + } else { + out.Limits = nil + } + if in.Requests != nil { + out.Requests = make(v1.ResourceList) + for key, val := range in.Requests { + newVal := new(resource.Quantity) + if err := deepCopy_resource_Quantity(val, newVal, c); err != nil { + return err + } + out.Requests[key] = *newVal + } + } else { + out.Requests = nil + } + return nil +} + +func deepCopy_v1_SELinuxOptions(in v1.SELinuxOptions, out *v1.SELinuxOptions, c *conversion.Cloner) error { + out.User = in.User + out.Role = in.Role + out.Type = in.Type + out.Level = in.Level + return nil +} + +func deepCopy_v1_SecretKeySelector(in v1.SecretKeySelector, out *v1.SecretKeySelector, c *conversion.Cloner) error { + if err := deepCopy_v1_LocalObjectReference(in.LocalObjectReference, &out.LocalObjectReference, c); err != nil { + return err + } + out.Key = in.Key + return nil +} + +func deepCopy_v1_SecretVolumeSource(in v1.SecretVolumeSource, out *v1.SecretVolumeSource, c *conversion.Cloner) error { + out.SecretName = in.SecretName + return nil +} + +func deepCopy_v1_SecurityContext(in v1.SecurityContext, out *v1.SecurityContext, c *conversion.Cloner) error { + if in.Capabilities != nil { + out.Capabilities = new(v1.Capabilities) + if err := deepCopy_v1_Capabilities(*in.Capabilities, out.Capabilities, c); err != nil { + return err + } + } else { + out.Capabilities = nil + } + if in.Privileged != nil { + out.Privileged = new(bool) + *out.Privileged = *in.Privileged + } else { + out.Privileged = nil + } + if in.SELinuxOptions != nil { + out.SELinuxOptions = new(v1.SELinuxOptions) + if err := deepCopy_v1_SELinuxOptions(*in.SELinuxOptions, out.SELinuxOptions, c); err != nil { + return err + } + } else { + out.SELinuxOptions = nil + } + if in.RunAsUser != nil { + out.RunAsUser = new(int64) + *out.RunAsUser = *in.RunAsUser + } else { + out.RunAsUser = nil + } + if in.RunAsNonRoot != nil { + out.RunAsNonRoot = new(bool) + *out.RunAsNonRoot = *in.RunAsNonRoot + } else { + out.RunAsNonRoot = nil + } + if in.ReadOnlyRootFilesystem != nil { + out.ReadOnlyRootFilesystem = new(bool) + *out.ReadOnlyRootFilesystem = *in.ReadOnlyRootFilesystem + } else { + out.ReadOnlyRootFilesystem = nil + } + return nil +} + +func deepCopy_v1_TCPSocketAction(in v1.TCPSocketAction, out *v1.TCPSocketAction, c *conversion.Cloner) error { + if err := deepCopy_intstr_IntOrString(in.Port, &out.Port, c); err != nil { + return err + } + return nil +} + +func deepCopy_v1_Volume(in v1.Volume, out *v1.Volume, c *conversion.Cloner) error { + out.Name = in.Name + if err := deepCopy_v1_VolumeSource(in.VolumeSource, &out.VolumeSource, c); err != nil { + return err + } + return nil +} + +func deepCopy_v1_VolumeMount(in v1.VolumeMount, out *v1.VolumeMount, c *conversion.Cloner) error { + out.Name = in.Name + out.ReadOnly = in.ReadOnly + out.MountPath = in.MountPath + return nil +} + +func deepCopy_v1_VolumeSource(in v1.VolumeSource, out *v1.VolumeSource, c *conversion.Cloner) error { + if in.HostPath != nil { + out.HostPath = new(v1.HostPathVolumeSource) + if err := deepCopy_v1_HostPathVolumeSource(*in.HostPath, out.HostPath, c); err != nil { + return err + } + } else { + out.HostPath = nil + } + if in.EmptyDir != nil { + out.EmptyDir = new(v1.EmptyDirVolumeSource) + if err := deepCopy_v1_EmptyDirVolumeSource(*in.EmptyDir, out.EmptyDir, c); err != nil { + return err + } + } else { + out.EmptyDir = nil + } + if in.GCEPersistentDisk != nil { + out.GCEPersistentDisk = new(v1.GCEPersistentDiskVolumeSource) + if err := deepCopy_v1_GCEPersistentDiskVolumeSource(*in.GCEPersistentDisk, out.GCEPersistentDisk, c); err != nil { + return err + } + } else { + out.GCEPersistentDisk = nil + } + if in.AWSElasticBlockStore != nil { + out.AWSElasticBlockStore = new(v1.AWSElasticBlockStoreVolumeSource) + if err := deepCopy_v1_AWSElasticBlockStoreVolumeSource(*in.AWSElasticBlockStore, out.AWSElasticBlockStore, c); err != nil { + return err + } + } else { + out.AWSElasticBlockStore = nil + } + if in.GitRepo != nil { + out.GitRepo = new(v1.GitRepoVolumeSource) + if err := deepCopy_v1_GitRepoVolumeSource(*in.GitRepo, out.GitRepo, c); err != nil { + return err + } + } else { + out.GitRepo = nil + } + if in.Secret != nil { + out.Secret = new(v1.SecretVolumeSource) + if err := deepCopy_v1_SecretVolumeSource(*in.Secret, out.Secret, c); err != nil { + return err + } + } else { + out.Secret = nil + } + if in.NFS != nil { + out.NFS = new(v1.NFSVolumeSource) + if err := deepCopy_v1_NFSVolumeSource(*in.NFS, out.NFS, c); err != nil { + return err + } + } else { + out.NFS = nil + } + if in.ISCSI != nil { + out.ISCSI = new(v1.ISCSIVolumeSource) + if err := deepCopy_v1_ISCSIVolumeSource(*in.ISCSI, out.ISCSI, c); err != nil { + return err + } + } else { + out.ISCSI = nil + } + if in.Glusterfs != nil { + out.Glusterfs = new(v1.GlusterfsVolumeSource) + if err := deepCopy_v1_GlusterfsVolumeSource(*in.Glusterfs, out.Glusterfs, c); err != nil { + return err + } + } else { + out.Glusterfs = nil + } + if in.PersistentVolumeClaim != nil { + out.PersistentVolumeClaim = new(v1.PersistentVolumeClaimVolumeSource) + if err := deepCopy_v1_PersistentVolumeClaimVolumeSource(*in.PersistentVolumeClaim, out.PersistentVolumeClaim, c); err != nil { + return err + } + } else { + out.PersistentVolumeClaim = nil + } + if in.RBD != nil { + out.RBD = new(v1.RBDVolumeSource) + if err := deepCopy_v1_RBDVolumeSource(*in.RBD, out.RBD, c); err != nil { + return err + } + } else { + out.RBD = nil + } + if in.FlexVolume != nil { + out.FlexVolume = new(v1.FlexVolumeSource) + if err := deepCopy_v1_FlexVolumeSource(*in.FlexVolume, out.FlexVolume, c); err != nil { + return err + } + } else { + out.FlexVolume = nil + } + if in.Cinder != nil { + out.Cinder = new(v1.CinderVolumeSource) + if err := deepCopy_v1_CinderVolumeSource(*in.Cinder, out.Cinder, c); err != nil { + return err + } + } else { + out.Cinder = nil + } + if in.CephFS != nil { + out.CephFS = new(v1.CephFSVolumeSource) + if err := deepCopy_v1_CephFSVolumeSource(*in.CephFS, out.CephFS, c); err != nil { + return err + } + } else { + out.CephFS = nil + } + if in.Flocker != nil { + out.Flocker = new(v1.FlockerVolumeSource) + if err := deepCopy_v1_FlockerVolumeSource(*in.Flocker, out.Flocker, c); err != nil { + return err + } + } else { + out.Flocker = nil + } + if in.DownwardAPI != nil { + out.DownwardAPI = new(v1.DownwardAPIVolumeSource) + if err := deepCopy_v1_DownwardAPIVolumeSource(*in.DownwardAPI, out.DownwardAPI, c); err != nil { + return err + } + } else { + out.DownwardAPI = nil + } + if in.FC != nil { + out.FC = new(v1.FCVolumeSource) + if err := deepCopy_v1_FCVolumeSource(*in.FC, out.FC, c); err != nil { + return err + } + } else { + out.FC = nil + } + if in.AzureFile != nil { + out.AzureFile = new(v1.AzureFileVolumeSource) + if err := deepCopy_v1_AzureFileVolumeSource(*in.AzureFile, out.AzureFile, c); err != nil { + return err + } + } else { + out.AzureFile = nil + } + if in.ConfigMap != nil { + out.ConfigMap = new(v1.ConfigMapVolumeSource) + if err := deepCopy_v1_ConfigMapVolumeSource(*in.ConfigMap, out.ConfigMap, c); err != nil { + return err + } + } else { + out.ConfigMap = nil + } + return nil +} + +func deepCopy_v1_Job(in Job, out *Job, c *conversion.Cloner) error { + if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { + return err + } + if err := deepCopy_v1_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { + return err + } + if err := deepCopy_v1_JobSpec(in.Spec, &out.Spec, c); err != nil { + return err + } + if err := deepCopy_v1_JobStatus(in.Status, &out.Status, c); err != nil { + return err + } + return nil +} + +func deepCopy_v1_JobCondition(in JobCondition, out *JobCondition, c *conversion.Cloner) error { + out.Type = in.Type + out.Status = in.Status + if err := deepCopy_unversioned_Time(in.LastProbeTime, &out.LastProbeTime, c); err != nil { + return err + } + if err := deepCopy_unversioned_Time(in.LastTransitionTime, &out.LastTransitionTime, c); err != nil { + return err + } + out.Reason = in.Reason + out.Message = in.Message + return nil +} + +func deepCopy_v1_JobList(in JobList, out *JobList, c *conversion.Cloner) error { + if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { + return err + } + if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { + return err + } + if in.Items != nil { + out.Items = make([]Job, len(in.Items)) + for i := range in.Items { + if err := deepCopy_v1_Job(in.Items[i], &out.Items[i], c); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func deepCopy_v1_JobSpec(in JobSpec, out *JobSpec, c *conversion.Cloner) error { + if in.Parallelism != nil { + out.Parallelism = new(int32) + *out.Parallelism = *in.Parallelism + } else { + out.Parallelism = nil + } + if in.Completions != nil { + out.Completions = new(int32) + *out.Completions = *in.Completions + } else { + out.Completions = nil + } + if in.ActiveDeadlineSeconds != nil { + out.ActiveDeadlineSeconds = new(int64) + *out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds + } else { + out.ActiveDeadlineSeconds = nil + } + if in.Selector != nil { + out.Selector = new(LabelSelector) + if err := deepCopy_v1_LabelSelector(*in.Selector, out.Selector, c); err != nil { + return err + } + } else { + out.Selector = nil + } + if in.ManualSelector != nil { + out.ManualSelector = new(bool) + *out.ManualSelector = *in.ManualSelector + } else { + out.ManualSelector = nil + } + if err := deepCopy_v1_PodTemplateSpec(in.Template, &out.Template, c); err != nil { + return err + } + return nil +} + +func deepCopy_v1_JobStatus(in JobStatus, out *JobStatus, c *conversion.Cloner) error { + if in.Conditions != nil { + out.Conditions = make([]JobCondition, len(in.Conditions)) + for i := range in.Conditions { + if err := deepCopy_v1_JobCondition(in.Conditions[i], &out.Conditions[i], c); err != nil { + return err + } + } + } else { + out.Conditions = nil + } + if in.StartTime != nil { + out.StartTime = new(unversioned.Time) + if err := deepCopy_unversioned_Time(*in.StartTime, out.StartTime, c); err != nil { + return err + } + } else { + out.StartTime = nil + } + if in.CompletionTime != nil { + out.CompletionTime = new(unversioned.Time) + if err := deepCopy_unversioned_Time(*in.CompletionTime, out.CompletionTime, c); err != nil { + return err + } + } else { + out.CompletionTime = nil + } + out.Active = in.Active + out.Succeeded = in.Succeeded + out.Failed = in.Failed + return nil +} + +func deepCopy_v1_LabelSelector(in LabelSelector, out *LabelSelector, c *conversion.Cloner) error { + if in.MatchLabels != nil { + out.MatchLabels = make(map[string]string) + for key, val := range in.MatchLabels { + out.MatchLabels[key] = val + } + } else { + out.MatchLabels = nil + } + if in.MatchExpressions != nil { + out.MatchExpressions = make([]LabelSelectorRequirement, len(in.MatchExpressions)) + for i := range in.MatchExpressions { + if err := deepCopy_v1_LabelSelectorRequirement(in.MatchExpressions[i], &out.MatchExpressions[i], c); err != nil { + return err + } + } + } else { + out.MatchExpressions = nil + } + return nil +} + +func deepCopy_v1_LabelSelectorRequirement(in LabelSelectorRequirement, out *LabelSelectorRequirement, c *conversion.Cloner) error { + out.Key = in.Key + out.Operator = in.Operator + if in.Values != nil { + out.Values = make([]string, len(in.Values)) + for i := range in.Values { + out.Values[i] = in.Values[i] + } + } else { + out.Values = nil + } + return nil +} + +func deepCopy_intstr_IntOrString(in intstr.IntOrString, out *intstr.IntOrString, c *conversion.Cloner) error { + out.Type = in.Type + out.IntVal = in.IntVal + out.StrVal = in.StrVal + return nil +} + +func init() { + err := api.Scheme.AddGeneratedDeepCopyFuncs( + deepCopy_resource_Quantity, + deepCopy_unversioned_ListMeta, + deepCopy_unversioned_Time, + deepCopy_unversioned_TypeMeta, + deepCopy_v1_AWSElasticBlockStoreVolumeSource, + deepCopy_v1_AzureFileVolumeSource, + deepCopy_v1_Capabilities, + deepCopy_v1_CephFSVolumeSource, + deepCopy_v1_CinderVolumeSource, + deepCopy_v1_ConfigMapKeySelector, + deepCopy_v1_ConfigMapVolumeSource, + deepCopy_v1_Container, + deepCopy_v1_ContainerPort, + deepCopy_v1_DownwardAPIVolumeFile, + deepCopy_v1_DownwardAPIVolumeSource, + deepCopy_v1_EmptyDirVolumeSource, + deepCopy_v1_EnvVar, + deepCopy_v1_EnvVarSource, + deepCopy_v1_ExecAction, + deepCopy_v1_FCVolumeSource, + deepCopy_v1_FlexVolumeSource, + deepCopy_v1_FlockerVolumeSource, + deepCopy_v1_GCEPersistentDiskVolumeSource, + deepCopy_v1_GitRepoVolumeSource, + deepCopy_v1_GlusterfsVolumeSource, + deepCopy_v1_HTTPGetAction, + deepCopy_v1_HTTPHeader, + deepCopy_v1_Handler, + deepCopy_v1_HostPathVolumeSource, + deepCopy_v1_ISCSIVolumeSource, + deepCopy_v1_KeyToPath, + deepCopy_v1_Lifecycle, + deepCopy_v1_LocalObjectReference, + deepCopy_v1_NFSVolumeSource, + deepCopy_v1_ObjectFieldSelector, + deepCopy_v1_ObjectMeta, + deepCopy_v1_PersistentVolumeClaimVolumeSource, + deepCopy_v1_PodSecurityContext, + deepCopy_v1_PodSpec, + deepCopy_v1_PodTemplateSpec, + deepCopy_v1_Probe, + deepCopy_v1_RBDVolumeSource, + deepCopy_v1_ResourceRequirements, + deepCopy_v1_SELinuxOptions, + deepCopy_v1_SecretKeySelector, + deepCopy_v1_SecretVolumeSource, + deepCopy_v1_SecurityContext, + deepCopy_v1_TCPSocketAction, + deepCopy_v1_Volume, + deepCopy_v1_VolumeMount, + deepCopy_v1_VolumeSource, + deepCopy_v1_Job, + deepCopy_v1_JobCondition, + deepCopy_v1_JobList, + deepCopy_v1_JobSpec, + deepCopy_v1_JobStatus, + deepCopy_v1_LabelSelector, + deepCopy_v1_LabelSelectorRequirement, + deepCopy_intstr_IntOrString, + ) + if err != nil { + // if one of the deep copy functions is malformed, detect it immediately. + panic(err) + } +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/defaults.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/defaults.go new file mode 100644 index 000000000..759ab0fb6 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/defaults.go @@ -0,0 +1,40 @@ +/* +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 v1 + +import ( + "k8s.io/kubernetes/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) { + scheme.AddDefaultingFuncs( + func(obj *Job) { + // For a non-parallel job, you can leave both `.spec.completions` and + // `.spec.parallelism` unset. When both are unset, both are defaulted to 1. + if obj.Spec.Completions == nil && obj.Spec.Parallelism == nil { + obj.Spec.Completions = new(int32) + *obj.Spec.Completions = 1 + obj.Spec.Parallelism = new(int32) + *obj.Spec.Parallelism = 1 + } + if obj.Spec.Parallelism == nil { + obj.Spec.Parallelism = new(int32) + *obj.Spec.Parallelism = 1 + } + }, + ) +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/register.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/register.go new file mode 100644 index 000000000..49f7376a3 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/register.go @@ -0,0 +1,47 @@ +/* +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 v1 + +import ( + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" +) + +// GroupName is the group name use in this package +const GroupName = "batch" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1"} + +func AddToScheme(scheme *runtime.Scheme) { + addKnownTypes(scheme) + addDefaultingFuncs(scheme) + addConversionFuncs(scheme) +} + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) { + scheme.AddKnownTypes(SchemeGroupVersion, + &Job{}, + &JobList{}, + &v1.ListOptions{}, + ) +} + +func (obj *Job) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *JobList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/types.generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/types.generated.go new file mode 100644 index 000000000..2c07db216 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/types.generated.go @@ -0,0 +1,3186 @@ +/* +Copyright 2015 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. +*/ + +// ************************************************************ +// DO NOT EDIT. +// THIS FILE IS AUTO-GENERATED BY codecgen. +// ************************************************************ + +package v1 + +import ( + "errors" + "fmt" + codec1978 "github.com/ugorji/go/codec" + pkg4_resource "k8s.io/kubernetes/pkg/api/resource" + pkg1_unversioned "k8s.io/kubernetes/pkg/api/unversioned" + pkg2_v1 "k8s.io/kubernetes/pkg/api/v1" + pkg3_types "k8s.io/kubernetes/pkg/types" + pkg6_intstr "k8s.io/kubernetes/pkg/util/intstr" + "reflect" + "runtime" + pkg5_inf "speter.net/go/exp/math/dec/inf" + time "time" +) + +const ( + // ----- content types ---- + codecSelferC_UTF81234 = 1 + codecSelferC_RAW1234 = 0 + // ----- value types used ---- + codecSelferValueTypeArray1234 = 10 + codecSelferValueTypeMap1234 = 9 + // ----- containerStateValues ---- + codecSelfer_containerMapKey1234 = 2 + codecSelfer_containerMapValue1234 = 3 + codecSelfer_containerMapEnd1234 = 4 + codecSelfer_containerArrayElem1234 = 6 + codecSelfer_containerArrayEnd1234 = 7 +) + +var ( + codecSelferBitsize1234 = uint8(reflect.TypeOf(uint(0)).Bits()) + codecSelferOnlyMapOrArrayEncodeToStructErr1234 = errors.New(`only encoded map or array can be decoded into a struct`) +) + +type codecSelfer1234 struct{} + +func init() { + if codec1978.GenVersion != 5 { + _, file, _, _ := runtime.Caller(0) + err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v", + 5, codec1978.GenVersion, file) + panic(err) + } + if false { // reference the types, but skip this branch at build/run time + var v0 pkg4_resource.Quantity + var v1 pkg1_unversioned.TypeMeta + var v2 pkg2_v1.ObjectMeta + var v3 pkg3_types.UID + var v4 pkg6_intstr.IntOrString + var v5 pkg5_inf.Dec + var v6 time.Time + _, _, _, _, _, _, _ = v0, v1, v2, v3, v4, v5, v6 + } +} + +func (x *Job) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [5]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = true + yyq2[1] = true + yyq2[2] = true + yyq2[3] = x.Kind != "" + yyq2[4] = x.APIVersion != "" + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(5) + } else { + yynn2 = 0 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + yy4 := &x.ObjectMeta + yy4.CodecEncodeSelf(e) + } else { + r.EncodeNil() + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("metadata")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy6 := &x.ObjectMeta + yy6.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + yy9 := &x.Spec + yy9.CodecEncodeSelf(e) + } else { + r.EncodeNil() + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("spec")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy11 := &x.Spec + yy11.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yy14 := &x.Status + yy14.CodecEncodeSelf(e) + } else { + r.EncodeNil() + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("status")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy16 := &x.Status + yy16.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[3] { + yym19 := z.EncBinary() + _ = yym19 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[3] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("kind")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym20 := z.EncBinary() + _ = yym20 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[4] { + yym22 := z.EncBinary() + _ = yym22 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[4] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym23 := z.EncBinary() + _ = yym23 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *Job) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *Job) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "metadata": + if r.TryDecodeAsNil() { + x.ObjectMeta = pkg2_v1.ObjectMeta{} + } else { + yyv4 := &x.ObjectMeta + yyv4.CodecDecodeSelf(d) + } + case "spec": + if r.TryDecodeAsNil() { + x.Spec = JobSpec{} + } else { + yyv5 := &x.Spec + yyv5.CodecDecodeSelf(d) + } + case "status": + if r.TryDecodeAsNil() { + x.Status = JobStatus{} + } else { + yyv6 := &x.Status + yyv6.CodecDecodeSelf(d) + } + case "kind": + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + case "apiVersion": + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *Job) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj9 int + var yyb9 bool + var yyhl9 bool = l >= 0 + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.ObjectMeta = pkg2_v1.ObjectMeta{} + } else { + yyv10 := &x.ObjectMeta + yyv10.CodecDecodeSelf(d) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Spec = JobSpec{} + } else { + yyv11 := &x.Spec + yyv11.CodecDecodeSelf(d) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Status = JobStatus{} + } else { + yyv12 := &x.Status + yyv12.CodecDecodeSelf(d) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + for { + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj9-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x *JobList) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [4]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = true + yyq2[2] = x.Kind != "" + yyq2[3] = x.APIVersion != "" + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(4) + } else { + yynn2 = 1 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + yy4 := &x.ListMeta + yym5 := z.EncBinary() + _ = yym5 + if false { + } else if z.HasExtensions() && z.EncExt(yy4) { + } else { + z.EncFallback(yy4) + } + } else { + r.EncodeNil() + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("metadata")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy6 := &x.ListMeta + yym7 := z.EncBinary() + _ = yym7 + if false { + } else if z.HasExtensions() && z.EncExt(yy6) { + } else { + z.EncFallback(yy6) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if x.Items == nil { + r.EncodeNil() + } else { + yym9 := z.EncBinary() + _ = yym9 + if false { + } else { + h.encSliceJob(([]Job)(x.Items), e) + } + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("items")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.Items == nil { + r.EncodeNil() + } else { + yym10 := z.EncBinary() + _ = yym10 + if false { + } else { + h.encSliceJob(([]Job)(x.Items), e) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yym12 := z.EncBinary() + _ = yym12 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("kind")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym13 := z.EncBinary() + _ = yym13 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[3] { + yym15 := z.EncBinary() + _ = yym15 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[3] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym16 := z.EncBinary() + _ = yym16 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *JobList) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *JobList) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "metadata": + if r.TryDecodeAsNil() { + x.ListMeta = pkg1_unversioned.ListMeta{} + } else { + yyv4 := &x.ListMeta + yym5 := z.DecBinary() + _ = yym5 + if false { + } else if z.HasExtensions() && z.DecExt(yyv4) { + } else { + z.DecFallback(yyv4, false) + } + } + case "items": + if r.TryDecodeAsNil() { + x.Items = nil + } else { + yyv6 := &x.Items + yym7 := z.DecBinary() + _ = yym7 + if false { + } else { + h.decSliceJob((*[]Job)(yyv6), d) + } + } + case "kind": + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + case "apiVersion": + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *JobList) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj10 int + var yyb10 bool + var yyhl10 bool = l >= 0 + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.ListMeta = pkg1_unversioned.ListMeta{} + } else { + yyv11 := &x.ListMeta + yym12 := z.DecBinary() + _ = yym12 + if false { + } else if z.HasExtensions() && z.DecExt(yyv11) { + } else { + z.DecFallback(yyv11, false) + } + } + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Items = nil + } else { + yyv13 := &x.Items + yym14 := z.DecBinary() + _ = yym14 + if false { + } else { + h.decSliceJob((*[]Job)(yyv13), d) + } + } + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + for { + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj10-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x *JobSpec) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [6]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = x.Parallelism != nil + yyq2[1] = x.Completions != nil + yyq2[2] = x.ActiveDeadlineSeconds != nil + yyq2[3] = x.Selector != nil + yyq2[4] = x.ManualSelector != nil + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(6) + } else { + yynn2 = 1 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + if x.Parallelism == nil { + r.EncodeNil() + } else { + yy4 := *x.Parallelism + yym5 := z.EncBinary() + _ = yym5 + if false { + } else { + r.EncodeInt(int64(yy4)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("parallelism")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.Parallelism == nil { + r.EncodeNil() + } else { + yy6 := *x.Parallelism + yym7 := z.EncBinary() + _ = yym7 + if false { + } else { + r.EncodeInt(int64(yy6)) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + if x.Completions == nil { + r.EncodeNil() + } else { + yy9 := *x.Completions + yym10 := z.EncBinary() + _ = yym10 + if false { + } else { + r.EncodeInt(int64(yy9)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("completions")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.Completions == nil { + r.EncodeNil() + } else { + yy11 := *x.Completions + yym12 := z.EncBinary() + _ = yym12 + if false { + } else { + r.EncodeInt(int64(yy11)) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + if x.ActiveDeadlineSeconds == nil { + r.EncodeNil() + } else { + yy14 := *x.ActiveDeadlineSeconds + yym15 := z.EncBinary() + _ = yym15 + if false { + } else { + r.EncodeInt(int64(yy14)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("activeDeadlineSeconds")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.ActiveDeadlineSeconds == nil { + r.EncodeNil() + } else { + yy16 := *x.ActiveDeadlineSeconds + yym17 := z.EncBinary() + _ = yym17 + if false { + } else { + r.EncodeInt(int64(yy16)) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[3] { + if x.Selector == nil { + r.EncodeNil() + } else { + x.Selector.CodecEncodeSelf(e) + } + } else { + r.EncodeNil() + } + } else { + if yyq2[3] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("selector")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.Selector == nil { + r.EncodeNil() + } else { + x.Selector.CodecEncodeSelf(e) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[4] { + if x.ManualSelector == nil { + r.EncodeNil() + } else { + yy22 := *x.ManualSelector + yym23 := z.EncBinary() + _ = yym23 + if false { + } else { + r.EncodeBool(bool(yy22)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[4] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("manualSelector")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.ManualSelector == nil { + r.EncodeNil() + } else { + yy24 := *x.ManualSelector + yym25 := z.EncBinary() + _ = yym25 + if false { + } else { + r.EncodeBool(bool(yy24)) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yy27 := &x.Template + yy27.CodecEncodeSelf(e) + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("template")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy29 := &x.Template + yy29.CodecEncodeSelf(e) + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *JobSpec) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *JobSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "parallelism": + if r.TryDecodeAsNil() { + if x.Parallelism != nil { + x.Parallelism = nil + } + } else { + if x.Parallelism == nil { + x.Parallelism = new(int32) + } + yym5 := z.DecBinary() + _ = yym5 + if false { + } else { + *((*int32)(x.Parallelism)) = int32(r.DecodeInt(32)) + } + } + case "completions": + if r.TryDecodeAsNil() { + if x.Completions != nil { + x.Completions = nil + } + } else { + if x.Completions == nil { + x.Completions = new(int32) + } + yym7 := z.DecBinary() + _ = yym7 + if false { + } else { + *((*int32)(x.Completions)) = int32(r.DecodeInt(32)) + } + } + case "activeDeadlineSeconds": + if r.TryDecodeAsNil() { + if x.ActiveDeadlineSeconds != nil { + x.ActiveDeadlineSeconds = nil + } + } else { + if x.ActiveDeadlineSeconds == nil { + x.ActiveDeadlineSeconds = new(int64) + } + yym9 := z.DecBinary() + _ = yym9 + if false { + } else { + *((*int64)(x.ActiveDeadlineSeconds)) = int64(r.DecodeInt(64)) + } + } + case "selector": + if r.TryDecodeAsNil() { + if x.Selector != nil { + x.Selector = nil + } + } else { + if x.Selector == nil { + x.Selector = new(LabelSelector) + } + x.Selector.CodecDecodeSelf(d) + } + case "manualSelector": + if r.TryDecodeAsNil() { + if x.ManualSelector != nil { + x.ManualSelector = nil + } + } else { + if x.ManualSelector == nil { + x.ManualSelector = new(bool) + } + yym12 := z.DecBinary() + _ = yym12 + if false { + } else { + *((*bool)(x.ManualSelector)) = r.DecodeBool() + } + } + case "template": + if r.TryDecodeAsNil() { + x.Template = pkg2_v1.PodTemplateSpec{} + } else { + yyv13 := &x.Template + yyv13.CodecDecodeSelf(d) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj14 int + var yyb14 bool + var yyhl14 bool = l >= 0 + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l + } else { + yyb14 = r.CheckBreak() + } + if yyb14 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.Parallelism != nil { + x.Parallelism = nil + } + } else { + if x.Parallelism == nil { + x.Parallelism = new(int32) + } + yym16 := z.DecBinary() + _ = yym16 + if false { + } else { + *((*int32)(x.Parallelism)) = int32(r.DecodeInt(32)) + } + } + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l + } else { + yyb14 = r.CheckBreak() + } + if yyb14 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.Completions != nil { + x.Completions = nil + } + } else { + if x.Completions == nil { + x.Completions = new(int32) + } + yym18 := z.DecBinary() + _ = yym18 + if false { + } else { + *((*int32)(x.Completions)) = int32(r.DecodeInt(32)) + } + } + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l + } else { + yyb14 = r.CheckBreak() + } + if yyb14 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.ActiveDeadlineSeconds != nil { + x.ActiveDeadlineSeconds = nil + } + } else { + if x.ActiveDeadlineSeconds == nil { + x.ActiveDeadlineSeconds = new(int64) + } + yym20 := z.DecBinary() + _ = yym20 + if false { + } else { + *((*int64)(x.ActiveDeadlineSeconds)) = int64(r.DecodeInt(64)) + } + } + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l + } else { + yyb14 = r.CheckBreak() + } + if yyb14 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.Selector != nil { + x.Selector = nil + } + } else { + if x.Selector == nil { + x.Selector = new(LabelSelector) + } + x.Selector.CodecDecodeSelf(d) + } + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l + } else { + yyb14 = r.CheckBreak() + } + if yyb14 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.ManualSelector != nil { + x.ManualSelector = nil + } + } else { + if x.ManualSelector == nil { + x.ManualSelector = new(bool) + } + yym23 := z.DecBinary() + _ = yym23 + if false { + } else { + *((*bool)(x.ManualSelector)) = r.DecodeBool() + } + } + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l + } else { + yyb14 = r.CheckBreak() + } + if yyb14 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Template = pkg2_v1.PodTemplateSpec{} + } else { + yyv24 := &x.Template + yyv24.CodecDecodeSelf(d) + } + for { + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l + } else { + yyb14 = r.CheckBreak() + } + if yyb14 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj14-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x *JobStatus) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [6]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = len(x.Conditions) != 0 + yyq2[1] = x.StartTime != nil + yyq2[2] = x.CompletionTime != nil + yyq2[3] = x.Active != 0 + yyq2[4] = x.Succeeded != 0 + yyq2[5] = x.Failed != 0 + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(6) + } else { + yynn2 = 0 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + if x.Conditions == nil { + r.EncodeNil() + } else { + yym4 := z.EncBinary() + _ = yym4 + if false { + } else { + h.encSliceJobCondition(([]JobCondition)(x.Conditions), e) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("conditions")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.Conditions == nil { + r.EncodeNil() + } else { + yym5 := z.EncBinary() + _ = yym5 + if false { + } else { + h.encSliceJobCondition(([]JobCondition)(x.Conditions), e) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + if x.StartTime == nil { + r.EncodeNil() + } else { + yym7 := z.EncBinary() + _ = yym7 + if false { + } else if z.HasExtensions() && z.EncExt(x.StartTime) { + } else if yym7 { + z.EncBinaryMarshal(x.StartTime) + } else if !yym7 && z.IsJSONHandle() { + z.EncJSONMarshal(x.StartTime) + } else { + z.EncFallback(x.StartTime) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("startTime")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.StartTime == nil { + r.EncodeNil() + } else { + yym8 := z.EncBinary() + _ = yym8 + if false { + } else if z.HasExtensions() && z.EncExt(x.StartTime) { + } else if yym8 { + z.EncBinaryMarshal(x.StartTime) + } else if !yym8 && z.IsJSONHandle() { + z.EncJSONMarshal(x.StartTime) + } else { + z.EncFallback(x.StartTime) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + if x.CompletionTime == nil { + r.EncodeNil() + } else { + yym10 := z.EncBinary() + _ = yym10 + if false { + } else if z.HasExtensions() && z.EncExt(x.CompletionTime) { + } else if yym10 { + z.EncBinaryMarshal(x.CompletionTime) + } else if !yym10 && z.IsJSONHandle() { + z.EncJSONMarshal(x.CompletionTime) + } else { + z.EncFallback(x.CompletionTime) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("completionTime")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.CompletionTime == nil { + r.EncodeNil() + } else { + yym11 := z.EncBinary() + _ = yym11 + if false { + } else if z.HasExtensions() && z.EncExt(x.CompletionTime) { + } else if yym11 { + z.EncBinaryMarshal(x.CompletionTime) + } else if !yym11 && z.IsJSONHandle() { + z.EncJSONMarshal(x.CompletionTime) + } else { + z.EncFallback(x.CompletionTime) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[3] { + yym13 := z.EncBinary() + _ = yym13 + if false { + } else { + r.EncodeInt(int64(x.Active)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[3] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("active")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym14 := z.EncBinary() + _ = yym14 + if false { + } else { + r.EncodeInt(int64(x.Active)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[4] { + yym16 := z.EncBinary() + _ = yym16 + if false { + } else { + r.EncodeInt(int64(x.Succeeded)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[4] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("succeeded")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym17 := z.EncBinary() + _ = yym17 + if false { + } else { + r.EncodeInt(int64(x.Succeeded)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[5] { + yym19 := z.EncBinary() + _ = yym19 + if false { + } else { + r.EncodeInt(int64(x.Failed)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[5] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("failed")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym20 := z.EncBinary() + _ = yym20 + if false { + } else { + r.EncodeInt(int64(x.Failed)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *JobStatus) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *JobStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "conditions": + if r.TryDecodeAsNil() { + x.Conditions = nil + } else { + yyv4 := &x.Conditions + yym5 := z.DecBinary() + _ = yym5 + if false { + } else { + h.decSliceJobCondition((*[]JobCondition)(yyv4), d) + } + } + case "startTime": + if r.TryDecodeAsNil() { + if x.StartTime != nil { + x.StartTime = nil + } + } else { + if x.StartTime == nil { + x.StartTime = new(pkg1_unversioned.Time) + } + yym7 := z.DecBinary() + _ = yym7 + if false { + } else if z.HasExtensions() && z.DecExt(x.StartTime) { + } else if yym7 { + z.DecBinaryUnmarshal(x.StartTime) + } else if !yym7 && z.IsJSONHandle() { + z.DecJSONUnmarshal(x.StartTime) + } else { + z.DecFallback(x.StartTime, false) + } + } + case "completionTime": + if r.TryDecodeAsNil() { + if x.CompletionTime != nil { + x.CompletionTime = nil + } + } else { + if x.CompletionTime == nil { + x.CompletionTime = new(pkg1_unversioned.Time) + } + yym9 := z.DecBinary() + _ = yym9 + if false { + } else if z.HasExtensions() && z.DecExt(x.CompletionTime) { + } else if yym9 { + z.DecBinaryUnmarshal(x.CompletionTime) + } else if !yym9 && z.IsJSONHandle() { + z.DecJSONUnmarshal(x.CompletionTime) + } else { + z.DecFallback(x.CompletionTime, false) + } + } + case "active": + if r.TryDecodeAsNil() { + x.Active = 0 + } else { + x.Active = int32(r.DecodeInt(32)) + } + case "succeeded": + if r.TryDecodeAsNil() { + x.Succeeded = 0 + } else { + x.Succeeded = int32(r.DecodeInt(32)) + } + case "failed": + if r.TryDecodeAsNil() { + x.Failed = 0 + } else { + x.Failed = int32(r.DecodeInt(32)) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *JobStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj13 int + var yyb13 bool + var yyhl13 bool = l >= 0 + yyj13++ + if yyhl13 { + yyb13 = yyj13 > l + } else { + yyb13 = r.CheckBreak() + } + if yyb13 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Conditions = nil + } else { + yyv14 := &x.Conditions + yym15 := z.DecBinary() + _ = yym15 + if false { + } else { + h.decSliceJobCondition((*[]JobCondition)(yyv14), d) + } + } + yyj13++ + if yyhl13 { + yyb13 = yyj13 > l + } else { + yyb13 = r.CheckBreak() + } + if yyb13 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.StartTime != nil { + x.StartTime = nil + } + } else { + if x.StartTime == nil { + x.StartTime = new(pkg1_unversioned.Time) + } + yym17 := z.DecBinary() + _ = yym17 + if false { + } else if z.HasExtensions() && z.DecExt(x.StartTime) { + } else if yym17 { + z.DecBinaryUnmarshal(x.StartTime) + } else if !yym17 && z.IsJSONHandle() { + z.DecJSONUnmarshal(x.StartTime) + } else { + z.DecFallback(x.StartTime, false) + } + } + yyj13++ + if yyhl13 { + yyb13 = yyj13 > l + } else { + yyb13 = r.CheckBreak() + } + if yyb13 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.CompletionTime != nil { + x.CompletionTime = nil + } + } else { + if x.CompletionTime == nil { + x.CompletionTime = new(pkg1_unversioned.Time) + } + yym19 := z.DecBinary() + _ = yym19 + if false { + } else if z.HasExtensions() && z.DecExt(x.CompletionTime) { + } else if yym19 { + z.DecBinaryUnmarshal(x.CompletionTime) + } else if !yym19 && z.IsJSONHandle() { + z.DecJSONUnmarshal(x.CompletionTime) + } else { + z.DecFallback(x.CompletionTime, false) + } + } + yyj13++ + if yyhl13 { + yyb13 = yyj13 > l + } else { + yyb13 = r.CheckBreak() + } + if yyb13 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Active = 0 + } else { + x.Active = int32(r.DecodeInt(32)) + } + yyj13++ + if yyhl13 { + yyb13 = yyj13 > l + } else { + yyb13 = r.CheckBreak() + } + if yyb13 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Succeeded = 0 + } else { + x.Succeeded = int32(r.DecodeInt(32)) + } + yyj13++ + if yyhl13 { + yyb13 = yyj13 > l + } else { + yyb13 = r.CheckBreak() + } + if yyb13 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Failed = 0 + } else { + x.Failed = int32(r.DecodeInt(32)) + } + for { + yyj13++ + if yyhl13 { + yyb13 = yyj13 > l + } else { + yyb13 = r.CheckBreak() + } + if yyb13 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj13-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x JobConditionType) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x)) + } +} + +func (x *JobConditionType) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + *((*string)(x)) = r.DecodeString() + } +} + +func (x *JobCondition) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [6]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[2] = true + yyq2[3] = true + yyq2[4] = x.Reason != "" + yyq2[5] = x.Message != "" + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(6) + } else { + yynn2 = 2 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + x.Type.CodecEncodeSelf(e) + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("type")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + x.Type.CodecEncodeSelf(e) + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yysf7 := &x.Status + yysf7.CodecEncodeSelf(e) + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("status")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yysf8 := &x.Status + yysf8.CodecEncodeSelf(e) + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yy10 := &x.LastProbeTime + yym11 := z.EncBinary() + _ = yym11 + if false { + } else if z.HasExtensions() && z.EncExt(yy10) { + } else if yym11 { + z.EncBinaryMarshal(yy10) + } else if !yym11 && z.IsJSONHandle() { + z.EncJSONMarshal(yy10) + } else { + z.EncFallback(yy10) + } + } else { + r.EncodeNil() + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("lastProbeTime")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy12 := &x.LastProbeTime + yym13 := z.EncBinary() + _ = yym13 + if false { + } else if z.HasExtensions() && z.EncExt(yy12) { + } else if yym13 { + z.EncBinaryMarshal(yy12) + } else if !yym13 && z.IsJSONHandle() { + z.EncJSONMarshal(yy12) + } else { + z.EncFallback(yy12) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[3] { + yy15 := &x.LastTransitionTime + yym16 := z.EncBinary() + _ = yym16 + if false { + } else if z.HasExtensions() && z.EncExt(yy15) { + } else if yym16 { + z.EncBinaryMarshal(yy15) + } else if !yym16 && z.IsJSONHandle() { + z.EncJSONMarshal(yy15) + } else { + z.EncFallback(yy15) + } + } else { + r.EncodeNil() + } + } else { + if yyq2[3] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("lastTransitionTime")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy17 := &x.LastTransitionTime + yym18 := z.EncBinary() + _ = yym18 + if false { + } else if z.HasExtensions() && z.EncExt(yy17) { + } else if yym18 { + z.EncBinaryMarshal(yy17) + } else if !yym18 && z.IsJSONHandle() { + z.EncJSONMarshal(yy17) + } else { + z.EncFallback(yy17) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[4] { + yym20 := z.EncBinary() + _ = yym20 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Reason)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[4] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("reason")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym21 := z.EncBinary() + _ = yym21 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Reason)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[5] { + yym23 := z.EncBinary() + _ = yym23 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Message)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[5] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("message")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym24 := z.EncBinary() + _ = yym24 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Message)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *JobCondition) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *JobCondition) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "type": + if r.TryDecodeAsNil() { + x.Type = "" + } else { + x.Type = JobConditionType(r.DecodeString()) + } + case "status": + if r.TryDecodeAsNil() { + x.Status = "" + } else { + x.Status = pkg2_v1.ConditionStatus(r.DecodeString()) + } + case "lastProbeTime": + if r.TryDecodeAsNil() { + x.LastProbeTime = pkg1_unversioned.Time{} + } else { + yyv6 := &x.LastProbeTime + yym7 := z.DecBinary() + _ = yym7 + if false { + } else if z.HasExtensions() && z.DecExt(yyv6) { + } else if yym7 { + z.DecBinaryUnmarshal(yyv6) + } else if !yym7 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv6) + } else { + z.DecFallback(yyv6, false) + } + } + case "lastTransitionTime": + if r.TryDecodeAsNil() { + x.LastTransitionTime = pkg1_unversioned.Time{} + } else { + yyv8 := &x.LastTransitionTime + yym9 := z.DecBinary() + _ = yym9 + if false { + } else if z.HasExtensions() && z.DecExt(yyv8) { + } else if yym9 { + z.DecBinaryUnmarshal(yyv8) + } else if !yym9 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv8) + } else { + z.DecFallback(yyv8, false) + } + } + case "reason": + if r.TryDecodeAsNil() { + x.Reason = "" + } else { + x.Reason = string(r.DecodeString()) + } + case "message": + if r.TryDecodeAsNil() { + x.Message = "" + } else { + x.Message = string(r.DecodeString()) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *JobCondition) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj12 int + var yyb12 bool + var yyhl12 bool = l >= 0 + yyj12++ + if yyhl12 { + yyb12 = yyj12 > l + } else { + yyb12 = r.CheckBreak() + } + if yyb12 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Type = "" + } else { + x.Type = JobConditionType(r.DecodeString()) + } + yyj12++ + if yyhl12 { + yyb12 = yyj12 > l + } else { + yyb12 = r.CheckBreak() + } + if yyb12 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Status = "" + } else { + x.Status = pkg2_v1.ConditionStatus(r.DecodeString()) + } + yyj12++ + if yyhl12 { + yyb12 = yyj12 > l + } else { + yyb12 = r.CheckBreak() + } + if yyb12 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.LastProbeTime = pkg1_unversioned.Time{} + } else { + yyv15 := &x.LastProbeTime + yym16 := z.DecBinary() + _ = yym16 + if false { + } else if z.HasExtensions() && z.DecExt(yyv15) { + } else if yym16 { + z.DecBinaryUnmarshal(yyv15) + } else if !yym16 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv15) + } else { + z.DecFallback(yyv15, false) + } + } + yyj12++ + if yyhl12 { + yyb12 = yyj12 > l + } else { + yyb12 = r.CheckBreak() + } + if yyb12 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.LastTransitionTime = pkg1_unversioned.Time{} + } else { + yyv17 := &x.LastTransitionTime + yym18 := z.DecBinary() + _ = yym18 + if false { + } else if z.HasExtensions() && z.DecExt(yyv17) { + } else if yym18 { + z.DecBinaryUnmarshal(yyv17) + } else if !yym18 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv17) + } else { + z.DecFallback(yyv17, false) + } + } + yyj12++ + if yyhl12 { + yyb12 = yyj12 > l + } else { + yyb12 = r.CheckBreak() + } + if yyb12 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Reason = "" + } else { + x.Reason = string(r.DecodeString()) + } + yyj12++ + if yyhl12 { + yyb12 = yyj12 > l + } else { + yyb12 = r.CheckBreak() + } + if yyb12 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Message = "" + } else { + x.Message = string(r.DecodeString()) + } + for { + yyj12++ + if yyhl12 { + yyb12 = yyj12 > l + } else { + yyb12 = r.CheckBreak() + } + if yyb12 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj12-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x *LabelSelector) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [2]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = len(x.MatchLabels) != 0 + yyq2[1] = len(x.MatchExpressions) != 0 + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(2) + } else { + yynn2 = 0 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + if x.MatchLabels == nil { + r.EncodeNil() + } else { + yym4 := z.EncBinary() + _ = yym4 + if false { + } else { + z.F.EncMapStringStringV(x.MatchLabels, false, e) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("matchLabels")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.MatchLabels == nil { + r.EncodeNil() + } else { + yym5 := z.EncBinary() + _ = yym5 + if false { + } else { + z.F.EncMapStringStringV(x.MatchLabels, false, e) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + if x.MatchExpressions == nil { + r.EncodeNil() + } else { + yym7 := z.EncBinary() + _ = yym7 + if false { + } else { + h.encSliceLabelSelectorRequirement(([]LabelSelectorRequirement)(x.MatchExpressions), e) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("matchExpressions")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.MatchExpressions == nil { + r.EncodeNil() + } else { + yym8 := z.EncBinary() + _ = yym8 + if false { + } else { + h.encSliceLabelSelectorRequirement(([]LabelSelectorRequirement)(x.MatchExpressions), e) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *LabelSelector) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *LabelSelector) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "matchLabels": + if r.TryDecodeAsNil() { + x.MatchLabels = nil + } else { + yyv4 := &x.MatchLabels + yym5 := z.DecBinary() + _ = yym5 + if false { + } else { + z.F.DecMapStringStringX(yyv4, false, d) + } + } + case "matchExpressions": + if r.TryDecodeAsNil() { + x.MatchExpressions = nil + } else { + yyv6 := &x.MatchExpressions + yym7 := z.DecBinary() + _ = yym7 + if false { + } else { + h.decSliceLabelSelectorRequirement((*[]LabelSelectorRequirement)(yyv6), d) + } + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *LabelSelector) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj8 int + var yyb8 bool + var yyhl8 bool = l >= 0 + yyj8++ + if yyhl8 { + yyb8 = yyj8 > l + } else { + yyb8 = r.CheckBreak() + } + if yyb8 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.MatchLabels = nil + } else { + yyv9 := &x.MatchLabels + yym10 := z.DecBinary() + _ = yym10 + if false { + } else { + z.F.DecMapStringStringX(yyv9, false, d) + } + } + yyj8++ + if yyhl8 { + yyb8 = yyj8 > l + } else { + yyb8 = r.CheckBreak() + } + if yyb8 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.MatchExpressions = nil + } else { + yyv11 := &x.MatchExpressions + yym12 := z.DecBinary() + _ = yym12 + if false { + } else { + h.decSliceLabelSelectorRequirement((*[]LabelSelectorRequirement)(yyv11), d) + } + } + for { + yyj8++ + if yyhl8 { + yyb8 = yyj8 > l + } else { + yyb8 = r.CheckBreak() + } + if yyb8 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj8-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x *LabelSelectorRequirement) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [3]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[2] = len(x.Values) != 0 + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(3) + } else { + yynn2 = 2 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym4 := z.EncBinary() + _ = yym4 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Key)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("key")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym5 := z.EncBinary() + _ = yym5 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Key)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + x.Operator.CodecEncodeSelf(e) + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("operator")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + x.Operator.CodecEncodeSelf(e) + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + if x.Values == nil { + r.EncodeNil() + } else { + yym10 := z.EncBinary() + _ = yym10 + if false { + } else { + z.F.EncSliceStringV(x.Values, false, e) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("values")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.Values == nil { + r.EncodeNil() + } else { + yym11 := z.EncBinary() + _ = yym11 + if false { + } else { + z.F.EncSliceStringV(x.Values, false, e) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *LabelSelectorRequirement) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *LabelSelectorRequirement) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "key": + if r.TryDecodeAsNil() { + x.Key = "" + } else { + x.Key = string(r.DecodeString()) + } + case "operator": + if r.TryDecodeAsNil() { + x.Operator = "" + } else { + x.Operator = LabelSelectorOperator(r.DecodeString()) + } + case "values": + if r.TryDecodeAsNil() { + x.Values = nil + } else { + yyv6 := &x.Values + yym7 := z.DecBinary() + _ = yym7 + if false { + } else { + z.F.DecSliceStringX(yyv6, false, d) + } + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *LabelSelectorRequirement) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj8 int + var yyb8 bool + var yyhl8 bool = l >= 0 + yyj8++ + if yyhl8 { + yyb8 = yyj8 > l + } else { + yyb8 = r.CheckBreak() + } + if yyb8 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Key = "" + } else { + x.Key = string(r.DecodeString()) + } + yyj8++ + if yyhl8 { + yyb8 = yyj8 > l + } else { + yyb8 = r.CheckBreak() + } + if yyb8 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Operator = "" + } else { + x.Operator = LabelSelectorOperator(r.DecodeString()) + } + yyj8++ + if yyhl8 { + yyb8 = yyj8 > l + } else { + yyb8 = r.CheckBreak() + } + if yyb8 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Values = nil + } else { + yyv11 := &x.Values + yym12 := z.DecBinary() + _ = yym12 + if false { + } else { + z.F.DecSliceStringX(yyv11, false, d) + } + } + for { + yyj8++ + if yyhl8 { + yyb8 = yyj8 > l + } else { + yyb8 = r.CheckBreak() + } + if yyb8 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj8-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x LabelSelectorOperator) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x)) + } +} + +func (x *LabelSelectorOperator) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + *((*string)(x)) = r.DecodeString() + } +} + +func (x codecSelfer1234) encSliceJob(v []Job, e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + r.EncodeArrayStart(len(v)) + for _, yyv1 := range v { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yy2 := &yyv1 + yy2.CodecEncodeSelf(e) + } + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x codecSelfer1234) decSliceJob(v *[]Job, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + + yyv1 := *v + yyh1, yyl1 := z.DecSliceHelperStart() + var yyc1 bool + _ = yyc1 + if yyl1 == 0 { + if yyv1 == nil { + yyv1 = []Job{} + yyc1 = true + } else if len(yyv1) != 0 { + yyv1 = yyv1[:0] + yyc1 = true + } + } else if yyl1 > 0 { + var yyrr1, yyrl1 int + var yyrt1 bool + _, _ = yyrl1, yyrt1 + yyrr1 = yyl1 // len(yyv1) + if yyl1 > cap(yyv1) { + + yyrg1 := len(yyv1) > 0 + yyv21 := yyv1 + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 640) + if yyrt1 { + if yyrl1 <= cap(yyv1) { + yyv1 = yyv1[:yyrl1] + } else { + yyv1 = make([]Job, yyrl1) + } + } else { + yyv1 = make([]Job, yyrl1) + } + yyc1 = true + yyrr1 = len(yyv1) + if yyrg1 { + copy(yyv1, yyv21) + } + } else if yyl1 != len(yyv1) { + yyv1 = yyv1[:yyl1] + yyc1 = true + } + yyj1 := 0 + for ; yyj1 < yyrr1; yyj1++ { + yyh1.ElemContainerState(yyj1) + if r.TryDecodeAsNil() { + yyv1[yyj1] = Job{} + } else { + yyv2 := &yyv1[yyj1] + yyv2.CodecDecodeSelf(d) + } + + } + if yyrt1 { + for ; yyj1 < yyl1; yyj1++ { + yyv1 = append(yyv1, Job{}) + yyh1.ElemContainerState(yyj1) + if r.TryDecodeAsNil() { + yyv1[yyj1] = Job{} + } else { + yyv3 := &yyv1[yyj1] + yyv3.CodecDecodeSelf(d) + } + + } + } + + } else { + yyj1 := 0 + for ; !r.CheckBreak(); yyj1++ { + + if yyj1 >= len(yyv1) { + yyv1 = append(yyv1, Job{}) // var yyz1 Job + yyc1 = true + } + yyh1.ElemContainerState(yyj1) + if yyj1 < len(yyv1) { + if r.TryDecodeAsNil() { + yyv1[yyj1] = Job{} + } else { + yyv4 := &yyv1[yyj1] + yyv4.CodecDecodeSelf(d) + } + + } else { + z.DecSwallow() + } + + } + if yyj1 < len(yyv1) { + yyv1 = yyv1[:yyj1] + yyc1 = true + } else if yyj1 == 0 && yyv1 == nil { + yyv1 = []Job{} + yyc1 = true + } + } + yyh1.End() + if yyc1 { + *v = yyv1 + } +} + +func (x codecSelfer1234) encSliceJobCondition(v []JobCondition, e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + r.EncodeArrayStart(len(v)) + for _, yyv1 := range v { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yy2 := &yyv1 + yy2.CodecEncodeSelf(e) + } + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x codecSelfer1234) decSliceJobCondition(v *[]JobCondition, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + + yyv1 := *v + yyh1, yyl1 := z.DecSliceHelperStart() + var yyc1 bool + _ = yyc1 + if yyl1 == 0 { + if yyv1 == nil { + yyv1 = []JobCondition{} + yyc1 = true + } else if len(yyv1) != 0 { + yyv1 = yyv1[:0] + yyc1 = true + } + } else if yyl1 > 0 { + var yyrr1, yyrl1 int + var yyrt1 bool + _, _ = yyrl1, yyrt1 + yyrr1 = yyl1 // len(yyv1) + if yyl1 > cap(yyv1) { + + yyrg1 := len(yyv1) > 0 + yyv21 := yyv1 + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 112) + if yyrt1 { + if yyrl1 <= cap(yyv1) { + yyv1 = yyv1[:yyrl1] + } else { + yyv1 = make([]JobCondition, yyrl1) + } + } else { + yyv1 = make([]JobCondition, yyrl1) + } + yyc1 = true + yyrr1 = len(yyv1) + if yyrg1 { + copy(yyv1, yyv21) + } + } else if yyl1 != len(yyv1) { + yyv1 = yyv1[:yyl1] + yyc1 = true + } + yyj1 := 0 + for ; yyj1 < yyrr1; yyj1++ { + yyh1.ElemContainerState(yyj1) + if r.TryDecodeAsNil() { + yyv1[yyj1] = JobCondition{} + } else { + yyv2 := &yyv1[yyj1] + yyv2.CodecDecodeSelf(d) + } + + } + if yyrt1 { + for ; yyj1 < yyl1; yyj1++ { + yyv1 = append(yyv1, JobCondition{}) + yyh1.ElemContainerState(yyj1) + if r.TryDecodeAsNil() { + yyv1[yyj1] = JobCondition{} + } else { + yyv3 := &yyv1[yyj1] + yyv3.CodecDecodeSelf(d) + } + + } + } + + } else { + yyj1 := 0 + for ; !r.CheckBreak(); yyj1++ { + + if yyj1 >= len(yyv1) { + yyv1 = append(yyv1, JobCondition{}) // var yyz1 JobCondition + yyc1 = true + } + yyh1.ElemContainerState(yyj1) + if yyj1 < len(yyv1) { + if r.TryDecodeAsNil() { + yyv1[yyj1] = JobCondition{} + } else { + yyv4 := &yyv1[yyj1] + yyv4.CodecDecodeSelf(d) + } + + } else { + z.DecSwallow() + } + + } + if yyj1 < len(yyv1) { + yyv1 = yyv1[:yyj1] + yyc1 = true + } else if yyj1 == 0 && yyv1 == nil { + yyv1 = []JobCondition{} + yyc1 = true + } + } + yyh1.End() + if yyc1 { + *v = yyv1 + } +} + +func (x codecSelfer1234) encSliceLabelSelectorRequirement(v []LabelSelectorRequirement, e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + r.EncodeArrayStart(len(v)) + for _, yyv1 := range v { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yy2 := &yyv1 + yy2.CodecEncodeSelf(e) + } + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x codecSelfer1234) decSliceLabelSelectorRequirement(v *[]LabelSelectorRequirement, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + + yyv1 := *v + yyh1, yyl1 := z.DecSliceHelperStart() + var yyc1 bool + _ = yyc1 + if yyl1 == 0 { + if yyv1 == nil { + yyv1 = []LabelSelectorRequirement{} + yyc1 = true + } else if len(yyv1) != 0 { + yyv1 = yyv1[:0] + yyc1 = true + } + } else if yyl1 > 0 { + var yyrr1, yyrl1 int + var yyrt1 bool + _, _ = yyrl1, yyrt1 + yyrr1 = yyl1 // len(yyv1) + if yyl1 > cap(yyv1) { + + yyrg1 := len(yyv1) > 0 + yyv21 := yyv1 + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 56) + if yyrt1 { + if yyrl1 <= cap(yyv1) { + yyv1 = yyv1[:yyrl1] + } else { + yyv1 = make([]LabelSelectorRequirement, yyrl1) + } + } else { + yyv1 = make([]LabelSelectorRequirement, yyrl1) + } + yyc1 = true + yyrr1 = len(yyv1) + if yyrg1 { + copy(yyv1, yyv21) + } + } else if yyl1 != len(yyv1) { + yyv1 = yyv1[:yyl1] + yyc1 = true + } + yyj1 := 0 + for ; yyj1 < yyrr1; yyj1++ { + yyh1.ElemContainerState(yyj1) + if r.TryDecodeAsNil() { + yyv1[yyj1] = LabelSelectorRequirement{} + } else { + yyv2 := &yyv1[yyj1] + yyv2.CodecDecodeSelf(d) + } + + } + if yyrt1 { + for ; yyj1 < yyl1; yyj1++ { + yyv1 = append(yyv1, LabelSelectorRequirement{}) + yyh1.ElemContainerState(yyj1) + if r.TryDecodeAsNil() { + yyv1[yyj1] = LabelSelectorRequirement{} + } else { + yyv3 := &yyv1[yyj1] + yyv3.CodecDecodeSelf(d) + } + + } + } + + } else { + yyj1 := 0 + for ; !r.CheckBreak(); yyj1++ { + + if yyj1 >= len(yyv1) { + yyv1 = append(yyv1, LabelSelectorRequirement{}) // var yyz1 LabelSelectorRequirement + yyc1 = true + } + yyh1.ElemContainerState(yyj1) + if yyj1 < len(yyv1) { + if r.TryDecodeAsNil() { + yyv1[yyj1] = LabelSelectorRequirement{} + } else { + yyv4 := &yyv1[yyj1] + yyv4.CodecDecodeSelf(d) + } + + } else { + z.DecSwallow() + } + + } + if yyj1 < len(yyv1) { + yyv1 = yyv1[:yyj1] + yyc1 = true + } else if yyj1 == 0 && yyv1 == nil { + yyv1 = []LabelSelectorRequirement{} + yyc1 = true + } + } + yyh1.End() + if yyc1 { + *v = yyv1 + } +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/types.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/types.go new file mode 100644 index 000000000..214ffe6d3 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/types.go @@ -0,0 +1,184 @@ +/* +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 v1 + +import ( + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" +) + +// Job represents the configuration of a single job. +type Job struct { + unversioned.TypeMeta `json:",inline"` + // Standard object's metadata. + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata + v1.ObjectMeta `json:"metadata,omitempty"` + + // Spec is a structure defining the expected behavior of a job. + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status + Spec JobSpec `json:"spec,omitempty"` + + // Status is a structure describing current status of a job. + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status + Status JobStatus `json:"status,omitempty"` +} + +// JobList is a collection of jobs. +type JobList struct { + unversioned.TypeMeta `json:",inline"` + // Standard list metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata + unversioned.ListMeta `json:"metadata,omitempty"` + + // Items is the list of Job. + Items []Job `json:"items"` +} + +// JobSpec describes how the job execution will look like. +type JobSpec struct { + + // Parallelism specifies the maximum desired number of pods the job should + // run at any given time. The actual number of pods running in steady state will + // be less than this number when ((.spec.completions - .status.successful) < .spec.parallelism), + // i.e. when the work left to do is less than max parallelism. + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md + Parallelism *int32 `json:"parallelism,omitempty"` + + // Completions specifies the desired number of successfully finished pods the + // job should be run with. Setting to nil means that the success of any + // pod signals the success of all pods, and allows parallelism to have any positive + // value. Setting to 1 means that parallelism is limited to 1 and the success of that + // pod signals the success of the job. + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md + Completions *int32 `json:"completions,omitempty"` + + // Optional duration in seconds relative to the startTime that the job may be active + // before the system tries to terminate it; value must be positive integer + ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty"` + + // Selector is a label query over pods that should match the pod count. + // Normally, the system sets this field for you. + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors + Selector *LabelSelector `json:"selector,omitempty"` + + // ManualSelector controls generation of pod labels and pod selectors. + // Leave `manualSelector` unset unless you are certain what you are doing. + // When false or unset, the system pick labels unique to this job + // and appends those labels to the pod template. When true, + // the user is responsible for picking unique labels and specifying + // the selector. Failure to pick a unique label may cause this + // and other jobs to not function correctly. However, You may see + // `manualSelector=true` in jobs that were created with the old `extensions/v1beta1` + // API. + // More info: http://releases.k8s.io/release-1.2/docs/design/selector-generation.md + ManualSelector *bool `json:"manualSelector,omitempty"` + + // Template is the object that describes the pod that will be created when + // executing a job. + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md + Template v1.PodTemplateSpec `json:"template"` +} + +// JobStatus represents the current state of a Job. +type JobStatus struct { + + // Conditions represent the latest available observations of an object's current state. + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md + Conditions []JobCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // StartTime represents time when the job was acknowledged by the Job Manager. + // It is not guaranteed to be set in happens-before order across separate operations. + // It is represented in RFC3339 form and is in UTC. + StartTime *unversioned.Time `json:"startTime,omitempty"` + + // CompletionTime represents time when the job was completed. It is not guaranteed to + // be set in happens-before order across separate operations. + // It is represented in RFC3339 form and is in UTC. + CompletionTime *unversioned.Time `json:"completionTime,omitempty"` + + // Active is the number of actively running pods. + Active int32 `json:"active,omitempty"` + + // Succeeded is the number of pods which reached Phase Succeeded. + Succeeded int32 `json:"succeeded,omitempty"` + + // Failed is the number of pods which reached Phase Failed. + Failed int32 `json:"failed,omitempty"` +} + +type JobConditionType string + +// These are valid conditions of a job. +const ( + // JobComplete means the job has completed its execution. + JobComplete JobConditionType = "Complete" + // JobFailed means the job has failed its execution. + JobFailed JobConditionType = "Failed" +) + +// JobCondition describes current state of a job. +type JobCondition struct { + // Type of job condition, Complete or Failed. + Type JobConditionType `json:"type"` + // Status of the condition, one of True, False, Unknown. + Status v1.ConditionStatus `json:"status"` + // Last time the condition was checked. + LastProbeTime unversioned.Time `json:"lastProbeTime,omitempty"` + // Last time the condition transit from one status to another. + LastTransitionTime unversioned.Time `json:"lastTransitionTime,omitempty"` + // (brief) reason for the condition's last transition. + Reason string `json:"reason,omitempty"` + // Human readable message indicating details about last transition. + Message string `json:"message,omitempty"` +} + +// A label selector is a label query over a set of resources. The result of matchLabels and +// matchExpressions are ANDed. An empty label selector matches all objects. A null +// label selector matches no objects. +type LabelSelector struct { + // matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + // map is equivalent to an element of matchExpressions, whose key field is "key", the + // operator is "In", and the values array contains only "value". The requirements are ANDed. + MatchLabels map[string]string `json:"matchLabels,omitempty"` + // matchExpressions is a list of label selector requirements. The requirements are ANDed. + MatchExpressions []LabelSelectorRequirement `json:"matchExpressions,omitempty"` +} + +// A label selector requirement is a selector that contains values, a key, and an operator that +// relates the key and values. +type LabelSelectorRequirement struct { + // key is the label key that the selector applies to. + Key string `json:"key" patchStrategy:"merge" patchMergeKey:"key"` + // operator represents a key's relationship to a set of values. + // Valid operators ard In, NotIn, Exists and DoesNotExist. + Operator LabelSelectorOperator `json:"operator"` + // values is an array of string values. If the operator is In or NotIn, + // the values array must be non-empty. If the operator is Exists or DoesNotExist, + // the values array must be empty. This array is replaced during a strategic + // merge patch. + Values []string `json:"values,omitempty"` +} + +// A label selector operator is the set of operators that can be used in a selector requirement. +type LabelSelectorOperator string + +const ( + LabelSelectorOpIn LabelSelectorOperator = "In" + LabelSelectorOpNotIn LabelSelectorOperator = "NotIn" + LabelSelectorOpExists LabelSelectorOperator = "Exists" + LabelSelectorOpDoesNotExist LabelSelectorOperator = "DoesNotExist" +) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/types_swagger_doc_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/types_swagger_doc_generated.go new file mode 100644 index 000000000..32e278eac --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/batch/v1/types_swagger_doc_generated.go @@ -0,0 +1,114 @@ +/* +Copyright 2015 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 v1 + +// This file contains a collection of methods that can be used from go-restful to +// generate Swagger API documentation for its models. Please read this PR for more +// information on the implementation: https://github.com/emicklei/go-restful/pull/215 +// +// TODOs are ignored from the parser (e.g. TODO(andronat):... || TODO:...) if and only if +// they are on one line! For multiple line or blocks that you want to ignore use ---. +// Any context after a --- is ignored. +// +// Those methods can be generated by using hack/update-generated-swagger-docs.sh + +// AUTO-GENERATED FUNCTIONS START HERE +var map_Job = map[string]string{ + "": "Job represents the configuration of a single job.", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "Spec is a structure defining the expected behavior of a job. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", + "status": "Status is a structure describing current status of a job. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", +} + +func (Job) SwaggerDoc() map[string]string { + return map_Job +} + +var map_JobCondition = map[string]string{ + "": "JobCondition describes current state of a job.", + "type": "Type of job condition, Complete or Failed.", + "status": "Status of the condition, one of True, False, Unknown.", + "lastProbeTime": "Last time the condition was checked.", + "lastTransitionTime": "Last time the condition transit from one status to another.", + "reason": "(brief) reason for the condition's last transition.", + "message": "Human readable message indicating details about last transition.", +} + +func (JobCondition) SwaggerDoc() map[string]string { + return map_JobCondition +} + +var map_JobList = map[string]string{ + "": "JobList is a collection of jobs.", + "metadata": "Standard list metadata More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "items": "Items is the list of Job.", +} + +func (JobList) SwaggerDoc() map[string]string { + return map_JobList +} + +var map_JobSpec = map[string]string{ + "": "JobSpec describes how the job execution will look like.", + "parallelism": "Parallelism specifies the maximum desired number of pods the job should run at any given time. The actual number of pods running in steady state will be less than this number when ((.spec.completions - .status.successful) < .spec.parallelism), i.e. when the work left to do is less than max parallelism. More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md", + "completions": "Completions specifies the desired number of successfully finished pods the job should be run with. Setting to nil means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job. More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md", + "activeDeadlineSeconds": "Optional duration in seconds relative to the startTime that the job may be active before the system tries to terminate it; value must be positive integer", + "selector": "Selector is a label query over pods that should match the pod count. Normally, the system sets this field for you. More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors", + "manualSelector": "ManualSelector controls generation of pod labels and pod selectors. Leave `manualSelector` unset unless you are certain what you are doing. When false or unset, the system pick labels unique to this job and appends those labels to the pod template. When true, the user is responsible for picking unique labels and specifying the selector. Failure to pick a unique label may cause this and other jobs to not function correctly. However, You may see `manualSelector=true` in jobs that were created with the old `extensions/v1beta1` API. More info: http://releases.k8s.io/release-1.2/docs/design/selector-generation.md", + "template": "Template is the object that describes the pod that will be created when executing a job. More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md", +} + +func (JobSpec) SwaggerDoc() map[string]string { + return map_JobSpec +} + +var map_JobStatus = map[string]string{ + "": "JobStatus represents the current state of a Job.", + "conditions": "Conditions represent the latest available observations of an object's current state. More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md", + "startTime": "StartTime represents time when the job was acknowledged by the Job Manager. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC.", + "completionTime": "CompletionTime represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC.", + "active": "Active is the number of actively running pods.", + "succeeded": "Succeeded is the number of pods which reached Phase Succeeded.", + "failed": "Failed is the number of pods which reached Phase Failed.", +} + +func (JobStatus) SwaggerDoc() map[string]string { + return map_JobStatus +} + +var map_LabelSelector = map[string]string{ + "": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", + "matchLabels": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + "matchExpressions": "matchExpressions is a list of label selector requirements. The requirements are ANDed.", +} + +func (LabelSelector) SwaggerDoc() map[string]string { + return map_LabelSelector +} + +var map_LabelSelectorRequirement = map[string]string{ + "": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + "key": "key is the label key that the selector applies to.", + "operator": "operator represents a key's relationship to a set of values. Valid operators ard In, NotIn, Exists and DoesNotExist.", + "values": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", +} + +func (LabelSelectorRequirement) SwaggerDoc() map[string]string { + return map_LabelSelectorRequirement +} + +// AUTO-GENERATED FUNCTIONS END HERE diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/helpers_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/helpers_test.go deleted file mode 100644 index 7aece8257..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/helpers_test.go +++ /dev/null @@ -1,71 +0,0 @@ -/* -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 componentconfig - -import ( - "strings" - "testing" - - "github.com/spf13/pflag" -) - -func TestIPVar(t *testing.T) { - defaultIP := "0.0.0.0" - cases := []struct { - argc string - expectErr bool - expectVal string - }{ - - { - argc: "blah --ip=1.2.3.4", - expectVal: "1.2.3.4", - }, - { - argc: "blah --ip=1.2.3.4a", - expectErr: true, - expectVal: defaultIP, - }, - } - for _, c := range cases { - fs := pflag.NewFlagSet("blah", pflag.PanicOnError) - ip := defaultIP - fs.Var(IPVar{&ip}, "ip", "the ip") - - var err error - func() { - defer func() { - if r := recover(); r != nil { - err = r.(error) - } - }() - fs.Parse(strings.Split(c.argc, " ")) - }() - - if c.expectErr && err == nil { - t.Errorf("did not observe an expected error") - continue - } - if !c.expectErr && err != nil { - t.Errorf("observed an unexpected error") - continue - } - if c.expectVal != ip { - t.Errorf("unexpected ip: expected %q, saw %q", c.expectVal, ip) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/install/install_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/install/install_test.go deleted file mode 100644 index 940e17567..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/install/install_test.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright 2014 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 install - -import ( - "encoding/json" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apimachinery/registered" - "k8s.io/kubernetes/pkg/apis/componentconfig" - "k8s.io/kubernetes/pkg/runtime" -) - -func TestCodec(t *testing.T) { - daemonSet := componentconfig.KubeProxyConfiguration{} - // We do want to use package registered rather than testapi here, because we - // want to test if the package install and package registered work as expected. - data, err := runtime.Encode(api.Codecs.LegacyCodec(registered.GroupOrDie(componentconfig.GroupName).GroupVersion), &daemonSet) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - other := componentconfig.KubeProxyConfiguration{} - if err := json.Unmarshal(data, &other); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if other.APIVersion != registered.GroupOrDie(componentconfig.GroupName).GroupVersion.String() || other.Kind != "KubeProxyConfiguration" { - t.Errorf("unexpected unmarshalled object %#v", other) - } -} - -func TestInterfacesFor(t *testing.T) { - if _, err := registered.GroupOrDie(componentconfig.GroupName).InterfacesFor(componentconfig.SchemeGroupVersion); err == nil { - t.Fatalf("unexpected non-error: %v", err) - } - for i, version := range registered.GroupOrDie(componentconfig.GroupName).GroupVersions { - if vi, err := registered.GroupOrDie(componentconfig.GroupName).InterfacesFor(version); err != nil || vi == nil { - t.Fatalf("%d: unexpected result: %v", i, err) - } - } -} - -func TestRESTMapper(t *testing.T) { - gv := unversioned.GroupVersion{Group: componentconfig.GroupName, Version: "v1alpha1"} - proxyGVK := gv.WithKind("KubeProxyConfiguration") - - if gvk, err := registered.GroupOrDie(componentconfig.GroupName).RESTMapper.KindFor(gv.WithResource("kubeproxyconfiguration")); err != nil || gvk != proxyGVK { - t.Errorf("unexpected version mapping: %v %v", gvk, err) - } - - if m, err := registered.GroupOrDie(componentconfig.GroupName).RESTMapper.RESTMapping(proxyGVK.GroupKind(), ""); err != nil || m.GroupVersionKind != proxyGVK || m.Resource != "kubeproxyconfigurations" { - t.Errorf("unexpected version mapping: %#v %v", m, err) - } - - for _, version := range registered.GroupOrDie(componentconfig.GroupName).GroupVersions { - mapping, err := registered.GroupOrDie(componentconfig.GroupName).RESTMapper.RESTMapping(proxyGVK.GroupKind(), version.Version) - if err != nil { - t.Errorf("unexpected error: %v", err) - continue - } - - if mapping.Resource != "kubeproxyconfigurations" { - t.Errorf("incorrect resource name: %#v", mapping) - } - if mapping.GroupVersionKind.GroupVersion() != version { - t.Errorf("incorrect groupVersion: %v", mapping) - } - - interfaces, _ := registered.GroupOrDie(componentconfig.GroupName).InterfacesFor(version) - if mapping.ObjectConvertor != interfaces.ObjectConvertor { - t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/types.generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/types.generated.go index 445449a2c..f3f54626b 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/types.generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/types.generated.go @@ -1094,6 +1094,32 @@ func (x *ProxyMode) CodecDecodeSelf(d *codec1978.Decoder) { } } +func (x HairpinMode) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x)) + } +} + +func (x *HairpinMode) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + *((*string)(x)) = r.DecodeString() + } +} + func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) @@ -1108,7 +1134,7 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [77]bool + var yyq2 [78]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false yyq2[47] = x.CloudProvider != "" @@ -1119,13 +1145,13 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { yyq2[52] = x.CgroupRoot != "" yyq2[54] = x.RktPath != "" yyq2[56] = x.RktStage1Image != "" - yyq2[72] = true - yyq2[73] = x.NodeIP != "" + yyq2[73] = true + yyq2[74] = x.NodeIP != "" var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(77) + r.EncodeArrayStart(78) } else { - yynn2 = 67 + yynn2 = 68 for _, b := range yyq2 { if b { yynn2++ @@ -2354,17 +2380,17 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym194 if false { } else { - r.EncodeBool(bool(x.HairpinMode)) + r.EncodeString(codecSelferC_UTF81234, string(x.HairpinMode)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("configureHairpinMode")) + r.EncodeString(codecSelferC_UTF81234, string("hairpinMode")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym195 := z.EncBinary() _ = yym195 if false { } else { - r.EncodeBool(bool(x.HairpinMode)) + r.EncodeString(codecSelferC_UTF81234, string(x.HairpinMode)) } } if yyr2 || yy2arr2 { @@ -2373,17 +2399,17 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym197 if false { } else { - r.EncodeInt(int64(x.MaxPods)) + r.EncodeBool(bool(x.BabysitDaemons)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("maxPods")) + r.EncodeString(codecSelferC_UTF81234, string("babysitDaemons")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym198 := z.EncBinary() _ = yym198 if false { } else { - r.EncodeInt(int64(x.MaxPods)) + r.EncodeBool(bool(x.BabysitDaemons)) } } if yyr2 || yy2arr2 { @@ -2392,17 +2418,17 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym200 if false { } else { - r.EncodeString(codecSelferC_UTF81234, string(x.DockerExecHandlerName)) + r.EncodeInt(int64(x.MaxPods)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("dockerExecHandlerName")) + r.EncodeString(codecSelferC_UTF81234, string("maxPods")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym201 := z.EncBinary() _ = yym201 if false { } else { - r.EncodeString(codecSelferC_UTF81234, string(x.DockerExecHandlerName)) + r.EncodeInt(int64(x.MaxPods)) } } if yyr2 || yy2arr2 { @@ -2411,17 +2437,17 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym203 if false { } else { - r.EncodeString(codecSelferC_UTF81234, string(x.PodCIDR)) + r.EncodeString(codecSelferC_UTF81234, string(x.DockerExecHandlerName)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("podCIDR")) + r.EncodeString(codecSelferC_UTF81234, string("dockerExecHandlerName")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym204 := z.EncBinary() _ = yym204 if false { } else { - r.EncodeString(codecSelferC_UTF81234, string(x.PodCIDR)) + r.EncodeString(codecSelferC_UTF81234, string(x.DockerExecHandlerName)) } } if yyr2 || yy2arr2 { @@ -2430,17 +2456,17 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym206 if false { } else { - r.EncodeString(codecSelferC_UTF81234, string(x.ResolverConfig)) + r.EncodeString(codecSelferC_UTF81234, string(x.PodCIDR)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("resolvConf")) + r.EncodeString(codecSelferC_UTF81234, string("podCIDR")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym207 := z.EncBinary() _ = yym207 if false { } else { - r.EncodeString(codecSelferC_UTF81234, string(x.ResolverConfig)) + r.EncodeString(codecSelferC_UTF81234, string(x.PodCIDR)) } } if yyr2 || yy2arr2 { @@ -2449,17 +2475,17 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym209 if false { } else { - r.EncodeBool(bool(x.CPUCFSQuota)) + r.EncodeString(codecSelferC_UTF81234, string(x.ResolverConfig)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("cpuCFSQuota")) + r.EncodeString(codecSelferC_UTF81234, string("resolvConf")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym210 := z.EncBinary() _ = yym210 if false { } else { - r.EncodeBool(bool(x.CPUCFSQuota)) + r.EncodeString(codecSelferC_UTF81234, string(x.ResolverConfig)) } } if yyr2 || yy2arr2 { @@ -2468,17 +2494,17 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym212 if false { } else { - r.EncodeBool(bool(x.Containerized)) + r.EncodeBool(bool(x.CPUCFSQuota)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("containerized")) + r.EncodeString(codecSelferC_UTF81234, string("cpuCFSQuota")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym213 := z.EncBinary() _ = yym213 if false { } else { - r.EncodeBool(bool(x.Containerized)) + r.EncodeBool(bool(x.CPUCFSQuota)) } } if yyr2 || yy2arr2 { @@ -2487,17 +2513,17 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym215 if false { } else { - r.EncodeUint(uint64(x.MaxOpenFiles)) + r.EncodeBool(bool(x.Containerized)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("maxOpenFiles")) + r.EncodeString(codecSelferC_UTF81234, string("containerized")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym216 := z.EncBinary() _ = yym216 if false { } else { - r.EncodeUint(uint64(x.MaxOpenFiles)) + r.EncodeBool(bool(x.Containerized)) } } if yyr2 || yy2arr2 { @@ -2506,17 +2532,17 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym218 if false { } else { - r.EncodeBool(bool(x.ReconcileCIDR)) + r.EncodeUint(uint64(x.MaxOpenFiles)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("reconcileCIDR")) + r.EncodeString(codecSelferC_UTF81234, string("maxOpenFiles")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym219 := z.EncBinary() _ = yym219 if false { } else { - r.EncodeBool(bool(x.ReconcileCIDR)) + r.EncodeUint(uint64(x.MaxOpenFiles)) } } if yyr2 || yy2arr2 { @@ -2525,17 +2551,17 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym221 if false { } else { - r.EncodeBool(bool(x.RegisterSchedulable)) + r.EncodeBool(bool(x.ReconcileCIDR)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("registerSchedulable")) + r.EncodeString(codecSelferC_UTF81234, string("reconcileCIDR")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym222 := z.EncBinary() _ = yym222 if false { } else { - r.EncodeBool(bool(x.RegisterSchedulable)) + r.EncodeBool(bool(x.ReconcileCIDR)) } } if yyr2 || yy2arr2 { @@ -2544,17 +2570,17 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym224 if false { } else { - r.EncodeFloat32(float32(x.KubeAPIQPS)) + r.EncodeBool(bool(x.RegisterSchedulable)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("kubeAPIQPS")) + r.EncodeString(codecSelferC_UTF81234, string("registerSchedulable")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym225 := z.EncBinary() _ = yym225 if false { } else { - r.EncodeFloat32(float32(x.KubeAPIQPS)) + r.EncodeBool(bool(x.RegisterSchedulable)) } } if yyr2 || yy2arr2 { @@ -2563,17 +2589,17 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym227 if false { } else { - r.EncodeInt(int64(x.KubeAPIBurst)) + r.EncodeFloat32(float32(x.KubeAPIQPS)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("kubeAPIBurst")) + r.EncodeString(codecSelferC_UTF81234, string("kubeAPIQPS")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym228 := z.EncBinary() _ = yym228 if false { } else { - r.EncodeInt(int64(x.KubeAPIBurst)) + r.EncodeFloat32(float32(x.KubeAPIQPS)) } } if yyr2 || yy2arr2 { @@ -2582,17 +2608,17 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym230 if false { } else { - r.EncodeBool(bool(x.SerializeImagePulls)) + r.EncodeInt(int64(x.KubeAPIBurst)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("serializeImagePulls")) + r.EncodeString(codecSelferC_UTF81234, string("kubeAPIBurst")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym231 := z.EncBinary() _ = yym231 if false { } else { - r.EncodeBool(bool(x.SerializeImagePulls)) + r.EncodeInt(int64(x.KubeAPIBurst)) } } if yyr2 || yy2arr2 { @@ -2600,6 +2626,25 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { yym233 := z.EncBinary() _ = yym233 if false { + } else { + r.EncodeBool(bool(x.SerializeImagePulls)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("serializeImagePulls")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym234 := z.EncBinary() + _ = yym234 + if false { + } else { + r.EncodeBool(bool(x.SerializeImagePulls)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym236 := z.EncBinary() + _ = yym236 + if false { } else { r.EncodeBool(bool(x.ExperimentalFlannelOverlay)) } @@ -2607,8 +2652,8 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("experimentalFlannelOverlay")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym234 := z.EncBinary() - _ = yym234 + yym237 := z.EncBinary() + _ = yym237 if false { } else { r.EncodeBool(bool(x.ExperimentalFlannelOverlay)) @@ -2616,42 +2661,42 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[72] { - yy236 := &x.OutOfDiskTransitionFrequency - yym237 := z.EncBinary() - _ = yym237 + if yyq2[73] { + yy239 := &x.OutOfDiskTransitionFrequency + yym240 := z.EncBinary() + _ = yym240 if false { - } else if z.HasExtensions() && z.EncExt(yy236) { - } else if !yym237 && z.IsJSONHandle() { - z.EncJSONMarshal(yy236) + } else if z.HasExtensions() && z.EncExt(yy239) { + } else if !yym240 && z.IsJSONHandle() { + z.EncJSONMarshal(yy239) } else { - z.EncFallback(yy236) + z.EncFallback(yy239) } } else { r.EncodeNil() } } else { - if yyq2[72] { + if yyq2[73] { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("outOfDiskTransitionFrequency")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy238 := &x.OutOfDiskTransitionFrequency - yym239 := z.EncBinary() - _ = yym239 + yy241 := &x.OutOfDiskTransitionFrequency + yym242 := z.EncBinary() + _ = yym242 if false { - } else if z.HasExtensions() && z.EncExt(yy238) { - } else if !yym239 && z.IsJSONHandle() { - z.EncJSONMarshal(yy238) + } else if z.HasExtensions() && z.EncExt(yy241) { + } else if !yym242 && z.IsJSONHandle() { + z.EncJSONMarshal(yy241) } else { - z.EncFallback(yy238) + z.EncFallback(yy241) } } } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[73] { - yym241 := z.EncBinary() - _ = yym241 + if yyq2[74] { + yym244 := z.EncBinary() + _ = yym244 if false { } else { r.EncodeString(codecSelferC_UTF81234, string(x.NodeIP)) @@ -2660,12 +2705,12 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { r.EncodeString(codecSelferC_UTF81234, "") } } else { - if yyq2[73] { + if yyq2[74] { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("nodeIP")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym242 := z.EncBinary() - _ = yym242 + yym245 := z.EncBinary() + _ = yym245 if false { } else { r.EncodeString(codecSelferC_UTF81234, string(x.NodeIP)) @@ -2677,8 +2722,8 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { if x.NodeLabels == nil { r.EncodeNil() } else { - yym244 := z.EncBinary() - _ = yym244 + yym247 := z.EncBinary() + _ = yym247 if false { } else { z.F.EncMapStringStringV(x.NodeLabels, false, e) @@ -2691,8 +2736,8 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { if x.NodeLabels == nil { r.EncodeNil() } else { - yym245 := z.EncBinary() - _ = yym245 + yym248 := z.EncBinary() + _ = yym248 if false { } else { z.F.EncMapStringStringV(x.NodeLabels, false, e) @@ -2701,8 +2746,8 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym247 := z.EncBinary() - _ = yym247 + yym250 := z.EncBinary() + _ = yym250 if false { } else { r.EncodeString(codecSelferC_UTF81234, string(x.NonMasqueradeCIDR)) @@ -2711,8 +2756,8 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("nonMasqueradeCIDR")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym248 := z.EncBinary() - _ = yym248 + yym251 := z.EncBinary() + _ = yym251 if false { } else { r.EncodeString(codecSelferC_UTF81234, string(x.NonMasqueradeCIDR)) @@ -2720,8 +2765,8 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym250 := z.EncBinary() - _ = yym250 + yym253 := z.EncBinary() + _ = yym253 if false { } else { r.EncodeBool(bool(x.EnableCustomMetrics)) @@ -2730,8 +2775,8 @@ func (x *KubeletConfiguration) CodecEncodeSelf(e *codec1978.Encoder) { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("enableCustomMetrics")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym251 := z.EncBinary() - _ = yym251 + yym254 := z.EncBinary() + _ = yym254 if false { } else { r.EncodeBool(bool(x.EnableCustomMetrics)) @@ -3218,11 +3263,17 @@ func (x *KubeletConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.Decode } else { x.ConfigureCBR0 = bool(r.DecodeBool()) } - case "configureHairpinMode": + case "hairpinMode": if r.TryDecodeAsNil() { - x.HairpinMode = false + x.HairpinMode = "" } else { - x.HairpinMode = bool(r.DecodeBool()) + x.HairpinMode = string(r.DecodeString()) + } + case "babysitDaemons": + if r.TryDecodeAsNil() { + x.BabysitDaemons = false + } else { + x.BabysitDaemons = bool(r.DecodeBool()) } case "maxPods": if r.TryDecodeAsNil() { @@ -3306,15 +3357,15 @@ func (x *KubeletConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.Decode if r.TryDecodeAsNil() { x.OutOfDiskTransitionFrequency = pkg1_unversioned.Duration{} } else { - yyv84 := &x.OutOfDiskTransitionFrequency - yym85 := z.DecBinary() - _ = yym85 + yyv85 := &x.OutOfDiskTransitionFrequency + yym86 := z.DecBinary() + _ = yym86 if false { - } else if z.HasExtensions() && z.DecExt(yyv84) { - } else if !yym85 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv84) + } else if z.HasExtensions() && z.DecExt(yyv85) { + } else if !yym86 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv85) } else { - z.DecFallback(yyv84, false) + z.DecFallback(yyv85, false) } } case "nodeIP": @@ -3327,12 +3378,12 @@ func (x *KubeletConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.Decode if r.TryDecodeAsNil() { x.NodeLabels = nil } else { - yyv87 := &x.NodeLabels - yym88 := z.DecBinary() - _ = yym88 + yyv88 := &x.NodeLabels + yym89 := z.DecBinary() + _ = yym89 if false { } else { - z.F.DecMapStringStringX(yyv87, false, d) + z.F.DecMapStringStringX(yyv88, false, d) } } case "nonMasqueradeCIDR": @@ -3358,16 +3409,16 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj91 int - var yyb91 bool - var yyhl91 bool = l >= 0 - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + var yyj92 int + var yyb92 bool + var yyhl92 bool = l >= 0 + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3377,13 +3428,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.Config = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3391,24 +3442,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco if r.TryDecodeAsNil() { x.SyncFrequency = pkg1_unversioned.Duration{} } else { - yyv93 := &x.SyncFrequency - yym94 := z.DecBinary() - _ = yym94 + yyv94 := &x.SyncFrequency + yym95 := z.DecBinary() + _ = yym95 if false { - } else if z.HasExtensions() && z.DecExt(yyv93) { - } else if !yym94 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv93) + } else if z.HasExtensions() && z.DecExt(yyv94) { + } else if !yym95 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv94) } else { - z.DecFallback(yyv93, false) + z.DecFallback(yyv94, false) } } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3416,24 +3467,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco if r.TryDecodeAsNil() { x.FileCheckFrequency = pkg1_unversioned.Duration{} } else { - yyv95 := &x.FileCheckFrequency - yym96 := z.DecBinary() - _ = yym96 + yyv96 := &x.FileCheckFrequency + yym97 := z.DecBinary() + _ = yym97 if false { - } else if z.HasExtensions() && z.DecExt(yyv95) { - } else if !yym96 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv95) + } else if z.HasExtensions() && z.DecExt(yyv96) { + } else if !yym97 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv96) } else { - z.DecFallback(yyv95, false) + z.DecFallback(yyv96, false) } } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3441,24 +3492,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco if r.TryDecodeAsNil() { x.HTTPCheckFrequency = pkg1_unversioned.Duration{} } else { - yyv97 := &x.HTTPCheckFrequency - yym98 := z.DecBinary() - _ = yym98 + yyv98 := &x.HTTPCheckFrequency + yym99 := z.DecBinary() + _ = yym99 if false { - } else if z.HasExtensions() && z.DecExt(yyv97) { - } else if !yym98 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv97) + } else if z.HasExtensions() && z.DecExt(yyv98) { + } else if !yym99 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv98) } else { - z.DecFallback(yyv97, false) + z.DecFallback(yyv98, false) } } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3468,13 +3519,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.ManifestURL = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3484,13 +3535,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.ManifestURLHeader = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3500,13 +3551,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.EnableServer = bool(r.DecodeBool()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3516,13 +3567,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.Address = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3532,13 +3583,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.Port = uint(r.DecodeUint(codecSelferBitsize1234)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3548,13 +3599,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.ReadOnlyPort = uint(r.DecodeUint(codecSelferBitsize1234)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3564,13 +3615,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.TLSCertFile = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3580,13 +3631,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.TLSPrivateKeyFile = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3596,13 +3647,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.CertDirectory = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3612,13 +3663,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.HostnameOverride = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3628,13 +3679,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.PodInfraContainerImage = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3644,13 +3695,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.DockerEndpoint = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3660,13 +3711,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.RootDirectory = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3676,13 +3727,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.AllowPrivileged = bool(r.DecodeBool()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3692,13 +3743,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.HostNetworkSources = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3708,13 +3759,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.HostPIDSources = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3724,13 +3775,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.HostIPCSources = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3740,13 +3791,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.RegistryPullQPS = float64(r.DecodeFloat(false)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3756,13 +3807,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.RegistryBurst = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3772,13 +3823,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.EventRecordQPS = float32(r.DecodeFloat(true)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3788,13 +3839,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.EventBurst = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3804,13 +3855,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.EnableDebuggingHandlers = bool(r.DecodeBool()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3818,24 +3869,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco if r.TryDecodeAsNil() { x.MinimumGCAge = pkg1_unversioned.Duration{} } else { - yyv121 := &x.MinimumGCAge - yym122 := z.DecBinary() - _ = yym122 + yyv122 := &x.MinimumGCAge + yym123 := z.DecBinary() + _ = yym123 if false { - } else if z.HasExtensions() && z.DecExt(yyv121) { - } else if !yym122 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv121) + } else if z.HasExtensions() && z.DecExt(yyv122) { + } else if !yym123 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv122) } else { - z.DecFallback(yyv121, false) + z.DecFallback(yyv122, false) } } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3845,13 +3896,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.MaxPerPodContainerCount = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3861,13 +3912,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.MaxContainerCount = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3877,13 +3928,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.CAdvisorPort = uint(r.DecodeUint(codecSelferBitsize1234)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3893,13 +3944,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.HealthzPort = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3909,13 +3960,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.HealthzBindAddress = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3925,13 +3976,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.OOMScoreAdj = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3941,13 +3992,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.RegisterNode = bool(r.DecodeBool()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3957,13 +4008,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.ClusterDomain = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3973,13 +4024,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.MasterServiceNamespace = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -3989,13 +4040,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.ClusterDNS = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4003,24 +4054,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco if r.TryDecodeAsNil() { x.StreamingConnectionIdleTimeout = pkg1_unversioned.Duration{} } else { - yyv133 := &x.StreamingConnectionIdleTimeout - yym134 := z.DecBinary() - _ = yym134 + yyv134 := &x.StreamingConnectionIdleTimeout + yym135 := z.DecBinary() + _ = yym135 if false { - } else if z.HasExtensions() && z.DecExt(yyv133) { - } else if !yym134 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv133) + } else if z.HasExtensions() && z.DecExt(yyv134) { + } else if !yym135 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv134) } else { - z.DecFallback(yyv133, false) + z.DecFallback(yyv134, false) } } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4028,24 +4079,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco if r.TryDecodeAsNil() { x.NodeStatusUpdateFrequency = pkg1_unversioned.Duration{} } else { - yyv135 := &x.NodeStatusUpdateFrequency - yym136 := z.DecBinary() - _ = yym136 + yyv136 := &x.NodeStatusUpdateFrequency + yym137 := z.DecBinary() + _ = yym137 if false { - } else if z.HasExtensions() && z.DecExt(yyv135) { - } else if !yym136 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv135) + } else if z.HasExtensions() && z.DecExt(yyv136) { + } else if !yym137 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv136) } else { - z.DecFallback(yyv135, false) + z.DecFallback(yyv136, false) } } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4053,24 +4104,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco if r.TryDecodeAsNil() { x.ImageMinimumGCAge = pkg1_unversioned.Duration{} } else { - yyv137 := &x.ImageMinimumGCAge - yym138 := z.DecBinary() - _ = yym138 + yyv138 := &x.ImageMinimumGCAge + yym139 := z.DecBinary() + _ = yym139 if false { - } else if z.HasExtensions() && z.DecExt(yyv137) { - } else if !yym138 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv137) + } else if z.HasExtensions() && z.DecExt(yyv138) { + } else if !yym139 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv138) } else { - z.DecFallback(yyv137, false) + z.DecFallback(yyv138, false) } } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4080,13 +4131,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.ImageGCHighThresholdPercent = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4096,13 +4147,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.ImageGCLowThresholdPercent = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4112,13 +4163,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.LowDiskSpaceThresholdMB = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4126,24 +4177,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco if r.TryDecodeAsNil() { x.VolumeStatsAggPeriod = pkg1_unversioned.Duration{} } else { - yyv142 := &x.VolumeStatsAggPeriod - yym143 := z.DecBinary() - _ = yym143 + yyv143 := &x.VolumeStatsAggPeriod + yym144 := z.DecBinary() + _ = yym144 if false { - } else if z.HasExtensions() && z.DecExt(yyv142) { - } else if !yym143 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv142) + } else if z.HasExtensions() && z.DecExt(yyv143) { + } else if !yym144 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv143) } else { - z.DecFallback(yyv142, false) + z.DecFallback(yyv143, false) } } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4153,13 +4204,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.NetworkPluginName = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4169,13 +4220,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.NetworkPluginDir = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4185,13 +4236,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.VolumePluginDir = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4201,13 +4252,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.CloudProvider = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4217,13 +4268,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.CloudConfigFile = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4233,13 +4284,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.KubeletCgroups = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4249,13 +4300,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.RuntimeCgroups = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4265,13 +4316,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.SystemCgroups = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4281,13 +4332,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.CgroupRoot = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4297,13 +4348,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.ContainerRuntime = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4313,13 +4364,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.RktPath = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4329,13 +4380,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.LockFilePath = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4345,13 +4396,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.RktStage1Image = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4361,29 +4412,45 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.ConfigureCBR0 = bool(r.DecodeBool()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.HairpinMode = false + x.HairpinMode = "" } else { - x.HairpinMode = bool(r.DecodeBool()) + x.HairpinMode = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.BabysitDaemons = false + } else { + x.BabysitDaemons = bool(r.DecodeBool()) + } + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l + } else { + yyb92 = r.CheckBreak() + } + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4393,13 +4460,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.MaxPods = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4409,13 +4476,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.DockerExecHandlerName = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4425,13 +4492,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.PodCIDR = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4441,13 +4508,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.ResolverConfig = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4457,13 +4524,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.CPUCFSQuota = bool(r.DecodeBool()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4473,13 +4540,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.Containerized = bool(r.DecodeBool()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4489,13 +4556,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.MaxOpenFiles = uint64(r.DecodeUint(64)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4505,13 +4572,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.ReconcileCIDR = bool(r.DecodeBool()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4521,13 +4588,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.RegisterSchedulable = bool(r.DecodeBool()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4537,13 +4604,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.KubeAPIQPS = float32(r.DecodeFloat(true)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4553,13 +4620,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.KubeAPIBurst = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4569,13 +4636,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.SerializeImagePulls = bool(r.DecodeBool()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4585,13 +4652,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.ExperimentalFlannelOverlay = bool(r.DecodeBool()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4599,24 +4666,24 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco if r.TryDecodeAsNil() { x.OutOfDiskTransitionFrequency = pkg1_unversioned.Duration{} } else { - yyv172 := &x.OutOfDiskTransitionFrequency - yym173 := z.DecBinary() - _ = yym173 + yyv174 := &x.OutOfDiskTransitionFrequency + yym175 := z.DecBinary() + _ = yym175 if false { - } else if z.HasExtensions() && z.DecExt(yyv172) { - } else if !yym173 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv172) + } else if z.HasExtensions() && z.DecExt(yyv174) { + } else if !yym175 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv174) } else { - z.DecFallback(yyv172, false) + z.DecFallback(yyv174, false) } } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4626,13 +4693,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.NodeIP = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4640,21 +4707,21 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco if r.TryDecodeAsNil() { x.NodeLabels = nil } else { - yyv175 := &x.NodeLabels - yym176 := z.DecBinary() - _ = yym176 + yyv177 := &x.NodeLabels + yym178 := z.DecBinary() + _ = yym178 if false { } else { - z.F.DecMapStringStringX(yyv175, false, d) + z.F.DecMapStringStringX(yyv177, false, d) } } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4664,13 +4731,13 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco } else { x.NonMasqueradeCIDR = string(r.DecodeString()) } - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -4681,17 +4748,17 @@ func (x *KubeletConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Deco x.EnableCustomMetrics = bool(r.DecodeBool()) } for { - yyj91++ - if yyhl91 { - yyb91 = yyj91 > l + yyj92++ + if yyhl92 { + yyb92 = yyj92 > l } else { - yyb91 = r.CheckBreak() + yyb92 = r.CheckBreak() } - if yyb91 { + if yyb92 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj91-1, "") + z.DecStructFieldNotFound(yyj92-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -5653,16 +5720,16 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [40]bool + var yyq2 [43]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false - yyq2[38] = x.Kind != "" - yyq2[39] = x.APIVersion != "" + yyq2[41] = x.Kind != "" + yyq2[42] = x.APIVersion != "" var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(40) + r.EncodeArrayStart(43) } else { - yynn2 = 38 + yynn2 = 41 for _, b := range yyq2 { if b { yynn2++ @@ -5901,170 +5968,227 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy40 := &x.ServiceSyncPeriod + yym40 := z.EncBinary() + _ = yym40 + if false { + } else { + r.EncodeInt(int64(x.LookupCacheSizeForRC)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("lookupCacheSizeForRC")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) yym41 := z.EncBinary() _ = yym41 if false { - } else if z.HasExtensions() && z.EncExt(yy40) { - } else if !yym41 && z.IsJSONHandle() { - z.EncJSONMarshal(yy40) } else { - z.EncFallback(yy40) + r.EncodeInt(int64(x.LookupCacheSizeForRC)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym43 := z.EncBinary() + _ = yym43 + if false { + } else { + r.EncodeInt(int64(x.LookupCacheSizeForRS)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("lookupCacheSizeForRS")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym44 := z.EncBinary() + _ = yym44 + if false { + } else { + r.EncodeInt(int64(x.LookupCacheSizeForRS)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym46 := z.EncBinary() + _ = yym46 + if false { + } else { + r.EncodeInt(int64(x.LookupCacheSizeForDaemonSet)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("lookupCacheSizeForDaemonSet")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym47 := z.EncBinary() + _ = yym47 + if false { + } else { + r.EncodeInt(int64(x.LookupCacheSizeForDaemonSet)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yy49 := &x.ServiceSyncPeriod + yym50 := z.EncBinary() + _ = yym50 + if false { + } else if z.HasExtensions() && z.EncExt(yy49) { + } else if !yym50 && z.IsJSONHandle() { + z.EncJSONMarshal(yy49) + } else { + z.EncFallback(yy49) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("serviceSyncPeriod")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy42 := &x.ServiceSyncPeriod - yym43 := z.EncBinary() - _ = yym43 + yy51 := &x.ServiceSyncPeriod + yym52 := z.EncBinary() + _ = yym52 if false { - } else if z.HasExtensions() && z.EncExt(yy42) { - } else if !yym43 && z.IsJSONHandle() { - z.EncJSONMarshal(yy42) + } else if z.HasExtensions() && z.EncExt(yy51) { + } else if !yym52 && z.IsJSONHandle() { + z.EncJSONMarshal(yy51) } else { - z.EncFallback(yy42) + z.EncFallback(yy51) } } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy45 := &x.NodeSyncPeriod - yym46 := z.EncBinary() - _ = yym46 + yy54 := &x.NodeSyncPeriod + yym55 := z.EncBinary() + _ = yym55 if false { - } else if z.HasExtensions() && z.EncExt(yy45) { - } else if !yym46 && z.IsJSONHandle() { - z.EncJSONMarshal(yy45) + } else if z.HasExtensions() && z.EncExt(yy54) { + } else if !yym55 && z.IsJSONHandle() { + z.EncJSONMarshal(yy54) } else { - z.EncFallback(yy45) + z.EncFallback(yy54) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("nodeSyncPeriod")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy47 := &x.NodeSyncPeriod - yym48 := z.EncBinary() - _ = yym48 + yy56 := &x.NodeSyncPeriod + yym57 := z.EncBinary() + _ = yym57 if false { - } else if z.HasExtensions() && z.EncExt(yy47) { - } else if !yym48 && z.IsJSONHandle() { - z.EncJSONMarshal(yy47) + } else if z.HasExtensions() && z.EncExt(yy56) { + } else if !yym57 && z.IsJSONHandle() { + z.EncJSONMarshal(yy56) } else { - z.EncFallback(yy47) + z.EncFallback(yy56) } } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy50 := &x.ResourceQuotaSyncPeriod - yym51 := z.EncBinary() - _ = yym51 + yy59 := &x.ResourceQuotaSyncPeriod + yym60 := z.EncBinary() + _ = yym60 if false { - } else if z.HasExtensions() && z.EncExt(yy50) { - } else if !yym51 && z.IsJSONHandle() { - z.EncJSONMarshal(yy50) + } else if z.HasExtensions() && z.EncExt(yy59) { + } else if !yym60 && z.IsJSONHandle() { + z.EncJSONMarshal(yy59) } else { - z.EncFallback(yy50) + z.EncFallback(yy59) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("resourceQuotaSyncPeriod")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy52 := &x.ResourceQuotaSyncPeriod - yym53 := z.EncBinary() - _ = yym53 + yy61 := &x.ResourceQuotaSyncPeriod + yym62 := z.EncBinary() + _ = yym62 if false { - } else if z.HasExtensions() && z.EncExt(yy52) { - } else if !yym53 && z.IsJSONHandle() { - z.EncJSONMarshal(yy52) + } else if z.HasExtensions() && z.EncExt(yy61) { + } else if !yym62 && z.IsJSONHandle() { + z.EncJSONMarshal(yy61) } else { - z.EncFallback(yy52) + z.EncFallback(yy61) } } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy55 := &x.NamespaceSyncPeriod - yym56 := z.EncBinary() - _ = yym56 + yy64 := &x.NamespaceSyncPeriod + yym65 := z.EncBinary() + _ = yym65 if false { - } else if z.HasExtensions() && z.EncExt(yy55) { - } else if !yym56 && z.IsJSONHandle() { - z.EncJSONMarshal(yy55) + } else if z.HasExtensions() && z.EncExt(yy64) { + } else if !yym65 && z.IsJSONHandle() { + z.EncJSONMarshal(yy64) } else { - z.EncFallback(yy55) + z.EncFallback(yy64) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("namespaceSyncPeriod")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy57 := &x.NamespaceSyncPeriod - yym58 := z.EncBinary() - _ = yym58 + yy66 := &x.NamespaceSyncPeriod + yym67 := z.EncBinary() + _ = yym67 if false { - } else if z.HasExtensions() && z.EncExt(yy57) { - } else if !yym58 && z.IsJSONHandle() { - z.EncJSONMarshal(yy57) + } else if z.HasExtensions() && z.EncExt(yy66) { + } else if !yym67 && z.IsJSONHandle() { + z.EncJSONMarshal(yy66) } else { - z.EncFallback(yy57) + z.EncFallback(yy66) } } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy60 := &x.PVClaimBinderSyncPeriod - yym61 := z.EncBinary() - _ = yym61 + yy69 := &x.PVClaimBinderSyncPeriod + yym70 := z.EncBinary() + _ = yym70 if false { - } else if z.HasExtensions() && z.EncExt(yy60) { - } else if !yym61 && z.IsJSONHandle() { - z.EncJSONMarshal(yy60) + } else if z.HasExtensions() && z.EncExt(yy69) { + } else if !yym70 && z.IsJSONHandle() { + z.EncJSONMarshal(yy69) } else { - z.EncFallback(yy60) + z.EncFallback(yy69) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("pvClaimBinderSyncPeriod")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy62 := &x.PVClaimBinderSyncPeriod - yym63 := z.EncBinary() - _ = yym63 + yy71 := &x.PVClaimBinderSyncPeriod + yym72 := z.EncBinary() + _ = yym72 if false { - } else if z.HasExtensions() && z.EncExt(yy62) { - } else if !yym63 && z.IsJSONHandle() { - z.EncJSONMarshal(yy62) + } else if z.HasExtensions() && z.EncExt(yy71) { + } else if !yym72 && z.IsJSONHandle() { + z.EncJSONMarshal(yy71) } else { - z.EncFallback(yy62) + z.EncFallback(yy71) } } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy65 := &x.MinResyncPeriod - yym66 := z.EncBinary() - _ = yym66 + yy74 := &x.MinResyncPeriod + yym75 := z.EncBinary() + _ = yym75 if false { - } else if z.HasExtensions() && z.EncExt(yy65) { - } else if !yym66 && z.IsJSONHandle() { - z.EncJSONMarshal(yy65) + } else if z.HasExtensions() && z.EncExt(yy74) { + } else if !yym75 && z.IsJSONHandle() { + z.EncJSONMarshal(yy74) } else { - z.EncFallback(yy65) + z.EncFallback(yy74) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("minResyncPeriod")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy67 := &x.MinResyncPeriod - yym68 := z.EncBinary() - _ = yym68 + yy76 := &x.MinResyncPeriod + yym77 := z.EncBinary() + _ = yym77 if false { - } else if z.HasExtensions() && z.EncExt(yy67) { - } else if !yym68 && z.IsJSONHandle() { - z.EncJSONMarshal(yy67) + } else if z.HasExtensions() && z.EncExt(yy76) { + } else if !yym77 && z.IsJSONHandle() { + z.EncJSONMarshal(yy76) } else { - z.EncFallback(yy67) + z.EncFallback(yy76) } } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym70 := z.EncBinary() - _ = yym70 + yym79 := z.EncBinary() + _ = yym79 if false { } else { r.EncodeInt(int64(x.TerminatedPodGCThreshold)) @@ -6073,8 +6197,8 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("terminatedPodGCThreshold")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym71 := z.EncBinary() - _ = yym71 + yym80 := z.EncBinary() + _ = yym80 if false { } else { r.EncodeInt(int64(x.TerminatedPodGCThreshold)) @@ -6082,126 +6206,75 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy73 := &x.HorizontalPodAutoscalerSyncPeriod - yym74 := z.EncBinary() - _ = yym74 + yy82 := &x.HorizontalPodAutoscalerSyncPeriod + yym83 := z.EncBinary() + _ = yym83 if false { - } else if z.HasExtensions() && z.EncExt(yy73) { - } else if !yym74 && z.IsJSONHandle() { - z.EncJSONMarshal(yy73) + } else if z.HasExtensions() && z.EncExt(yy82) { + } else if !yym83 && z.IsJSONHandle() { + z.EncJSONMarshal(yy82) } else { - z.EncFallback(yy73) + z.EncFallback(yy82) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("horizontalPodAutoscalerSyncPeriod")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy75 := &x.HorizontalPodAutoscalerSyncPeriod - yym76 := z.EncBinary() - _ = yym76 + yy84 := &x.HorizontalPodAutoscalerSyncPeriod + yym85 := z.EncBinary() + _ = yym85 if false { - } else if z.HasExtensions() && z.EncExt(yy75) { - } else if !yym76 && z.IsJSONHandle() { - z.EncJSONMarshal(yy75) + } else if z.HasExtensions() && z.EncExt(yy84) { + } else if !yym85 && z.IsJSONHandle() { + z.EncJSONMarshal(yy84) } else { - z.EncFallback(yy75) + z.EncFallback(yy84) } } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy78 := &x.DeploymentControllerSyncPeriod - yym79 := z.EncBinary() - _ = yym79 + yy87 := &x.DeploymentControllerSyncPeriod + yym88 := z.EncBinary() + _ = yym88 if false { - } else if z.HasExtensions() && z.EncExt(yy78) { - } else if !yym79 && z.IsJSONHandle() { - z.EncJSONMarshal(yy78) + } else if z.HasExtensions() && z.EncExt(yy87) { + } else if !yym88 && z.IsJSONHandle() { + z.EncJSONMarshal(yy87) } else { - z.EncFallback(yy78) + z.EncFallback(yy87) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("deploymentControllerSyncPeriod")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy80 := &x.DeploymentControllerSyncPeriod - yym81 := z.EncBinary() - _ = yym81 + yy89 := &x.DeploymentControllerSyncPeriod + yym90 := z.EncBinary() + _ = yym90 if false { - } else if z.HasExtensions() && z.EncExt(yy80) { - } else if !yym81 && z.IsJSONHandle() { - z.EncJSONMarshal(yy80) + } else if z.HasExtensions() && z.EncExt(yy89) { + } else if !yym90 && z.IsJSONHandle() { + z.EncJSONMarshal(yy89) } else { - z.EncFallback(yy80) + z.EncFallback(yy89) } } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy83 := &x.PodEvictionTimeout - yym84 := z.EncBinary() - _ = yym84 + yy92 := &x.PodEvictionTimeout + yym93 := z.EncBinary() + _ = yym93 if false { - } else if z.HasExtensions() && z.EncExt(yy83) { - } else if !yym84 && z.IsJSONHandle() { - z.EncJSONMarshal(yy83) + } else if z.HasExtensions() && z.EncExt(yy92) { + } else if !yym93 && z.IsJSONHandle() { + z.EncJSONMarshal(yy92) } else { - z.EncFallback(yy83) + z.EncFallback(yy92) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("podEvictionTimeout")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy85 := &x.PodEvictionTimeout - yym86 := z.EncBinary() - _ = yym86 - if false { - } else if z.HasExtensions() && z.EncExt(yy85) { - } else if !yym86 && z.IsJSONHandle() { - z.EncJSONMarshal(yy85) - } else { - z.EncFallback(yy85) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym88 := z.EncBinary() - _ = yym88 - if false { - } else { - r.EncodeFloat32(float32(x.DeletingPodsQps)) - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("deletingPodsQps")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym89 := z.EncBinary() - _ = yym89 - if false { - } else { - r.EncodeFloat32(float32(x.DeletingPodsQps)) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym91 := z.EncBinary() - _ = yym91 - if false { - } else { - r.EncodeInt(int64(x.DeletingPodsBurst)) - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("deletingPodsBurst")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym92 := z.EncBinary() - _ = yym92 - if false { - } else { - r.EncodeInt(int64(x.DeletingPodsBurst)) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy94 := &x.NodeMonitorGracePeriod + yy94 := &x.PodEvictionTimeout yym95 := z.EncBinary() _ = yym95 if false { @@ -6211,25 +6284,76 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode } else { z.EncFallback(yy94) } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("nodeMonitorGracePeriod")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy96 := &x.NodeMonitorGracePeriod + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) yym97 := z.EncBinary() _ = yym97 if false { - } else if z.HasExtensions() && z.EncExt(yy96) { - } else if !yym97 && z.IsJSONHandle() { - z.EncJSONMarshal(yy96) } else { - z.EncFallback(yy96) + r.EncodeFloat32(float32(x.DeletingPodsQps)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("deletingPodsQps")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym98 := z.EncBinary() + _ = yym98 + if false { + } else { + r.EncodeFloat32(float32(x.DeletingPodsQps)) } } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym99 := z.EncBinary() - _ = yym99 + yym100 := z.EncBinary() + _ = yym100 + if false { + } else { + r.EncodeInt(int64(x.DeletingPodsBurst)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("deletingPodsBurst")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym101 := z.EncBinary() + _ = yym101 + if false { + } else { + r.EncodeInt(int64(x.DeletingPodsBurst)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yy103 := &x.NodeMonitorGracePeriod + yym104 := z.EncBinary() + _ = yym104 + if false { + } else if z.HasExtensions() && z.EncExt(yy103) { + } else if !yym104 && z.IsJSONHandle() { + z.EncJSONMarshal(yy103) + } else { + z.EncFallback(yy103) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("nodeMonitorGracePeriod")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy105 := &x.NodeMonitorGracePeriod + yym106 := z.EncBinary() + _ = yym106 + if false { + } else if z.HasExtensions() && z.EncExt(yy105) { + } else if !yym106 && z.IsJSONHandle() { + z.EncJSONMarshal(yy105) + } else { + z.EncFallback(yy105) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym108 := z.EncBinary() + _ = yym108 if false { } else { r.EncodeInt(int64(x.RegisterRetryCount)) @@ -6238,8 +6362,8 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("registerRetryCount")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym100 := z.EncBinary() - _ = yym100 + yym109 := z.EncBinary() + _ = yym109 if false { } else { r.EncodeInt(int64(x.RegisterRetryCount)) @@ -6247,113 +6371,56 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy102 := &x.NodeStartupGracePeriod - yym103 := z.EncBinary() - _ = yym103 + yy111 := &x.NodeStartupGracePeriod + yym112 := z.EncBinary() + _ = yym112 if false { - } else if z.HasExtensions() && z.EncExt(yy102) { - } else if !yym103 && z.IsJSONHandle() { - z.EncJSONMarshal(yy102) + } else if z.HasExtensions() && z.EncExt(yy111) { + } else if !yym112 && z.IsJSONHandle() { + z.EncJSONMarshal(yy111) } else { - z.EncFallback(yy102) + z.EncFallback(yy111) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("nodeStartupGracePeriod")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy104 := &x.NodeStartupGracePeriod - yym105 := z.EncBinary() - _ = yym105 + yy113 := &x.NodeStartupGracePeriod + yym114 := z.EncBinary() + _ = yym114 if false { - } else if z.HasExtensions() && z.EncExt(yy104) { - } else if !yym105 && z.IsJSONHandle() { - z.EncJSONMarshal(yy104) + } else if z.HasExtensions() && z.EncExt(yy113) { + } else if !yym114 && z.IsJSONHandle() { + z.EncJSONMarshal(yy113) } else { - z.EncFallback(yy104) + z.EncFallback(yy113) } } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy107 := &x.NodeMonitorPeriod - yym108 := z.EncBinary() - _ = yym108 + yy116 := &x.NodeMonitorPeriod + yym117 := z.EncBinary() + _ = yym117 if false { - } else if z.HasExtensions() && z.EncExt(yy107) { - } else if !yym108 && z.IsJSONHandle() { - z.EncJSONMarshal(yy107) + } else if z.HasExtensions() && z.EncExt(yy116) { + } else if !yym117 && z.IsJSONHandle() { + z.EncJSONMarshal(yy116) } else { - z.EncFallback(yy107) + z.EncFallback(yy116) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("nodeMonitorPeriod")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy109 := &x.NodeMonitorPeriod - yym110 := z.EncBinary() - _ = yym110 - if false { - } else if z.HasExtensions() && z.EncExt(yy109) { - } else if !yym110 && z.IsJSONHandle() { - z.EncJSONMarshal(yy109) - } else { - z.EncFallback(yy109) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym112 := z.EncBinary() - _ = yym112 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.ServiceAccountKeyFile)) - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("serviceAccountKeyFile")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym113 := z.EncBinary() - _ = yym113 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.ServiceAccountKeyFile)) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym115 := z.EncBinary() - _ = yym115 - if false { - } else { - r.EncodeBool(bool(x.EnableProfiling)) - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("enableProfiling")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym116 := z.EncBinary() - _ = yym116 - if false { - } else { - r.EncodeBool(bool(x.EnableProfiling)) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym118 := z.EncBinary() - _ = yym118 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.ClusterName)) - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("clusterName")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy118 := &x.NodeMonitorPeriod yym119 := z.EncBinary() _ = yym119 if false { + } else if z.HasExtensions() && z.EncExt(yy118) { + } else if !yym119 && z.IsJSONHandle() { + z.EncJSONMarshal(yy118) } else { - r.EncodeString(codecSelferC_UTF81234, string(x.ClusterName)) + z.EncFallback(yy118) } } if yyr2 || yy2arr2 { @@ -6362,17 +6429,17 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode _ = yym121 if false { } else { - r.EncodeString(codecSelferC_UTF81234, string(x.ClusterCIDR)) + r.EncodeString(codecSelferC_UTF81234, string(x.ServiceAccountKeyFile)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("clusterCIDR")) + r.EncodeString(codecSelferC_UTF81234, string("serviceAccountKeyFile")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym122 := z.EncBinary() _ = yym122 if false { } else { - r.EncodeString(codecSelferC_UTF81234, string(x.ClusterCIDR)) + r.EncodeString(codecSelferC_UTF81234, string(x.ServiceAccountKeyFile)) } } if yyr2 || yy2arr2 { @@ -6381,17 +6448,17 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode _ = yym124 if false { } else { - r.EncodeBool(bool(x.AllocateNodeCIDRs)) + r.EncodeBool(bool(x.EnableProfiling)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("allocateNodeCIDRs")) + r.EncodeString(codecSelferC_UTF81234, string("enableProfiling")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym125 := z.EncBinary() _ = yym125 if false { } else { - r.EncodeBool(bool(x.AllocateNodeCIDRs)) + r.EncodeBool(bool(x.EnableProfiling)) } } if yyr2 || yy2arr2 { @@ -6400,17 +6467,17 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode _ = yym127 if false { } else { - r.EncodeString(codecSelferC_UTF81234, string(x.RootCAFile)) + r.EncodeString(codecSelferC_UTF81234, string(x.ClusterName)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("rootCAFile")) + r.EncodeString(codecSelferC_UTF81234, string("clusterName")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym128 := z.EncBinary() _ = yym128 if false { } else { - r.EncodeString(codecSelferC_UTF81234, string(x.RootCAFile)) + r.EncodeString(codecSelferC_UTF81234, string(x.ClusterName)) } } if yyr2 || yy2arr2 { @@ -6419,17 +6486,17 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode _ = yym130 if false { } else { - r.EncodeFloat32(float32(x.KubeAPIQPS)) + r.EncodeString(codecSelferC_UTF81234, string(x.ClusterCIDR)) } } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("kubeAPIQPS")) + r.EncodeString(codecSelferC_UTF81234, string("clusterCIDR")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym131 := z.EncBinary() _ = yym131 if false { } else { - r.EncodeFloat32(float32(x.KubeAPIQPS)) + r.EncodeString(codecSelferC_UTF81234, string(x.ClusterCIDR)) } } if yyr2 || yy2arr2 { @@ -6437,6 +6504,63 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode yym133 := z.EncBinary() _ = yym133 if false { + } else { + r.EncodeBool(bool(x.AllocateNodeCIDRs)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("allocateNodeCIDRs")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym134 := z.EncBinary() + _ = yym134 + if false { + } else { + r.EncodeBool(bool(x.AllocateNodeCIDRs)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym136 := z.EncBinary() + _ = yym136 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.RootCAFile)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("rootCAFile")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym137 := z.EncBinary() + _ = yym137 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.RootCAFile)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym139 := z.EncBinary() + _ = yym139 + if false { + } else { + r.EncodeFloat32(float32(x.KubeAPIQPS)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("kubeAPIQPS")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym140 := z.EncBinary() + _ = yym140 + if false { + } else { + r.EncodeFloat32(float32(x.KubeAPIQPS)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym142 := z.EncBinary() + _ = yym142 + if false { } else { r.EncodeInt(int64(x.KubeAPIBurst)) } @@ -6444,8 +6568,8 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("kubeAPIBurst")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym134 := z.EncBinary() - _ = yym134 + yym143 := z.EncBinary() + _ = yym143 if false { } else { r.EncodeInt(int64(x.KubeAPIBurst)) @@ -6453,31 +6577,31 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy136 := &x.LeaderElection - yy136.CodecEncodeSelf(e) + yy145 := &x.LeaderElection + yy145.CodecEncodeSelf(e) } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("leaderElection")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy138 := &x.LeaderElection - yy138.CodecEncodeSelf(e) + yy147 := &x.LeaderElection + yy147.CodecEncodeSelf(e) } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy141 := &x.VolumeConfiguration - yy141.CodecEncodeSelf(e) + yy150 := &x.VolumeConfiguration + yy150.CodecEncodeSelf(e) } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("volumeConfiguration")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy143 := &x.VolumeConfiguration - yy143.CodecEncodeSelf(e) + yy152 := &x.VolumeConfiguration + yy152.CodecEncodeSelf(e) } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[38] { - yym146 := z.EncBinary() - _ = yym146 + if yyq2[41] { + yym155 := z.EncBinary() + _ = yym155 if false { } else { r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) @@ -6486,12 +6610,12 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode r.EncodeString(codecSelferC_UTF81234, "") } } else { - if yyq2[38] { + if yyq2[41] { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("kind")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym147 := z.EncBinary() - _ = yym147 + yym156 := z.EncBinary() + _ = yym156 if false { } else { r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) @@ -6500,9 +6624,9 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[39] { - yym149 := z.EncBinary() - _ = yym149 + if yyq2[42] { + yym158 := z.EncBinary() + _ = yym158 if false { } else { r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) @@ -6511,12 +6635,12 @@ func (x *KubeControllerManagerConfiguration) CodecEncodeSelf(e *codec1978.Encode r.EncodeString(codecSelferC_UTF81234, "") } } else { - if yyq2[39] { + if yyq2[42] { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym150 := z.EncBinary() - _ = yym150 + yym159 := z.EncBinary() + _ = yym159 if false { } else { r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) @@ -6656,94 +6780,112 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromMap(l int, d *co } else { x.ConcurrentNamespaceSyncs = int(r.DecodeInt(codecSelferBitsize1234)) } + case "lookupCacheSizeForRC": + if r.TryDecodeAsNil() { + x.LookupCacheSizeForRC = 0 + } else { + x.LookupCacheSizeForRC = int(r.DecodeInt(codecSelferBitsize1234)) + } + case "lookupCacheSizeForRS": + if r.TryDecodeAsNil() { + x.LookupCacheSizeForRS = 0 + } else { + x.LookupCacheSizeForRS = int(r.DecodeInt(codecSelferBitsize1234)) + } + case "lookupCacheSizeForDaemonSet": + if r.TryDecodeAsNil() { + x.LookupCacheSizeForDaemonSet = 0 + } else { + x.LookupCacheSizeForDaemonSet = int(r.DecodeInt(codecSelferBitsize1234)) + } case "serviceSyncPeriod": if r.TryDecodeAsNil() { x.ServiceSyncPeriod = pkg1_unversioned.Duration{} } else { - yyv16 := &x.ServiceSyncPeriod - yym17 := z.DecBinary() - _ = yym17 + yyv19 := &x.ServiceSyncPeriod + yym20 := z.DecBinary() + _ = yym20 if false { - } else if z.HasExtensions() && z.DecExt(yyv16) { - } else if !yym17 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv16) + } else if z.HasExtensions() && z.DecExt(yyv19) { + } else if !yym20 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv19) } else { - z.DecFallback(yyv16, false) + z.DecFallback(yyv19, false) } } case "nodeSyncPeriod": if r.TryDecodeAsNil() { x.NodeSyncPeriod = pkg1_unversioned.Duration{} } else { - yyv18 := &x.NodeSyncPeriod - yym19 := z.DecBinary() - _ = yym19 + yyv21 := &x.NodeSyncPeriod + yym22 := z.DecBinary() + _ = yym22 if false { - } else if z.HasExtensions() && z.DecExt(yyv18) { - } else if !yym19 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv18) + } else if z.HasExtensions() && z.DecExt(yyv21) { + } else if !yym22 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv21) } else { - z.DecFallback(yyv18, false) + z.DecFallback(yyv21, false) } } case "resourceQuotaSyncPeriod": if r.TryDecodeAsNil() { x.ResourceQuotaSyncPeriod = pkg1_unversioned.Duration{} } else { - yyv20 := &x.ResourceQuotaSyncPeriod - yym21 := z.DecBinary() - _ = yym21 + yyv23 := &x.ResourceQuotaSyncPeriod + yym24 := z.DecBinary() + _ = yym24 if false { - } else if z.HasExtensions() && z.DecExt(yyv20) { - } else if !yym21 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv20) + } else if z.HasExtensions() && z.DecExt(yyv23) { + } else if !yym24 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv23) } else { - z.DecFallback(yyv20, false) + z.DecFallback(yyv23, false) } } case "namespaceSyncPeriod": if r.TryDecodeAsNil() { x.NamespaceSyncPeriod = pkg1_unversioned.Duration{} } else { - yyv22 := &x.NamespaceSyncPeriod - yym23 := z.DecBinary() - _ = yym23 + yyv25 := &x.NamespaceSyncPeriod + yym26 := z.DecBinary() + _ = yym26 if false { - } else if z.HasExtensions() && z.DecExt(yyv22) { - } else if !yym23 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv22) + } else if z.HasExtensions() && z.DecExt(yyv25) { + } else if !yym26 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv25) } else { - z.DecFallback(yyv22, false) + z.DecFallback(yyv25, false) } } case "pvClaimBinderSyncPeriod": if r.TryDecodeAsNil() { x.PVClaimBinderSyncPeriod = pkg1_unversioned.Duration{} } else { - yyv24 := &x.PVClaimBinderSyncPeriod - yym25 := z.DecBinary() - _ = yym25 + yyv27 := &x.PVClaimBinderSyncPeriod + yym28 := z.DecBinary() + _ = yym28 if false { - } else if z.HasExtensions() && z.DecExt(yyv24) { - } else if !yym25 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv24) + } else if z.HasExtensions() && z.DecExt(yyv27) { + } else if !yym28 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv27) } else { - z.DecFallback(yyv24, false) + z.DecFallback(yyv27, false) } } case "minResyncPeriod": if r.TryDecodeAsNil() { x.MinResyncPeriod = pkg1_unversioned.Duration{} } else { - yyv26 := &x.MinResyncPeriod - yym27 := z.DecBinary() - _ = yym27 + yyv29 := &x.MinResyncPeriod + yym30 := z.DecBinary() + _ = yym30 if false { - } else if z.HasExtensions() && z.DecExt(yyv26) { - } else if !yym27 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv26) + } else if z.HasExtensions() && z.DecExt(yyv29) { + } else if !yym30 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv29) } else { - z.DecFallback(yyv26, false) + z.DecFallback(yyv29, false) } } case "terminatedPodGCThreshold": @@ -6756,45 +6898,45 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromMap(l int, d *co if r.TryDecodeAsNil() { x.HorizontalPodAutoscalerSyncPeriod = pkg1_unversioned.Duration{} } else { - yyv29 := &x.HorizontalPodAutoscalerSyncPeriod - yym30 := z.DecBinary() - _ = yym30 + yyv32 := &x.HorizontalPodAutoscalerSyncPeriod + yym33 := z.DecBinary() + _ = yym33 if false { - } else if z.HasExtensions() && z.DecExt(yyv29) { - } else if !yym30 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv29) + } else if z.HasExtensions() && z.DecExt(yyv32) { + } else if !yym33 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv32) } else { - z.DecFallback(yyv29, false) + z.DecFallback(yyv32, false) } } case "deploymentControllerSyncPeriod": if r.TryDecodeAsNil() { x.DeploymentControllerSyncPeriod = pkg1_unversioned.Duration{} } else { - yyv31 := &x.DeploymentControllerSyncPeriod - yym32 := z.DecBinary() - _ = yym32 + yyv34 := &x.DeploymentControllerSyncPeriod + yym35 := z.DecBinary() + _ = yym35 if false { - } else if z.HasExtensions() && z.DecExt(yyv31) { - } else if !yym32 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv31) + } else if z.HasExtensions() && z.DecExt(yyv34) { + } else if !yym35 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv34) } else { - z.DecFallback(yyv31, false) + z.DecFallback(yyv34, false) } } case "podEvictionTimeout": if r.TryDecodeAsNil() { x.PodEvictionTimeout = pkg1_unversioned.Duration{} } else { - yyv33 := &x.PodEvictionTimeout - yym34 := z.DecBinary() - _ = yym34 + yyv36 := &x.PodEvictionTimeout + yym37 := z.DecBinary() + _ = yym37 if false { - } else if z.HasExtensions() && z.DecExt(yyv33) { - } else if !yym34 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv33) + } else if z.HasExtensions() && z.DecExt(yyv36) { + } else if !yym37 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv36) } else { - z.DecFallback(yyv33, false) + z.DecFallback(yyv36, false) } } case "deletingPodsQps": @@ -6813,15 +6955,15 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromMap(l int, d *co if r.TryDecodeAsNil() { x.NodeMonitorGracePeriod = pkg1_unversioned.Duration{} } else { - yyv37 := &x.NodeMonitorGracePeriod - yym38 := z.DecBinary() - _ = yym38 + yyv40 := &x.NodeMonitorGracePeriod + yym41 := z.DecBinary() + _ = yym41 if false { - } else if z.HasExtensions() && z.DecExt(yyv37) { - } else if !yym38 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv37) + } else if z.HasExtensions() && z.DecExt(yyv40) { + } else if !yym41 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv40) } else { - z.DecFallback(yyv37, false) + z.DecFallback(yyv40, false) } } case "registerRetryCount": @@ -6834,30 +6976,30 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromMap(l int, d *co if r.TryDecodeAsNil() { x.NodeStartupGracePeriod = pkg1_unversioned.Duration{} } else { - yyv40 := &x.NodeStartupGracePeriod - yym41 := z.DecBinary() - _ = yym41 + yyv43 := &x.NodeStartupGracePeriod + yym44 := z.DecBinary() + _ = yym44 if false { - } else if z.HasExtensions() && z.DecExt(yyv40) { - } else if !yym41 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv40) + } else if z.HasExtensions() && z.DecExt(yyv43) { + } else if !yym44 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv43) } else { - z.DecFallback(yyv40, false) + z.DecFallback(yyv43, false) } } case "nodeMonitorPeriod": if r.TryDecodeAsNil() { x.NodeMonitorPeriod = pkg1_unversioned.Duration{} } else { - yyv42 := &x.NodeMonitorPeriod - yym43 := z.DecBinary() - _ = yym43 + yyv45 := &x.NodeMonitorPeriod + yym46 := z.DecBinary() + _ = yym46 if false { - } else if z.HasExtensions() && z.DecExt(yyv42) { - } else if !yym43 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv42) + } else if z.HasExtensions() && z.DecExt(yyv45) { + } else if !yym46 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv45) } else { - z.DecFallback(yyv42, false) + z.DecFallback(yyv45, false) } } case "serviceAccountKeyFile": @@ -6912,15 +7054,15 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromMap(l int, d *co if r.TryDecodeAsNil() { x.LeaderElection = LeaderElectionConfiguration{} } else { - yyv52 := &x.LeaderElection - yyv52.CodecDecodeSelf(d) + yyv55 := &x.LeaderElection + yyv55.CodecDecodeSelf(d) } case "volumeConfiguration": if r.TryDecodeAsNil() { x.VolumeConfiguration = VolumeConfiguration{} } else { - yyv53 := &x.VolumeConfiguration - yyv53.CodecDecodeSelf(d) + yyv56 := &x.VolumeConfiguration + yyv56.CodecDecodeSelf(d) } case "kind": if r.TryDecodeAsNil() { @@ -6945,16 +7087,16 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj56 int - var yyb56 bool - var yyhl56 bool = l >= 0 - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + var yyj59 int + var yyb59 bool + var yyhl59 bool = l >= 0 + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -6964,13 +7106,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.Port = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -6980,13 +7122,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.Address = string(r.DecodeString()) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -6996,13 +7138,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.CloudProvider = string(r.DecodeString()) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7012,13 +7154,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.CloudConfigFile = string(r.DecodeString()) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7028,13 +7170,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.ConcurrentEndpointSyncs = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7044,13 +7186,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.ConcurrentRSSyncs = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7060,13 +7202,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.ConcurrentRCSyncs = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7076,13 +7218,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.ConcurrentResourceQuotaSyncs = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7092,13 +7234,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.ConcurrentDeploymentSyncs = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7108,13 +7250,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.ConcurrentDaemonSetSyncs = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7124,13 +7266,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.ConcurrentJobSyncs = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7140,13 +7282,61 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.ConcurrentNamespaceSyncs = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.LookupCacheSizeForRC = 0 + } else { + x.LookupCacheSizeForRC = int(r.DecodeInt(codecSelferBitsize1234)) + } + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l + } else { + yyb59 = r.CheckBreak() + } + if yyb59 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.LookupCacheSizeForRS = 0 + } else { + x.LookupCacheSizeForRS = int(r.DecodeInt(codecSelferBitsize1234)) + } + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l + } else { + yyb59 = r.CheckBreak() + } + if yyb59 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.LookupCacheSizeForDaemonSet = 0 + } else { + x.LookupCacheSizeForDaemonSet = int(r.DecodeInt(codecSelferBitsize1234)) + } + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l + } else { + yyb59 = r.CheckBreak() + } + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7154,82 +7344,7 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * if r.TryDecodeAsNil() { x.ServiceSyncPeriod = pkg1_unversioned.Duration{} } else { - yyv69 := &x.ServiceSyncPeriod - yym70 := z.DecBinary() - _ = yym70 - if false { - } else if z.HasExtensions() && z.DecExt(yyv69) { - } else if !yym70 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv69) - } else { - z.DecFallback(yyv69, false) - } - } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l - } else { - yyb56 = r.CheckBreak() - } - if yyb56 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.NodeSyncPeriod = pkg1_unversioned.Duration{} - } else { - yyv71 := &x.NodeSyncPeriod - yym72 := z.DecBinary() - _ = yym72 - if false { - } else if z.HasExtensions() && z.DecExt(yyv71) { - } else if !yym72 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv71) - } else { - z.DecFallback(yyv71, false) - } - } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l - } else { - yyb56 = r.CheckBreak() - } - if yyb56 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.ResourceQuotaSyncPeriod = pkg1_unversioned.Duration{} - } else { - yyv73 := &x.ResourceQuotaSyncPeriod - yym74 := z.DecBinary() - _ = yym74 - if false { - } else if z.HasExtensions() && z.DecExt(yyv73) { - } else if !yym74 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv73) - } else { - z.DecFallback(yyv73, false) - } - } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l - } else { - yyb56 = r.CheckBreak() - } - if yyb56 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.NamespaceSyncPeriod = pkg1_unversioned.Duration{} - } else { - yyv75 := &x.NamespaceSyncPeriod + yyv75 := &x.ServiceSyncPeriod yym76 := z.DecBinary() _ = yym76 if false { @@ -7240,21 +7355,21 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * z.DecFallback(yyv75, false) } } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.PVClaimBinderSyncPeriod = pkg1_unversioned.Duration{} + x.NodeSyncPeriod = pkg1_unversioned.Duration{} } else { - yyv77 := &x.PVClaimBinderSyncPeriod + yyv77 := &x.NodeSyncPeriod yym78 := z.DecBinary() _ = yym78 if false { @@ -7265,21 +7380,21 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * z.DecFallback(yyv77, false) } } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.MinResyncPeriod = pkg1_unversioned.Duration{} + x.ResourceQuotaSyncPeriod = pkg1_unversioned.Duration{} } else { - yyv79 := &x.MinResyncPeriod + yyv79 := &x.ResourceQuotaSyncPeriod yym80 := z.DecBinary() _ = yym80 if false { @@ -7290,13 +7405,88 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * z.DecFallback(yyv79, false) } } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.NamespaceSyncPeriod = pkg1_unversioned.Duration{} + } else { + yyv81 := &x.NamespaceSyncPeriod + yym82 := z.DecBinary() + _ = yym82 + if false { + } else if z.HasExtensions() && z.DecExt(yyv81) { + } else if !yym82 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv81) + } else { + z.DecFallback(yyv81, false) + } + } + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l + } else { + yyb59 = r.CheckBreak() + } + if yyb59 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.PVClaimBinderSyncPeriod = pkg1_unversioned.Duration{} + } else { + yyv83 := &x.PVClaimBinderSyncPeriod + yym84 := z.DecBinary() + _ = yym84 + if false { + } else if z.HasExtensions() && z.DecExt(yyv83) { + } else if !yym84 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv83) + } else { + z.DecFallback(yyv83, false) + } + } + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l + } else { + yyb59 = r.CheckBreak() + } + if yyb59 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.MinResyncPeriod = pkg1_unversioned.Duration{} + } else { + yyv85 := &x.MinResyncPeriod + yym86 := z.DecBinary() + _ = yym86 + if false { + } else if z.HasExtensions() && z.DecExt(yyv85) { + } else if !yym86 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv85) + } else { + z.DecFallback(yyv85, false) + } + } + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l + } else { + yyb59 = r.CheckBreak() + } + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7306,13 +7496,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.TerminatedPodGCThreshold = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7320,24 +7510,24 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * if r.TryDecodeAsNil() { x.HorizontalPodAutoscalerSyncPeriod = pkg1_unversioned.Duration{} } else { - yyv82 := &x.HorizontalPodAutoscalerSyncPeriod - yym83 := z.DecBinary() - _ = yym83 + yyv88 := &x.HorizontalPodAutoscalerSyncPeriod + yym89 := z.DecBinary() + _ = yym89 if false { - } else if z.HasExtensions() && z.DecExt(yyv82) { - } else if !yym83 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv82) + } else if z.HasExtensions() && z.DecExt(yyv88) { + } else if !yym89 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv88) } else { - z.DecFallback(yyv82, false) + z.DecFallback(yyv88, false) } } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7345,89 +7535,7 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * if r.TryDecodeAsNil() { x.DeploymentControllerSyncPeriod = pkg1_unversioned.Duration{} } else { - yyv84 := &x.DeploymentControllerSyncPeriod - yym85 := z.DecBinary() - _ = yym85 - if false { - } else if z.HasExtensions() && z.DecExt(yyv84) { - } else if !yym85 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv84) - } else { - z.DecFallback(yyv84, false) - } - } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l - } else { - yyb56 = r.CheckBreak() - } - if yyb56 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.PodEvictionTimeout = pkg1_unversioned.Duration{} - } else { - yyv86 := &x.PodEvictionTimeout - yym87 := z.DecBinary() - _ = yym87 - if false { - } else if z.HasExtensions() && z.DecExt(yyv86) { - } else if !yym87 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv86) - } else { - z.DecFallback(yyv86, false) - } - } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l - } else { - yyb56 = r.CheckBreak() - } - if yyb56 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.DeletingPodsQps = 0 - } else { - x.DeletingPodsQps = float32(r.DecodeFloat(true)) - } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l - } else { - yyb56 = r.CheckBreak() - } - if yyb56 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.DeletingPodsBurst = 0 - } else { - x.DeletingPodsBurst = int(r.DecodeInt(codecSelferBitsize1234)) - } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l - } else { - yyb56 = r.CheckBreak() - } - if yyb56 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.NodeMonitorGracePeriod = pkg1_unversioned.Duration{} - } else { - yyv90 := &x.NodeMonitorGracePeriod + yyv90 := &x.DeploymentControllerSyncPeriod yym91 := z.DecBinary() _ = yym91 if false { @@ -7438,13 +7546,95 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * z.DecFallback(yyv90, false) } } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.PodEvictionTimeout = pkg1_unversioned.Duration{} + } else { + yyv92 := &x.PodEvictionTimeout + yym93 := z.DecBinary() + _ = yym93 + if false { + } else if z.HasExtensions() && z.DecExt(yyv92) { + } else if !yym93 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv92) + } else { + z.DecFallback(yyv92, false) + } + } + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l + } else { + yyb59 = r.CheckBreak() + } + if yyb59 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.DeletingPodsQps = 0 + } else { + x.DeletingPodsQps = float32(r.DecodeFloat(true)) + } + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l + } else { + yyb59 = r.CheckBreak() + } + if yyb59 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.DeletingPodsBurst = 0 + } else { + x.DeletingPodsBurst = int(r.DecodeInt(codecSelferBitsize1234)) + } + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l + } else { + yyb59 = r.CheckBreak() + } + if yyb59 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.NodeMonitorGracePeriod = pkg1_unversioned.Duration{} + } else { + yyv96 := &x.NodeMonitorGracePeriod + yym97 := z.DecBinary() + _ = yym97 + if false { + } else if z.HasExtensions() && z.DecExt(yyv96) { + } else if !yym97 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv96) + } else { + z.DecFallback(yyv96, false) + } + } + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l + } else { + yyb59 = r.CheckBreak() + } + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7454,13 +7644,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.RegisterRetryCount = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7468,24 +7658,24 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * if r.TryDecodeAsNil() { x.NodeStartupGracePeriod = pkg1_unversioned.Duration{} } else { - yyv93 := &x.NodeStartupGracePeriod - yym94 := z.DecBinary() - _ = yym94 + yyv99 := &x.NodeStartupGracePeriod + yym100 := z.DecBinary() + _ = yym100 if false { - } else if z.HasExtensions() && z.DecExt(yyv93) { - } else if !yym94 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv93) + } else if z.HasExtensions() && z.DecExt(yyv99) { + } else if !yym100 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv99) } else { - z.DecFallback(yyv93, false) + z.DecFallback(yyv99, false) } } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7493,24 +7683,24 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * if r.TryDecodeAsNil() { x.NodeMonitorPeriod = pkg1_unversioned.Duration{} } else { - yyv95 := &x.NodeMonitorPeriod - yym96 := z.DecBinary() - _ = yym96 + yyv101 := &x.NodeMonitorPeriod + yym102 := z.DecBinary() + _ = yym102 if false { - } else if z.HasExtensions() && z.DecExt(yyv95) { - } else if !yym96 && z.IsJSONHandle() { - z.DecJSONUnmarshal(yyv95) + } else if z.HasExtensions() && z.DecExt(yyv101) { + } else if !yym102 && z.IsJSONHandle() { + z.DecJSONUnmarshal(yyv101) } else { - z.DecFallback(yyv95, false) + z.DecFallback(yyv101, false) } } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7520,13 +7710,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.ServiceAccountKeyFile = string(r.DecodeString()) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7536,13 +7726,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.EnableProfiling = bool(r.DecodeBool()) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7552,13 +7742,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.ClusterName = string(r.DecodeString()) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7568,13 +7758,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.ClusterCIDR = string(r.DecodeString()) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7584,13 +7774,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.AllocateNodeCIDRs = bool(r.DecodeBool()) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7600,13 +7790,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.RootCAFile = string(r.DecodeString()) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7616,13 +7806,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.KubeAPIQPS = float32(r.DecodeFloat(true)) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7632,13 +7822,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.KubeAPIBurst = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7646,16 +7836,16 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * if r.TryDecodeAsNil() { x.LeaderElection = LeaderElectionConfiguration{} } else { - yyv105 := &x.LeaderElection - yyv105.CodecDecodeSelf(d) + yyv111 := &x.LeaderElection + yyv111.CodecDecodeSelf(d) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7663,16 +7853,16 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * if r.TryDecodeAsNil() { x.VolumeConfiguration = VolumeConfiguration{} } else { - yyv106 := &x.VolumeConfiguration - yyv106.CodecDecodeSelf(d) + yyv112 := &x.VolumeConfiguration + yyv112.CodecDecodeSelf(d) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7682,13 +7872,13 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * } else { x.Kind = string(r.DecodeString()) } - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7699,17 +7889,17 @@ func (x *KubeControllerManagerConfiguration) codecDecodeSelfFromArray(l int, d * x.APIVersion = string(r.DecodeString()) } for { - yyj56++ - if yyhl56 { - yyb56 = yyj56 > l + yyj59++ + if yyhl59 { + yyb59 = yyj59 > l } else { - yyb56 = r.CheckBreak() + yyb59 = r.CheckBreak() } - if yyb56 { + if yyb59 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj56-1, "") + z.DecStructFieldNotFound(yyj59-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/types.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/types.go index 5f15c7a40..fa52286da 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/types.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/componentconfig/types.go @@ -78,6 +78,24 @@ const ( ProxyModeIPTables ProxyMode = "iptables" ) +// HairpinMode denotes how the kubelet should configure networking to handle +// hairpin packets. +type HairpinMode string + +// Enum settings for different ways to handle hairpin packets. +const ( + // Set the hairpin flag on the veth of containers in the respective + // container runtime. + HairpinVeth = "hairpin-veth" + // Make the container bridge promiscuous. This will force it to accept + // hairpin packets, even if the flag isn't set on ports of the bridge. + PromiscuousBridge = "promiscuous-bridge" + // Neither of the above. If the kubelet is started in this hairpin mode + // and kube-proxy is running in iptables mode, hairpin packets will be + // dropped by the container bridge. + HairpinNone = "none" +) + // TODO: curate the ordering and structure of this config object type KubeletConfiguration struct { // config is the path to the config file or directory of files @@ -252,11 +270,18 @@ type KubeletConfiguration struct { // configureCBR0 enables the kublet to configure cbr0 based on // Node.Spec.PodCIDR. ConfigureCBR0 bool `json:"configureCbr0"` - // Should the kubelet set the hairpin flag on veth interfaces for containers - // it creates? Setting this flag allows endpoints in a Service to - // loadbalance back to themselves if they should try to access their own - // Service. - HairpinMode bool `json:"configureHairpinMode"` + // How should the kubelet configure the container bridge for hairpin packets. + // Setting this flag allows endpoints in a Service to loadbalance back to + // themselves if they should try to access their own Service. Values: + // "promiscuous-bridge": make the container bridge promiscuous. + // "hairpin-veth": set the hairpin flag on container veth interfaces. + // "none": do nothing. + // Setting --configure-cbr0 to false implies that to achieve hairpin NAT + // one must set --hairpin-mode=veth-flag, because bridge assumes the + // existence of a container bridge named cbr0. + HairpinMode string `json:"hairpinMode"` + // The node has babysitter process monitoring docker and kubelet. + BabysitDaemons bool `json:"babysitDaemons"` // maxPods is the number of pods that can run on this Kubelet. MaxPods int `json:"maxPods"` // dockerExecHandlerName is the handler to use when executing a command @@ -376,11 +401,11 @@ type KubeControllerManagerConfiguration struct { // but more CPU (and network) load. ConcurrentEndpointSyncs int `json:"concurrentEndpointSyncs"` // concurrentRSSyncs is the number of replica sets that are allowed to sync - // concurrently. Larger number = more reponsive replica management, but more + // concurrently. Larger number = more responsive replica management, but more // CPU (and network) load. ConcurrentRSSyncs int `json:"concurrentRSSyncs"` // concurrentRCSyncs is the number of replication controllers that are - // allowed to sync concurrently. Larger number = more reponsive replica + // allowed to sync concurrently. Larger number = more responsive replica // management, but more CPU (and network) load. ConcurrentRCSyncs int `json:"concurrentRCSyncs"` // concurrentResourceQuotaSyncs is the number of resource quotas that are @@ -388,20 +413,29 @@ type KubeControllerManagerConfiguration struct { // management, but more CPU (and network) load. ConcurrentResourceQuotaSyncs int `json:"concurrentResourceQuotaSyncs"` // concurrentDeploymentSyncs is the number of deployment objects that are - // allowed to sync concurrently. Larger number = more reponsive deployments, + // allowed to sync concurrently. Larger number = more responsive deployments, // but more CPU (and network) load. ConcurrentDeploymentSyncs int `json:"concurrentDeploymentSyncs"` // concurrentDaemonSetSyncs is the number of daemonset objects that are - // allowed to sync concurrently. Larger number = more reponsive DaemonSet, + // allowed to sync concurrently. Larger number = more responsive daemonset, // but more CPU (and network) load. ConcurrentDaemonSetSyncs int `json:"concurrentDaemonSetSyncs"` // concurrentJobSyncs is the number of job objects that are - // allowed to sync concurrently. Larger number = more reponsive jobs, + // allowed to sync concurrently. Larger number = more responsive jobs, // but more CPU (and network) load. ConcurrentJobSyncs int `json:"concurrentJobSyncs"` // concurrentNamespaceSyncs is the number of namespace objects that are // allowed to sync concurrently. ConcurrentNamespaceSyncs int `json:"concurrentNamespaceSyncs"` + // lookupCacheSizeForRC is the size of lookup cache for replication controllers. + // Larger number = more responsive replica management, but more MEM load. + LookupCacheSizeForRC int `json:"lookupCacheSizeForRC"` + // lookupCacheSizeForRS is the size of lookup cache for replicatsets. + // Larger number = more responsive replica management, but more MEM load. + LookupCacheSizeForRS int `json:"lookupCacheSizeForRS"` + // lookupCacheSizeForDaemonSet is the size of lookup cache for daemonsets. + // Larger number = more responsive daemonset, but more MEM load. + LookupCacheSizeForDaemonSet int `json:"lookupCacheSizeForDaemonSet"` // serviceSyncPeriod is the period for syncing services with their external // load balancers. ServiceSyncPeriod unversioned.Duration `json:"serviceSyncPeriod"` diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/helpers.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/helpers.go deleted file mode 100644 index c6d79d663..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/helpers.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2015 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 extensions - -// TODO(madhusudancs): Fix this when Scale group issues are resolved (see issue #18528). -// import ( -// "fmt" - -// "k8s.io/kubernetes/pkg/api" -// "k8s.io/kubernetes/pkg/api/unversioned" -// ) - -// // ScaleFromDeployment returns a scale subresource for a deployment. -// func ScaleFromDeployment(deployment *Deployment) (*Scale, error) { -// selector, err := unversioned.LabelSelectorAsSelector(deployment.Spec.Selector) -// if err != nil { -// return nil, fmt.Errorf("invalid label selector: %v", err) -// } -// return &Scale{ -// ObjectMeta: api.ObjectMeta{ -// Name: deployment.Name, -// Namespace: deployment.Namespace, -// CreationTimestamp: deployment.CreationTimestamp, -// }, -// Spec: ScaleSpec{ -// Replicas: deployment.Spec.Replicas, -// }, -// Status: ScaleStatus{ -// Replicas: deployment.Status.Replicas, -// Selector: selector.String(), -// }, -// }, nil -// } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/install/install_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/install/install_test.go deleted file mode 100644 index 311fad56d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/install/install_test.go +++ /dev/null @@ -1,119 +0,0 @@ -/* -Copyright 2014 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 install - -import ( - "encoding/json" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apimachinery/registered" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" - "k8s.io/kubernetes/pkg/runtime" -) - -func TestResourceVersioner(t *testing.T) { - daemonSet := extensions.DaemonSet{ObjectMeta: api.ObjectMeta{ResourceVersion: "10"}} - version, err := accessor.ResourceVersion(&daemonSet) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if version != "10" { - t.Errorf("unexpected version %v", version) - } - - daemonSetList := extensions.DaemonSetList{ListMeta: unversioned.ListMeta{ResourceVersion: "10"}} - version, err = accessor.ResourceVersion(&daemonSetList) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if version != "10" { - t.Errorf("unexpected version %v", version) - } -} - -func TestCodec(t *testing.T) { - daemonSet := extensions.DaemonSet{} - // We do want to use package registered rather than testapi here, because we - // want to test if the package install and package registered work as expected. - data, err := runtime.Encode(api.Codecs.LegacyCodec(registered.GroupOrDie(extensions.GroupName).GroupVersion), &daemonSet) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - other := extensions.DaemonSet{} - if err := json.Unmarshal(data, &other); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if other.APIVersion != registered.GroupOrDie(extensions.GroupName).GroupVersion.String() || other.Kind != "DaemonSet" { - t.Errorf("unexpected unmarshalled object %#v", other) - } -} - -func TestInterfacesFor(t *testing.T) { - if _, err := registered.GroupOrDie(extensions.GroupName).InterfacesFor(extensions.SchemeGroupVersion); err == nil { - t.Fatalf("unexpected non-error: %v", err) - } - for i, version := range registered.GroupOrDie(extensions.GroupName).GroupVersions { - if vi, err := registered.GroupOrDie(extensions.GroupName).InterfacesFor(version); err != nil || vi == nil { - t.Fatalf("%d: unexpected result: %v", i, err) - } - } -} - -func TestRESTMapper(t *testing.T) { - gv := v1beta1.SchemeGroupVersion - hpaGVK := gv.WithKind("HorizontalPodAutoscaler") - daemonSetGVK := gv.WithKind("DaemonSet") - - if gvk, err := registered.GroupOrDie(extensions.GroupName).RESTMapper.KindFor(gv.WithResource("horizontalpodautoscalers")); err != nil || gvk != hpaGVK { - t.Errorf("unexpected version mapping: %v %v", gvk, err) - } - - if m, err := registered.GroupOrDie(extensions.GroupName).RESTMapper.RESTMapping(daemonSetGVK.GroupKind(), ""); err != nil || m.GroupVersionKind != daemonSetGVK || m.Resource != "daemonsets" { - t.Errorf("unexpected version mapping: %#v %v", m, err) - } - - for _, version := range registered.GroupOrDie(extensions.GroupName).GroupVersions { - mapping, err := registered.GroupOrDie(extensions.GroupName).RESTMapper.RESTMapping(hpaGVK.GroupKind(), version.Version) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if mapping.Resource != "horizontalpodautoscalers" { - t.Errorf("incorrect resource name: %#v", mapping) - } - if mapping.GroupVersionKind.GroupVersion() != version { - t.Errorf("incorrect groupVersion: %v", mapping) - } - - interfaces, _ := registered.GroupOrDie(extensions.GroupName).InterfacesFor(version) - if mapping.ObjectConvertor != interfaces.ObjectConvertor { - t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces) - } - - rc := &extensions.HorizontalPodAutoscaler{ObjectMeta: api.ObjectMeta{Name: "foo"}} - name, err := mapping.MetadataAccessor.Name(rc) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if name != "foo" { - t.Errorf("unable to retrieve object meta with: %v", mapping.MetadataAccessor) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/register.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/register.go index db7b93770..057940d16 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/register.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/register.go @@ -47,8 +47,6 @@ func AddToScheme(scheme *runtime.Scheme) { func addKnownTypes(scheme *runtime.Scheme) { // TODO this gets cleaned up when the types are fixed scheme.AddKnownTypes(SchemeGroupVersion, - &ClusterAutoscaler{}, - &ClusterAutoscalerList{}, &Deployment{}, &DeploymentList{}, &DeploymentRollback{}, @@ -75,8 +73,6 @@ func addKnownTypes(scheme *runtime.Scheme) { ) } -func (obj *ClusterAutoscaler) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *ClusterAutoscalerList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } func (obj *Deployment) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } func (obj *DeploymentList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } func (obj *DeploymentRollback) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/types.generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/types.generated.go index fabe00c2f..2df949897 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/types.generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/types.generated.go @@ -68,7 +68,7 @@ func init() { if false { // reference the types, but skip this branch at build/run time var v0 pkg2_api.ObjectMeta var v1 pkg4_resource.Quantity - var v2 pkg1_unversioned.TypeMeta + var v2 pkg1_unversioned.LabelSelector var v3 pkg3_types.UID var v4 pkg6_intstr.IntOrString var v5 pkg5_inf.Dec @@ -263,7 +263,7 @@ func (x *ScaleStatus) CodecEncodeSelf(e *codec1978.Encoder) { var yyq2 [2]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false - yyq2[1] = len(x.Selector) != 0 + yyq2[1] = x.Selector != nil var yynn2 int if yyr2 || yy2arr2 { r.EncodeArrayStart(2) @@ -305,8 +305,9 @@ func (x *ScaleStatus) CodecEncodeSelf(e *codec1978.Encoder) { yym7 := z.EncBinary() _ = yym7 if false { + } else if z.HasExtensions() && z.EncExt(x.Selector) { } else { - z.F.EncMapStringStringV(x.Selector, false, e) + z.EncFallback(x.Selector) } } } else { @@ -323,8 +324,9 @@ func (x *ScaleStatus) CodecEncodeSelf(e *codec1978.Encoder) { yym8 := z.EncBinary() _ = yym8 if false { + } else if z.HasExtensions() && z.EncExt(x.Selector) { } else { - z.F.EncMapStringStringV(x.Selector, false, e) + z.EncFallback(x.Selector) } } } @@ -398,14 +400,19 @@ func (x *ScaleStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { } case "selector": if r.TryDecodeAsNil() { - x.Selector = nil + if x.Selector != nil { + x.Selector = nil + } } else { - yyv5 := &x.Selector + if x.Selector == nil { + x.Selector = new(pkg1_unversioned.LabelSelector) + } yym6 := z.DecBinary() _ = yym6 if false { + } else if z.HasExtensions() && z.DecExt(x.Selector) { } else { - z.F.DecMapStringStringX(yyv5, false, d) + z.DecFallback(x.Selector, false) } } default: @@ -450,14 +457,19 @@ func (x *ScaleStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.Selector = nil + if x.Selector != nil { + x.Selector = nil + } } else { - yyv9 := &x.Selector + if x.Selector == nil { + x.Selector = new(pkg1_unversioned.LabelSelector) + } yym10 := z.DecBinary() _ = yym10 if false { + } else if z.HasExtensions() && z.DecExt(x.Selector) { } else { - z.F.DecMapStringStringX(yyv9, false, d) + z.DecFallback(x.Selector, false) } } for { @@ -7022,16 +7034,17 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [4]bool + var yyq2 [5]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false - yyq2[0] = x.Replicas != 0 - yyq2[1] = x.UpdatedReplicas != 0 - yyq2[2] = x.AvailableReplicas != 0 - yyq2[3] = x.UnavailableReplicas != 0 + yyq2[0] = x.ObservedGeneration != 0 + yyq2[1] = x.Replicas != 0 + yyq2[2] = x.UpdatedReplicas != 0 + yyq2[3] = x.AvailableReplicas != 0 + yyq2[4] = x.UnavailableReplicas != 0 var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(4) + r.EncodeArrayStart(5) } else { yynn2 = 0 for _, b := range yyq2 { @@ -7049,7 +7062,7 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym4 if false { } else { - r.EncodeInt(int64(x.Replicas)) + r.EncodeInt(int64(x.ObservedGeneration)) } } else { r.EncodeInt(0) @@ -7057,13 +7070,13 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { if yyq2[0] { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("replicas")) + r.EncodeString(codecSelferC_UTF81234, string("observedGeneration")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym5 := z.EncBinary() _ = yym5 if false { } else { - r.EncodeInt(int64(x.Replicas)) + r.EncodeInt(int64(x.ObservedGeneration)) } } } @@ -7074,7 +7087,7 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym7 if false { } else { - r.EncodeInt(int64(x.UpdatedReplicas)) + r.EncodeInt(int64(x.Replicas)) } } else { r.EncodeInt(0) @@ -7082,13 +7095,13 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { if yyq2[1] { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("updatedReplicas")) + r.EncodeString(codecSelferC_UTF81234, string("replicas")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym8 := z.EncBinary() _ = yym8 if false { } else { - r.EncodeInt(int64(x.UpdatedReplicas)) + r.EncodeInt(int64(x.Replicas)) } } } @@ -7099,7 +7112,7 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym10 if false { } else { - r.EncodeInt(int64(x.AvailableReplicas)) + r.EncodeInt(int64(x.UpdatedReplicas)) } } else { r.EncodeInt(0) @@ -7107,13 +7120,13 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { if yyq2[2] { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("availableReplicas")) + r.EncodeString(codecSelferC_UTF81234, string("updatedReplicas")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym11 := z.EncBinary() _ = yym11 if false { } else { - r.EncodeInt(int64(x.AvailableReplicas)) + r.EncodeInt(int64(x.UpdatedReplicas)) } } } @@ -7124,7 +7137,7 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym13 if false { } else { - r.EncodeInt(int64(x.UnavailableReplicas)) + r.EncodeInt(int64(x.AvailableReplicas)) } } else { r.EncodeInt(0) @@ -7132,11 +7145,36 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { if yyq2[3] { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("unavailableReplicas")) + r.EncodeString(codecSelferC_UTF81234, string("availableReplicas")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym14 := z.EncBinary() _ = yym14 if false { + } else { + r.EncodeInt(int64(x.AvailableReplicas)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[4] { + yym16 := z.EncBinary() + _ = yym16 + if false { + } else { + r.EncodeInt(int64(x.UnavailableReplicas)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[4] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("unavailableReplicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym17 := z.EncBinary() + _ = yym17 + if false { } else { r.EncodeInt(int64(x.UnavailableReplicas)) } @@ -7203,6 +7241,12 @@ func (x *DeploymentStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { yys3 := string(yys3Slc) z.DecSendContainerState(codecSelfer_containerMapValue1234) switch yys3 { + case "observedGeneration": + if r.TryDecodeAsNil() { + x.ObservedGeneration = 0 + } else { + x.ObservedGeneration = int64(r.DecodeInt(64)) + } case "replicas": if r.TryDecodeAsNil() { x.Replicas = 0 @@ -7238,16 +7282,32 @@ func (x *DeploymentStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj8 int - var yyb8 bool - var yyhl8 bool = l >= 0 - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + var yyj9 int + var yyb9 bool + var yyhl9 bool = l >= 0 + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l } else { - yyb8 = r.CheckBreak() + yyb9 = r.CheckBreak() } - if yyb8 { + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.ObservedGeneration = 0 + } else { + x.ObservedGeneration = int64(r.DecodeInt(64)) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7257,13 +7317,13 @@ func (x *DeploymentStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) } else { x.Replicas = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l } else { - yyb8 = r.CheckBreak() + yyb9 = r.CheckBreak() } - if yyb8 { + if yyb9 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7273,13 +7333,13 @@ func (x *DeploymentStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) } else { x.UpdatedReplicas = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l } else { - yyb8 = r.CheckBreak() + yyb9 = r.CheckBreak() } - if yyb8 { + if yyb9 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7289,13 +7349,13 @@ func (x *DeploymentStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) } else { x.AvailableReplicas = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l } else { - yyb8 = r.CheckBreak() + yyb9 = r.CheckBreak() } - if yyb8 { + if yyb9 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7306,17 +7366,17 @@ func (x *DeploymentStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) x.UnavailableReplicas = int(r.DecodeInt(codecSelferBitsize1234)) } for { - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l } else { - yyb8 = r.CheckBreak() + yyb9 = r.CheckBreak() } - if yyb8 { + if yyb9 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj8-1, "") + z.DecStructFieldNotFound(yyj9-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -9877,16 +9937,17 @@ func (x *JobSpec) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [5]bool + var yyq2 [6]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false yyq2[0] = x.Parallelism != nil yyq2[1] = x.Completions != nil yyq2[2] = x.ActiveDeadlineSeconds != nil yyq2[3] = x.Selector != nil + yyq2[4] = x.ManualSelector != nil var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(5) + r.EncodeArrayStart(6) } else { yynn2 = 1 for _, b := range yyq2 { @@ -10039,14 +10100,49 @@ func (x *JobSpec) CodecEncodeSelf(e *codec1978.Encoder) { } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy22 := &x.Template - yy22.CodecEncodeSelf(e) + if yyq2[4] { + if x.ManualSelector == nil { + r.EncodeNil() + } else { + yy22 := *x.ManualSelector + yym23 := z.EncBinary() + _ = yym23 + if false { + } else { + r.EncodeBool(bool(yy22)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[4] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("manualSelector")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.ManualSelector == nil { + r.EncodeNil() + } else { + yy24 := *x.ManualSelector + yym25 := z.EncBinary() + _ = yym25 + if false { + } else { + r.EncodeBool(bool(yy24)) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yy27 := &x.Template + yy27.CodecEncodeSelf(e) } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("template")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy24 := &x.Template - yy24.CodecEncodeSelf(e) + yy29 := &x.Template + yy29.CodecEncodeSelf(e) } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayEnd1234) @@ -10174,12 +10270,28 @@ func (x *JobSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { z.DecFallback(x.Selector, false) } } + case "manualSelector": + if r.TryDecodeAsNil() { + if x.ManualSelector != nil { + x.ManualSelector = nil + } + } else { + if x.ManualSelector == nil { + x.ManualSelector = new(bool) + } + yym13 := z.DecBinary() + _ = yym13 + if false { + } else { + *((*bool)(x.ManualSelector)) = r.DecodeBool() + } + } case "template": if r.TryDecodeAsNil() { x.Template = pkg2_api.PodTemplateSpec{} } else { - yyv12 := &x.Template - yyv12.CodecDecodeSelf(d) + yyv14 := &x.Template + yyv14.CodecDecodeSelf(d) } default: z.DecStructFieldNotFound(-1, yys3) @@ -10192,16 +10304,16 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj13 int - var yyb13 bool - var yyhl13 bool = l >= 0 - yyj13++ - if yyhl13 { - yyb13 = yyj13 > l + var yyj15 int + var yyb15 bool + var yyhl15 bool = l >= 0 + yyj15++ + if yyhl15 { + yyb15 = yyj15 > l } else { - yyb13 = r.CheckBreak() + yyb15 = r.CheckBreak() } - if yyb13 { + if yyb15 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -10214,20 +10326,20 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if x.Parallelism == nil { x.Parallelism = new(int) } - yym15 := z.DecBinary() - _ = yym15 + yym17 := z.DecBinary() + _ = yym17 if false { } else { *((*int)(x.Parallelism)) = int(r.DecodeInt(codecSelferBitsize1234)) } } - yyj13++ - if yyhl13 { - yyb13 = yyj13 > l + yyj15++ + if yyhl15 { + yyb15 = yyj15 > l } else { - yyb13 = r.CheckBreak() + yyb15 = r.CheckBreak() } - if yyb13 { + if yyb15 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -10240,20 +10352,20 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if x.Completions == nil { x.Completions = new(int) } - yym17 := z.DecBinary() - _ = yym17 + yym19 := z.DecBinary() + _ = yym19 if false { } else { *((*int)(x.Completions)) = int(r.DecodeInt(codecSelferBitsize1234)) } } - yyj13++ - if yyhl13 { - yyb13 = yyj13 > l + yyj15++ + if yyhl15 { + yyb15 = yyj15 > l } else { - yyb13 = r.CheckBreak() + yyb15 = r.CheckBreak() } - if yyb13 { + if yyb15 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -10266,20 +10378,20 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if x.ActiveDeadlineSeconds == nil { x.ActiveDeadlineSeconds = new(int64) } - yym19 := z.DecBinary() - _ = yym19 + yym21 := z.DecBinary() + _ = yym21 if false { } else { *((*int64)(x.ActiveDeadlineSeconds)) = int64(r.DecodeInt(64)) } } - yyj13++ - if yyhl13 { - yyb13 = yyj13 > l + yyj15++ + if yyhl15 { + yyb15 = yyj15 > l } else { - yyb13 = r.CheckBreak() + yyb15 = r.CheckBreak() } - if yyb13 { + if yyb15 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -10292,21 +10404,47 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if x.Selector == nil { x.Selector = new(pkg1_unversioned.LabelSelector) } - yym21 := z.DecBinary() - _ = yym21 + yym23 := z.DecBinary() + _ = yym23 if false { } else if z.HasExtensions() && z.DecExt(x.Selector) { } else { z.DecFallback(x.Selector, false) } } - yyj13++ - if yyhl13 { - yyb13 = yyj13 > l + yyj15++ + if yyhl15 { + yyb15 = yyj15 > l } else { - yyb13 = r.CheckBreak() + yyb15 = r.CheckBreak() } - if yyb13 { + if yyb15 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.ManualSelector != nil { + x.ManualSelector = nil + } + } else { + if x.ManualSelector == nil { + x.ManualSelector = new(bool) + } + yym25 := z.DecBinary() + _ = yym25 + if false { + } else { + *((*bool)(x.ManualSelector)) = r.DecodeBool() + } + } + yyj15++ + if yyhl15 { + yyb15 = yyj15 > l + } else { + yyb15 = r.CheckBreak() + } + if yyb15 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -10314,21 +10452,21 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if r.TryDecodeAsNil() { x.Template = pkg2_api.PodTemplateSpec{} } else { - yyv22 := &x.Template - yyv22.CodecDecodeSelf(d) + yyv26 := &x.Template + yyv26.CodecDecodeSelf(d) } for { - yyj13++ - if yyhl13 { - yyb13 = yyj13 > l + yyj15++ + if yyhl15 { + yyb15 = yyj15 > l } else { - yyb13 = r.CheckBreak() + yyb15 = r.CheckBreak() } - if yyb13 { + if yyb15 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj13-1, "") + z.DecStructFieldNotFound(yyj15-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -13766,1134 +13904,6 @@ func (x *IngressBackend) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } -func (x NodeResource) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x)) - } -} - -func (x *NodeResource) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - *((*string)(x)) = r.DecodeString() - } -} - -func (x *NodeUtilization) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [2]bool - _, _, _ = yysep2, yyq2, yy2arr2 - const yyr2 bool = false - var yynn2 int - if yyr2 || yy2arr2 { - r.EncodeArrayStart(2) - } else { - yynn2 = 2 - for _, b := range yyq2 { - if b { - yynn2++ - } - } - r.EncodeMapStart(yynn2) - yynn2 = 0 - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - x.Resource.CodecEncodeSelf(e) - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("resource")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - x.Resource.CodecEncodeSelf(e) - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym7 := z.EncBinary() - _ = yym7 - if false { - } else { - r.EncodeFloat64(float64(x.Value)) - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("value")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym8 := z.EncBinary() - _ = yym8 - if false { - } else { - r.EncodeFloat64(float64(x.Value)) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - z.EncSendContainerState(codecSelfer_containerMapEnd1234) - } - } - } -} - -func (x *NodeUtilization) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap1234 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerMapEnd1234) - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray1234 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) - } - } -} - -func (x *NodeUtilization) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - z.DecSendContainerState(codecSelfer_containerMapKey1234) - yys3Slc = r.DecodeBytes(yys3Slc, true, true) - yys3 := string(yys3Slc) - z.DecSendContainerState(codecSelfer_containerMapValue1234) - switch yys3 { - case "resource": - if r.TryDecodeAsNil() { - x.Resource = "" - } else { - x.Resource = NodeResource(r.DecodeString()) - } - case "value": - if r.TryDecodeAsNil() { - x.Value = 0 - } else { - x.Value = float64(r.DecodeFloat(false)) - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - z.DecSendContainerState(codecSelfer_containerMapEnd1234) -} - -func (x *NodeUtilization) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj6 int - var yyb6 bool - var yyhl6 bool = l >= 0 - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l - } else { - yyb6 = r.CheckBreak() - } - if yyb6 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.Resource = "" - } else { - x.Resource = NodeResource(r.DecodeString()) - } - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l - } else { - yyb6 = r.CheckBreak() - } - if yyb6 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.Value = 0 - } else { - x.Value = float64(r.DecodeFloat(false)) - } - for { - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l - } else { - yyb6 = r.CheckBreak() - } - if yyb6 { - break - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj6-1, "") - } - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) -} - -func (x *ClusterAutoscalerSpec) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [3]bool - _, _, _ = yysep2, yyq2, yy2arr2 - const yyr2 bool = false - var yynn2 int - if yyr2 || yy2arr2 { - r.EncodeArrayStart(3) - } else { - yynn2 = 3 - for _, b := range yyq2 { - if b { - yynn2++ - } - } - r.EncodeMapStart(yynn2) - yynn2 = 0 - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym4 := z.EncBinary() - _ = yym4 - if false { - } else { - r.EncodeInt(int64(x.MinNodes)) - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("minNodes")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym5 := z.EncBinary() - _ = yym5 - if false { - } else { - r.EncodeInt(int64(x.MinNodes)) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym7 := z.EncBinary() - _ = yym7 - if false { - } else { - r.EncodeInt(int64(x.MaxNodes)) - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("maxNodes")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym8 := z.EncBinary() - _ = yym8 - if false { - } else { - r.EncodeInt(int64(x.MaxNodes)) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if x.TargetUtilization == nil { - r.EncodeNil() - } else { - yym10 := z.EncBinary() - _ = yym10 - if false { - } else { - h.encSliceNodeUtilization(([]NodeUtilization)(x.TargetUtilization), e) - } - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("target")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - if x.TargetUtilization == nil { - r.EncodeNil() - } else { - yym11 := z.EncBinary() - _ = yym11 - if false { - } else { - h.encSliceNodeUtilization(([]NodeUtilization)(x.TargetUtilization), e) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - z.EncSendContainerState(codecSelfer_containerMapEnd1234) - } - } - } -} - -func (x *ClusterAutoscalerSpec) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap1234 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerMapEnd1234) - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray1234 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) - } - } -} - -func (x *ClusterAutoscalerSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - z.DecSendContainerState(codecSelfer_containerMapKey1234) - yys3Slc = r.DecodeBytes(yys3Slc, true, true) - yys3 := string(yys3Slc) - z.DecSendContainerState(codecSelfer_containerMapValue1234) - switch yys3 { - case "minNodes": - if r.TryDecodeAsNil() { - x.MinNodes = 0 - } else { - x.MinNodes = int(r.DecodeInt(codecSelferBitsize1234)) - } - case "maxNodes": - if r.TryDecodeAsNil() { - x.MaxNodes = 0 - } else { - x.MaxNodes = int(r.DecodeInt(codecSelferBitsize1234)) - } - case "target": - if r.TryDecodeAsNil() { - x.TargetUtilization = nil - } else { - yyv6 := &x.TargetUtilization - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - h.decSliceNodeUtilization((*[]NodeUtilization)(yyv6), d) - } - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - z.DecSendContainerState(codecSelfer_containerMapEnd1234) -} - -func (x *ClusterAutoscalerSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj8 int - var yyb8 bool - var yyhl8 bool = l >= 0 - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.MinNodes = 0 - } else { - x.MinNodes = int(r.DecodeInt(codecSelferBitsize1234)) - } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.MaxNodes = 0 - } else { - x.MaxNodes = int(r.DecodeInt(codecSelferBitsize1234)) - } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.TargetUtilization = nil - } else { - yyv11 := &x.TargetUtilization - yym12 := z.DecBinary() - _ = yym12 - if false { - } else { - h.decSliceNodeUtilization((*[]NodeUtilization)(yyv11), d) - } - } - for { - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - break - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj8-1, "") - } - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) -} - -func (x *ClusterAutoscaler) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [4]bool - _, _, _ = yysep2, yyq2, yy2arr2 - const yyr2 bool = false - yyq2[0] = true - yyq2[1] = true - yyq2[2] = x.Kind != "" - yyq2[3] = x.APIVersion != "" - var yynn2 int - if yyr2 || yy2arr2 { - r.EncodeArrayStart(4) - } else { - yynn2 = 0 - for _, b := range yyq2 { - if b { - yynn2++ - } - } - r.EncodeMapStart(yynn2) - yynn2 = 0 - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[0] { - yy4 := &x.ObjectMeta - yy4.CodecEncodeSelf(e) - } else { - r.EncodeNil() - } - } else { - if yyq2[0] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("metadata")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy6 := &x.ObjectMeta - yy6.CodecEncodeSelf(e) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[1] { - yy9 := &x.Spec - yy9.CodecEncodeSelf(e) - } else { - r.EncodeNil() - } - } else { - if yyq2[1] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("spec")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy11 := &x.Spec - yy11.CodecEncodeSelf(e) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[2] { - yym14 := z.EncBinary() - _ = yym14 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) - } - } else { - r.EncodeString(codecSelferC_UTF81234, "") - } - } else { - if yyq2[2] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("kind")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym15 := z.EncBinary() - _ = yym15 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[3] { - yym17 := z.EncBinary() - _ = yym17 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) - } - } else { - r.EncodeString(codecSelferC_UTF81234, "") - } - } else { - if yyq2[3] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym18 := z.EncBinary() - _ = yym18 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - z.EncSendContainerState(codecSelfer_containerMapEnd1234) - } - } - } -} - -func (x *ClusterAutoscaler) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap1234 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerMapEnd1234) - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray1234 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) - } - } -} - -func (x *ClusterAutoscaler) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - z.DecSendContainerState(codecSelfer_containerMapKey1234) - yys3Slc = r.DecodeBytes(yys3Slc, true, true) - yys3 := string(yys3Slc) - z.DecSendContainerState(codecSelfer_containerMapValue1234) - switch yys3 { - case "metadata": - if r.TryDecodeAsNil() { - x.ObjectMeta = pkg2_api.ObjectMeta{} - } else { - yyv4 := &x.ObjectMeta - yyv4.CodecDecodeSelf(d) - } - case "spec": - if r.TryDecodeAsNil() { - x.Spec = ClusterAutoscalerSpec{} - } else { - yyv5 := &x.Spec - yyv5.CodecDecodeSelf(d) - } - case "kind": - if r.TryDecodeAsNil() { - x.Kind = "" - } else { - x.Kind = string(r.DecodeString()) - } - case "apiVersion": - if r.TryDecodeAsNil() { - x.APIVersion = "" - } else { - x.APIVersion = string(r.DecodeString()) - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - z.DecSendContainerState(codecSelfer_containerMapEnd1234) -} - -func (x *ClusterAutoscaler) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj8 int - var yyb8 bool - var yyhl8 bool = l >= 0 - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.ObjectMeta = pkg2_api.ObjectMeta{} - } else { - yyv9 := &x.ObjectMeta - yyv9.CodecDecodeSelf(d) - } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.Spec = ClusterAutoscalerSpec{} - } else { - yyv10 := &x.Spec - yyv10.CodecDecodeSelf(d) - } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.Kind = "" - } else { - x.Kind = string(r.DecodeString()) - } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.APIVersion = "" - } else { - x.APIVersion = string(r.DecodeString()) - } - for { - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - break - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj8-1, "") - } - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) -} - -func (x *ClusterAutoscalerList) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [4]bool - _, _, _ = yysep2, yyq2, yy2arr2 - const yyr2 bool = false - yyq2[0] = true - yyq2[2] = x.Kind != "" - yyq2[3] = x.APIVersion != "" - var yynn2 int - if yyr2 || yy2arr2 { - r.EncodeArrayStart(4) - } else { - yynn2 = 1 - for _, b := range yyq2 { - if b { - yynn2++ - } - } - r.EncodeMapStart(yynn2) - yynn2 = 0 - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[0] { - yy4 := &x.ListMeta - yym5 := z.EncBinary() - _ = yym5 - if false { - } else if z.HasExtensions() && z.EncExt(yy4) { - } else { - z.EncFallback(yy4) - } - } else { - r.EncodeNil() - } - } else { - if yyq2[0] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("metadata")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy6 := &x.ListMeta - yym7 := z.EncBinary() - _ = yym7 - if false { - } else if z.HasExtensions() && z.EncExt(yy6) { - } else { - z.EncFallback(yy6) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if x.Items == nil { - r.EncodeNil() - } else { - yym9 := z.EncBinary() - _ = yym9 - if false { - } else { - h.encSliceClusterAutoscaler(([]ClusterAutoscaler)(x.Items), e) - } - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("items")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - if x.Items == nil { - r.EncodeNil() - } else { - yym10 := z.EncBinary() - _ = yym10 - if false { - } else { - h.encSliceClusterAutoscaler(([]ClusterAutoscaler)(x.Items), e) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[2] { - yym12 := z.EncBinary() - _ = yym12 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) - } - } else { - r.EncodeString(codecSelferC_UTF81234, "") - } - } else { - if yyq2[2] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("kind")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym13 := z.EncBinary() - _ = yym13 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[3] { - yym15 := z.EncBinary() - _ = yym15 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) - } - } else { - r.EncodeString(codecSelferC_UTF81234, "") - } - } else { - if yyq2[3] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym16 := z.EncBinary() - _ = yym16 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - z.EncSendContainerState(codecSelfer_containerMapEnd1234) - } - } - } -} - -func (x *ClusterAutoscalerList) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap1234 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerMapEnd1234) - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray1234 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) - } - } -} - -func (x *ClusterAutoscalerList) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - z.DecSendContainerState(codecSelfer_containerMapKey1234) - yys3Slc = r.DecodeBytes(yys3Slc, true, true) - yys3 := string(yys3Slc) - z.DecSendContainerState(codecSelfer_containerMapValue1234) - switch yys3 { - case "metadata": - if r.TryDecodeAsNil() { - x.ListMeta = pkg1_unversioned.ListMeta{} - } else { - yyv4 := &x.ListMeta - yym5 := z.DecBinary() - _ = yym5 - if false { - } else if z.HasExtensions() && z.DecExt(yyv4) { - } else { - z.DecFallback(yyv4, false) - } - } - case "items": - if r.TryDecodeAsNil() { - x.Items = nil - } else { - yyv6 := &x.Items - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - h.decSliceClusterAutoscaler((*[]ClusterAutoscaler)(yyv6), d) - } - } - case "kind": - if r.TryDecodeAsNil() { - x.Kind = "" - } else { - x.Kind = string(r.DecodeString()) - } - case "apiVersion": - if r.TryDecodeAsNil() { - x.APIVersion = "" - } else { - x.APIVersion = string(r.DecodeString()) - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - z.DecSendContainerState(codecSelfer_containerMapEnd1234) -} - -func (x *ClusterAutoscalerList) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj10 int - var yyb10 bool - var yyhl10 bool = l >= 0 - yyj10++ - if yyhl10 { - yyb10 = yyj10 > l - } else { - yyb10 = r.CheckBreak() - } - if yyb10 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.ListMeta = pkg1_unversioned.ListMeta{} - } else { - yyv11 := &x.ListMeta - yym12 := z.DecBinary() - _ = yym12 - if false { - } else if z.HasExtensions() && z.DecExt(yyv11) { - } else { - z.DecFallback(yyv11, false) - } - } - yyj10++ - if yyhl10 { - yyb10 = yyj10 > l - } else { - yyb10 = r.CheckBreak() - } - if yyb10 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.Items = nil - } else { - yyv13 := &x.Items - yym14 := z.DecBinary() - _ = yym14 - if false { - } else { - h.decSliceClusterAutoscaler((*[]ClusterAutoscaler)(yyv13), d) - } - } - yyj10++ - if yyhl10 { - yyb10 = yyj10 > l - } else { - yyb10 = r.CheckBreak() - } - if yyb10 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.Kind = "" - } else { - x.Kind = string(r.DecodeString()) - } - yyj10++ - if yyhl10 { - yyb10 = yyj10 > l - } else { - yyb10 = r.CheckBreak() - } - if yyb10 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.APIVersion = "" - } else { - x.APIVersion = string(r.DecodeString()) - } - for { - yyj10++ - if yyhl10 { - yyb10 = yyj10 > l - } else { - yyb10 = r.CheckBreak() - } - if yyb10 { - break - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj10-1, "") - } - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) -} - func (x *ReplicaSet) CodecEncodeSelf(e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) @@ -15599,7 +14609,7 @@ func (x *ReplicaSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false yyq2[1] = x.Selector != nil - yyq2[2] = x.Template != nil + yyq2[2] = true var yynn2 int if yyr2 || yy2arr2 { r.EncodeArrayStart(3) @@ -15670,11 +14680,8 @@ func (x *ReplicaSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) if yyq2[2] { - if x.Template == nil { - r.EncodeNil() - } else { - x.Template.CodecEncodeSelf(e) - } + yy10 := &x.Template + yy10.CodecEncodeSelf(e) } else { r.EncodeNil() } @@ -15683,11 +14690,8 @@ func (x *ReplicaSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("template")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - if x.Template == nil { - r.EncodeNil() - } else { - x.Template.CodecEncodeSelf(e) - } + yy12 := &x.Template + yy12.CodecEncodeSelf(e) } } if yyr2 || yy2arr2 { @@ -15776,14 +14780,10 @@ func (x *ReplicaSetSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { } case "template": if r.TryDecodeAsNil() { - if x.Template != nil { - x.Template = nil - } + x.Template = pkg2_api.PodTemplateSpec{} } else { - if x.Template == nil { - x.Template = new(pkg2_api.PodTemplateSpec) - } - x.Template.CodecDecodeSelf(d) + yyv7 := &x.Template + yyv7.CodecDecodeSelf(d) } default: z.DecStructFieldNotFound(-1, yys3) @@ -15854,14 +14854,10 @@ func (x *ReplicaSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - if x.Template != nil { - x.Template = nil - } + x.Template = pkg2_api.PodTemplateSpec{} } else { - if x.Template == nil { - x.Template = new(pkg2_api.PodTemplateSpec) - } - x.Template.CodecDecodeSelf(d) + yyv12 := &x.Template + yyv12.CodecDecodeSelf(d) } for { yyj8++ @@ -15893,13 +14889,14 @@ func (x *ReplicaSetStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [2]bool + var yyq2 [3]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false - yyq2[1] = x.ObservedGeneration != 0 + yyq2[1] = x.FullyLabeledReplicas != 0 + yyq2[2] = x.ObservedGeneration != 0 var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(2) + r.EncodeArrayStart(3) } else { yynn2 = 1 for _, b := range yyq2 { @@ -15936,7 +14933,7 @@ func (x *ReplicaSetStatus) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym7 if false { } else { - r.EncodeInt(int64(x.ObservedGeneration)) + r.EncodeInt(int64(x.FullyLabeledReplicas)) } } else { r.EncodeInt(0) @@ -15944,11 +14941,36 @@ func (x *ReplicaSetStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { if yyq2[1] { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("observedGeneration")) + r.EncodeString(codecSelferC_UTF81234, string("fullyLabeledReplicas")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym8 := z.EncBinary() _ = yym8 if false { + } else { + r.EncodeInt(int64(x.FullyLabeledReplicas)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yym10 := z.EncBinary() + _ = yym10 + if false { + } else { + r.EncodeInt(int64(x.ObservedGeneration)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("observedGeneration")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym11 := z.EncBinary() + _ = yym11 + if false { } else { r.EncodeInt(int64(x.ObservedGeneration)) } @@ -16021,6 +15043,12 @@ func (x *ReplicaSetStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { } else { x.Replicas = int(r.DecodeInt(codecSelferBitsize1234)) } + case "fullyLabeledReplicas": + if r.TryDecodeAsNil() { + x.FullyLabeledReplicas = 0 + } else { + x.FullyLabeledReplicas = int(r.DecodeInt(codecSelferBitsize1234)) + } case "observedGeneration": if r.TryDecodeAsNil() { x.ObservedGeneration = 0 @@ -16038,16 +15066,16 @@ func (x *ReplicaSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj6 int - var yyb6 bool - var yyhl6 bool = l >= 0 - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l + var yyj7 int + var yyb7 bool + var yyhl7 bool = l >= 0 + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb6 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb6 { + if yyb7 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -16057,13 +15085,29 @@ func (x *ReplicaSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) } else { x.Replicas = int(r.DecodeInt(codecSelferBitsize1234)) } - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb6 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb6 { + if yyb7 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.FullyLabeledReplicas = 0 + } else { + x.FullyLabeledReplicas = int(r.DecodeInt(codecSelferBitsize1234)) + } + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l + } else { + yyb7 = r.CheckBreak() + } + if yyb7 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -16074,17 +15118,17 @@ func (x *ReplicaSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) x.ObservedGeneration = int64(r.DecodeInt(64)) } for { - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb6 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb6 { + if yyb7 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj6-1, "") + z.DecStructFieldNotFound(yyj7-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -16631,7 +15675,7 @@ func (x *PodSecurityPolicySpec) CodecEncodeSelf(e *codec1978.Encoder) { if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) if yyq2[7] { - yy25 := &x.SELinuxContext + yy25 := &x.SELinux yy25.CodecEncodeSelf(e) } else { r.EncodeNil() @@ -16639,9 +15683,9 @@ func (x *PodSecurityPolicySpec) CodecEncodeSelf(e *codec1978.Encoder) { } else { if yyq2[7] { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("seLinuxContext")) + r.EncodeString(codecSelferC_UTF81234, string("seLinux")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy27 := &x.SELinuxContext + yy27 := &x.SELinux yy27.CodecEncodeSelf(e) } } @@ -16783,11 +15827,11 @@ func (x *PodSecurityPolicySpec) codecDecodeSelfFromMap(l int, d *codec1978.Decod } else { x.HostIPC = bool(r.DecodeBool()) } - case "seLinuxContext": + case "seLinux": if r.TryDecodeAsNil() { - x.SELinuxContext = SELinuxContextStrategyOptions{} + x.SELinux = SELinuxStrategyOptions{} } else { - yyv14 := &x.SELinuxContext + yyv14 := &x.SELinux yyv14.CodecDecodeSelf(d) } case "runAsUser": @@ -16953,9 +15997,9 @@ func (x *PodSecurityPolicySpec) codecDecodeSelfFromArray(l int, d *codec1978.Dec } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.SELinuxContext = SELinuxContextStrategyOptions{} + x.SELinux = SELinuxStrategyOptions{} } else { - yyv27 := &x.SELinuxContext + yyv27 := &x.SELinux yyv27.CodecDecodeSelf(d) } yyj16++ @@ -17220,7 +16264,7 @@ func (x *FSType) CodecDecodeSelf(d *codec1978.Decoder) { } } -func (x *SELinuxContextStrategyOptions) CodecEncodeSelf(e *codec1978.Encoder) { +func (x *SELinuxStrategyOptions) CodecEncodeSelf(e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) _, _, _ = h, z, r @@ -17253,12 +16297,12 @@ func (x *SELinuxContextStrategyOptions) CodecEncodeSelf(e *codec1978.Encoder) { } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - x.Type.CodecEncodeSelf(e) + x.Rule.CodecEncodeSelf(e) } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("type")) + r.EncodeString(codecSelferC_UTF81234, string("rule")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - x.Type.CodecEncodeSelf(e) + x.Rule.CodecEncodeSelf(e) } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) @@ -17292,7 +16336,7 @@ func (x *SELinuxContextStrategyOptions) CodecEncodeSelf(e *codec1978.Encoder) { } } -func (x *SELinuxContextStrategyOptions) CodecDecodeSelf(d *codec1978.Decoder) { +func (x *SELinuxStrategyOptions) CodecDecodeSelf(d *codec1978.Decoder) { var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r @@ -17322,7 +16366,7 @@ func (x *SELinuxContextStrategyOptions) CodecDecodeSelf(d *codec1978.Decoder) { } } -func (x *SELinuxContextStrategyOptions) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { +func (x *SELinuxStrategyOptions) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r @@ -17344,11 +16388,11 @@ func (x *SELinuxContextStrategyOptions) codecDecodeSelfFromMap(l int, d *codec19 yys3 := string(yys3Slc) z.DecSendContainerState(codecSelfer_containerMapValue1234) switch yys3 { - case "type": + case "rule": if r.TryDecodeAsNil() { - x.Type = "" + x.Rule = "" } else { - x.Type = SELinuxContextStrategy(r.DecodeString()) + x.Rule = SELinuxStrategy(r.DecodeString()) } case "seLinuxOptions": if r.TryDecodeAsNil() { @@ -17368,7 +16412,7 @@ func (x *SELinuxContextStrategyOptions) codecDecodeSelfFromMap(l int, d *codec19 z.DecSendContainerState(codecSelfer_containerMapEnd1234) } -func (x *SELinuxContextStrategyOptions) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { +func (x *SELinuxStrategyOptions) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r @@ -17387,9 +16431,9 @@ func (x *SELinuxContextStrategyOptions) codecDecodeSelfFromArray(l int, d *codec } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.Type = "" + x.Rule = "" } else { - x.Type = SELinuxContextStrategy(r.DecodeString()) + x.Rule = SELinuxStrategy(r.DecodeString()) } yyj6++ if yyhl6 { @@ -17428,7 +16472,7 @@ func (x *SELinuxContextStrategyOptions) codecDecodeSelfFromArray(l int, d *codec z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } -func (x SELinuxContextStrategy) CodecEncodeSelf(e *codec1978.Encoder) { +func (x SELinuxStrategy) CodecEncodeSelf(e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) _, _, _ = h, z, r @@ -17441,7 +16485,7 @@ func (x SELinuxContextStrategy) CodecEncodeSelf(e *codec1978.Encoder) { } } -func (x *SELinuxContextStrategy) CodecDecodeSelf(d *codec1978.Decoder) { +func (x *SELinuxStrategy) CodecDecodeSelf(d *codec1978.Decoder) { var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r @@ -17487,12 +16531,12 @@ func (x *RunAsUserStrategyOptions) CodecEncodeSelf(e *codec1978.Encoder) { } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - x.Type.CodecEncodeSelf(e) + x.Rule.CodecEncodeSelf(e) } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("type")) + r.EncodeString(codecSelferC_UTF81234, string("rule")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - x.Type.CodecEncodeSelf(e) + x.Rule.CodecEncodeSelf(e) } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) @@ -17588,11 +16632,11 @@ func (x *RunAsUserStrategyOptions) codecDecodeSelfFromMap(l int, d *codec1978.De yys3 := string(yys3Slc) z.DecSendContainerState(codecSelfer_containerMapValue1234) switch yys3 { - case "type": + case "rule": if r.TryDecodeAsNil() { - x.Type = "" + x.Rule = "" } else { - x.Type = RunAsUserStrategy(r.DecodeString()) + x.Rule = RunAsUserStrategy(r.DecodeString()) } case "ranges": if r.TryDecodeAsNil() { @@ -17632,9 +16676,9 @@ func (x *RunAsUserStrategyOptions) codecDecodeSelfFromArray(l int, d *codec1978. } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.Type = "" + x.Rule = "" } else { - x.Type = RunAsUserStrategy(r.DecodeString()) + x.Rule = RunAsUserStrategy(r.DecodeString()) } yyj7++ if yyhl7 { @@ -18881,7 +17925,7 @@ func (x codecSelfer1234) decSliceDeployment(v *[]Deployment, d *codec1978.Decode yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 624) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 632) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] @@ -19238,7 +18282,7 @@ func (x codecSelfer1234) decSliceJob(v *[]Job, d *codec1978.Decoder) { yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 616) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 624) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] @@ -19913,244 +18957,6 @@ func (x codecSelfer1234) decSliceHTTPIngressPath(v *[]HTTPIngressPath, d *codec1 } } -func (x codecSelfer1234) encSliceNodeUtilization(v []NodeUtilization, e *codec1978.Encoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - r.EncodeArrayStart(len(v)) - for _, yyv1 := range v { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy2 := &yyv1 - yy2.CodecEncodeSelf(e) - } - z.EncSendContainerState(codecSelfer_containerArrayEnd1234) -} - -func (x codecSelfer1234) decSliceNodeUtilization(v *[]NodeUtilization, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - - yyv1 := *v - yyh1, yyl1 := z.DecSliceHelperStart() - var yyc1 bool - _ = yyc1 - if yyl1 == 0 { - if yyv1 == nil { - yyv1 = []NodeUtilization{} - yyc1 = true - } else if len(yyv1) != 0 { - yyv1 = yyv1[:0] - yyc1 = true - } - } else if yyl1 > 0 { - var yyrr1, yyrl1 int - var yyrt1 bool - _, _ = yyrl1, yyrt1 - yyrr1 = yyl1 // len(yyv1) - if yyl1 > cap(yyv1) { - - yyrg1 := len(yyv1) > 0 - yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 24) - if yyrt1 { - if yyrl1 <= cap(yyv1) { - yyv1 = yyv1[:yyrl1] - } else { - yyv1 = make([]NodeUtilization, yyrl1) - } - } else { - yyv1 = make([]NodeUtilization, yyrl1) - } - yyc1 = true - yyrr1 = len(yyv1) - if yyrg1 { - copy(yyv1, yyv21) - } - } else if yyl1 != len(yyv1) { - yyv1 = yyv1[:yyl1] - yyc1 = true - } - yyj1 := 0 - for ; yyj1 < yyrr1; yyj1++ { - yyh1.ElemContainerState(yyj1) - if r.TryDecodeAsNil() { - yyv1[yyj1] = NodeUtilization{} - } else { - yyv2 := &yyv1[yyj1] - yyv2.CodecDecodeSelf(d) - } - - } - if yyrt1 { - for ; yyj1 < yyl1; yyj1++ { - yyv1 = append(yyv1, NodeUtilization{}) - yyh1.ElemContainerState(yyj1) - if r.TryDecodeAsNil() { - yyv1[yyj1] = NodeUtilization{} - } else { - yyv3 := &yyv1[yyj1] - yyv3.CodecDecodeSelf(d) - } - - } - } - - } else { - yyj1 := 0 - for ; !r.CheckBreak(); yyj1++ { - - if yyj1 >= len(yyv1) { - yyv1 = append(yyv1, NodeUtilization{}) // var yyz1 NodeUtilization - yyc1 = true - } - yyh1.ElemContainerState(yyj1) - if yyj1 < len(yyv1) { - if r.TryDecodeAsNil() { - yyv1[yyj1] = NodeUtilization{} - } else { - yyv4 := &yyv1[yyj1] - yyv4.CodecDecodeSelf(d) - } - - } else { - z.DecSwallow() - } - - } - if yyj1 < len(yyv1) { - yyv1 = yyv1[:yyj1] - yyc1 = true - } else if yyj1 == 0 && yyv1 == nil { - yyv1 = []NodeUtilization{} - yyc1 = true - } - } - yyh1.End() - if yyc1 { - *v = yyv1 - } -} - -func (x codecSelfer1234) encSliceClusterAutoscaler(v []ClusterAutoscaler, e *codec1978.Encoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - r.EncodeArrayStart(len(v)) - for _, yyv1 := range v { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy2 := &yyv1 - yy2.CodecEncodeSelf(e) - } - z.EncSendContainerState(codecSelfer_containerArrayEnd1234) -} - -func (x codecSelfer1234) decSliceClusterAutoscaler(v *[]ClusterAutoscaler, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - - yyv1 := *v - yyh1, yyl1 := z.DecSliceHelperStart() - var yyc1 bool - _ = yyc1 - if yyl1 == 0 { - if yyv1 == nil { - yyv1 = []ClusterAutoscaler{} - yyc1 = true - } else if len(yyv1) != 0 { - yyv1 = yyv1[:0] - yyc1 = true - } - } else if yyl1 > 0 { - var yyrr1, yyrl1 int - var yyrt1 bool - _, _ = yyrl1, yyrt1 - yyrr1 = yyl1 // len(yyv1) - if yyl1 > cap(yyv1) { - - yyrg1 := len(yyv1) > 0 - yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 232) - if yyrt1 { - if yyrl1 <= cap(yyv1) { - yyv1 = yyv1[:yyrl1] - } else { - yyv1 = make([]ClusterAutoscaler, yyrl1) - } - } else { - yyv1 = make([]ClusterAutoscaler, yyrl1) - } - yyc1 = true - yyrr1 = len(yyv1) - if yyrg1 { - copy(yyv1, yyv21) - } - } else if yyl1 != len(yyv1) { - yyv1 = yyv1[:yyl1] - yyc1 = true - } - yyj1 := 0 - for ; yyj1 < yyrr1; yyj1++ { - yyh1.ElemContainerState(yyj1) - if r.TryDecodeAsNil() { - yyv1[yyj1] = ClusterAutoscaler{} - } else { - yyv2 := &yyv1[yyj1] - yyv2.CodecDecodeSelf(d) - } - - } - if yyrt1 { - for ; yyj1 < yyl1; yyj1++ { - yyv1 = append(yyv1, ClusterAutoscaler{}) - yyh1.ElemContainerState(yyj1) - if r.TryDecodeAsNil() { - yyv1[yyj1] = ClusterAutoscaler{} - } else { - yyv3 := &yyv1[yyj1] - yyv3.CodecDecodeSelf(d) - } - - } - } - - } else { - yyj1 := 0 - for ; !r.CheckBreak(); yyj1++ { - - if yyj1 >= len(yyv1) { - yyv1 = append(yyv1, ClusterAutoscaler{}) // var yyz1 ClusterAutoscaler - yyc1 = true - } - yyh1.ElemContainerState(yyj1) - if yyj1 < len(yyv1) { - if r.TryDecodeAsNil() { - yyv1[yyj1] = ClusterAutoscaler{} - } else { - yyv4 := &yyv1[yyj1] - yyv4.CodecDecodeSelf(d) - } - - } else { - z.DecSwallow() - } - - } - if yyj1 < len(yyv1) { - yyv1 = yyv1[:yyj1] - yyc1 = true - } else if yyj1 == 0 && yyv1 == nil { - yyv1 = []ClusterAutoscaler{} - yyc1 = true - } - } - yyh1.End() - if yyc1 { - *v = yyv1 - } -} - func (x codecSelfer1234) encSliceReplicaSet(v []ReplicaSet, e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) @@ -20190,7 +18996,7 @@ func (x codecSelfer1234) decSliceReplicaSet(v *[]ReplicaSet, d *codec1978.Decode yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 232) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 560) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/types.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/types.go index 4bfbef8d0..f8a736bdf 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/types.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/types.go @@ -46,8 +46,9 @@ type ScaleStatus struct { // actual number of observed instances of the scaled object. Replicas int `json:"replicas"` - // label query over pods that should match the replicas count. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors - Selector map[string]string `json:"selector,omitempty"` + // label query over pods that should match the replicas count. + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors + Selector *unversioned.LabelSelector `json:"selector,omitempty"` } // +genclient=true,noMethods=true @@ -55,13 +56,13 @@ type ScaleStatus struct { // represents a scaling request for a resource. type Scale struct { unversioned.TypeMeta `json:",inline"` - // Standard object metadata; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata. + // Standard object metadata; More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata. api.ObjectMeta `json:"metadata,omitempty"` - // defines the behavior of the scale. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status. + // defines the behavior of the scale. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status. Spec ScaleSpec `json:"spec,omitempty"` - // current status of the scale. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status. Read-only. + // current status of the scale. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status. Read-only. Status ScaleStatus `json:"status,omitempty"` } @@ -72,9 +73,9 @@ type ReplicationControllerDummy struct { // SubresourceReference contains enough information to let you inspect or modify the referred subresource. type SubresourceReference struct { - // Kind of the referent; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds" + // Kind of the referent; More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds" Kind string `json:"kind,omitempty"` - // Name of the referent; More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names + // Name of the referent; More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#names Name string `json:"name,omitempty"` // API version of the referent APIVersion string `json:"apiVersion,omitempty"` @@ -152,7 +153,7 @@ type HorizontalPodAutoscaler struct { unversioned.TypeMeta `json:",inline"` api.ObjectMeta `json:"metadata,omitempty"` - // behaviour of autoscaler. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status. + // behaviour of autoscaler. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status. Spec HorizontalPodAutoscalerSpec `json:"spec,omitempty"` // current information about the autoscaler. @@ -332,6 +333,9 @@ type RollingUpdateDeployment struct { } type DeploymentStatus struct { + // The generation observed by the deployment controller. + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + // Total number of non-terminated pods targeted by this deployment (their labels match the selector). Replicas int `json:"replicas,omitempty"` @@ -404,14 +408,14 @@ type DaemonSetSpec struct { // Selector is a label query over pods that are managed by the daemon set. // Must match in order to be controlled. // If empty, defaulted to labels on Pod template. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors Selector *unversioned.LabelSelector `json:"selector,omitempty"` // Template is the object that describes the pod that will be created. // The DaemonSet will create exactly one copy of this pod on every node // that matches the template's node selector (or on every node if no node // selector is specified). - // More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#pod-template + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md#pod-template Template api.PodTemplateSpec `json:"template"` // TODO(madhusudancs): Uncomment while implementing DaemonSet updates. @@ -458,18 +462,18 @@ type DaemonSetStatus struct { type DaemonSet struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata api.ObjectMeta `json:"metadata,omitempty"` // Spec defines the desired behavior of this daemon set. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Spec DaemonSetSpec `json:"spec,omitempty"` // Status is the current status of this daemon set. This data may be // out of date by some window of time. // Populated by the system. // Read-only. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Status DaemonSetStatus `json:"status,omitempty"` } @@ -477,7 +481,7 @@ type DaemonSet struct { type DaemonSetList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata unversioned.ListMeta `json:"metadata,omitempty"` // Items is a list of daemon sets. @@ -487,7 +491,7 @@ type DaemonSetList struct { type ThirdPartyResourceDataList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata unversioned.ListMeta `json:"metadata,omitempty"` // Items is a list of third party objects Items []ThirdPartyResourceData `json:"items"` @@ -499,15 +503,15 @@ type ThirdPartyResourceDataList struct { type Job struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata api.ObjectMeta `json:"metadata,omitempty"` // Spec is a structure defining the expected behavior of a job. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Spec JobSpec `json:"spec,omitempty"` // Status is a structure describing current status of a job. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Status JobStatus `json:"status,omitempty"` } @@ -515,7 +519,7 @@ type Job struct { type JobList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata unversioned.ListMeta `json:"metadata,omitempty"` // Items is the list of Job. @@ -532,7 +536,10 @@ type JobSpec struct { Parallelism *int `json:"parallelism,omitempty"` // Completions specifies the desired number of successfully finished pods the - // job should be run with. When unset, any pod exiting signals the job to complete. + // job should be run with. Setting to nil means that the success of any + // pod signals the success of all pods, and allows parallelism to have any positive + // value. Setting to 1 means that parallelism is limited to 1 and the success of that + // pod signals the success of the job. Completions *int `json:"completions,omitempty"` // Optional duration in seconds relative to the startTime that the job may be active @@ -540,8 +547,20 @@ type JobSpec struct { ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty"` // Selector is a label query over pods that should match the pod count. + // Normally, the system sets this field for you. Selector *unversioned.LabelSelector `json:"selector,omitempty"` + // ManualSelector controls generation of pod labels and pod selectors. + // Leave `manualSelector` unset unless you are certain what you are doing. + // When false or unset, the system pick labels unique to this job + // and appends those labels to the pod template. When true, + // the user is responsible for picking unique labels and specifying + // the selector. Failure to pick a unique label may cause this + // and other jobs to not function correctly. However, You may see + // `manualSelector=true` in jobs that were created with the old `extensions/v1beta1` + // API. + ManualSelector *bool `json:"manualSelector,omitempty"` + // Template is the object that describes the pod that will be created when // executing a job. Template api.PodTemplateSpec `json:"template"` @@ -608,15 +627,15 @@ type JobCondition struct { type Ingress struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata api.ObjectMeta `json:"metadata,omitempty"` // Spec is the desired state of the Ingress. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Spec IngressSpec `json:"spec,omitempty"` // Status is the current state of the Ingress. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Status IngressStatus `json:"status,omitempty"` } @@ -624,7 +643,7 @@ type Ingress struct { type IngressList struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata unversioned.ListMeta `json:"metadata,omitempty"` // Items is the list of Ingress. @@ -750,67 +769,6 @@ type IngressBackend struct { ServicePort intstr.IntOrString `json:"servicePort"` } -type NodeResource string - -const ( - // Percentage of node's CPUs that is currently used. - CpuConsumption NodeResource = "CpuConsumption" - - // Percentage of node's CPUs that is currently requested for pods. - CpuRequest NodeResource = "CpuRequest" - - // Percentage od node's memory that is currently used. - MemConsumption NodeResource = "MemConsumption" - - // Percentage of node's CPUs that is currently requested for pods. - MemRequest NodeResource = "MemRequest" -) - -// NodeUtilization describes what percentage of a particular resource is used on a node. -type NodeUtilization struct { - Resource NodeResource `json:"resource"` - - // The accepted values are from 0 to 1. - Value float64 `json:"value"` -} - -// Configuration of the Cluster Autoscaler -type ClusterAutoscalerSpec struct { - // Minimum number of nodes that the cluster should have. - MinNodes int `json:"minNodes"` - - // Maximum number of nodes that the cluster should have. - MaxNodes int `json:"maxNodes"` - - // Target average utilization of the cluster nodes. New nodes will be added if one of the - // targets is exceeded. Cluster size will be decreased if the current utilization is too low - // for all targets. - TargetUtilization []NodeUtilization `json:"target"` -} - -type ClusterAutoscaler struct { - unversioned.TypeMeta `json:",inline"` - - // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata - // For now (experimental api) it is required that the name is set to "ClusterAutoscaler" and namespace is "default". - api.ObjectMeta `json:"metadata,omitempty"` - - // Spec defines the desired behavior of this daemon set. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status - Spec ClusterAutoscalerSpec `json:"spec,omitempty"` -} - -// There will be just one (or none) ClusterAutoscaler. -type ClusterAutoscalerList struct { - unversioned.TypeMeta `json:",inline"` - // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata - unversioned.ListMeta `json:"metadata,omitempty"` - - Items []ClusterAutoscaler `json:"items"` -} - // +genclient=true // ReplicaSet represents the configuration of a replica set. @@ -844,12 +802,12 @@ type ReplicaSetSpec struct { // Selector is a label query over pods that should match the replica count. // Must match in order to be controlled. // If empty, defaulted to labels on pod template. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors Selector *unversioned.LabelSelector `json:"selector,omitempty"` // Template is the object that describes the pod that will be created if // insufficient replicas are detected. - Template *api.PodTemplateSpec `json:"template,omitempty"` + Template api.PodTemplateSpec `json:"template,omitempty"` } // ReplicaSetStatus represents the current status of a ReplicaSet. @@ -857,6 +815,9 @@ type ReplicaSetStatus struct { // Replicas is the number of actual replicas. Replicas int `json:"replicas"` + // The number of pods that have labels matching the labels of the pod template of the replicaset. + FullyLabeledReplicas int `json:"fullyLabeledReplicas,omitempty"` + // ObservedGeneration is the most recent generation observed by the controller. ObservedGeneration int64 `json:"observedGeneration,omitempty"` } @@ -888,8 +849,8 @@ type PodSecurityPolicySpec struct { HostPID bool `json:"hostPID,omitempty"` // HostIPC determines if the policy allows the use of HostIPC in the pod spec. HostIPC bool `json:"hostIPC,omitempty"` - // SELinuxContext is the strategy that will dictate the allowable labels that may be set. - SELinuxContext SELinuxContextStrategyOptions `json:"seLinuxContext,omitempty"` + // SELinux is the strategy that will dictate the allowable labels that may be set. + SELinux SELinuxStrategyOptions `json:"seLinux,omitempty"` // RunAsUser is the strategy that will dictate the allowable RunAsUser values that may be set. RunAsUser RunAsUserStrategyOptions `json:"runAsUser,omitempty"` } @@ -924,30 +885,30 @@ var ( FC FSType = "fc" ) -// SELinuxContextStrategyOptions defines the strategy type and any options used to create the strategy. -type SELinuxContextStrategyOptions struct { - // Type is the strategy that will dictate the allowable labels that may be set. - Type SELinuxContextStrategy `json:"type"` +// SELinuxStrategyOptions defines the strategy type and any options used to create the strategy. +type SELinuxStrategyOptions struct { + // Rule is the strategy that will dictate the allowable labels that may be set. + Rule SELinuxStrategy `json:"rule"` // seLinuxOptions required to run as; required for MustRunAs - // More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context + // More info: http://releases.k8s.io/release-1.2/docs/design/security_context.md#security-context SELinuxOptions *api.SELinuxOptions `json:"seLinuxOptions,omitempty"` } -// SELinuxContextStrategyType denotes strategy types for generating SELinux options for a -// SecurityContext. -type SELinuxContextStrategy string +// SELinuxStrategy denotes strategy types for generating SELinux options for a +// Security. +type SELinuxStrategy string const ( // container must have SELinux labels of X applied. - SELinuxStrategyMustRunAs SELinuxContextStrategy = "MustRunAs" + SELinuxStrategyMustRunAs SELinuxStrategy = "MustRunAs" // container may make requests for any SELinux context labels. - SELinuxStrategyRunAsAny SELinuxContextStrategy = "RunAsAny" + SELinuxStrategyRunAsAny SELinuxStrategy = "RunAsAny" ) // RunAsUserStrategyOptions defines the strategy type and any options used to create the strategy. type RunAsUserStrategyOptions struct { - // Type is the strategy that will dictate the allowable RunAsUser values that may be set. - Type RunAsUserStrategy `json:"type"` + // Rule is the strategy that will dictate the allowable RunAsUser values that may be set. + Rule RunAsUserStrategy `json:"rule"` // Ranges are the allowed ranges of uids that may be used. Ranges []IDRange `json:"ranges,omitempty"` } @@ -960,7 +921,7 @@ type IDRange struct { Max int64 `json:"max"` } -// RunAsUserStrategyType denotes strategy types for generating RunAsUser values for a +// RunAsUserStrategy denotes strategy types for generating RunAsUser values for a // SecurityContext. type RunAsUserStrategy string diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/conversion.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/conversion.go index 595091e02..5f4841cb4 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/conversion.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/conversion.go @@ -34,6 +34,8 @@ func addConversionFuncs(scheme *runtime.Scheme) { err := scheme.AddConversionFuncs( Convert_api_PodSpec_To_v1_PodSpec, Convert_v1_PodSpec_To_api_PodSpec, + Convert_extensions_ScaleStatus_To_v1beta1_ScaleStatus, + Convert_v1beta1_ScaleStatus_To_extensions_ScaleStatus, Convert_extensions_DeploymentSpec_To_v1beta1_DeploymentSpec, Convert_v1beta1_DeploymentSpec_To_extensions_DeploymentSpec, Convert_extensions_DeploymentStrategy_To_v1beta1_DeploymentStrategy, @@ -42,6 +44,8 @@ func addConversionFuncs(scheme *runtime.Scheme) { Convert_v1beta1_RollingUpdateDeployment_To_extensions_RollingUpdateDeployment, Convert_extensions_ReplicaSetSpec_To_v1beta1_ReplicaSetSpec, Convert_v1beta1_ReplicaSetSpec_To_extensions_ReplicaSetSpec, + Convert_extensions_JobSpec_To_v1beta1_JobSpec, + Convert_v1beta1_JobSpec_To_extensions_JobSpec, ) if err != nil { // If one of the conversion functions is malformed, detect it immediately. @@ -91,6 +95,58 @@ func Convert_v1_PodSpec_To_api_PodSpec(in *v1.PodSpec, out *api.PodSpec, s conve return v1.Convert_v1_PodSpec_To_api_PodSpec(in, out, s) } +func Convert_extensions_ScaleStatus_To_v1beta1_ScaleStatus(in *extensions.ScaleStatus, out *ScaleStatus, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.ScaleStatus))(in) + } + out.Replicas = int32(in.Replicas) + + out.Selector = nil + out.TargetSelector = "" + if in.Selector != nil { + if in.Selector.MatchExpressions == nil || len(in.Selector.MatchExpressions) == 0 { + out.Selector = in.Selector.MatchLabels + } + + selector, err := unversioned.LabelSelectorAsSelector(in.Selector) + if err != nil { + return fmt.Errorf("invalid label selector: %v", err) + } + out.TargetSelector = selector.String() + } + return nil +} + +func Convert_v1beta1_ScaleStatus_To_extensions_ScaleStatus(in *ScaleStatus, out *extensions.ScaleStatus, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*ScaleStatus))(in) + } + out.Replicas = int(in.Replicas) + + // Normally when 2 fields map to the same internal value we favor the old field, since + // old clients can't be expected to know about new fields but clients that know about the + // new field can be expected to know about the old field (though that's not quite true, due + // to kubectl apply). However, these fields are readonly, so any non-nil value should work. + if in.TargetSelector != "" { + labelSelector, err := unversioned.ParseToLabelSelector(in.TargetSelector) + if err != nil { + out.Selector = nil + return fmt.Errorf("failed to parse target selector: %v", err) + } + out.Selector = labelSelector + } else if in.Selector != nil { + out.Selector = new(unversioned.LabelSelector) + selector := make(map[string]string) + for key, val := range in.Selector { + selector[key] = val + } + out.Selector.MatchLabels = selector + } else { + out.Selector = nil + } + return nil +} + func Convert_extensions_DeploymentSpec_To_v1beta1_DeploymentSpec(in *extensions.DeploymentSpec, out *DeploymentSpec, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*extensions.DeploymentSpec))(in) @@ -241,13 +297,9 @@ func Convert_extensions_ReplicaSetSpec_To_v1beta1_ReplicaSetSpec(in *extensions. } else { out.Selector = nil } - if in.Template != nil { - out.Template = new(v1.PodTemplateSpec) - if err := v1.Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(in.Template, out.Template, s); err != nil { - return err - } - } else { - out.Template = nil + + if err := v1.Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err } return nil } @@ -267,13 +319,112 @@ func Convert_v1beta1_ReplicaSetSpec_To_extensions_ReplicaSetSpec(in *ReplicaSetS } else { out.Selector = nil } - if in.Template != nil { - out.Template = new(api.PodTemplateSpec) - if err := v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(in.Template, out.Template, s); err != nil { - return err - } - } else { - out.Template = nil + if err := v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err + } + return nil +} + +func Convert_extensions_JobSpec_To_v1beta1_JobSpec(in *extensions.JobSpec, out *JobSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.JobSpec))(in) + } + if in.Parallelism != nil { + out.Parallelism = new(int32) + *out.Parallelism = int32(*in.Parallelism) + } else { + out.Parallelism = nil + } + if in.Completions != nil { + out.Completions = new(int32) + *out.Completions = int32(*in.Completions) + } else { + out.Completions = nil + } + if in.ActiveDeadlineSeconds != nil { + out.ActiveDeadlineSeconds = new(int64) + *out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds + } else { + out.ActiveDeadlineSeconds = nil + } + // unable to generate simple pointer conversion for unversioned.LabelSelector -> v1beta1.LabelSelector + if in.Selector != nil { + out.Selector = new(LabelSelector) + if err := Convert_unversioned_LabelSelector_To_v1beta1_LabelSelector(in.Selector, out.Selector, s); err != nil { + return err + } + } else { + out.Selector = nil + } + + // BEGIN non-standard conversion + // autoSelector has opposite meaning as manualSelector. + // in both cases, unset means false, and unset is always preferred to false. + // unset vs set-false distinction is not preserved. + manualSelector := in.ManualSelector != nil && *in.ManualSelector + autoSelector := !manualSelector + if autoSelector { + out.AutoSelector = new(bool) + *out.AutoSelector = true + } else { + out.AutoSelector = nil + } + // END non-standard conversion + + if err := Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err + } + return nil +} + +func Convert_v1beta1_JobSpec_To_extensions_JobSpec(in *JobSpec, out *extensions.JobSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*JobSpec))(in) + } + if in.Parallelism != nil { + out.Parallelism = new(int) + *out.Parallelism = int(*in.Parallelism) + } else { + out.Parallelism = nil + } + if in.Completions != nil { + out.Completions = new(int) + *out.Completions = int(*in.Completions) + } else { + out.Completions = nil + } + if in.ActiveDeadlineSeconds != nil { + out.ActiveDeadlineSeconds = new(int64) + *out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds + } else { + out.ActiveDeadlineSeconds = nil + } + // unable to generate simple pointer conversion for v1beta1.LabelSelector -> unversioned.LabelSelector + if in.Selector != nil { + out.Selector = new(unversioned.LabelSelector) + if err := Convert_v1beta1_LabelSelector_To_unversioned_LabelSelector(in.Selector, out.Selector, s); err != nil { + return err + } + } else { + out.Selector = nil + } + + // BEGIN non-standard conversion + // autoSelector has opposite meaning as manualSelector. + // in both cases, unset means false, and unset is always preferred to false. + // unset vs set-false distinction is not preserved. + autoSelector := bool(in.AutoSelector != nil && *in.AutoSelector) + manualSelector := !autoSelector + if manualSelector { + out.ManualSelector = new(bool) + *out.ManualSelector = true + } else { + out.ManualSelector = nil + } + // END non-standard conversion + + if err := Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err } return nil } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/conversion_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/conversion_generated.go index f1c50ad2a..26ba97ece 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/conversion_generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/conversion_generated.go @@ -2700,76 +2700,6 @@ func Convert_extensions_CPUTargetUtilization_To_v1beta1_CPUTargetUtilization(in return autoConvert_extensions_CPUTargetUtilization_To_v1beta1_CPUTargetUtilization(in, out, s) } -func autoConvert_extensions_ClusterAutoscaler_To_v1beta1_ClusterAutoscaler(in *extensions.ClusterAutoscaler, out *ClusterAutoscaler, s conversion.Scope) error { - if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { - defaulting.(func(*extensions.ClusterAutoscaler))(in) - } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } - if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { - return err - } - if err := Convert_extensions_ClusterAutoscalerSpec_To_v1beta1_ClusterAutoscalerSpec(&in.Spec, &out.Spec, s); err != nil { - return err - } - return nil -} - -func Convert_extensions_ClusterAutoscaler_To_v1beta1_ClusterAutoscaler(in *extensions.ClusterAutoscaler, out *ClusterAutoscaler, s conversion.Scope) error { - return autoConvert_extensions_ClusterAutoscaler_To_v1beta1_ClusterAutoscaler(in, out, s) -} - -func autoConvert_extensions_ClusterAutoscalerList_To_v1beta1_ClusterAutoscalerList(in *extensions.ClusterAutoscalerList, out *ClusterAutoscalerList, s conversion.Scope) error { - if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { - defaulting.(func(*extensions.ClusterAutoscalerList))(in) - } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } - if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]ClusterAutoscaler, len(in.Items)) - for i := range in.Items { - if err := Convert_extensions_ClusterAutoscaler_To_v1beta1_ClusterAutoscaler(&in.Items[i], &out.Items[i], s); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func Convert_extensions_ClusterAutoscalerList_To_v1beta1_ClusterAutoscalerList(in *extensions.ClusterAutoscalerList, out *ClusterAutoscalerList, s conversion.Scope) error { - return autoConvert_extensions_ClusterAutoscalerList_To_v1beta1_ClusterAutoscalerList(in, out, s) -} - -func autoConvert_extensions_ClusterAutoscalerSpec_To_v1beta1_ClusterAutoscalerSpec(in *extensions.ClusterAutoscalerSpec, out *ClusterAutoscalerSpec, s conversion.Scope) error { - if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { - defaulting.(func(*extensions.ClusterAutoscalerSpec))(in) - } - out.MinNodes = int32(in.MinNodes) - out.MaxNodes = int32(in.MaxNodes) - if in.TargetUtilization != nil { - out.TargetUtilization = make([]NodeUtilization, len(in.TargetUtilization)) - for i := range in.TargetUtilization { - if err := Convert_extensions_NodeUtilization_To_v1beta1_NodeUtilization(&in.TargetUtilization[i], &out.TargetUtilization[i], s); err != nil { - return err - } - } - } else { - out.TargetUtilization = nil - } - return nil -} - -func Convert_extensions_ClusterAutoscalerSpec_To_v1beta1_ClusterAutoscalerSpec(in *extensions.ClusterAutoscalerSpec, out *ClusterAutoscalerSpec, s conversion.Scope) error { - return autoConvert_extensions_ClusterAutoscalerSpec_To_v1beta1_ClusterAutoscalerSpec(in, out, s) -} - func autoConvert_extensions_DaemonSet_To_v1beta1_DaemonSet(in *extensions.DaemonSet, out *DaemonSet, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*extensions.DaemonSet))(in) @@ -2979,6 +2909,7 @@ func autoConvert_extensions_DeploymentStatus_To_v1beta1_DeploymentStatus(in *ext if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*extensions.DeploymentStatus))(in) } + out.ObservedGeneration = in.ObservedGeneration out.Replicas = int32(in.Replicas) out.UpdatedReplicas = int32(in.UpdatedReplicas) out.AvailableReplicas = int32(in.AvailableReplicas) @@ -3459,16 +3390,13 @@ func autoConvert_extensions_JobSpec_To_v1beta1_JobSpec(in *extensions.JobSpec, o } else { out.Selector = nil } + // in.ManualSelector has no peer in out if err := Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { return err } return nil } -func Convert_extensions_JobSpec_To_v1beta1_JobSpec(in *extensions.JobSpec, out *JobSpec, s conversion.Scope) error { - return autoConvert_extensions_JobSpec_To_v1beta1_JobSpec(in, out, s) -} - func autoConvert_extensions_JobStatus_To_v1beta1_JobStatus(in *extensions.JobStatus, out *JobStatus, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*extensions.JobStatus))(in) @@ -3511,19 +3439,6 @@ func Convert_extensions_JobStatus_To_v1beta1_JobStatus(in *extensions.JobStatus, return autoConvert_extensions_JobStatus_To_v1beta1_JobStatus(in, out, s) } -func autoConvert_extensions_NodeUtilization_To_v1beta1_NodeUtilization(in *extensions.NodeUtilization, out *NodeUtilization, s conversion.Scope) error { - if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { - defaulting.(func(*extensions.NodeUtilization))(in) - } - out.Resource = NodeResource(in.Resource) - out.Value = in.Value - return nil -} - -func Convert_extensions_NodeUtilization_To_v1beta1_NodeUtilization(in *extensions.NodeUtilization, out *NodeUtilization, s conversion.Scope) error { - return autoConvert_extensions_NodeUtilization_To_v1beta1_NodeUtilization(in, out, s) -} - func autoConvert_extensions_PodSecurityPolicy_To_v1beta1_PodSecurityPolicy(in *extensions.PodSecurityPolicy, out *PodSecurityPolicy, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*extensions.PodSecurityPolicy))(in) @@ -3605,7 +3520,7 @@ func autoConvert_extensions_PodSecurityPolicySpec_To_v1beta1_PodSecurityPolicySp } out.HostPID = in.HostPID out.HostIPC = in.HostIPC - if err := Convert_extensions_SELinuxContextStrategyOptions_To_v1beta1_SELinuxContextStrategyOptions(&in.SELinuxContext, &out.SELinuxContext, s); err != nil { + if err := Convert_extensions_SELinuxStrategyOptions_To_v1beta1_SELinuxStrategyOptions(&in.SELinux, &out.SELinux, s); err != nil { return err } if err := Convert_extensions_RunAsUserStrategyOptions_To_v1beta1_RunAsUserStrategyOptions(&in.RunAsUser, &out.RunAsUser, s); err != nil { @@ -3684,14 +3599,8 @@ func autoConvert_extensions_ReplicaSetSpec_To_v1beta1_ReplicaSetSpec(in *extensi } else { out.Selector = nil } - // unable to generate simple pointer conversion for api.PodTemplateSpec -> v1.PodTemplateSpec - if in.Template != nil { - out.Template = new(v1.PodTemplateSpec) - if err := Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(in.Template, out.Template, s); err != nil { - return err - } - } else { - out.Template = nil + if err := Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err } return nil } @@ -3701,6 +3610,7 @@ func autoConvert_extensions_ReplicaSetStatus_To_v1beta1_ReplicaSetStatus(in *ext defaulting.(func(*extensions.ReplicaSetStatus))(in) } out.Replicas = int32(in.Replicas) + out.FullyLabeledReplicas = int32(in.FullyLabeledReplicas) out.ObservedGeneration = in.ObservedGeneration return nil } @@ -3752,7 +3662,7 @@ func autoConvert_extensions_RunAsUserStrategyOptions_To_v1beta1_RunAsUserStrateg if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*extensions.RunAsUserStrategyOptions))(in) } - out.Type = RunAsUserStrategy(in.Type) + out.Rule = RunAsUserStrategy(in.Rule) if in.Ranges != nil { out.Ranges = make([]IDRange, len(in.Ranges)) for i := range in.Ranges { @@ -3770,11 +3680,11 @@ func Convert_extensions_RunAsUserStrategyOptions_To_v1beta1_RunAsUserStrategyOpt return autoConvert_extensions_RunAsUserStrategyOptions_To_v1beta1_RunAsUserStrategyOptions(in, out, s) } -func autoConvert_extensions_SELinuxContextStrategyOptions_To_v1beta1_SELinuxContextStrategyOptions(in *extensions.SELinuxContextStrategyOptions, out *SELinuxContextStrategyOptions, s conversion.Scope) error { +func autoConvert_extensions_SELinuxStrategyOptions_To_v1beta1_SELinuxStrategyOptions(in *extensions.SELinuxStrategyOptions, out *SELinuxStrategyOptions, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { - defaulting.(func(*extensions.SELinuxContextStrategyOptions))(in) + defaulting.(func(*extensions.SELinuxStrategyOptions))(in) } - out.Type = SELinuxContextStrategy(in.Type) + out.Rule = SELinuxStrategy(in.Rule) // unable to generate simple pointer conversion for api.SELinuxOptions -> v1.SELinuxOptions if in.SELinuxOptions != nil { out.SELinuxOptions = new(v1.SELinuxOptions) @@ -3787,8 +3697,8 @@ func autoConvert_extensions_SELinuxContextStrategyOptions_To_v1beta1_SELinuxCont return nil } -func Convert_extensions_SELinuxContextStrategyOptions_To_v1beta1_SELinuxContextStrategyOptions(in *extensions.SELinuxContextStrategyOptions, out *SELinuxContextStrategyOptions, s conversion.Scope) error { - return autoConvert_extensions_SELinuxContextStrategyOptions_To_v1beta1_SELinuxContextStrategyOptions(in, out, s) +func Convert_extensions_SELinuxStrategyOptions_To_v1beta1_SELinuxStrategyOptions(in *extensions.SELinuxStrategyOptions, out *SELinuxStrategyOptions, s conversion.Scope) error { + return autoConvert_extensions_SELinuxStrategyOptions_To_v1beta1_SELinuxStrategyOptions(in, out, s) } func autoConvert_extensions_Scale_To_v1beta1_Scale(in *extensions.Scale, out *Scale, s conversion.Scope) error { @@ -3831,21 +3741,10 @@ func autoConvert_extensions_ScaleStatus_To_v1beta1_ScaleStatus(in *extensions.Sc defaulting.(func(*extensions.ScaleStatus))(in) } out.Replicas = int32(in.Replicas) - if in.Selector != nil { - out.Selector = make(map[string]string) - for key, val := range in.Selector { - out.Selector[key] = val - } - } else { - out.Selector = nil - } + // in.Selector has no peer in out return nil } -func Convert_extensions_ScaleStatus_To_v1beta1_ScaleStatus(in *extensions.ScaleStatus, out *ScaleStatus, s conversion.Scope) error { - return autoConvert_extensions_ScaleStatus_To_v1beta1_ScaleStatus(in, out, s) -} - func autoConvert_extensions_SubresourceReference_To_v1beta1_SubresourceReference(in *extensions.SubresourceReference, out *SubresourceReference, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*extensions.SubresourceReference))(in) @@ -3988,76 +3887,6 @@ func Convert_v1beta1_CPUTargetUtilization_To_extensions_CPUTargetUtilization(in return autoConvert_v1beta1_CPUTargetUtilization_To_extensions_CPUTargetUtilization(in, out, s) } -func autoConvert_v1beta1_ClusterAutoscaler_To_extensions_ClusterAutoscaler(in *ClusterAutoscaler, out *extensions.ClusterAutoscaler, s conversion.Scope) error { - if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { - defaulting.(func(*ClusterAutoscaler))(in) - } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } - if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { - return err - } - if err := Convert_v1beta1_ClusterAutoscalerSpec_To_extensions_ClusterAutoscalerSpec(&in.Spec, &out.Spec, s); err != nil { - return err - } - return nil -} - -func Convert_v1beta1_ClusterAutoscaler_To_extensions_ClusterAutoscaler(in *ClusterAutoscaler, out *extensions.ClusterAutoscaler, s conversion.Scope) error { - return autoConvert_v1beta1_ClusterAutoscaler_To_extensions_ClusterAutoscaler(in, out, s) -} - -func autoConvert_v1beta1_ClusterAutoscalerList_To_extensions_ClusterAutoscalerList(in *ClusterAutoscalerList, out *extensions.ClusterAutoscalerList, s conversion.Scope) error { - if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { - defaulting.(func(*ClusterAutoscalerList))(in) - } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } - if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]extensions.ClusterAutoscaler, len(in.Items)) - for i := range in.Items { - if err := Convert_v1beta1_ClusterAutoscaler_To_extensions_ClusterAutoscaler(&in.Items[i], &out.Items[i], s); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func Convert_v1beta1_ClusterAutoscalerList_To_extensions_ClusterAutoscalerList(in *ClusterAutoscalerList, out *extensions.ClusterAutoscalerList, s conversion.Scope) error { - return autoConvert_v1beta1_ClusterAutoscalerList_To_extensions_ClusterAutoscalerList(in, out, s) -} - -func autoConvert_v1beta1_ClusterAutoscalerSpec_To_extensions_ClusterAutoscalerSpec(in *ClusterAutoscalerSpec, out *extensions.ClusterAutoscalerSpec, s conversion.Scope) error { - if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { - defaulting.(func(*ClusterAutoscalerSpec))(in) - } - out.MinNodes = int(in.MinNodes) - out.MaxNodes = int(in.MaxNodes) - if in.TargetUtilization != nil { - out.TargetUtilization = make([]extensions.NodeUtilization, len(in.TargetUtilization)) - for i := range in.TargetUtilization { - if err := Convert_v1beta1_NodeUtilization_To_extensions_NodeUtilization(&in.TargetUtilization[i], &out.TargetUtilization[i], s); err != nil { - return err - } - } - } else { - out.TargetUtilization = nil - } - return nil -} - -func Convert_v1beta1_ClusterAutoscalerSpec_To_extensions_ClusterAutoscalerSpec(in *ClusterAutoscalerSpec, out *extensions.ClusterAutoscalerSpec, s conversion.Scope) error { - return autoConvert_v1beta1_ClusterAutoscalerSpec_To_extensions_ClusterAutoscalerSpec(in, out, s) -} - func autoConvert_v1beta1_DaemonSet_To_extensions_DaemonSet(in *DaemonSet, out *extensions.DaemonSet, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*DaemonSet))(in) @@ -4265,6 +4094,7 @@ func autoConvert_v1beta1_DeploymentStatus_To_extensions_DeploymentStatus(in *Dep if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*DeploymentStatus))(in) } + out.ObservedGeneration = in.ObservedGeneration out.Replicas = int(in.Replicas) out.UpdatedReplicas = int(in.UpdatedReplicas) out.AvailableReplicas = int(in.AvailableReplicas) @@ -4728,16 +4558,13 @@ func autoConvert_v1beta1_JobSpec_To_extensions_JobSpec(in *JobSpec, out *extensi } else { out.Selector = nil } + // in.AutoSelector has no peer in out if err := Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { return err } return nil } -func Convert_v1beta1_JobSpec_To_extensions_JobSpec(in *JobSpec, out *extensions.JobSpec, s conversion.Scope) error { - return autoConvert_v1beta1_JobSpec_To_extensions_JobSpec(in, out, s) -} - func autoConvert_v1beta1_JobStatus_To_extensions_JobStatus(in *JobStatus, out *extensions.JobStatus, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*JobStatus))(in) @@ -4858,19 +4685,6 @@ func Convert_v1beta1_ListOptions_To_api_ListOptions(in *ListOptions, out *api.Li return autoConvert_v1beta1_ListOptions_To_api_ListOptions(in, out, s) } -func autoConvert_v1beta1_NodeUtilization_To_extensions_NodeUtilization(in *NodeUtilization, out *extensions.NodeUtilization, s conversion.Scope) error { - if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { - defaulting.(func(*NodeUtilization))(in) - } - out.Resource = extensions.NodeResource(in.Resource) - out.Value = in.Value - return nil -} - -func Convert_v1beta1_NodeUtilization_To_extensions_NodeUtilization(in *NodeUtilization, out *extensions.NodeUtilization, s conversion.Scope) error { - return autoConvert_v1beta1_NodeUtilization_To_extensions_NodeUtilization(in, out, s) -} - func autoConvert_v1beta1_PodSecurityPolicy_To_extensions_PodSecurityPolicy(in *PodSecurityPolicy, out *extensions.PodSecurityPolicy, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodSecurityPolicy))(in) @@ -4952,7 +4766,7 @@ func autoConvert_v1beta1_PodSecurityPolicySpec_To_extensions_PodSecurityPolicySp } out.HostPID = in.HostPID out.HostIPC = in.HostIPC - if err := Convert_v1beta1_SELinuxContextStrategyOptions_To_extensions_SELinuxContextStrategyOptions(&in.SELinuxContext, &out.SELinuxContext, s); err != nil { + if err := Convert_v1beta1_SELinuxStrategyOptions_To_extensions_SELinuxStrategyOptions(&in.SELinux, &out.SELinux, s); err != nil { return err } if err := Convert_v1beta1_RunAsUserStrategyOptions_To_extensions_RunAsUserStrategyOptions(&in.RunAsUser, &out.RunAsUser, s); err != nil { @@ -5029,14 +4843,8 @@ func autoConvert_v1beta1_ReplicaSetSpec_To_extensions_ReplicaSetSpec(in *Replica } else { out.Selector = nil } - // unable to generate simple pointer conversion for v1.PodTemplateSpec -> api.PodTemplateSpec - if in.Template != nil { - out.Template = new(api.PodTemplateSpec) - if err := Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(in.Template, out.Template, s); err != nil { - return err - } - } else { - out.Template = nil + if err := Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err } return nil } @@ -5046,6 +4854,7 @@ func autoConvert_v1beta1_ReplicaSetStatus_To_extensions_ReplicaSetStatus(in *Rep defaulting.(func(*ReplicaSetStatus))(in) } out.Replicas = int(in.Replicas) + out.FullyLabeledReplicas = int(in.FullyLabeledReplicas) out.ObservedGeneration = in.ObservedGeneration return nil } @@ -5093,7 +4902,7 @@ func autoConvert_v1beta1_RunAsUserStrategyOptions_To_extensions_RunAsUserStrateg if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*RunAsUserStrategyOptions))(in) } - out.Type = extensions.RunAsUserStrategy(in.Type) + out.Rule = extensions.RunAsUserStrategy(in.Rule) if in.Ranges != nil { out.Ranges = make([]extensions.IDRange, len(in.Ranges)) for i := range in.Ranges { @@ -5111,11 +4920,11 @@ func Convert_v1beta1_RunAsUserStrategyOptions_To_extensions_RunAsUserStrategyOpt return autoConvert_v1beta1_RunAsUserStrategyOptions_To_extensions_RunAsUserStrategyOptions(in, out, s) } -func autoConvert_v1beta1_SELinuxContextStrategyOptions_To_extensions_SELinuxContextStrategyOptions(in *SELinuxContextStrategyOptions, out *extensions.SELinuxContextStrategyOptions, s conversion.Scope) error { +func autoConvert_v1beta1_SELinuxStrategyOptions_To_extensions_SELinuxStrategyOptions(in *SELinuxStrategyOptions, out *extensions.SELinuxStrategyOptions, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { - defaulting.(func(*SELinuxContextStrategyOptions))(in) + defaulting.(func(*SELinuxStrategyOptions))(in) } - out.Type = extensions.SELinuxContextStrategy(in.Type) + out.Rule = extensions.SELinuxStrategy(in.Rule) // unable to generate simple pointer conversion for v1.SELinuxOptions -> api.SELinuxOptions if in.SELinuxOptions != nil { out.SELinuxOptions = new(api.SELinuxOptions) @@ -5128,8 +4937,8 @@ func autoConvert_v1beta1_SELinuxContextStrategyOptions_To_extensions_SELinuxCont return nil } -func Convert_v1beta1_SELinuxContextStrategyOptions_To_extensions_SELinuxContextStrategyOptions(in *SELinuxContextStrategyOptions, out *extensions.SELinuxContextStrategyOptions, s conversion.Scope) error { - return autoConvert_v1beta1_SELinuxContextStrategyOptions_To_extensions_SELinuxContextStrategyOptions(in, out, s) +func Convert_v1beta1_SELinuxStrategyOptions_To_extensions_SELinuxStrategyOptions(in *SELinuxStrategyOptions, out *extensions.SELinuxStrategyOptions, s conversion.Scope) error { + return autoConvert_v1beta1_SELinuxStrategyOptions_To_extensions_SELinuxStrategyOptions(in, out, s) } func autoConvert_v1beta1_Scale_To_extensions_Scale(in *Scale, out *extensions.Scale, s conversion.Scope) error { @@ -5172,21 +4981,11 @@ func autoConvert_v1beta1_ScaleStatus_To_extensions_ScaleStatus(in *ScaleStatus, defaulting.(func(*ScaleStatus))(in) } out.Replicas = int(in.Replicas) - if in.Selector != nil { - out.Selector = make(map[string]string) - for key, val := range in.Selector { - out.Selector[key] = val - } - } else { - out.Selector = nil - } + // in.Selector has no peer in out + // in.TargetSelector has no peer in out return nil } -func Convert_v1beta1_ScaleStatus_To_extensions_ScaleStatus(in *ScaleStatus, out *extensions.ScaleStatus, s conversion.Scope) error { - return autoConvert_v1beta1_ScaleStatus_To_extensions_ScaleStatus(in, out, s) -} - func autoConvert_v1beta1_SubresourceReference_To_extensions_SubresourceReference(in *SubresourceReference, out *extensions.SubresourceReference, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*SubresourceReference))(in) @@ -5357,9 +5156,6 @@ func init() { autoConvert_api_Volume_To_v1_Volume, autoConvert_extensions_APIVersion_To_v1beta1_APIVersion, autoConvert_extensions_CPUTargetUtilization_To_v1beta1_CPUTargetUtilization, - autoConvert_extensions_ClusterAutoscalerList_To_v1beta1_ClusterAutoscalerList, - autoConvert_extensions_ClusterAutoscalerSpec_To_v1beta1_ClusterAutoscalerSpec, - autoConvert_extensions_ClusterAutoscaler_To_v1beta1_ClusterAutoscaler, autoConvert_extensions_DaemonSetList_To_v1beta1_DaemonSetList, autoConvert_extensions_DaemonSetSpec_To_v1beta1_DaemonSetSpec, autoConvert_extensions_DaemonSetStatus_To_v1beta1_DaemonSetStatus, @@ -5391,7 +5187,6 @@ func init() { autoConvert_extensions_JobSpec_To_v1beta1_JobSpec, autoConvert_extensions_JobStatus_To_v1beta1_JobStatus, autoConvert_extensions_Job_To_v1beta1_Job, - autoConvert_extensions_NodeUtilization_To_v1beta1_NodeUtilization, autoConvert_extensions_PodSecurityPolicyList_To_v1beta1_PodSecurityPolicyList, autoConvert_extensions_PodSecurityPolicySpec_To_v1beta1_PodSecurityPolicySpec, autoConvert_extensions_PodSecurityPolicy_To_v1beta1_PodSecurityPolicy, @@ -5403,7 +5198,7 @@ func init() { autoConvert_extensions_RollbackConfig_To_v1beta1_RollbackConfig, autoConvert_extensions_RollingUpdateDeployment_To_v1beta1_RollingUpdateDeployment, autoConvert_extensions_RunAsUserStrategyOptions_To_v1beta1_RunAsUserStrategyOptions, - autoConvert_extensions_SELinuxContextStrategyOptions_To_v1beta1_SELinuxContextStrategyOptions, + autoConvert_extensions_SELinuxStrategyOptions_To_v1beta1_SELinuxStrategyOptions, autoConvert_extensions_ScaleSpec_To_v1beta1_ScaleSpec, autoConvert_extensions_ScaleStatus_To_v1beta1_ScaleStatus, autoConvert_extensions_Scale_To_v1beta1_Scale, @@ -5464,9 +5259,6 @@ func init() { autoConvert_v1_Volume_To_api_Volume, autoConvert_v1beta1_APIVersion_To_extensions_APIVersion, autoConvert_v1beta1_CPUTargetUtilization_To_extensions_CPUTargetUtilization, - autoConvert_v1beta1_ClusterAutoscalerList_To_extensions_ClusterAutoscalerList, - autoConvert_v1beta1_ClusterAutoscalerSpec_To_extensions_ClusterAutoscalerSpec, - autoConvert_v1beta1_ClusterAutoscaler_To_extensions_ClusterAutoscaler, autoConvert_v1beta1_DaemonSetList_To_extensions_DaemonSetList, autoConvert_v1beta1_DaemonSetSpec_To_extensions_DaemonSetSpec, autoConvert_v1beta1_DaemonSetStatus_To_extensions_DaemonSetStatus, @@ -5500,7 +5292,6 @@ func init() { autoConvert_v1beta1_LabelSelectorRequirement_To_unversioned_LabelSelectorRequirement, autoConvert_v1beta1_LabelSelector_To_unversioned_LabelSelector, autoConvert_v1beta1_ListOptions_To_api_ListOptions, - autoConvert_v1beta1_NodeUtilization_To_extensions_NodeUtilization, autoConvert_v1beta1_PodSecurityPolicyList_To_extensions_PodSecurityPolicyList, autoConvert_v1beta1_PodSecurityPolicySpec_To_extensions_PodSecurityPolicySpec, autoConvert_v1beta1_PodSecurityPolicy_To_extensions_PodSecurityPolicy, @@ -5512,7 +5303,7 @@ func init() { autoConvert_v1beta1_RollbackConfig_To_extensions_RollbackConfig, autoConvert_v1beta1_RollingUpdateDeployment_To_extensions_RollingUpdateDeployment, autoConvert_v1beta1_RunAsUserStrategyOptions_To_extensions_RunAsUserStrategyOptions, - autoConvert_v1beta1_SELinuxContextStrategyOptions_To_extensions_SELinuxContextStrategyOptions, + autoConvert_v1beta1_SELinuxStrategyOptions_To_extensions_SELinuxStrategyOptions, autoConvert_v1beta1_ScaleSpec_To_extensions_ScaleSpec, autoConvert_v1beta1_ScaleStatus_To_extensions_ScaleStatus, autoConvert_v1beta1_Scale_To_extensions_Scale, diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/deep_copy_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/deep_copy_generated.go index d1ba9f6de..845e4dd7f 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/deep_copy_generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/deep_copy_generated.go @@ -1030,55 +1030,6 @@ func deepCopy_v1beta1_CPUTargetUtilization(in CPUTargetUtilization, out *CPUTarg return nil } -func deepCopy_v1beta1_ClusterAutoscaler(in ClusterAutoscaler, out *ClusterAutoscaler, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_v1_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_v1beta1_ClusterAutoscalerSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - return nil -} - -func deepCopy_v1beta1_ClusterAutoscalerList(in ClusterAutoscalerList, out *ClusterAutoscalerList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]ClusterAutoscaler, len(in.Items)) - for i := range in.Items { - if err := deepCopy_v1beta1_ClusterAutoscaler(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_v1beta1_ClusterAutoscalerSpec(in ClusterAutoscalerSpec, out *ClusterAutoscalerSpec, c *conversion.Cloner) error { - out.MinNodes = in.MinNodes - out.MaxNodes = in.MaxNodes - if in.TargetUtilization != nil { - out.TargetUtilization = make([]NodeUtilization, len(in.TargetUtilization)) - for i := range in.TargetUtilization { - if err := deepCopy_v1beta1_NodeUtilization(in.TargetUtilization[i], &out.TargetUtilization[i], c); err != nil { - return err - } - } - } else { - out.TargetUtilization = nil - } - return nil -} - func deepCopy_v1beta1_DaemonSet(in DaemonSet, out *DaemonSet, c *conversion.Cloner) error { if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { return err @@ -1233,6 +1184,7 @@ func deepCopy_v1beta1_DeploymentSpec(in DeploymentSpec, out *DeploymentSpec, c * } func deepCopy_v1beta1_DeploymentStatus(in DeploymentStatus, out *DeploymentStatus, c *conversion.Cloner) error { + out.ObservedGeneration = in.ObservedGeneration out.Replicas = in.Replicas out.UpdatedReplicas = in.UpdatedReplicas out.AvailableReplicas = in.AvailableReplicas @@ -1564,6 +1516,12 @@ func deepCopy_v1beta1_JobSpec(in JobSpec, out *JobSpec, c *conversion.Cloner) er } else { out.Selector = nil } + if in.AutoSelector != nil { + out.AutoSelector = new(bool) + *out.AutoSelector = *in.AutoSelector + } else { + out.AutoSelector = nil + } if err := deepCopy_v1_PodTemplateSpec(in.Template, &out.Template, c); err != nil { return err } @@ -1656,12 +1614,6 @@ func deepCopy_v1beta1_ListOptions(in ListOptions, out *ListOptions, c *conversio return nil } -func deepCopy_v1beta1_NodeUtilization(in NodeUtilization, out *NodeUtilization, c *conversion.Cloner) error { - out.Resource = in.Resource - out.Value = in.Value - return nil -} - func deepCopy_v1beta1_PodSecurityPolicy(in PodSecurityPolicy, out *PodSecurityPolicy, c *conversion.Cloner) error { if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { return err @@ -1726,7 +1678,7 @@ func deepCopy_v1beta1_PodSecurityPolicySpec(in PodSecurityPolicySpec, out *PodSe } out.HostPID = in.HostPID out.HostIPC = in.HostIPC - if err := deepCopy_v1beta1_SELinuxContextStrategyOptions(in.SELinuxContext, &out.SELinuxContext, c); err != nil { + if err := deepCopy_v1beta1_SELinuxStrategyOptions(in.SELinux, &out.SELinux, c); err != nil { return err } if err := deepCopy_v1beta1_RunAsUserStrategyOptions(in.RunAsUser, &out.RunAsUser, c); err != nil { @@ -1786,19 +1738,15 @@ func deepCopy_v1beta1_ReplicaSetSpec(in ReplicaSetSpec, out *ReplicaSetSpec, c * } else { out.Selector = nil } - if in.Template != nil { - out.Template = new(v1.PodTemplateSpec) - if err := deepCopy_v1_PodTemplateSpec(*in.Template, out.Template, c); err != nil { - return err - } - } else { - out.Template = nil + if err := deepCopy_v1_PodTemplateSpec(in.Template, &out.Template, c); err != nil { + return err } return nil } func deepCopy_v1beta1_ReplicaSetStatus(in ReplicaSetStatus, out *ReplicaSetStatus, c *conversion.Cloner) error { out.Replicas = in.Replicas + out.FullyLabeledReplicas = in.FullyLabeledReplicas out.ObservedGeneration = in.ObservedGeneration return nil } @@ -1836,7 +1784,7 @@ func deepCopy_v1beta1_RollingUpdateDeployment(in RollingUpdateDeployment, out *R } func deepCopy_v1beta1_RunAsUserStrategyOptions(in RunAsUserStrategyOptions, out *RunAsUserStrategyOptions, c *conversion.Cloner) error { - out.Type = in.Type + out.Rule = in.Rule if in.Ranges != nil { out.Ranges = make([]IDRange, len(in.Ranges)) for i := range in.Ranges { @@ -1850,8 +1798,8 @@ func deepCopy_v1beta1_RunAsUserStrategyOptions(in RunAsUserStrategyOptions, out return nil } -func deepCopy_v1beta1_SELinuxContextStrategyOptions(in SELinuxContextStrategyOptions, out *SELinuxContextStrategyOptions, c *conversion.Cloner) error { - out.Type = in.Type +func deepCopy_v1beta1_SELinuxStrategyOptions(in SELinuxStrategyOptions, out *SELinuxStrategyOptions, c *conversion.Cloner) error { + out.Rule = in.Rule if in.SELinuxOptions != nil { out.SELinuxOptions = new(v1.SELinuxOptions) if err := deepCopy_v1_SELinuxOptions(*in.SELinuxOptions, out.SELinuxOptions, c); err != nil { @@ -1894,6 +1842,7 @@ func deepCopy_v1beta1_ScaleStatus(in ScaleStatus, out *ScaleStatus, c *conversio } else { out.Selector = nil } + out.TargetSelector = in.TargetSelector return nil } @@ -2048,9 +1997,6 @@ func init() { deepCopy_v1_VolumeSource, deepCopy_v1beta1_APIVersion, deepCopy_v1beta1_CPUTargetUtilization, - deepCopy_v1beta1_ClusterAutoscaler, - deepCopy_v1beta1_ClusterAutoscalerList, - deepCopy_v1beta1_ClusterAutoscalerSpec, deepCopy_v1beta1_DaemonSet, deepCopy_v1beta1_DaemonSetList, deepCopy_v1beta1_DaemonSetSpec, @@ -2085,7 +2031,6 @@ func init() { deepCopy_v1beta1_LabelSelector, deepCopy_v1beta1_LabelSelectorRequirement, deepCopy_v1beta1_ListOptions, - deepCopy_v1beta1_NodeUtilization, deepCopy_v1beta1_PodSecurityPolicy, deepCopy_v1beta1_PodSecurityPolicyList, deepCopy_v1beta1_PodSecurityPolicySpec, @@ -2097,7 +2042,7 @@ func init() { deepCopy_v1beta1_RollbackConfig, deepCopy_v1beta1_RollingUpdateDeployment, deepCopy_v1beta1_RunAsUserStrategyOptions, - deepCopy_v1beta1_SELinuxContextStrategyOptions, + deepCopy_v1beta1_SELinuxStrategyOptions, deepCopy_v1beta1_Scale, deepCopy_v1beta1_ScaleSpec, deepCopy_v1beta1_ScaleStatus, diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/defaults.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/defaults.go index cb197a965..46d4c3785 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/defaults.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/defaults.go @@ -86,7 +86,14 @@ func addDefaultingFuncs(scheme *runtime.Scheme) { labels := obj.Spec.Template.Labels // TODO: support templates defined elsewhere when we support them in the API if labels != nil { - if obj.Spec.Selector == nil { + // if an autoselector is requested, we'll build the selector later with controller-uid and job-name + autoSelector := bool(obj.Spec.AutoSelector != nil && *obj.Spec.AutoSelector) + + // otherwise, we are using a manual selector + manualSelector := !autoSelector + + // and default behavior for an unspecified manual selector is to use the pod template labels + if manualSelector && obj.Spec.Selector == nil { obj.Spec.Selector = &LabelSelector{ MatchLabels: labels, } @@ -118,10 +125,8 @@ func addDefaultingFuncs(scheme *runtime.Scheme) { } }, func(obj *ReplicaSet) { - var labels map[string]string - if obj.Spec.Template != nil { - labels = obj.Spec.Template.Labels - } + labels := obj.Spec.Template.Labels + // TODO: support templates defined elsewhere when we support them in the API if labels != nil { if obj.Spec.Selector == nil { diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/defaults_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/defaults_test.go deleted file mode 100644 index 85b991980..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/defaults_test.go +++ /dev/null @@ -1,663 +0,0 @@ -/* -Copyright 2015 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 v1beta1_test - -import ( - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api" - _ "k8s.io/kubernetes/pkg/api/install" - "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/v1" - _ "k8s.io/kubernetes/pkg/apis/extensions/install" - . "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/intstr" -) - -func TestSetDefaultDaemonSet(t *testing.T) { - defaultLabels := map[string]string{"foo": "bar"} - period := int64(v1.DefaultTerminationGracePeriodSeconds) - defaultTemplate := v1.PodTemplateSpec{ - Spec: v1.PodSpec{ - DNSPolicy: v1.DNSClusterFirst, - RestartPolicy: v1.RestartPolicyAlways, - SecurityContext: &v1.PodSecurityContext{}, - TerminationGracePeriodSeconds: &period, - }, - ObjectMeta: v1.ObjectMeta{ - Labels: defaultLabels, - }, - } - templateNoLabel := v1.PodTemplateSpec{ - Spec: v1.PodSpec{ - DNSPolicy: v1.DNSClusterFirst, - RestartPolicy: v1.RestartPolicyAlways, - SecurityContext: &v1.PodSecurityContext{}, - TerminationGracePeriodSeconds: &period, - }, - } - tests := []struct { - original *DaemonSet - expected *DaemonSet - }{ - { // Labels change/defaulting test. - original: &DaemonSet{ - Spec: DaemonSetSpec{ - Template: defaultTemplate, - }, - }, - expected: &DaemonSet{ - ObjectMeta: v1.ObjectMeta{ - Labels: defaultLabels, - }, - Spec: DaemonSetSpec{ - Selector: &LabelSelector{ - MatchLabels: defaultLabels, - }, - Template: defaultTemplate, - }, - }, - }, - { // Labels change/defaulting test. - original: &DaemonSet{ - ObjectMeta: v1.ObjectMeta{ - Labels: map[string]string{ - "bar": "foo", - }, - }, - Spec: DaemonSetSpec{ - Template: defaultTemplate, - }, - }, - expected: &DaemonSet{ - ObjectMeta: v1.ObjectMeta{ - Labels: map[string]string{ - "bar": "foo", - }, - }, - Spec: DaemonSetSpec{ - Selector: &LabelSelector{ - MatchLabels: defaultLabels, - }, - Template: defaultTemplate, - }, - }, - }, - { // Update strategy. - original: &DaemonSet{}, - expected: &DaemonSet{ - Spec: DaemonSetSpec{ - Template: templateNoLabel, - }, - }, - }, - { // Update strategy. - original: &DaemonSet{ - Spec: DaemonSetSpec{}, - }, - expected: &DaemonSet{ - Spec: DaemonSetSpec{ - Template: templateNoLabel, - }, - }, - }, - { // Custom unique label key. - original: &DaemonSet{ - Spec: DaemonSetSpec{}, - }, - expected: &DaemonSet{ - Spec: DaemonSetSpec{ - Template: templateNoLabel, - }, - }, - }, - } - - for i, test := range tests { - original := test.original - expected := test.expected - obj2 := roundTrip(t, runtime.Object(original)) - got, ok := obj2.(*DaemonSet) - if !ok { - t.Errorf("(%d) unexpected object: %v", i, got) - t.FailNow() - } - if !reflect.DeepEqual(got.Spec, expected.Spec) { - t.Errorf("(%d) got different than expected\ngot:\n\t%+v\nexpected:\n\t%+v", i, got.Spec, expected.Spec) - } - } -} - -func TestSetDefaultDeployment(t *testing.T) { - defaultIntOrString := intstr.FromInt(1) - differentIntOrString := intstr.FromInt(5) - period := int64(v1.DefaultTerminationGracePeriodSeconds) - defaultTemplate := v1.PodTemplateSpec{ - Spec: v1.PodSpec{ - DNSPolicy: v1.DNSClusterFirst, - RestartPolicy: v1.RestartPolicyAlways, - SecurityContext: &v1.PodSecurityContext{}, - TerminationGracePeriodSeconds: &period, - }, - } - tests := []struct { - original *Deployment - expected *Deployment - }{ - { - original: &Deployment{}, - expected: &Deployment{ - Spec: DeploymentSpec{ - Replicas: newInt32(1), - Strategy: DeploymentStrategy{ - Type: RollingUpdateDeploymentStrategyType, - RollingUpdate: &RollingUpdateDeployment{ - MaxSurge: &defaultIntOrString, - MaxUnavailable: &defaultIntOrString, - }, - }, - Template: defaultTemplate, - }, - }, - }, - { - original: &Deployment{ - Spec: DeploymentSpec{ - Replicas: newInt32(5), - Strategy: DeploymentStrategy{ - RollingUpdate: &RollingUpdateDeployment{ - MaxSurge: &differentIntOrString, - }, - }, - }, - }, - expected: &Deployment{ - Spec: DeploymentSpec{ - Replicas: newInt32(5), - Strategy: DeploymentStrategy{ - Type: RollingUpdateDeploymentStrategyType, - RollingUpdate: &RollingUpdateDeployment{ - MaxSurge: &differentIntOrString, - MaxUnavailable: &defaultIntOrString, - }, - }, - Template: defaultTemplate, - }, - }, - }, - { - original: &Deployment{ - Spec: DeploymentSpec{ - Replicas: newInt32(5), - Strategy: DeploymentStrategy{ - Type: RecreateDeploymentStrategyType, - }, - }, - }, - expected: &Deployment{ - Spec: DeploymentSpec{ - Replicas: newInt32(5), - Strategy: DeploymentStrategy{ - Type: RecreateDeploymentStrategyType, - }, - Template: defaultTemplate, - }, - }, - }, - { - original: &Deployment{ - Spec: DeploymentSpec{ - Replicas: newInt32(5), - Strategy: DeploymentStrategy{ - Type: RecreateDeploymentStrategyType, - }, - }, - }, - expected: &Deployment{ - Spec: DeploymentSpec{ - Replicas: newInt32(5), - Strategy: DeploymentStrategy{ - Type: RecreateDeploymentStrategyType, - }, - Template: defaultTemplate, - }, - }, - }, - } - - for _, test := range tests { - original := test.original - expected := test.expected - obj2 := roundTrip(t, runtime.Object(original)) - got, ok := obj2.(*Deployment) - if !ok { - t.Errorf("unexpected object: %v", got) - t.FailNow() - } - if !reflect.DeepEqual(got.Spec, expected.Spec) { - t.Errorf("got different than expected:\n\t%+v\ngot:\n\t%+v", got.Spec, expected.Spec) - } - } -} - -func TestSetDefaultJobParallelismAndCompletions(t *testing.T) { - tests := []struct { - original *Job - expected *Job - }{ - // both unspecified -> sets both to 1 - { - original: &Job{ - Spec: JobSpec{}, - }, - expected: &Job{ - Spec: JobSpec{ - Completions: newInt32(1), - Parallelism: newInt32(1), - }, - }, - }, - // WQ: Parallelism explicitly 0 and completions unset -> no change - { - original: &Job{ - Spec: JobSpec{ - Parallelism: newInt32(0), - }, - }, - expected: &Job{ - Spec: JobSpec{ - Parallelism: newInt32(0), - }, - }, - }, - // WQ: Parallelism explicitly 2 and completions unset -> no change - { - original: &Job{ - Spec: JobSpec{ - Parallelism: newInt32(2), - }, - }, - expected: &Job{ - Spec: JobSpec{ - Parallelism: newInt32(2), - }, - }, - }, - // Completions explicitly 2 and parallelism unset -> parallelism is defaulted - { - original: &Job{ - Spec: JobSpec{ - Completions: newInt32(2), - }, - }, - expected: &Job{ - Spec: JobSpec{ - Completions: newInt32(2), - Parallelism: newInt32(1), - }, - }, - }, - // Both set -> no change - { - original: &Job{ - Spec: JobSpec{ - Completions: newInt32(10), - Parallelism: newInt32(11), - }, - }, - expected: &Job{ - Spec: JobSpec{ - Completions: newInt32(10), - Parallelism: newInt32(11), - }, - }, - }, - // Both set, flipped -> no change - { - original: &Job{ - Spec: JobSpec{ - Completions: newInt32(11), - Parallelism: newInt32(10), - }, - }, - expected: &Job{ - Spec: JobSpec{ - Completions: newInt32(11), - Parallelism: newInt32(10), - }, - }, - }, - } - - for _, tc := range tests { - original := tc.original - expected := tc.expected - obj2 := roundTrip(t, runtime.Object(original)) - got, ok := obj2.(*Job) - if !ok { - t.Errorf("unexpected object: %v", got) - t.FailNow() - } - if (got.Spec.Completions == nil) != (expected.Spec.Completions == nil) { - t.Errorf("got different *completions than expected: %v %v", got.Spec.Completions, expected.Spec.Completions) - } - if got.Spec.Completions != nil && expected.Spec.Completions != nil { - if *got.Spec.Completions != *expected.Spec.Completions { - t.Errorf("got different completions than expected: %d %d", *got.Spec.Completions, *expected.Spec.Completions) - } - } - if (got.Spec.Parallelism == nil) != (expected.Spec.Parallelism == nil) { - t.Errorf("got different *Parallelism than expected: %v %v", got.Spec.Parallelism, expected.Spec.Parallelism) - } - if got.Spec.Parallelism != nil && expected.Spec.Parallelism != nil { - if *got.Spec.Parallelism != *expected.Spec.Parallelism { - t.Errorf("got different parallelism than expected: %d %d", *got.Spec.Parallelism, *expected.Spec.Parallelism) - } - } - } -} - -func TestSetDefaultJobSelector(t *testing.T) { - expected := &Job{ - Spec: JobSpec{ - Selector: &LabelSelector{ - MatchLabels: map[string]string{"job": "selector"}, - }, - Completions: newInt32(1), - Parallelism: newInt32(1), - }, - } - tests := []*Job{ - // selector set explicitly, completions and parallelism - default - { - Spec: JobSpec{ - Selector: &LabelSelector{ - MatchLabels: map[string]string{"job": "selector"}, - }, - }, - }, - // selector from template labels, completions and parallelism - default - { - Spec: JobSpec{ - Template: v1.PodTemplateSpec{ - ObjectMeta: v1.ObjectMeta{ - Labels: map[string]string{"job": "selector"}, - }, - }, - }, - }, - } - - for _, original := range tests { - obj2 := roundTrip(t, runtime.Object(original)) - got, ok := obj2.(*Job) - if !ok { - t.Errorf("unexpected object: %v", got) - t.FailNow() - } - if !reflect.DeepEqual(got.Spec.Selector, expected.Spec.Selector) { - t.Errorf("got different selectors %#v %#v", got.Spec.Selector, expected.Spec.Selector) - } - } -} - -func TestSetDefaultReplicaSet(t *testing.T) { - tests := []struct { - rs *ReplicaSet - expectLabels bool - expectSelector bool - }{ - { - rs: &ReplicaSet{ - Spec: ReplicaSetSpec{ - Template: &v1.PodTemplateSpec{ - ObjectMeta: v1.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - }, - expectLabels: true, - expectSelector: true, - }, - { - rs: &ReplicaSet{ - ObjectMeta: v1.ObjectMeta{ - Labels: map[string]string{ - "bar": "foo", - }, - }, - Spec: ReplicaSetSpec{ - Template: &v1.PodTemplateSpec{ - ObjectMeta: v1.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - }, - expectLabels: false, - expectSelector: true, - }, - { - rs: &ReplicaSet{ - ObjectMeta: v1.ObjectMeta{ - Labels: map[string]string{ - "bar": "foo", - }, - }, - Spec: ReplicaSetSpec{ - Selector: &LabelSelector{ - MatchLabels: map[string]string{ - "some": "other", - }, - }, - Template: &v1.PodTemplateSpec{ - ObjectMeta: v1.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - }, - expectLabels: false, - expectSelector: false, - }, - { - rs: &ReplicaSet{ - Spec: ReplicaSetSpec{ - Selector: &LabelSelector{ - MatchLabels: map[string]string{ - "some": "other", - }, - }, - Template: &v1.PodTemplateSpec{ - ObjectMeta: v1.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - }, - expectLabels: true, - expectSelector: false, - }, - } - - for _, test := range tests { - rs := test.rs - obj2 := roundTrip(t, runtime.Object(rs)) - rs2, ok := obj2.(*ReplicaSet) - if !ok { - t.Errorf("unexpected object: %v", rs2) - t.FailNow() - } - if test.expectSelector != reflect.DeepEqual(rs2.Spec.Selector.MatchLabels, rs2.Spec.Template.Labels) { - if test.expectSelector { - t.Errorf("expected: %v, got: %v", rs2.Spec.Template.Labels, rs2.Spec.Selector) - } else { - t.Errorf("unexpected equality: %v", rs.Spec.Selector) - } - } - if test.expectLabels != reflect.DeepEqual(rs2.Labels, rs2.Spec.Template.Labels) { - if test.expectLabels { - t.Errorf("expected: %v, got: %v", rs2.Spec.Template.Labels, rs2.Labels) - } else { - t.Errorf("unexpected equality: %v", rs.Labels) - } - } - } -} - -func TestSetDefaultReplicaSetReplicas(t *testing.T) { - tests := []struct { - rs ReplicaSet - expectReplicas int32 - }{ - { - rs: ReplicaSet{ - Spec: ReplicaSetSpec{ - Template: &v1.PodTemplateSpec{ - ObjectMeta: v1.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - }, - expectReplicas: 1, - }, - { - rs: ReplicaSet{ - Spec: ReplicaSetSpec{ - Replicas: newInt32(0), - Template: &v1.PodTemplateSpec{ - ObjectMeta: v1.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - }, - expectReplicas: 0, - }, - { - rs: ReplicaSet{ - Spec: ReplicaSetSpec{ - Replicas: newInt32(3), - Template: &v1.PodTemplateSpec{ - ObjectMeta: v1.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - }, - expectReplicas: 3, - }, - } - - for _, test := range tests { - rs := &test.rs - obj2 := roundTrip(t, runtime.Object(rs)) - rs2, ok := obj2.(*ReplicaSet) - if !ok { - t.Errorf("unexpected object: %v", rs2) - t.FailNow() - } - if rs2.Spec.Replicas == nil { - t.Errorf("unexpected nil Replicas") - } else if test.expectReplicas != *rs2.Spec.Replicas { - t.Errorf("expected: %d replicas, got: %d", test.expectReplicas, *rs2.Spec.Replicas) - } - } -} - -func TestDefaultRequestIsNotSetForReplicaSet(t *testing.T) { - s := v1.PodSpec{} - s.Containers = []v1.Container{ - { - Resources: v1.ResourceRequirements{ - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("100m"), - }, - }, - }, - } - rs := &ReplicaSet{ - Spec: ReplicaSetSpec{ - Replicas: newInt32(3), - Template: &v1.PodTemplateSpec{ - ObjectMeta: v1.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - Spec: s, - }, - }, - } - output := roundTrip(t, runtime.Object(rs)) - rs2 := output.(*ReplicaSet) - defaultRequest := rs2.Spec.Template.Spec.Containers[0].Resources.Requests - requestValue := defaultRequest[v1.ResourceCPU] - if requestValue.String() != "0" { - t.Errorf("Expected 0 request value, got: %s", requestValue.String()) - } -} - -func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { - data, err := runtime.Encode(api.Codecs.LegacyCodec(SchemeGroupVersion), obj) - if err != nil { - t.Errorf("%v\n %#v", err, obj) - return nil - } - obj2, err := runtime.Decode(api.Codecs.UniversalDecoder(), data) - if err != nil { - t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj) - return nil - } - obj3 := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object) - err = api.Scheme.Convert(obj2, obj3) - if err != nil { - t.Errorf("%v\nSource: %#v", err, obj2) - return nil - } - return obj3 -} - -func newInt32(val int32) *int32 { - p := new(int32) - *p = val - return p -} - -func newString(val string) *string { - p := new(string) - *p = val - return p -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/register.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/register.go index d2750a639..026a9f681 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/register.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/register.go @@ -37,8 +37,6 @@ func AddToScheme(scheme *runtime.Scheme) { // Adds the list of known types to api.Scheme. func addKnownTypes(scheme *runtime.Scheme) { scheme.AddKnownTypes(SchemeGroupVersion, - &ClusterAutoscaler{}, - &ClusterAutoscalerList{}, &Deployment{}, &DeploymentList{}, &DeploymentRollback{}, @@ -65,8 +63,6 @@ func addKnownTypes(scheme *runtime.Scheme) { ) } -func (obj *ClusterAutoscaler) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *ClusterAutoscalerList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } func (obj *Deployment) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } func (obj *DeploymentList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } func (obj *DeploymentRollback) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/types.generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/types.generated.go index 1e2b081ec..ffdeebcf5 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/types.generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/types.generated.go @@ -260,13 +260,14 @@ func (x *ScaleStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [2]bool + var yyq2 [3]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false yyq2[1] = len(x.Selector) != 0 + yyq2[2] = x.TargetSelector != "" var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(2) + r.EncodeArrayStart(3) } else { yynn2 = 1 for _, b := range yyq2 { @@ -329,6 +330,31 @@ func (x *ScaleStatus) CodecEncodeSelf(e *codec1978.Encoder) { } } } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yym10 := z.EncBinary() + _ = yym10 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.TargetSelector)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("targetSelector")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym11 := z.EncBinary() + _ = yym11 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.TargetSelector)) + } + } + } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayEnd1234) } else { @@ -408,6 +434,12 @@ func (x *ScaleStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { z.F.DecMapStringStringX(yyv5, false, d) } } + case "targetSelector": + if r.TryDecodeAsNil() { + x.TargetSelector = "" + } else { + x.TargetSelector = string(r.DecodeString()) + } default: z.DecStructFieldNotFound(-1, yys3) } // end switch yys3 @@ -419,16 +451,16 @@ func (x *ScaleStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj7 int - var yyb7 bool - var yyhl7 bool = l >= 0 - yyj7++ - if yyhl7 { - yyb7 = yyj7 > l + var yyj8 int + var yyb8 bool + var yyhl8 bool = l >= 0 + yyj8++ + if yyhl8 { + yyb8 = yyj8 > l } else { - yyb7 = r.CheckBreak() + yyb8 = r.CheckBreak() } - if yyb7 { + if yyb8 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -438,13 +470,13 @@ func (x *ScaleStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { } else { x.Replicas = int32(r.DecodeInt(32)) } - yyj7++ - if yyhl7 { - yyb7 = yyj7 > l + yyj8++ + if yyhl8 { + yyb8 = yyj8 > l } else { - yyb7 = r.CheckBreak() + yyb8 = r.CheckBreak() } - if yyb7 { + if yyb8 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -452,26 +484,42 @@ func (x *ScaleStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if r.TryDecodeAsNil() { x.Selector = nil } else { - yyv9 := &x.Selector - yym10 := z.DecBinary() - _ = yym10 + yyv10 := &x.Selector + yym11 := z.DecBinary() + _ = yym11 if false { } else { - z.F.DecMapStringStringX(yyv9, false, d) + z.F.DecMapStringStringX(yyv10, false, d) } } + yyj8++ + if yyhl8 { + yyb8 = yyj8 > l + } else { + yyb8 = r.CheckBreak() + } + if yyb8 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.TargetSelector = "" + } else { + x.TargetSelector = string(r.DecodeString()) + } for { - yyj7++ - if yyhl7 { - yyb7 = yyj7 > l + yyj8++ + if yyhl8 { + yyb8 = yyj8 > l } else { - yyb7 = r.CheckBreak() + yyb8 = r.CheckBreak() } - if yyb7 { + if yyb8 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj7-1, "") + z.DecStructFieldNotFound(yyj8-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -7056,16 +7104,17 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [4]bool + var yyq2 [5]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false - yyq2[0] = x.Replicas != 0 - yyq2[1] = x.UpdatedReplicas != 0 - yyq2[2] = x.AvailableReplicas != 0 - yyq2[3] = x.UnavailableReplicas != 0 + yyq2[0] = x.ObservedGeneration != 0 + yyq2[1] = x.Replicas != 0 + yyq2[2] = x.UpdatedReplicas != 0 + yyq2[3] = x.AvailableReplicas != 0 + yyq2[4] = x.UnavailableReplicas != 0 var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(4) + r.EncodeArrayStart(5) } else { yynn2 = 0 for _, b := range yyq2 { @@ -7083,7 +7132,7 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym4 if false { } else { - r.EncodeInt(int64(x.Replicas)) + r.EncodeInt(int64(x.ObservedGeneration)) } } else { r.EncodeInt(0) @@ -7091,13 +7140,13 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { if yyq2[0] { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("replicas")) + r.EncodeString(codecSelferC_UTF81234, string("observedGeneration")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym5 := z.EncBinary() _ = yym5 if false { } else { - r.EncodeInt(int64(x.Replicas)) + r.EncodeInt(int64(x.ObservedGeneration)) } } } @@ -7108,7 +7157,7 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym7 if false { } else { - r.EncodeInt(int64(x.UpdatedReplicas)) + r.EncodeInt(int64(x.Replicas)) } } else { r.EncodeInt(0) @@ -7116,13 +7165,13 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { if yyq2[1] { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("updatedReplicas")) + r.EncodeString(codecSelferC_UTF81234, string("replicas")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym8 := z.EncBinary() _ = yym8 if false { } else { - r.EncodeInt(int64(x.UpdatedReplicas)) + r.EncodeInt(int64(x.Replicas)) } } } @@ -7133,7 +7182,7 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym10 if false { } else { - r.EncodeInt(int64(x.AvailableReplicas)) + r.EncodeInt(int64(x.UpdatedReplicas)) } } else { r.EncodeInt(0) @@ -7141,13 +7190,13 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { if yyq2[2] { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("availableReplicas")) + r.EncodeString(codecSelferC_UTF81234, string("updatedReplicas")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym11 := z.EncBinary() _ = yym11 if false { } else { - r.EncodeInt(int64(x.AvailableReplicas)) + r.EncodeInt(int64(x.UpdatedReplicas)) } } } @@ -7158,7 +7207,7 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym13 if false { } else { - r.EncodeInt(int64(x.UnavailableReplicas)) + r.EncodeInt(int64(x.AvailableReplicas)) } } else { r.EncodeInt(0) @@ -7166,11 +7215,36 @@ func (x *DeploymentStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { if yyq2[3] { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("unavailableReplicas")) + r.EncodeString(codecSelferC_UTF81234, string("availableReplicas")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym14 := z.EncBinary() _ = yym14 if false { + } else { + r.EncodeInt(int64(x.AvailableReplicas)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[4] { + yym16 := z.EncBinary() + _ = yym16 + if false { + } else { + r.EncodeInt(int64(x.UnavailableReplicas)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[4] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("unavailableReplicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym17 := z.EncBinary() + _ = yym17 + if false { } else { r.EncodeInt(int64(x.UnavailableReplicas)) } @@ -7237,6 +7311,12 @@ func (x *DeploymentStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { yys3 := string(yys3Slc) z.DecSendContainerState(codecSelfer_containerMapValue1234) switch yys3 { + case "observedGeneration": + if r.TryDecodeAsNil() { + x.ObservedGeneration = 0 + } else { + x.ObservedGeneration = int64(r.DecodeInt(64)) + } case "replicas": if r.TryDecodeAsNil() { x.Replicas = 0 @@ -7272,16 +7352,32 @@ func (x *DeploymentStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj8 int - var yyb8 bool - var yyhl8 bool = l >= 0 - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + var yyj9 int + var yyb9 bool + var yyhl9 bool = l >= 0 + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l } else { - yyb8 = r.CheckBreak() + yyb9 = r.CheckBreak() } - if yyb8 { + if yyb9 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.ObservedGeneration = 0 + } else { + x.ObservedGeneration = int64(r.DecodeInt(64)) + } + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l + } else { + yyb9 = r.CheckBreak() + } + if yyb9 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7291,13 +7387,13 @@ func (x *DeploymentStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) } else { x.Replicas = int32(r.DecodeInt(32)) } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l } else { - yyb8 = r.CheckBreak() + yyb9 = r.CheckBreak() } - if yyb8 { + if yyb9 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7307,13 +7403,13 @@ func (x *DeploymentStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) } else { x.UpdatedReplicas = int32(r.DecodeInt(32)) } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l } else { - yyb8 = r.CheckBreak() + yyb9 = r.CheckBreak() } - if yyb8 { + if yyb9 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7323,13 +7419,13 @@ func (x *DeploymentStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) } else { x.AvailableReplicas = int32(r.DecodeInt(32)) } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l } else { - yyb8 = r.CheckBreak() + yyb9 = r.CheckBreak() } - if yyb8 { + if yyb9 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7340,17 +7436,17 @@ func (x *DeploymentStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) x.UnavailableReplicas = int32(r.DecodeInt(32)) } for { - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + yyj9++ + if yyhl9 { + yyb9 = yyj9 > l } else { - yyb8 = r.CheckBreak() + yyb9 = r.CheckBreak() } - if yyb8 { + if yyb9 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj8-1, "") + z.DecStructFieldNotFound(yyj9-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -9887,16 +9983,17 @@ func (x *JobSpec) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [5]bool + var yyq2 [6]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false yyq2[0] = x.Parallelism != nil yyq2[1] = x.Completions != nil yyq2[2] = x.ActiveDeadlineSeconds != nil yyq2[3] = x.Selector != nil + yyq2[4] = x.AutoSelector != nil var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(5) + r.EncodeArrayStart(6) } else { yynn2 = 1 for _, b := range yyq2 { @@ -10037,14 +10134,49 @@ func (x *JobSpec) CodecEncodeSelf(e *codec1978.Encoder) { } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy22 := &x.Template - yy22.CodecEncodeSelf(e) + if yyq2[4] { + if x.AutoSelector == nil { + r.EncodeNil() + } else { + yy22 := *x.AutoSelector + yym23 := z.EncBinary() + _ = yym23 + if false { + } else { + r.EncodeBool(bool(yy22)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[4] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("autoSelector")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.AutoSelector == nil { + r.EncodeNil() + } else { + yy24 := *x.AutoSelector + yym25 := z.EncBinary() + _ = yym25 + if false { + } else { + r.EncodeBool(bool(yy24)) + } + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yy27 := &x.Template + yy27.CodecEncodeSelf(e) } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("template")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy24 := &x.Template - yy24.CodecEncodeSelf(e) + yy29 := &x.Template + yy29.CodecEncodeSelf(e) } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayEnd1234) @@ -10166,12 +10298,28 @@ func (x *JobSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { } x.Selector.CodecDecodeSelf(d) } + case "autoSelector": + if r.TryDecodeAsNil() { + if x.AutoSelector != nil { + x.AutoSelector = nil + } + } else { + if x.AutoSelector == nil { + x.AutoSelector = new(bool) + } + yym12 := z.DecBinary() + _ = yym12 + if false { + } else { + *((*bool)(x.AutoSelector)) = r.DecodeBool() + } + } case "template": if r.TryDecodeAsNil() { x.Template = pkg2_v1.PodTemplateSpec{} } else { - yyv11 := &x.Template - yyv11.CodecDecodeSelf(d) + yyv13 := &x.Template + yyv13.CodecDecodeSelf(d) } default: z.DecStructFieldNotFound(-1, yys3) @@ -10184,16 +10332,16 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj12 int - var yyb12 bool - var yyhl12 bool = l >= 0 - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + var yyj14 int + var yyb14 bool + var yyhl14 bool = l >= 0 + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -10206,20 +10354,20 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if x.Parallelism == nil { x.Parallelism = new(int32) } - yym14 := z.DecBinary() - _ = yym14 + yym16 := z.DecBinary() + _ = yym16 if false { } else { *((*int32)(x.Parallelism)) = int32(r.DecodeInt(32)) } } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -10232,20 +10380,20 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if x.Completions == nil { x.Completions = new(int32) } - yym16 := z.DecBinary() - _ = yym16 + yym18 := z.DecBinary() + _ = yym18 if false { } else { *((*int32)(x.Completions)) = int32(r.DecodeInt(32)) } } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -10258,20 +10406,20 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if x.ActiveDeadlineSeconds == nil { x.ActiveDeadlineSeconds = new(int64) } - yym18 := z.DecBinary() - _ = yym18 + yym20 := z.DecBinary() + _ = yym20 if false { } else { *((*int64)(x.ActiveDeadlineSeconds)) = int64(r.DecodeInt(64)) } } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -10286,13 +10434,39 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { } x.Selector.CodecDecodeSelf(d) } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.AutoSelector != nil { + x.AutoSelector = nil + } + } else { + if x.AutoSelector == nil { + x.AutoSelector = new(bool) + } + yym23 := z.DecBinary() + _ = yym23 + if false { + } else { + *((*bool)(x.AutoSelector)) = r.DecodeBool() + } + } + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l + } else { + yyb14 = r.CheckBreak() + } + if yyb14 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -10300,21 +10474,21 @@ func (x *JobSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if r.TryDecodeAsNil() { x.Template = pkg2_v1.PodTemplateSpec{} } else { - yyv20 := &x.Template - yyv20.CodecDecodeSelf(d) + yyv24 := &x.Template + yyv24.CodecDecodeSelf(d) } for { - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj12-1, "") + z.DecStructFieldNotFound(yyj14-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -13752,1134 +13926,6 @@ func (x *IngressBackend) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } -func (x NodeResource) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x)) - } -} - -func (x *NodeResource) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - *((*string)(x)) = r.DecodeString() - } -} - -func (x *NodeUtilization) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [2]bool - _, _, _ = yysep2, yyq2, yy2arr2 - const yyr2 bool = false - var yynn2 int - if yyr2 || yy2arr2 { - r.EncodeArrayStart(2) - } else { - yynn2 = 2 - for _, b := range yyq2 { - if b { - yynn2++ - } - } - r.EncodeMapStart(yynn2) - yynn2 = 0 - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - x.Resource.CodecEncodeSelf(e) - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("resource")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - x.Resource.CodecEncodeSelf(e) - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym7 := z.EncBinary() - _ = yym7 - if false { - } else { - r.EncodeFloat64(float64(x.Value)) - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("value")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym8 := z.EncBinary() - _ = yym8 - if false { - } else { - r.EncodeFloat64(float64(x.Value)) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - z.EncSendContainerState(codecSelfer_containerMapEnd1234) - } - } - } -} - -func (x *NodeUtilization) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap1234 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerMapEnd1234) - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray1234 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) - } - } -} - -func (x *NodeUtilization) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - z.DecSendContainerState(codecSelfer_containerMapKey1234) - yys3Slc = r.DecodeBytes(yys3Slc, true, true) - yys3 := string(yys3Slc) - z.DecSendContainerState(codecSelfer_containerMapValue1234) - switch yys3 { - case "resource": - if r.TryDecodeAsNil() { - x.Resource = "" - } else { - x.Resource = NodeResource(r.DecodeString()) - } - case "value": - if r.TryDecodeAsNil() { - x.Value = 0 - } else { - x.Value = float64(r.DecodeFloat(false)) - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - z.DecSendContainerState(codecSelfer_containerMapEnd1234) -} - -func (x *NodeUtilization) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj6 int - var yyb6 bool - var yyhl6 bool = l >= 0 - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l - } else { - yyb6 = r.CheckBreak() - } - if yyb6 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.Resource = "" - } else { - x.Resource = NodeResource(r.DecodeString()) - } - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l - } else { - yyb6 = r.CheckBreak() - } - if yyb6 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.Value = 0 - } else { - x.Value = float64(r.DecodeFloat(false)) - } - for { - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l - } else { - yyb6 = r.CheckBreak() - } - if yyb6 { - break - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj6-1, "") - } - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) -} - -func (x *ClusterAutoscalerSpec) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [3]bool - _, _, _ = yysep2, yyq2, yy2arr2 - const yyr2 bool = false - var yynn2 int - if yyr2 || yy2arr2 { - r.EncodeArrayStart(3) - } else { - yynn2 = 3 - for _, b := range yyq2 { - if b { - yynn2++ - } - } - r.EncodeMapStart(yynn2) - yynn2 = 0 - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym4 := z.EncBinary() - _ = yym4 - if false { - } else { - r.EncodeInt(int64(x.MinNodes)) - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("minNodes")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym5 := z.EncBinary() - _ = yym5 - if false { - } else { - r.EncodeInt(int64(x.MinNodes)) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym7 := z.EncBinary() - _ = yym7 - if false { - } else { - r.EncodeInt(int64(x.MaxNodes)) - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("maxNodes")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym8 := z.EncBinary() - _ = yym8 - if false { - } else { - r.EncodeInt(int64(x.MaxNodes)) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if x.TargetUtilization == nil { - r.EncodeNil() - } else { - yym10 := z.EncBinary() - _ = yym10 - if false { - } else { - h.encSliceNodeUtilization(([]NodeUtilization)(x.TargetUtilization), e) - } - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("target")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - if x.TargetUtilization == nil { - r.EncodeNil() - } else { - yym11 := z.EncBinary() - _ = yym11 - if false { - } else { - h.encSliceNodeUtilization(([]NodeUtilization)(x.TargetUtilization), e) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - z.EncSendContainerState(codecSelfer_containerMapEnd1234) - } - } - } -} - -func (x *ClusterAutoscalerSpec) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap1234 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerMapEnd1234) - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray1234 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) - } - } -} - -func (x *ClusterAutoscalerSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - z.DecSendContainerState(codecSelfer_containerMapKey1234) - yys3Slc = r.DecodeBytes(yys3Slc, true, true) - yys3 := string(yys3Slc) - z.DecSendContainerState(codecSelfer_containerMapValue1234) - switch yys3 { - case "minNodes": - if r.TryDecodeAsNil() { - x.MinNodes = 0 - } else { - x.MinNodes = int32(r.DecodeInt(32)) - } - case "maxNodes": - if r.TryDecodeAsNil() { - x.MaxNodes = 0 - } else { - x.MaxNodes = int32(r.DecodeInt(32)) - } - case "target": - if r.TryDecodeAsNil() { - x.TargetUtilization = nil - } else { - yyv6 := &x.TargetUtilization - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - h.decSliceNodeUtilization((*[]NodeUtilization)(yyv6), d) - } - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - z.DecSendContainerState(codecSelfer_containerMapEnd1234) -} - -func (x *ClusterAutoscalerSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj8 int - var yyb8 bool - var yyhl8 bool = l >= 0 - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.MinNodes = 0 - } else { - x.MinNodes = int32(r.DecodeInt(32)) - } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.MaxNodes = 0 - } else { - x.MaxNodes = int32(r.DecodeInt(32)) - } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.TargetUtilization = nil - } else { - yyv11 := &x.TargetUtilization - yym12 := z.DecBinary() - _ = yym12 - if false { - } else { - h.decSliceNodeUtilization((*[]NodeUtilization)(yyv11), d) - } - } - for { - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - break - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj8-1, "") - } - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) -} - -func (x *ClusterAutoscaler) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [4]bool - _, _, _ = yysep2, yyq2, yy2arr2 - const yyr2 bool = false - yyq2[0] = true - yyq2[1] = true - yyq2[2] = x.Kind != "" - yyq2[3] = x.APIVersion != "" - var yynn2 int - if yyr2 || yy2arr2 { - r.EncodeArrayStart(4) - } else { - yynn2 = 0 - for _, b := range yyq2 { - if b { - yynn2++ - } - } - r.EncodeMapStart(yynn2) - yynn2 = 0 - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[0] { - yy4 := &x.ObjectMeta - yy4.CodecEncodeSelf(e) - } else { - r.EncodeNil() - } - } else { - if yyq2[0] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("metadata")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy6 := &x.ObjectMeta - yy6.CodecEncodeSelf(e) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[1] { - yy9 := &x.Spec - yy9.CodecEncodeSelf(e) - } else { - r.EncodeNil() - } - } else { - if yyq2[1] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("spec")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy11 := &x.Spec - yy11.CodecEncodeSelf(e) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[2] { - yym14 := z.EncBinary() - _ = yym14 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) - } - } else { - r.EncodeString(codecSelferC_UTF81234, "") - } - } else { - if yyq2[2] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("kind")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym15 := z.EncBinary() - _ = yym15 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[3] { - yym17 := z.EncBinary() - _ = yym17 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) - } - } else { - r.EncodeString(codecSelferC_UTF81234, "") - } - } else { - if yyq2[3] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym18 := z.EncBinary() - _ = yym18 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - z.EncSendContainerState(codecSelfer_containerMapEnd1234) - } - } - } -} - -func (x *ClusterAutoscaler) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap1234 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerMapEnd1234) - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray1234 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) - } - } -} - -func (x *ClusterAutoscaler) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - z.DecSendContainerState(codecSelfer_containerMapKey1234) - yys3Slc = r.DecodeBytes(yys3Slc, true, true) - yys3 := string(yys3Slc) - z.DecSendContainerState(codecSelfer_containerMapValue1234) - switch yys3 { - case "metadata": - if r.TryDecodeAsNil() { - x.ObjectMeta = pkg2_v1.ObjectMeta{} - } else { - yyv4 := &x.ObjectMeta - yyv4.CodecDecodeSelf(d) - } - case "spec": - if r.TryDecodeAsNil() { - x.Spec = ClusterAutoscalerSpec{} - } else { - yyv5 := &x.Spec - yyv5.CodecDecodeSelf(d) - } - case "kind": - if r.TryDecodeAsNil() { - x.Kind = "" - } else { - x.Kind = string(r.DecodeString()) - } - case "apiVersion": - if r.TryDecodeAsNil() { - x.APIVersion = "" - } else { - x.APIVersion = string(r.DecodeString()) - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - z.DecSendContainerState(codecSelfer_containerMapEnd1234) -} - -func (x *ClusterAutoscaler) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj8 int - var yyb8 bool - var yyhl8 bool = l >= 0 - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.ObjectMeta = pkg2_v1.ObjectMeta{} - } else { - yyv9 := &x.ObjectMeta - yyv9.CodecDecodeSelf(d) - } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.Spec = ClusterAutoscalerSpec{} - } else { - yyv10 := &x.Spec - yyv10.CodecDecodeSelf(d) - } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.Kind = "" - } else { - x.Kind = string(r.DecodeString()) - } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.APIVersion = "" - } else { - x.APIVersion = string(r.DecodeString()) - } - for { - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - break - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj8-1, "") - } - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) -} - -func (x *ClusterAutoscalerList) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [4]bool - _, _, _ = yysep2, yyq2, yy2arr2 - const yyr2 bool = false - yyq2[0] = true - yyq2[2] = x.Kind != "" - yyq2[3] = x.APIVersion != "" - var yynn2 int - if yyr2 || yy2arr2 { - r.EncodeArrayStart(4) - } else { - yynn2 = 1 - for _, b := range yyq2 { - if b { - yynn2++ - } - } - r.EncodeMapStart(yynn2) - yynn2 = 0 - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[0] { - yy4 := &x.ListMeta - yym5 := z.EncBinary() - _ = yym5 - if false { - } else if z.HasExtensions() && z.EncExt(yy4) { - } else { - z.EncFallback(yy4) - } - } else { - r.EncodeNil() - } - } else { - if yyq2[0] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("metadata")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy6 := &x.ListMeta - yym7 := z.EncBinary() - _ = yym7 - if false { - } else if z.HasExtensions() && z.EncExt(yy6) { - } else { - z.EncFallback(yy6) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if x.Items == nil { - r.EncodeNil() - } else { - yym9 := z.EncBinary() - _ = yym9 - if false { - } else { - h.encSliceClusterAutoscaler(([]ClusterAutoscaler)(x.Items), e) - } - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("items")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - if x.Items == nil { - r.EncodeNil() - } else { - yym10 := z.EncBinary() - _ = yym10 - if false { - } else { - h.encSliceClusterAutoscaler(([]ClusterAutoscaler)(x.Items), e) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[2] { - yym12 := z.EncBinary() - _ = yym12 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) - } - } else { - r.EncodeString(codecSelferC_UTF81234, "") - } - } else { - if yyq2[2] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("kind")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym13 := z.EncBinary() - _ = yym13 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[3] { - yym15 := z.EncBinary() - _ = yym15 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) - } - } else { - r.EncodeString(codecSelferC_UTF81234, "") - } - } else { - if yyq2[3] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym16 := z.EncBinary() - _ = yym16 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - z.EncSendContainerState(codecSelfer_containerMapEnd1234) - } - } - } -} - -func (x *ClusterAutoscalerList) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap1234 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerMapEnd1234) - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray1234 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) - } - } -} - -func (x *ClusterAutoscalerList) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - z.DecSendContainerState(codecSelfer_containerMapKey1234) - yys3Slc = r.DecodeBytes(yys3Slc, true, true) - yys3 := string(yys3Slc) - z.DecSendContainerState(codecSelfer_containerMapValue1234) - switch yys3 { - case "metadata": - if r.TryDecodeAsNil() { - x.ListMeta = pkg1_unversioned.ListMeta{} - } else { - yyv4 := &x.ListMeta - yym5 := z.DecBinary() - _ = yym5 - if false { - } else if z.HasExtensions() && z.DecExt(yyv4) { - } else { - z.DecFallback(yyv4, false) - } - } - case "items": - if r.TryDecodeAsNil() { - x.Items = nil - } else { - yyv6 := &x.Items - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - h.decSliceClusterAutoscaler((*[]ClusterAutoscaler)(yyv6), d) - } - } - case "kind": - if r.TryDecodeAsNil() { - x.Kind = "" - } else { - x.Kind = string(r.DecodeString()) - } - case "apiVersion": - if r.TryDecodeAsNil() { - x.APIVersion = "" - } else { - x.APIVersion = string(r.DecodeString()) - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - z.DecSendContainerState(codecSelfer_containerMapEnd1234) -} - -func (x *ClusterAutoscalerList) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj10 int - var yyb10 bool - var yyhl10 bool = l >= 0 - yyj10++ - if yyhl10 { - yyb10 = yyj10 > l - } else { - yyb10 = r.CheckBreak() - } - if yyb10 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.ListMeta = pkg1_unversioned.ListMeta{} - } else { - yyv11 := &x.ListMeta - yym12 := z.DecBinary() - _ = yym12 - if false { - } else if z.HasExtensions() && z.DecExt(yyv11) { - } else { - z.DecFallback(yyv11, false) - } - } - yyj10++ - if yyhl10 { - yyb10 = yyj10 > l - } else { - yyb10 = r.CheckBreak() - } - if yyb10 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.Items = nil - } else { - yyv13 := &x.Items - yym14 := z.DecBinary() - _ = yym14 - if false { - } else { - h.decSliceClusterAutoscaler((*[]ClusterAutoscaler)(yyv13), d) - } - } - yyj10++ - if yyhl10 { - yyb10 = yyj10 > l - } else { - yyb10 = r.CheckBreak() - } - if yyb10 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.Kind = "" - } else { - x.Kind = string(r.DecodeString()) - } - yyj10++ - if yyhl10 { - yyb10 = yyj10 > l - } else { - yyb10 = r.CheckBreak() - } - if yyb10 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.APIVersion = "" - } else { - x.APIVersion = string(r.DecodeString()) - } - for { - yyj10++ - if yyhl10 { - yyb10 = yyj10 > l - } else { - yyb10 = r.CheckBreak() - } - if yyb10 { - break - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj10-1, "") - } - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) -} - func (x *ExportOptions) CodecEncodeSelf(e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) @@ -16916,7 +15962,7 @@ func (x *ReplicaSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { const yyr2 bool = false yyq2[0] = x.Replicas != nil yyq2[1] = x.Selector != nil - yyq2[2] = x.Template != nil + yyq2[2] = true var yynn2 int if yyr2 || yy2arr2 { r.EncodeArrayStart(3) @@ -16991,11 +16037,8 @@ func (x *ReplicaSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) if yyq2[2] { - if x.Template == nil { - r.EncodeNil() - } else { - x.Template.CodecEncodeSelf(e) - } + yy12 := &x.Template + yy12.CodecEncodeSelf(e) } else { r.EncodeNil() } @@ -17004,11 +16047,8 @@ func (x *ReplicaSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { z.EncSendContainerState(codecSelfer_containerMapKey1234) r.EncodeString(codecSelferC_UTF81234, string("template")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - if x.Template == nil { - r.EncodeNil() - } else { - x.Template.CodecEncodeSelf(e) - } + yy14 := &x.Template + yy14.CodecEncodeSelf(e) } } if yyr2 || yy2arr2 { @@ -17101,14 +16141,10 @@ func (x *ReplicaSetSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { } case "template": if r.TryDecodeAsNil() { - if x.Template != nil { - x.Template = nil - } + x.Template = pkg2_v1.PodTemplateSpec{} } else { - if x.Template == nil { - x.Template = new(pkg2_v1.PodTemplateSpec) - } - x.Template.CodecDecodeSelf(d) + yyv7 := &x.Template + yyv7.CodecDecodeSelf(d) } default: z.DecStructFieldNotFound(-1, yys3) @@ -17183,14 +16219,10 @@ func (x *ReplicaSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - if x.Template != nil { - x.Template = nil - } + x.Template = pkg2_v1.PodTemplateSpec{} } else { - if x.Template == nil { - x.Template = new(pkg2_v1.PodTemplateSpec) - } - x.Template.CodecDecodeSelf(d) + yyv12 := &x.Template + yyv12.CodecDecodeSelf(d) } for { yyj8++ @@ -17222,13 +16254,14 @@ func (x *ReplicaSetStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [2]bool + var yyq2 [3]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false - yyq2[1] = x.ObservedGeneration != 0 + yyq2[1] = x.FullyLabeledReplicas != 0 + yyq2[2] = x.ObservedGeneration != 0 var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(2) + r.EncodeArrayStart(3) } else { yynn2 = 1 for _, b := range yyq2 { @@ -17265,7 +16298,7 @@ func (x *ReplicaSetStatus) CodecEncodeSelf(e *codec1978.Encoder) { _ = yym7 if false { } else { - r.EncodeInt(int64(x.ObservedGeneration)) + r.EncodeInt(int64(x.FullyLabeledReplicas)) } } else { r.EncodeInt(0) @@ -17273,11 +16306,36 @@ func (x *ReplicaSetStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { if yyq2[1] { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("observedGeneration")) + r.EncodeString(codecSelferC_UTF81234, string("fullyLabeledReplicas")) z.EncSendContainerState(codecSelfer_containerMapValue1234) yym8 := z.EncBinary() _ = yym8 if false { + } else { + r.EncodeInt(int64(x.FullyLabeledReplicas)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yym10 := z.EncBinary() + _ = yym10 + if false { + } else { + r.EncodeInt(int64(x.ObservedGeneration)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("observedGeneration")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym11 := z.EncBinary() + _ = yym11 + if false { } else { r.EncodeInt(int64(x.ObservedGeneration)) } @@ -17350,6 +16408,12 @@ func (x *ReplicaSetStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { } else { x.Replicas = int32(r.DecodeInt(32)) } + case "fullyLabeledReplicas": + if r.TryDecodeAsNil() { + x.FullyLabeledReplicas = 0 + } else { + x.FullyLabeledReplicas = int32(r.DecodeInt(32)) + } case "observedGeneration": if r.TryDecodeAsNil() { x.ObservedGeneration = 0 @@ -17367,16 +16431,16 @@ func (x *ReplicaSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj6 int - var yyb6 bool - var yyhl6 bool = l >= 0 - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l + var yyj7 int + var yyb7 bool + var yyhl7 bool = l >= 0 + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb6 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb6 { + if yyb7 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -17386,13 +16450,29 @@ func (x *ReplicaSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) } else { x.Replicas = int32(r.DecodeInt(32)) } - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb6 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb6 { + if yyb7 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.FullyLabeledReplicas = 0 + } else { + x.FullyLabeledReplicas = int32(r.DecodeInt(32)) + } + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l + } else { + yyb7 = r.CheckBreak() + } + if yyb7 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -17403,17 +16483,17 @@ func (x *ReplicaSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) x.ObservedGeneration = int64(r.DecodeInt(64)) } for { - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l } else { - yyb6 = r.CheckBreak() + yyb7 = r.CheckBreak() } - if yyb6 { + if yyb7 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj6-1, "") + z.DecStructFieldNotFound(yyj7-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -17960,7 +17040,7 @@ func (x *PodSecurityPolicySpec) CodecEncodeSelf(e *codec1978.Encoder) { if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) if yyq2[7] { - yy25 := &x.SELinuxContext + yy25 := &x.SELinux yy25.CodecEncodeSelf(e) } else { r.EncodeNil() @@ -17968,9 +17048,9 @@ func (x *PodSecurityPolicySpec) CodecEncodeSelf(e *codec1978.Encoder) { } else { if yyq2[7] { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("seLinuxContext")) + r.EncodeString(codecSelferC_UTF81234, string("seLinux")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy27 := &x.SELinuxContext + yy27 := &x.SELinux yy27.CodecEncodeSelf(e) } } @@ -18112,11 +17192,11 @@ func (x *PodSecurityPolicySpec) codecDecodeSelfFromMap(l int, d *codec1978.Decod } else { x.HostIPC = bool(r.DecodeBool()) } - case "seLinuxContext": + case "seLinux": if r.TryDecodeAsNil() { - x.SELinuxContext = SELinuxContextStrategyOptions{} + x.SELinux = SELinuxStrategyOptions{} } else { - yyv14 := &x.SELinuxContext + yyv14 := &x.SELinux yyv14.CodecDecodeSelf(d) } case "runAsUser": @@ -18282,9 +17362,9 @@ func (x *PodSecurityPolicySpec) codecDecodeSelfFromArray(l int, d *codec1978.Dec } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.SELinuxContext = SELinuxContextStrategyOptions{} + x.SELinux = SELinuxStrategyOptions{} } else { - yyv27 := &x.SELinuxContext + yyv27 := &x.SELinux yyv27.CodecDecodeSelf(d) } yyj16++ @@ -18549,7 +17629,7 @@ func (x *HostPortRange) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } -func (x *SELinuxContextStrategyOptions) CodecEncodeSelf(e *codec1978.Encoder) { +func (x *SELinuxStrategyOptions) CodecEncodeSelf(e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) _, _, _ = h, z, r @@ -18582,12 +17662,12 @@ func (x *SELinuxContextStrategyOptions) CodecEncodeSelf(e *codec1978.Encoder) { } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - x.Type.CodecEncodeSelf(e) + x.Rule.CodecEncodeSelf(e) } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("type")) + r.EncodeString(codecSelferC_UTF81234, string("rule")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - x.Type.CodecEncodeSelf(e) + x.Rule.CodecEncodeSelf(e) } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) @@ -18621,7 +17701,7 @@ func (x *SELinuxContextStrategyOptions) CodecEncodeSelf(e *codec1978.Encoder) { } } -func (x *SELinuxContextStrategyOptions) CodecDecodeSelf(d *codec1978.Decoder) { +func (x *SELinuxStrategyOptions) CodecDecodeSelf(d *codec1978.Decoder) { var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r @@ -18651,7 +17731,7 @@ func (x *SELinuxContextStrategyOptions) CodecDecodeSelf(d *codec1978.Decoder) { } } -func (x *SELinuxContextStrategyOptions) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { +func (x *SELinuxStrategyOptions) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r @@ -18673,11 +17753,11 @@ func (x *SELinuxContextStrategyOptions) codecDecodeSelfFromMap(l int, d *codec19 yys3 := string(yys3Slc) z.DecSendContainerState(codecSelfer_containerMapValue1234) switch yys3 { - case "type": + case "rule": if r.TryDecodeAsNil() { - x.Type = "" + x.Rule = "" } else { - x.Type = SELinuxContextStrategy(r.DecodeString()) + x.Rule = SELinuxStrategy(r.DecodeString()) } case "seLinuxOptions": if r.TryDecodeAsNil() { @@ -18697,7 +17777,7 @@ func (x *SELinuxContextStrategyOptions) codecDecodeSelfFromMap(l int, d *codec19 z.DecSendContainerState(codecSelfer_containerMapEnd1234) } -func (x *SELinuxContextStrategyOptions) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { +func (x *SELinuxStrategyOptions) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r @@ -18716,9 +17796,9 @@ func (x *SELinuxContextStrategyOptions) codecDecodeSelfFromArray(l int, d *codec } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.Type = "" + x.Rule = "" } else { - x.Type = SELinuxContextStrategy(r.DecodeString()) + x.Rule = SELinuxStrategy(r.DecodeString()) } yyj6++ if yyhl6 { @@ -18757,7 +17837,7 @@ func (x *SELinuxContextStrategyOptions) codecDecodeSelfFromArray(l int, d *codec z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } -func (x SELinuxContextStrategy) CodecEncodeSelf(e *codec1978.Encoder) { +func (x SELinuxStrategy) CodecEncodeSelf(e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) _, _, _ = h, z, r @@ -18770,7 +17850,7 @@ func (x SELinuxContextStrategy) CodecEncodeSelf(e *codec1978.Encoder) { } } -func (x *SELinuxContextStrategy) CodecDecodeSelf(d *codec1978.Decoder) { +func (x *SELinuxStrategy) CodecDecodeSelf(d *codec1978.Decoder) { var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r @@ -18816,12 +17896,12 @@ func (x *RunAsUserStrategyOptions) CodecEncodeSelf(e *codec1978.Encoder) { } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) - x.Type.CodecEncodeSelf(e) + x.Rule.CodecEncodeSelf(e) } else { z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("type")) + r.EncodeString(codecSelferC_UTF81234, string("rule")) z.EncSendContainerState(codecSelfer_containerMapValue1234) - x.Type.CodecEncodeSelf(e) + x.Rule.CodecEncodeSelf(e) } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayElem1234) @@ -18917,11 +17997,11 @@ func (x *RunAsUserStrategyOptions) codecDecodeSelfFromMap(l int, d *codec1978.De yys3 := string(yys3Slc) z.DecSendContainerState(codecSelfer_containerMapValue1234) switch yys3 { - case "type": + case "rule": if r.TryDecodeAsNil() { - x.Type = "" + x.Rule = "" } else { - x.Type = RunAsUserStrategy(r.DecodeString()) + x.Rule = RunAsUserStrategy(r.DecodeString()) } case "ranges": if r.TryDecodeAsNil() { @@ -18961,9 +18041,9 @@ func (x *RunAsUserStrategyOptions) codecDecodeSelfFromArray(l int, d *codec1978. } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.Type = "" + x.Rule = "" } else { - x.Type = RunAsUserStrategy(r.DecodeString()) + x.Rule = RunAsUserStrategy(r.DecodeString()) } yyj7++ if yyhl7 { @@ -20210,7 +19290,7 @@ func (x codecSelfer1234) decSliceDeployment(v *[]Deployment, d *codec1978.Decode yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 632) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 640) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] @@ -20567,7 +19647,7 @@ func (x codecSelfer1234) decSliceJob(v *[]Job, d *codec1978.Decoder) { yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 632) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 640) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] @@ -21242,244 +20322,6 @@ func (x codecSelfer1234) decSliceHTTPIngressPath(v *[]HTTPIngressPath, d *codec1 } } -func (x codecSelfer1234) encSliceNodeUtilization(v []NodeUtilization, e *codec1978.Encoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - r.EncodeArrayStart(len(v)) - for _, yyv1 := range v { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy2 := &yyv1 - yy2.CodecEncodeSelf(e) - } - z.EncSendContainerState(codecSelfer_containerArrayEnd1234) -} - -func (x codecSelfer1234) decSliceNodeUtilization(v *[]NodeUtilization, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - - yyv1 := *v - yyh1, yyl1 := z.DecSliceHelperStart() - var yyc1 bool - _ = yyc1 - if yyl1 == 0 { - if yyv1 == nil { - yyv1 = []NodeUtilization{} - yyc1 = true - } else if len(yyv1) != 0 { - yyv1 = yyv1[:0] - yyc1 = true - } - } else if yyl1 > 0 { - var yyrr1, yyrl1 int - var yyrt1 bool - _, _ = yyrl1, yyrt1 - yyrr1 = yyl1 // len(yyv1) - if yyl1 > cap(yyv1) { - - yyrg1 := len(yyv1) > 0 - yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 24) - if yyrt1 { - if yyrl1 <= cap(yyv1) { - yyv1 = yyv1[:yyrl1] - } else { - yyv1 = make([]NodeUtilization, yyrl1) - } - } else { - yyv1 = make([]NodeUtilization, yyrl1) - } - yyc1 = true - yyrr1 = len(yyv1) - if yyrg1 { - copy(yyv1, yyv21) - } - } else if yyl1 != len(yyv1) { - yyv1 = yyv1[:yyl1] - yyc1 = true - } - yyj1 := 0 - for ; yyj1 < yyrr1; yyj1++ { - yyh1.ElemContainerState(yyj1) - if r.TryDecodeAsNil() { - yyv1[yyj1] = NodeUtilization{} - } else { - yyv2 := &yyv1[yyj1] - yyv2.CodecDecodeSelf(d) - } - - } - if yyrt1 { - for ; yyj1 < yyl1; yyj1++ { - yyv1 = append(yyv1, NodeUtilization{}) - yyh1.ElemContainerState(yyj1) - if r.TryDecodeAsNil() { - yyv1[yyj1] = NodeUtilization{} - } else { - yyv3 := &yyv1[yyj1] - yyv3.CodecDecodeSelf(d) - } - - } - } - - } else { - yyj1 := 0 - for ; !r.CheckBreak(); yyj1++ { - - if yyj1 >= len(yyv1) { - yyv1 = append(yyv1, NodeUtilization{}) // var yyz1 NodeUtilization - yyc1 = true - } - yyh1.ElemContainerState(yyj1) - if yyj1 < len(yyv1) { - if r.TryDecodeAsNil() { - yyv1[yyj1] = NodeUtilization{} - } else { - yyv4 := &yyv1[yyj1] - yyv4.CodecDecodeSelf(d) - } - - } else { - z.DecSwallow() - } - - } - if yyj1 < len(yyv1) { - yyv1 = yyv1[:yyj1] - yyc1 = true - } else if yyj1 == 0 && yyv1 == nil { - yyv1 = []NodeUtilization{} - yyc1 = true - } - } - yyh1.End() - if yyc1 { - *v = yyv1 - } -} - -func (x codecSelfer1234) encSliceClusterAutoscaler(v []ClusterAutoscaler, e *codec1978.Encoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - r.EncodeArrayStart(len(v)) - for _, yyv1 := range v { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yy2 := &yyv1 - yy2.CodecEncodeSelf(e) - } - z.EncSendContainerState(codecSelfer_containerArrayEnd1234) -} - -func (x codecSelfer1234) decSliceClusterAutoscaler(v *[]ClusterAutoscaler, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - - yyv1 := *v - yyh1, yyl1 := z.DecSliceHelperStart() - var yyc1 bool - _ = yyc1 - if yyl1 == 0 { - if yyv1 == nil { - yyv1 = []ClusterAutoscaler{} - yyc1 = true - } else if len(yyv1) != 0 { - yyv1 = yyv1[:0] - yyc1 = true - } - } else if yyl1 > 0 { - var yyrr1, yyrl1 int - var yyrt1 bool - _, _ = yyrl1, yyrt1 - yyrr1 = yyl1 // len(yyv1) - if yyl1 > cap(yyv1) { - - yyrg1 := len(yyv1) > 0 - yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 224) - if yyrt1 { - if yyrl1 <= cap(yyv1) { - yyv1 = yyv1[:yyrl1] - } else { - yyv1 = make([]ClusterAutoscaler, yyrl1) - } - } else { - yyv1 = make([]ClusterAutoscaler, yyrl1) - } - yyc1 = true - yyrr1 = len(yyv1) - if yyrg1 { - copy(yyv1, yyv21) - } - } else if yyl1 != len(yyv1) { - yyv1 = yyv1[:yyl1] - yyc1 = true - } - yyj1 := 0 - for ; yyj1 < yyrr1; yyj1++ { - yyh1.ElemContainerState(yyj1) - if r.TryDecodeAsNil() { - yyv1[yyj1] = ClusterAutoscaler{} - } else { - yyv2 := &yyv1[yyj1] - yyv2.CodecDecodeSelf(d) - } - - } - if yyrt1 { - for ; yyj1 < yyl1; yyj1++ { - yyv1 = append(yyv1, ClusterAutoscaler{}) - yyh1.ElemContainerState(yyj1) - if r.TryDecodeAsNil() { - yyv1[yyj1] = ClusterAutoscaler{} - } else { - yyv3 := &yyv1[yyj1] - yyv3.CodecDecodeSelf(d) - } - - } - } - - } else { - yyj1 := 0 - for ; !r.CheckBreak(); yyj1++ { - - if yyj1 >= len(yyv1) { - yyv1 = append(yyv1, ClusterAutoscaler{}) // var yyz1 ClusterAutoscaler - yyc1 = true - } - yyh1.ElemContainerState(yyj1) - if yyj1 < len(yyv1) { - if r.TryDecodeAsNil() { - yyv1[yyj1] = ClusterAutoscaler{} - } else { - yyv4 := &yyv1[yyj1] - yyv4.CodecDecodeSelf(d) - } - - } else { - z.DecSwallow() - } - - } - if yyj1 < len(yyv1) { - yyv1 = yyv1[:yyj1] - yyc1 = true - } else if yyj1 == 0 && yyv1 == nil { - yyv1 = []ClusterAutoscaler{} - yyc1 = true - } - } - yyh1.End() - if yyc1 { - *v = yyv1 - } -} - func (x codecSelfer1234) encSliceLabelSelectorRequirement(v []LabelSelectorRequirement, e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) @@ -21638,7 +20480,7 @@ func (x codecSelfer1234) decSliceReplicaSet(v *[]ReplicaSet, d *codec1978.Decode yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 232) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 576) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/types.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/types.go index 4de157e3c..1a67438b8 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/types.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/types.go @@ -34,8 +34,16 @@ type ScaleStatus struct { // actual number of observed instances of the scaled object. Replicas int32 `json:"replicas"` - // label query over pods that should match the replicas count. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors + // label query over pods that should match the replicas count. More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors Selector map[string]string `json:"selector,omitempty"` + + // label selector for pods that should match the replicas count. This is a serializated + // version of both map-based and more expressive set-based selectors. This is done to + // avoid introspection in the clients. The string will be in the same format as the + // query-param syntax. If the target type only supports map-based selectors, both this + // field and map-based selector field are populated. + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors + TargetSelector string `json:"targetSelector,omitempty"` } // +genclient=true,noMethods=true @@ -43,13 +51,13 @@ type ScaleStatus struct { // represents a scaling request for a resource. type Scale struct { unversioned.TypeMeta `json:",inline"` - // Standard object metadata; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata. + // Standard object metadata; More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata. v1.ObjectMeta `json:"metadata,omitempty"` - // defines the behavior of the scale. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status. + // defines the behavior of the scale. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status. Spec ScaleSpec `json:"spec,omitempty"` - // current status of the scale. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status. Read-only. + // current status of the scale. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status. Read-only. Status ScaleStatus `json:"status,omitempty"` } @@ -60,9 +68,9 @@ type ReplicationControllerDummy struct { // SubresourceReference contains enough information to let you inspect or modify the referred subresource. type SubresourceReference struct { - // Kind of the referent; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds" + // Kind of the referent; More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds" Kind string `json:"kind,omitempty"` - // Name of the referent; More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names + // Name of the referent; More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#names Name string `json:"name,omitempty"` // API version of the referent APIVersion string `json:"apiVersion,omitempty"` @@ -138,10 +146,10 @@ type HorizontalPodAutoscalerStatus struct { // configuration of a horizontal pod autoscaler. type HorizontalPodAutoscaler struct { unversioned.TypeMeta `json:",inline"` - // Standard object metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // Standard object metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata v1.ObjectMeta `json:"metadata,omitempty"` - // behaviour of autoscaler. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status. + // behaviour of autoscaler. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status. Spec HorizontalPodAutoscalerSpec `json:"spec,omitempty"` // current information about the autoscaler. @@ -328,6 +336,9 @@ type RollingUpdateDeployment struct { // DeploymentStatus is the most recently observed status of the Deployment. type DeploymentStatus struct { + // The generation observed by the deployment controller. + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + // Total number of non-terminated pods targeted by this deployment (their labels match the selector). Replicas int32 `json:"replicas,omitempty"` @@ -402,14 +413,14 @@ type DaemonSetSpec struct { // Selector is a label query over pods that are managed by the daemon set. // Must match in order to be controlled. // If empty, defaulted to labels on Pod template. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors Selector *LabelSelector `json:"selector,omitempty"` // Template is the object that describes the pod that will be created. // The DaemonSet will create exactly one copy of this pod on every node // that matches the template's node selector (or on every node if no node // selector is specified). - // More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#pod-template + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md#pod-template Template v1.PodTemplateSpec `json:"template"` // TODO(madhusudancs): Uncomment while implementing DaemonSet updates. @@ -439,17 +450,17 @@ const ( type DaemonSetStatus struct { // CurrentNumberScheduled is the number of nodes that are running at least 1 // daemon pod and are supposed to run the daemon pod. - // More info: http://releases.k8s.io/HEAD/docs/admin/daemon.md + // More info: http://releases.k8s.io/release-1.2/docs/admin/daemons.md CurrentNumberScheduled int32 `json:"currentNumberScheduled"` // NumberMisscheduled is the number of nodes that are running the daemon pod, but are // not supposed to run the daemon pod. - // More info: http://releases.k8s.io/HEAD/docs/admin/daemon.md + // More info: http://releases.k8s.io/release-1.2/docs/admin/daemons.md NumberMisscheduled int32 `json:"numberMisscheduled"` // DesiredNumberScheduled is the total number of nodes that should be running the daemon // pod (including nodes correctly running the daemon pod). - // More info: http://releases.k8s.io/HEAD/docs/admin/daemon.md + // More info: http://releases.k8s.io/release-1.2/docs/admin/daemons.md DesiredNumberScheduled int32 `json:"desiredNumberScheduled"` } @@ -459,18 +470,18 @@ type DaemonSetStatus struct { type DaemonSet struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata v1.ObjectMeta `json:"metadata,omitempty"` // Spec defines the desired behavior of this daemon set. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Spec DaemonSetSpec `json:"spec,omitempty"` // Status is the current status of this daemon set. This data may be // out of date by some window of time. // Populated by the system. // Read-only. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Status DaemonSetStatus `json:"status,omitempty"` } @@ -478,7 +489,7 @@ type DaemonSet struct { type DaemonSetList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata unversioned.ListMeta `json:"metadata,omitempty"` // Items is a list of daemon sets. @@ -489,7 +500,7 @@ type DaemonSetList struct { type ThirdPartyResourceDataList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata unversioned.ListMeta `json:"metadata,omitempty"` // Items is the list of ThirdpartyResourceData. @@ -502,15 +513,15 @@ type ThirdPartyResourceDataList struct { type Job struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata v1.ObjectMeta `json:"metadata,omitempty"` // Spec is a structure defining the expected behavior of a job. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Spec JobSpec `json:"spec,omitempty"` // Status is a structure describing current status of a job. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Status JobStatus `json:"status,omitempty"` } @@ -518,7 +529,7 @@ type Job struct { type JobList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata unversioned.ListMeta `json:"metadata,omitempty"` // Items is the list of Job. @@ -532,7 +543,7 @@ type JobSpec struct { // run at any given time. The actual number of pods running in steady state will // be less than this number when ((.spec.completions - .status.successful) < .spec.parallelism), // i.e. when the work left to do is less than max parallelism. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md Parallelism *int32 `json:"parallelism,omitempty"` // Completions specifies the desired number of successfully finished pods the @@ -540,6 +551,7 @@ type JobSpec struct { // pod signals the success of all pods, and allows parallelism to have any positive // value. Setting to 1 means that parallelism is limited to 1 and the success of that // pod signals the success of the job. + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md Completions *int32 `json:"completions,omitempty"` // Optional duration in seconds relative to the startTime that the job may be active @@ -547,12 +559,20 @@ type JobSpec struct { ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty"` // Selector is a label query over pods that should match the pod count. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors + // Normally, the system sets this field for you. + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors Selector *LabelSelector `json:"selector,omitempty"` + // AutoSelector controls generation of pod labels and pod selectors. + // It was not present in the original extensions/v1beta1 Job definition, but exists + // to allow conversion from batch/v1 Jobs, where it corresponds to, but has the opposite + // meaning as, ManualSelector. + // More info: http://releases.k8s.io/release-1.2/docs/design/selector-generation.md + AutoSelector *bool `json:"autoSelector,omitempty"` + // Template is the object that describes the pod that will be created when // executing a job. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md Template v1.PodTemplateSpec `json:"template"` } @@ -560,7 +580,7 @@ type JobSpec struct { type JobStatus struct { // Conditions represent the latest available observations of an object's current state. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md Conditions []JobCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` // StartTime represents time when the job was acknowledged by the Job Manager. @@ -618,15 +638,15 @@ type JobCondition struct { type Ingress struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata v1.ObjectMeta `json:"metadata,omitempty"` // Spec is the desired state of the Ingress. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Spec IngressSpec `json:"spec,omitempty"` // Status is the current state of the Ingress. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Status IngressStatus `json:"status,omitempty"` } @@ -634,7 +654,7 @@ type Ingress struct { type IngressList struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata unversioned.ListMeta `json:"metadata,omitempty"` // Items is the list of Ingress. @@ -760,67 +780,6 @@ type IngressBackend struct { ServicePort intstr.IntOrString `json:"servicePort"` } -type NodeResource string - -const ( - // Percentage of node's CPUs that is currently used. - CpuConsumption NodeResource = "CpuConsumption" - - // Percentage of node's CPUs that is currently requested for pods. - CpuRequest NodeResource = "CpuRequest" - - // Percentage od node's memory that is currently used. - MemConsumption NodeResource = "MemConsumption" - - // Percentage of node's CPUs that is currently requested for pods. - MemRequest NodeResource = "MemRequest" -) - -// NodeUtilization describes what percentage of a particular resource is used on a node. -type NodeUtilization struct { - Resource NodeResource `json:"resource"` - - // The accepted values are from 0 to 1. - Value float64 `json:"value"` -} - -// Configuration of the Cluster Autoscaler -type ClusterAutoscalerSpec struct { - // Minimum number of nodes that the cluster should have. - MinNodes int32 `json:"minNodes"` - - // Maximum number of nodes that the cluster should have. - MaxNodes int32 `json:"maxNodes"` - - // Target average utilization of the cluster nodes. New nodes will be added if one of the - // targets is exceeded. Cluster size will be decreased if the current utilization is too low - // for all targets. - TargetUtilization []NodeUtilization `json:"target"` -} - -type ClusterAutoscaler struct { - unversioned.TypeMeta `json:",inline"` - - // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata - // For now (experimental api) it is required that the name is set to "ClusterAutoscaler" and namespace is "default". - v1.ObjectMeta `json:"metadata,omitempty"` - - // Spec defines the desired behavior of this daemon set. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status - Spec ClusterAutoscalerSpec `json:"spec,omitempty"` -} - -// There will be just one (or none) ClusterAutoscaler. -type ClusterAutoscalerList struct { - unversioned.TypeMeta `json:",inline"` - // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata - unversioned.ListMeta `json:"metadata,omitempty"` - - Items []ClusterAutoscaler `json:"items"` -} - // ExportOptions is the query options to the standard REST get call. type ExportOptions struct { unversioned.TypeMeta `json:",inline"` @@ -895,18 +854,18 @@ type ReplicaSet struct { // If the Labels of a ReplicaSet are empty, they are defaulted to // be the same as the Pod(s) that the ReplicaSet manages. - // Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata v1.ObjectMeta `json:"metadata,omitempty"` // Spec defines the specification of the desired behavior of the ReplicaSet. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Spec ReplicaSetSpec `json:"spec,omitempty"` // Status is the most recently observed status of the ReplicaSet. // This data may be out of date by some window of time. // Populated by the system. // Read-only. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status Status ReplicaSetStatus `json:"status,omitempty"` } @@ -914,11 +873,11 @@ type ReplicaSet struct { type ReplicaSetList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds unversioned.ListMeta `json:"metadata,omitempty"` // List of ReplicaSets. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md Items []ReplicaSet `json:"items"` } @@ -927,27 +886,30 @@ type ReplicaSetSpec struct { // Replicas is the number of desired replicas. // This is a pointer to distinguish between explicit zero and unspecified. // Defaults to 1. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md#what-is-a-replication-controller Replicas *int32 `json:"replicas,omitempty"` // Selector is a label query over pods that should match the replica count. // If the selector is empty, it is defaulted to the labels present on the pod template. // Label keys and values that must match in order to be controlled by this replica set. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors Selector *LabelSelector `json:"selector,omitempty"` // Template is the object that describes the pod that will be created if // insufficient replicas are detected. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#pod-template - Template *v1.PodTemplateSpec `json:"template,omitempty"` + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md#pod-template + Template v1.PodTemplateSpec `json:"template,omitempty"` } // ReplicaSetStatus represents the current status of a ReplicaSet. type ReplicaSetStatus struct { // Replicas is the most recently oberved number of replicas. - // More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller + // More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md#what-is-a-replication-controller Replicas int32 `json:"replicas"` + // The number of pods that have labels matching the labels of the pod template of the replicaset. + FullyLabeledReplicas int32 `json:"fullyLabeledReplicas,omitempty"` + // ObservedGeneration reflects the generation of the most recently observed ReplicaSet. ObservedGeneration int64 `json:"observedGeneration,omitempty"` } @@ -957,7 +919,7 @@ type ReplicaSetStatus struct { type PodSecurityPolicy struct { unversioned.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata v1.ObjectMeta `json:"metadata,omitempty"` // spec defines the policy enforced. @@ -981,8 +943,8 @@ type PodSecurityPolicySpec struct { HostPID bool `json:"hostPID,omitempty"` // hostIPC determines if the policy allows the use of HostIPC in the pod spec. HostIPC bool `json:"hostIPC,omitempty"` - // seLinuxContext is the strategy that will dictate the allowable labels that may be set. - SELinuxContext SELinuxContextStrategyOptions `json:"seLinuxContext,omitempty"` + // seLinux is the strategy that will dictate the allowable labels that may be set. + SELinux SELinuxStrategyOptions `json:"seLinux,omitempty"` // runAsUser is the strategy that will dictate the allowable RunAsUser values that may be set. RunAsUser RunAsUserStrategyOptions `json:"runAsUser,omitempty"` } @@ -1017,30 +979,30 @@ type HostPortRange struct { Max int32 `json:"max"` } -// SELinux Context Strategy Options defines the strategy type and any options used to create the strategy. -type SELinuxContextStrategyOptions struct { +// SELinux Strategy Options defines the strategy type and any options used to create the strategy. +type SELinuxStrategyOptions struct { // type is the strategy that will dictate the allowable labels that may be set. - Type SELinuxContextStrategy `json:"type"` + Rule SELinuxStrategy `json:"rule"` // seLinuxOptions required to run as; required for MustRunAs - // More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context + // More info: http://releases.k8s.io/release-1.2/docs/design/security_context.md#security-context SELinuxOptions *v1.SELinuxOptions `json:"seLinuxOptions,omitempty"` } -// SELinux Context Strategy Type denotes strategy types for generating SELinux options for a +// SELinuxStrategy denotes strategy types for generating SELinux options for a // Security Context. -type SELinuxContextStrategy string +type SELinuxStrategy string const ( // container must have SELinux labels of X applied. - SELinuxStrategyMustRunAs SELinuxContextStrategy = "MustRunAs" + SELinuxStrategyMustRunAs SELinuxStrategy = "MustRunAs" // container may make requests for any SELinux context labels. - SELinuxStrategyRunAsAny SELinuxContextStrategy = "RunAsAny" + SELinuxStrategyRunAsAny SELinuxStrategy = "RunAsAny" ) // Run A sUser Strategy Options defines the strategy type and any options used to create the strategy. type RunAsUserStrategyOptions struct { - // type is the strategy that will dictate the allowable RunAsUser values that may be set. - Type RunAsUserStrategy `json:"type"` + // Rule is the strategy that will dictate the allowable RunAsUser values that may be set. + Rule RunAsUserStrategy `json:"rule"` // Ranges are the allowed ranges of uids that may be used. Ranges []IDRange `json:"ranges,omitempty"` } @@ -1053,7 +1015,7 @@ type IDRange struct { Max int64 `json:"max"` } -// Run As User Strategy Type denotes strategy types for generating RunAsUser values for a +// RunAsUserStrategy denotes strategy types for generating RunAsUser values for a // Security Context. type RunAsUserStrategy string @@ -1070,7 +1032,7 @@ const ( type PodSecurityPolicyList struct { unversioned.TypeMeta `json:",inline"` // Standard list metadata. - // More info: http://docs.k8s.io/api-conventions.md#metadata + // More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata unversioned.ListMeta `json:"metadata,omitempty"` // Items is a list of schema objects. diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/types_swagger_doc_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/types_swagger_doc_generated.go index e6176c293..44e446215 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/types_swagger_doc_generated.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/v1beta1/types_swagger_doc_generated.go @@ -16,7 +16,7 @@ limitations under the License. package v1beta1 -// This file contains a collection of methods that can be used from go-resful to +// This file contains a collection of methods that can be used from go-restful to // generate Swagger API documentation for its models. Please read this PR for more // information on the implementation: https://github.com/emicklei/go-restful/pull/215 // @@ -45,35 +45,6 @@ func (CPUTargetUtilization) SwaggerDoc() map[string]string { return map_CPUTargetUtilization } -var map_ClusterAutoscaler = map[string]string{ - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata For now (experimental api) it is required that the name is set to \"ClusterAutoscaler\" and namespace is \"default\".", - "spec": "Spec defines the desired behavior of this daemon set. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", -} - -func (ClusterAutoscaler) SwaggerDoc() map[string]string { - return map_ClusterAutoscaler -} - -var map_ClusterAutoscalerList = map[string]string{ - "": "There will be just one (or none) ClusterAutoscaler.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", -} - -func (ClusterAutoscalerList) SwaggerDoc() map[string]string { - return map_ClusterAutoscalerList -} - -var map_ClusterAutoscalerSpec = map[string]string{ - "": "Configuration of the Cluster Autoscaler", - "minNodes": "Minimum number of nodes that the cluster should have.", - "maxNodes": "Maximum number of nodes that the cluster should have.", - "target": "Target average utilization of the cluster nodes. New nodes will be added if one of the targets is exceeded. Cluster size will be decreased if the current utilization is too low for all targets.", -} - -func (ClusterAutoscalerSpec) SwaggerDoc() map[string]string { - return map_ClusterAutoscalerSpec -} - var map_CustomMetricCurrentStatus = map[string]string{ "name": "Custom Metric name.", "value": "Custom Metric value (average).", @@ -95,9 +66,9 @@ func (CustomMetricTarget) SwaggerDoc() map[string]string { var map_DaemonSet = map[string]string{ "": "DaemonSet represents the configuration of a daemon set.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "Spec defines the desired behavior of this daemon set. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", - "status": "Status is the current status of this daemon set. This data may be out of date by some window of time. Populated by the system. Read-only. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "Spec defines the desired behavior of this daemon set. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", + "status": "Status is the current status of this daemon set. This data may be out of date by some window of time. Populated by the system. Read-only. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", } func (DaemonSet) SwaggerDoc() map[string]string { @@ -106,7 +77,7 @@ func (DaemonSet) SwaggerDoc() map[string]string { var map_DaemonSetList = map[string]string{ "": "DaemonSetList is a collection of daemon sets.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", "items": "Items is a list of daemon sets.", } @@ -116,8 +87,8 @@ func (DaemonSetList) SwaggerDoc() map[string]string { var map_DaemonSetSpec = map[string]string{ "": "DaemonSetSpec is the specification of a daemon set.", - "selector": "Selector is a label query over pods that are managed by the daemon set. Must match in order to be controlled. If empty, defaulted to labels on Pod template. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors", - "template": "Template is the object that describes the pod that will be created. The DaemonSet will create exactly one copy of this pod on every node that matches the template's node selector (or on every node if no node selector is specified). More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#pod-template", + "selector": "Selector is a label query over pods that are managed by the daemon set. Must match in order to be controlled. If empty, defaulted to labels on Pod template. More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors", + "template": "Template is the object that describes the pod that will be created. The DaemonSet will create exactly one copy of this pod on every node that matches the template's node selector (or on every node if no node selector is specified). More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md#pod-template", } func (DaemonSetSpec) SwaggerDoc() map[string]string { @@ -126,9 +97,9 @@ func (DaemonSetSpec) SwaggerDoc() map[string]string { var map_DaemonSetStatus = map[string]string{ "": "DaemonSetStatus represents the current status of a daemon set.", - "currentNumberScheduled": "CurrentNumberScheduled is the number of nodes that are running at least 1 daemon pod and are supposed to run the daemon pod. More info: http://releases.k8s.io/HEAD/docs/admin/daemon.md", - "numberMisscheduled": "NumberMisscheduled is the number of nodes that are running the daemon pod, but are not supposed to run the daemon pod. More info: http://releases.k8s.io/HEAD/docs/admin/daemon.md", - "desiredNumberScheduled": "DesiredNumberScheduled is the total number of nodes that should be running the daemon pod (including nodes correctly running the daemon pod). More info: http://releases.k8s.io/HEAD/docs/admin/daemon.md", + "currentNumberScheduled": "CurrentNumberScheduled is the number of nodes that are running at least 1 daemon pod and are supposed to run the daemon pod. More info: http://releases.k8s.io/release-1.2/docs/admin/daemons.md", + "numberMisscheduled": "NumberMisscheduled is the number of nodes that are running the daemon pod, but are not supposed to run the daemon pod. More info: http://releases.k8s.io/release-1.2/docs/admin/daemons.md", + "desiredNumberScheduled": "DesiredNumberScheduled is the total number of nodes that should be running the daemon pod (including nodes correctly running the daemon pod). More info: http://releases.k8s.io/release-1.2/docs/admin/daemons.md", } func (DaemonSetStatus) SwaggerDoc() map[string]string { @@ -185,6 +156,7 @@ func (DeploymentSpec) SwaggerDoc() map[string]string { var map_DeploymentStatus = map[string]string{ "": "DeploymentStatus is the most recently observed status of the Deployment.", + "observedGeneration": "The generation observed by the deployment controller.", "replicas": "Total number of non-terminated pods targeted by this deployment (their labels match the selector).", "updatedReplicas": "Total number of non-terminated pods targeted by this deployment that have the desired template spec.", "availableReplicas": "Total number of available pods (ready for at least minReadySeconds) targeted by this deployment.", @@ -236,8 +208,8 @@ func (HTTPIngressRuleValue) SwaggerDoc() map[string]string { var map_HorizontalPodAutoscaler = map[string]string{ "": "configuration of a horizontal pod autoscaler.", - "metadata": "Standard object metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "behaviour of autoscaler. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status.", + "metadata": "Standard object metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "behaviour of autoscaler. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status.", "status": "current information about the autoscaler.", } @@ -302,9 +274,9 @@ func (IDRange) SwaggerDoc() map[string]string { var map_Ingress = map[string]string{ "": "Ingress is a collection of rules that allow inbound connections to reach the endpoints defined by a backend. An Ingress can be configured to give services externally-reachable urls, load balance traffic, terminate SSL, offer name based virtual hosting etc.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "Spec is the desired state of the Ingress. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", - "status": "Status is the current state of the Ingress. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "Spec is the desired state of the Ingress. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", + "status": "Status is the current state of the Ingress. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", } func (Ingress) SwaggerDoc() map[string]string { @@ -323,7 +295,7 @@ func (IngressBackend) SwaggerDoc() map[string]string { var map_IngressList = map[string]string{ "": "IngressList is a collection of Ingress.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", "items": "Items is the list of Ingress.", } @@ -380,9 +352,9 @@ func (IngressTLS) SwaggerDoc() map[string]string { var map_Job = map[string]string{ "": "Job represents the configuration of a single job.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "Spec is a structure defining the expected behavior of a job. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", - "status": "Status is a structure describing current status of a job. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "Spec is a structure defining the expected behavior of a job. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", + "status": "Status is a structure describing current status of a job. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", } func (Job) SwaggerDoc() map[string]string { @@ -405,7 +377,7 @@ func (JobCondition) SwaggerDoc() map[string]string { var map_JobList = map[string]string{ "": "JobList is a collection of jobs.", - "metadata": "Standard list metadata More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", + "metadata": "Standard list metadata More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", "items": "Items is the list of Job.", } @@ -415,11 +387,12 @@ func (JobList) SwaggerDoc() map[string]string { var map_JobSpec = map[string]string{ "": "JobSpec describes how the job execution will look like.", - "parallelism": "Parallelism specifies the maximum desired number of pods the job should run at any given time. The actual number of pods running in steady state will be less than this number when ((.spec.completions - .status.successful) < .spec.parallelism), i.e. when the work left to do is less than max parallelism. More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md", - "completions": "Completions specifies the desired number of successfully finished pods the job should be run with. Setting to nil means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job.", + "parallelism": "Parallelism specifies the maximum desired number of pods the job should run at any given time. The actual number of pods running in steady state will be less than this number when ((.spec.completions - .status.successful) < .spec.parallelism), i.e. when the work left to do is less than max parallelism. More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md", + "completions": "Completions specifies the desired number of successfully finished pods the job should be run with. Setting to nil means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job. More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md", "activeDeadlineSeconds": "Optional duration in seconds relative to the startTime that the job may be active before the system tries to terminate it; value must be positive integer", - "selector": "Selector is a label query over pods that should match the pod count. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors", - "template": "Template is the object that describes the pod that will be created when executing a job. More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md", + "selector": "Selector is a label query over pods that should match the pod count. Normally, the system sets this field for you. More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors", + "autoSelector": "AutoSelector controls generation of pod labels and pod selectors. It was not present in the original extensions/v1beta1 Job definition, but exists to allow conversion from batch/v1 Jobs, where it corresponds to, but has the opposite meaning as, ManualSelector. More info: http://releases.k8s.io/release-1.2/docs/design/selector-generation.md", + "template": "Template is the object that describes the pod that will be created when executing a job. More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md", } func (JobSpec) SwaggerDoc() map[string]string { @@ -428,7 +401,7 @@ func (JobSpec) SwaggerDoc() map[string]string { var map_JobStatus = map[string]string{ "": "JobStatus represents the current state of a Job.", - "conditions": "Conditions represent the latest available observations of an object's current state. More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md", + "conditions": "Conditions represent the latest available observations of an object's current state. More info: http://releases.k8s.io/release-1.2/docs/user-guide/jobs.md", "startTime": "StartTime represents time when the job was acknowledged by the Job Manager. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC.", "completionTime": "CompletionTime represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC.", "active": "Active is the number of actively running pods.", @@ -474,18 +447,9 @@ func (ListOptions) SwaggerDoc() map[string]string { return map_ListOptions } -var map_NodeUtilization = map[string]string{ - "": "NodeUtilization describes what percentage of a particular resource is used on a node.", - "value": "The accepted values are from 0 to 1.", -} - -func (NodeUtilization) SwaggerDoc() map[string]string { - return map_NodeUtilization -} - var map_PodSecurityPolicy = map[string]string{ "": "Pod Security Policy governs the ability to make requests that affect the Security Context that will be applied to a pod and container.", - "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", "spec": "spec defines the policy enforced.", } @@ -495,7 +459,7 @@ func (PodSecurityPolicy) SwaggerDoc() map[string]string { var map_PodSecurityPolicyList = map[string]string{ "": "Pod Security Policy List is a list of PodSecurityPolicy objects.", - "metadata": "Standard list metadata. More info: http://docs.k8s.io/api-conventions.md#metadata", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", "items": "Items is a list of schema objects.", } @@ -504,16 +468,16 @@ func (PodSecurityPolicyList) SwaggerDoc() map[string]string { } var map_PodSecurityPolicySpec = map[string]string{ - "": "Pod Security Policy Spec defines the policy enforced.", - "privileged": "privileged determines if a pod can request to be run as privileged.", - "capabilities": "capabilities is a list of capabilities that can be added.", - "volumes": "volumes is a white list of allowed volume plugins. Empty indicates that all plugins may be used.", - "hostNetwork": "hostNetwork determines if the policy allows the use of HostNetwork in the pod spec.", - "hostPorts": "hostPorts determines which host port ranges are allowed to be exposed.", - "hostPID": "hostPID determines if the policy allows the use of HostPID in the pod spec.", - "hostIPC": "hostIPC determines if the policy allows the use of HostIPC in the pod spec.", - "seLinuxContext": "seLinuxContext is the strategy that will dictate the allowable labels that may be set.", - "runAsUser": "runAsUser is the strategy that will dictate the allowable RunAsUser values that may be set.", + "": "Pod Security Policy Spec defines the policy enforced.", + "privileged": "privileged determines if a pod can request to be run as privileged.", + "capabilities": "capabilities is a list of capabilities that can be added.", + "volumes": "volumes is a white list of allowed volume plugins. Empty indicates that all plugins may be used.", + "hostNetwork": "hostNetwork determines if the policy allows the use of HostNetwork in the pod spec.", + "hostPorts": "hostPorts determines which host port ranges are allowed to be exposed.", + "hostPID": "hostPID determines if the policy allows the use of HostPID in the pod spec.", + "hostIPC": "hostIPC determines if the policy allows the use of HostIPC in the pod spec.", + "seLinux": "seLinux is the strategy that will dictate the allowable labels that may be set.", + "runAsUser": "runAsUser is the strategy that will dictate the allowable RunAsUser values that may be set.", } func (PodSecurityPolicySpec) SwaggerDoc() map[string]string { @@ -522,9 +486,9 @@ func (PodSecurityPolicySpec) SwaggerDoc() map[string]string { var map_ReplicaSet = map[string]string{ "": "ReplicaSet represents the configuration of a ReplicaSet.", - "metadata": "If the Labels of a ReplicaSet are empty, they are defaulted to be the same as the Pod(s) that the ReplicaSet manages. Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", - "spec": "Spec defines the specification of the desired behavior of the ReplicaSet. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", - "status": "Status is the most recently observed status of the ReplicaSet. This data may be out of date by some window of time. Populated by the system. Read-only. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", + "metadata": "If the Labels of a ReplicaSet are empty, they are defaulted to be the same as the Pod(s) that the ReplicaSet manages. Standard object's metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", + "spec": "Spec defines the specification of the desired behavior of the ReplicaSet. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", + "status": "Status is the most recently observed status of the ReplicaSet. This data may be out of date by some window of time. Populated by the system. Read-only. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status", } func (ReplicaSet) SwaggerDoc() map[string]string { @@ -533,8 +497,8 @@ func (ReplicaSet) SwaggerDoc() map[string]string { var map_ReplicaSetList = map[string]string{ "": "ReplicaSetList is a collection of ReplicaSets.", - "metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds", - "items": "List of ReplicaSets. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md", + "metadata": "Standard list metadata. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds", + "items": "List of ReplicaSets. More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md", } func (ReplicaSetList) SwaggerDoc() map[string]string { @@ -543,9 +507,9 @@ func (ReplicaSetList) SwaggerDoc() map[string]string { var map_ReplicaSetSpec = map[string]string{ "": "ReplicaSetSpec is the specification of a ReplicaSet.", - "replicas": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller", - "selector": "Selector is a label query over pods that should match the replica count. If the selector is empty, it is defaulted to the labels present on the pod template. Label keys and values that must match in order to be controlled by this replica set. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors", - "template": "Template is the object that describes the pod that will be created if insufficient replicas are detected. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#pod-template", + "replicas": "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md#what-is-a-replication-controller", + "selector": "Selector is a label query over pods that should match the replica count. If the selector is empty, it is defaulted to the labels present on the pod template. Label keys and values that must match in order to be controlled by this replica set. More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors", + "template": "Template is the object that describes the pod that will be created if insufficient replicas are detected. More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md#pod-template", } func (ReplicaSetSpec) SwaggerDoc() map[string]string { @@ -553,9 +517,10 @@ func (ReplicaSetSpec) SwaggerDoc() map[string]string { } var map_ReplicaSetStatus = map[string]string{ - "": "ReplicaSetStatus represents the current status of a ReplicaSet.", - "replicas": "Replicas is the most recently oberved number of replicas. More info: http://releases.k8s.io/HEAD/docs/user-guide/replication-controller.md#what-is-a-replication-controller", - "observedGeneration": "ObservedGeneration reflects the generation of the most recently observed ReplicaSet.", + "": "ReplicaSetStatus represents the current status of a ReplicaSet.", + "replicas": "Replicas is the most recently oberved number of replicas. More info: http://releases.k8s.io/release-1.2/docs/user-guide/replication-controller.md#what-is-a-replication-controller", + "fullyLabeledReplicas": "The number of pods that have labels matching the labels of the pod template of the replicaset.", + "observedGeneration": "ObservedGeneration reflects the generation of the most recently observed ReplicaSet.", } func (ReplicaSetStatus) SwaggerDoc() map[string]string { @@ -590,7 +555,7 @@ func (RollingUpdateDeployment) SwaggerDoc() map[string]string { var map_RunAsUserStrategyOptions = map[string]string{ "": "Run A sUser Strategy Options defines the strategy type and any options used to create the strategy.", - "type": "type is the strategy that will dictate the allowable RunAsUser values that may be set.", + "rule": "Rule is the strategy that will dictate the allowable RunAsUser values that may be set.", "ranges": "Ranges are the allowed ranges of uids that may be used.", } @@ -598,21 +563,21 @@ func (RunAsUserStrategyOptions) SwaggerDoc() map[string]string { return map_RunAsUserStrategyOptions } -var map_SELinuxContextStrategyOptions = map[string]string{ - "": "SELinux Context Strategy Options defines the strategy type and any options used to create the strategy.", - "type": "type is the strategy that will dictate the allowable labels that may be set.", - "seLinuxOptions": "seLinuxOptions required to run as; required for MustRunAs More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context", +var map_SELinuxStrategyOptions = map[string]string{ + "": "SELinux Strategy Options defines the strategy type and any options used to create the strategy.", + "rule": "type is the strategy that will dictate the allowable labels that may be set.", + "seLinuxOptions": "seLinuxOptions required to run as; required for MustRunAs More info: http://releases.k8s.io/release-1.2/docs/design/security_context.md#security-context", } -func (SELinuxContextStrategyOptions) SwaggerDoc() map[string]string { - return map_SELinuxContextStrategyOptions +func (SELinuxStrategyOptions) SwaggerDoc() map[string]string { + return map_SELinuxStrategyOptions } var map_Scale = map[string]string{ "": "represents a scaling request for a resource.", - "metadata": "Standard object metadata; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata.", - "spec": "defines the behavior of the scale. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status.", - "status": "current status of the scale. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status. Read-only.", + "metadata": "Standard object metadata; More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata.", + "spec": "defines the behavior of the scale. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status.", + "status": "current status of the scale. More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#spec-and-status. Read-only.", } func (Scale) SwaggerDoc() map[string]string { @@ -629,9 +594,10 @@ func (ScaleSpec) SwaggerDoc() map[string]string { } var map_ScaleStatus = map[string]string{ - "": "represents the current status of a scale subresource.", - "replicas": "actual number of observed instances of the scaled object.", - "selector": "label query over pods that should match the replicas count. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors", + "": "represents the current status of a scale subresource.", + "replicas": "actual number of observed instances of the scaled object.", + "selector": "label query over pods that should match the replicas count. More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors", + "targetSelector": "label selector for pods that should match the replicas count. This is a serializated version of both map-based and more expressive set-based selectors. This is done to avoid introspection in the clients. The string will be in the same format as the query-param syntax. If the target type only supports map-based selectors, both this field and map-based selector field are populated. More info: http://releases.k8s.io/release-1.2/docs/user-guide/labels.md#label-selectors", } func (ScaleStatus) SwaggerDoc() map[string]string { @@ -640,8 +606,8 @@ func (ScaleStatus) SwaggerDoc() map[string]string { var map_SubresourceReference = map[string]string{ "": "SubresourceReference contains enough information to let you inspect or modify the referred subresource.", - "kind": "Kind of the referent; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds\"", - "name": "Name of the referent; More info: http://releases.k8s.io/HEAD/docs/user-guide/identifiers.md#names", + "kind": "Kind of the referent; More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#types-kinds\"", + "name": "Name of the referent; More info: http://releases.k8s.io/release-1.2/docs/user-guide/identifiers.md#names", "apiVersion": "API version of the referent", "subresource": "Subresource name of the referent", } @@ -673,7 +639,7 @@ func (ThirdPartyResourceData) SwaggerDoc() map[string]string { var map_ThirdPartyResourceDataList = map[string]string{ "": "ThirdPartyResrouceDataList is a list of ThirdPartyResourceData.", - "metadata": "Standard list metadata More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", + "metadata": "Standard list metadata More info: http://releases.k8s.io/release-1.2/docs/devel/api-conventions.md#metadata", "items": "Items is the list of ThirdpartyResourceData.", } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/validation/validation.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/validation/validation.go deleted file mode 100644 index 8a12486f5..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/validation/validation.go +++ /dev/null @@ -1,820 +0,0 @@ -/* -Copyright 2015 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 validation - -import ( - "encoding/json" - "net" - "regexp" - "strconv" - "strings" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - unversionedvalidation "k8s.io/kubernetes/pkg/api/unversioned/validation" - apivalidation "k8s.io/kubernetes/pkg/api/validation" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/controller/podautoscaler" - "k8s.io/kubernetes/pkg/labels" - "k8s.io/kubernetes/pkg/util/intstr" - "k8s.io/kubernetes/pkg/util/sets" - "k8s.io/kubernetes/pkg/util/validation" - "k8s.io/kubernetes/pkg/util/validation/field" -) - -// ValidateHorizontalPodAutoscaler can be used to check whether the given autoscaler name is valid. -// Prefix indicates this name will be used as part of generation, in which case trailing dashes are allowed. -func ValidateHorizontalPodAutoscalerName(name string, prefix bool) (bool, string) { - // TODO: finally move it to pkg/api/validation and use nameIsDNSSubdomain function - return apivalidation.ValidateReplicationControllerName(name, prefix) -} - -func validateHorizontalPodAutoscalerSpec(autoscaler extensions.HorizontalPodAutoscalerSpec, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - if autoscaler.MinReplicas != nil && *autoscaler.MinReplicas < 1 { - allErrs = append(allErrs, field.Invalid(fldPath.Child("minReplicas"), *autoscaler.MinReplicas, "must be greater than 0")) - } - if autoscaler.MaxReplicas < 1 { - allErrs = append(allErrs, field.Invalid(fldPath.Child("maxReplicas"), autoscaler.MaxReplicas, "must be greater than 0")) - } - if autoscaler.MinReplicas != nil && autoscaler.MaxReplicas < *autoscaler.MinReplicas { - allErrs = append(allErrs, field.Invalid(fldPath.Child("maxReplicas"), autoscaler.MaxReplicas, "must be greater than or equal to `minReplicas`")) - } - if autoscaler.CPUUtilization != nil && autoscaler.CPUUtilization.TargetPercentage < 1 { - allErrs = append(allErrs, field.Invalid(fldPath.Child("cpuUtilization", "targetPercentage"), autoscaler.CPUUtilization.TargetPercentage, "must be greater than 0")) - } - if refErrs := ValidateSubresourceReference(autoscaler.ScaleRef, fldPath.Child("scaleRef")); len(refErrs) > 0 { - allErrs = append(allErrs, refErrs...) - } else if autoscaler.ScaleRef.Subresource != "scale" { - allErrs = append(allErrs, field.NotSupported(fldPath.Child("scaleRef", "subresource"), autoscaler.ScaleRef.Subresource, []string{"scale"})) - } - return allErrs -} - -func ValidateSubresourceReference(ref extensions.SubresourceReference, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - if len(ref.Kind) == 0 { - allErrs = append(allErrs, field.Required(fldPath.Child("kind"), "")) - } else if ok, msg := apivalidation.IsValidPathSegmentName(ref.Kind); !ok { - allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), ref.Kind, msg)) - } - - if len(ref.Name) == 0 { - allErrs = append(allErrs, field.Required(fldPath.Child("name"), "")) - } else if ok, msg := apivalidation.IsValidPathSegmentName(ref.Name); !ok { - allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), ref.Name, msg)) - } - - if len(ref.Subresource) == 0 { - allErrs = append(allErrs, field.Required(fldPath.Child("subresource"), "")) - } else if ok, msg := apivalidation.IsValidPathSegmentName(ref.Subresource); !ok { - allErrs = append(allErrs, field.Invalid(fldPath.Child("subresource"), ref.Subresource, msg)) - } - return allErrs -} - -func validateHorizontalPodAutoscalerAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - if annotationValue, found := annotations[podautoscaler.HpaCustomMetricsTargetAnnotationName]; found { - // Try to parse the annotation - var targetList extensions.CustomMetricTargetList - if err := json.Unmarshal([]byte(annotationValue), &targetList); err != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("annotations"), annotations, "failed to parse custom metrics target annotation")) - } else { - if len(targetList.Items) == 0 { - allErrs = append(allErrs, field.Required(fldPath.Child("annotations", "items"), "custom metrics target must not be empty")) - } - for _, target := range targetList.Items { - if target.Name == "" { - allErrs = append(allErrs, field.Required(fldPath.Child("annotations", "items", "name"), "missing custom metric target name")) - } - if target.TargetValue.MilliValue() <= 0 { - allErrs = append(allErrs, field.Invalid(fldPath.Child("annotations", "items", "value"), target.TargetValue, "custom metric target value must be greater than 0")) - } - } - } - } - return allErrs -} - -func ValidateHorizontalPodAutoscaler(autoscaler *extensions.HorizontalPodAutoscaler) field.ErrorList { - allErrs := apivalidation.ValidateObjectMeta(&autoscaler.ObjectMeta, true, ValidateHorizontalPodAutoscalerName, field.NewPath("metadata")) - allErrs = append(allErrs, validateHorizontalPodAutoscalerSpec(autoscaler.Spec, field.NewPath("spec"))...) - allErrs = append(allErrs, validateHorizontalPodAutoscalerAnnotations(autoscaler.Annotations, field.NewPath("metadata"))...) - return allErrs -} - -func ValidateHorizontalPodAutoscalerUpdate(newAutoscaler, oldAutoscaler *extensions.HorizontalPodAutoscaler) field.ErrorList { - allErrs := apivalidation.ValidateObjectMetaUpdate(&newAutoscaler.ObjectMeta, &oldAutoscaler.ObjectMeta, field.NewPath("metadata")) - allErrs = append(allErrs, validateHorizontalPodAutoscalerSpec(newAutoscaler.Spec, field.NewPath("spec"))...) - return allErrs -} - -func ValidateHorizontalPodAutoscalerStatusUpdate(newAutoscaler, oldAutoscaler *extensions.HorizontalPodAutoscaler) field.ErrorList { - allErrs := apivalidation.ValidateObjectMetaUpdate(&newAutoscaler.ObjectMeta, &oldAutoscaler.ObjectMeta, field.NewPath("metadata")) - status := newAutoscaler.Status - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.CurrentReplicas), field.NewPath("status", "currentReplicas"))...) - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.DesiredReplicas), field.NewPath("status", "desiredReplicasa"))...) - return allErrs -} - -func ValidateThirdPartyResourceUpdate(update, old *extensions.ThirdPartyResource) field.ErrorList { - allErrs := field.ErrorList{} - allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...) - allErrs = append(allErrs, ValidateThirdPartyResource(update)...) - return allErrs -} - -func ValidateThirdPartyResourceName(name string, prefix bool) (bool, string) { - return apivalidation.NameIsDNSSubdomain(name, prefix) -} - -func ValidateThirdPartyResource(obj *extensions.ThirdPartyResource) field.ErrorList { - allErrs := field.ErrorList{} - allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, ValidateThirdPartyResourceName, field.NewPath("metadata"))...) - - versions := sets.String{} - for ix := range obj.Versions { - version := &obj.Versions[ix] - if len(version.Name) == 0 { - allErrs = append(allErrs, field.Invalid(field.NewPath("versions").Index(ix).Child("name"), version, "must not be empty")) - } - if versions.Has(version.Name) { - allErrs = append(allErrs, field.Duplicate(field.NewPath("versions").Index(ix).Child("name"), version)) - } - versions.Insert(version.Name) - } - return allErrs -} - -// ValidateDaemonSet tests if required fields in the DaemonSet are set. -func ValidateDaemonSet(ds *extensions.DaemonSet) field.ErrorList { - allErrs := apivalidation.ValidateObjectMeta(&ds.ObjectMeta, true, ValidateDaemonSetName, field.NewPath("metadata")) - allErrs = append(allErrs, ValidateDaemonSetSpec(&ds.Spec, field.NewPath("spec"))...) - return allErrs -} - -// ValidateDaemonSetUpdate tests if required fields in the DaemonSet are set. -func ValidateDaemonSetUpdate(ds, oldDS *extensions.DaemonSet) field.ErrorList { - allErrs := apivalidation.ValidateObjectMetaUpdate(&ds.ObjectMeta, &oldDS.ObjectMeta, field.NewPath("metadata")) - allErrs = append(allErrs, ValidateDaemonSetSpec(&ds.Spec, field.NewPath("spec"))...) - return allErrs -} - -// validateDaemonSetStatus validates a DaemonSetStatus -func validateDaemonSetStatus(status *extensions.DaemonSetStatus, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.CurrentNumberScheduled), fldPath.Child("currentNumberScheduled"))...) - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberMisscheduled), fldPath.Child("numberMisscheduled"))...) - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.DesiredNumberScheduled), fldPath.Child("desiredNumberScheduled"))...) - return allErrs -} - -// ValidateDaemonSetStatus validates tests if required fields in the DaemonSet Status section -func ValidateDaemonSetStatusUpdate(ds, oldDS *extensions.DaemonSet) field.ErrorList { - allErrs := apivalidation.ValidateObjectMetaUpdate(&ds.ObjectMeta, &oldDS.ObjectMeta, field.NewPath("metadata")) - allErrs = append(allErrs, validateDaemonSetStatus(&ds.Status, field.NewPath("status"))...) - return allErrs -} - -// ValidateDaemonSetSpec tests if required fields in the DaemonSetSpec are set. -func ValidateDaemonSetSpec(spec *extensions.DaemonSetSpec, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - - allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...) - - selector, err := unversioned.LabelSelectorAsSelector(spec.Selector) - if err == nil && !selector.Matches(labels.Set(spec.Template.Labels)) { - allErrs = append(allErrs, field.Invalid(fldPath.Child("template", "metadata", "labels"), spec.Template.Labels, "`selector` does not match template `labels`")) - } - - allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(&spec.Template, fldPath.Child("template"))...) - // Daemons typically run on more than one node, so mark Read-Write persistent disks as invalid. - allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(spec.Template.Spec.Volumes, fldPath.Child("template", "spec", "volumes"))...) - // RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec(). - if spec.Template.Spec.RestartPolicy != api.RestartPolicyAlways { - allErrs = append(allErrs, field.NotSupported(fldPath.Child("template", "spec", "restartPolicy"), spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)})) - } - return allErrs -} - -// ValidateDaemonSetName can be used to check whether the given daemon set name is valid. -// Prefix indicates this name will be used as part of generation, in which case -// trailing dashes are allowed. -func ValidateDaemonSetName(name string, prefix bool) (bool, string) { - return apivalidation.NameIsDNSSubdomain(name, prefix) -} - -// Validates that the given name can be used as a deployment name. -func ValidateDeploymentName(name string, prefix bool) (bool, string) { - return apivalidation.NameIsDNSSubdomain(name, prefix) -} - -func ValidatePositiveIntOrPercent(intOrPercent intstr.IntOrString, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - if intOrPercent.Type == intstr.String { - if !validation.IsValidPercent(intOrPercent.StrVal) { - allErrs = append(allErrs, field.Invalid(fldPath, intOrPercent, "must be an integer or percentage (e.g '5%')")) - } - } else if intOrPercent.Type == intstr.Int { - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(intOrPercent.IntValue()), fldPath)...) - } - return allErrs -} - -func getPercentValue(intOrStringValue intstr.IntOrString) (int, bool) { - if intOrStringValue.Type != intstr.String || !validation.IsValidPercent(intOrStringValue.StrVal) { - return 0, false - } - value, _ := strconv.Atoi(intOrStringValue.StrVal[:len(intOrStringValue.StrVal)-1]) - return value, true -} - -func getIntOrPercentValue(intOrStringValue intstr.IntOrString) int { - value, isPercent := getPercentValue(intOrStringValue) - if isPercent { - return value - } - return intOrStringValue.IntValue() -} - -func IsNotMoreThan100Percent(intOrStringValue intstr.IntOrString, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - value, isPercent := getPercentValue(intOrStringValue) - if !isPercent || value <= 100 { - return nil - } - allErrs = append(allErrs, field.Invalid(fldPath, intOrStringValue, "must not be greater than 100%")) - return allErrs -} - -func ValidateRollingUpdateDeployment(rollingUpdate *extensions.RollingUpdateDeployment, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...) - allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxSurge, fldPath.Child("maxSurge"))...) - if getIntOrPercentValue(rollingUpdate.MaxUnavailable) == 0 && getIntOrPercentValue(rollingUpdate.MaxSurge) == 0 { - // Both MaxSurge and MaxUnavailable cannot be zero. - allErrs = append(allErrs, field.Invalid(fldPath.Child("maxUnavailable"), rollingUpdate.MaxUnavailable, "may not be 0 when `maxSurge` is 0")) - } - // Validate that MaxUnavailable is not more than 100%. - allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...) - return allErrs -} - -func ValidateDeploymentStrategy(strategy *extensions.DeploymentStrategy, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - if strategy.RollingUpdate == nil { - return allErrs - } - switch strategy.Type { - case extensions.RecreateDeploymentStrategyType: - allErrs = append(allErrs, field.Forbidden(fldPath.Child("rollingUpdate"), "may not be specified when strategy `type` is '"+string(extensions.RecreateDeploymentStrategyType+"'"))) - case extensions.RollingUpdateDeploymentStrategyType: - allErrs = append(allErrs, ValidateRollingUpdateDeployment(strategy.RollingUpdate, fldPath.Child("rollingUpdate"))...) - } - return allErrs -} - -func ValidateRollback(rollback *extensions.RollbackConfig, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - v := rollback.Revision - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(v), fldPath.Child("version"))...) - return allErrs -} - -// Validates given deployment spec. -func ValidateDeploymentSpec(spec *extensions.DeploymentSpec, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...) - - if spec.Selector == nil { - allErrs = append(allErrs, field.Required(fldPath.Child("selector"), "")) - } else { - allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...) - if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 { - allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is not valid for deployment.")) - } - } - - selector, err := unversioned.LabelSelectorAsSelector(spec.Selector) - if err != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "invalid label selector.")) - } else { - allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(&spec.Template, selector, spec.Replicas, fldPath.Child("template"))...) - } - - allErrs = append(allErrs, ValidateDeploymentStrategy(&spec.Strategy, fldPath.Child("strategy"))...) - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...) - if spec.RevisionHistoryLimit != nil { - // zero is a valid RevisionHistoryLimit - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.RevisionHistoryLimit), fldPath.Child("revisionHistoryLimit"))...) - } - if spec.RollbackTo != nil { - allErrs = append(allErrs, ValidateRollback(spec.RollbackTo, fldPath.Child("rollback"))...) - } - return allErrs -} - -func ValidateDeploymentUpdate(update, old *extensions.Deployment) field.ErrorList { - allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata")) - allErrs = append(allErrs, ValidateDeploymentSpec(&update.Spec, field.NewPath("spec"))...) - return allErrs -} - -func ValidateDeployment(obj *extensions.Deployment) field.ErrorList { - allErrs := apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, ValidateDeploymentName, field.NewPath("metadata")) - allErrs = append(allErrs, ValidateDeploymentSpec(&obj.Spec, field.NewPath("spec"))...) - return allErrs -} - -func ValidateDeploymentRollback(obj *extensions.DeploymentRollback) field.ErrorList { - allErrs := apivalidation.ValidateAnnotations(obj.UpdatedAnnotations, field.NewPath("updatedAnnotations")) - if len(obj.Name) == 0 { - allErrs = append(allErrs, field.Required(field.NewPath("name"), "name is required")) - } - allErrs = append(allErrs, ValidateRollback(&obj.RollbackTo, field.NewPath("rollback"))...) - return allErrs -} - -func ValidateThirdPartyResourceDataUpdate(update, old *extensions.ThirdPartyResourceData) field.ErrorList { - return ValidateThirdPartyResourceData(update) -} - -func ValidateThirdPartyResourceData(obj *extensions.ThirdPartyResourceData) field.ErrorList { - allErrs := field.ErrorList{} - if len(obj.Name) == 0 { - allErrs = append(allErrs, field.Required(field.NewPath("name"), "")) - } - return allErrs -} - -func ValidateJob(job *extensions.Job) field.ErrorList { - // Jobs and rcs have the same name validation - allErrs := apivalidation.ValidateObjectMeta(&job.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata")) - allErrs = append(allErrs, ValidateJobSpec(&job.Spec, field.NewPath("spec"))...) - return allErrs -} - -func ValidateJobSpec(spec *extensions.JobSpec, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - - if spec.Parallelism != nil { - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.Parallelism), fldPath.Child("parallelism"))...) - } - if spec.Completions != nil { - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.Completions), fldPath.Child("completions"))...) - } - if spec.ActiveDeadlineSeconds != nil { - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.ActiveDeadlineSeconds), fldPath.Child("activeDeadlineSeconds"))...) - } - if spec.Selector == nil { - allErrs = append(allErrs, field.Required(fldPath.Child("selector"), "")) - } else { - allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...) - } - - if selector, err := unversioned.LabelSelectorAsSelector(spec.Selector); err == nil { - labels := labels.Set(spec.Template.Labels) - if !selector.Matches(labels) { - allErrs = append(allErrs, field.Invalid(fldPath.Child("template", "metadata", "labels"), spec.Template.Labels, "`selector` does not match template `labels`")) - } - } - - allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(&spec.Template, fldPath.Child("template"))...) - if spec.Template.Spec.RestartPolicy != api.RestartPolicyOnFailure && - spec.Template.Spec.RestartPolicy != api.RestartPolicyNever { - allErrs = append(allErrs, field.NotSupported(fldPath.Child("template", "spec", "restartPolicy"), - spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyOnFailure), string(api.RestartPolicyNever)})) - } - return allErrs -} - -func ValidateJobStatus(status *extensions.JobStatus, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Active), fldPath.Child("active"))...) - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Succeeded), fldPath.Child("succeeded"))...) - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Failed), fldPath.Child("failed"))...) - return allErrs -} - -func ValidateJobUpdate(job, oldJob *extensions.Job) field.ErrorList { - allErrs := apivalidation.ValidateObjectMetaUpdate(&oldJob.ObjectMeta, &job.ObjectMeta, field.NewPath("metadata")) - allErrs = append(allErrs, ValidateJobSpecUpdate(job.Spec, oldJob.Spec, field.NewPath("spec"))...) - return allErrs -} - -func ValidateJobUpdateStatus(job, oldJob *extensions.Job) field.ErrorList { - allErrs := apivalidation.ValidateObjectMetaUpdate(&oldJob.ObjectMeta, &job.ObjectMeta, field.NewPath("metadata")) - allErrs = append(allErrs, ValidateJobStatusUpdate(job.Status, oldJob.Status)...) - return allErrs -} - -func ValidateJobSpecUpdate(spec, oldSpec extensions.JobSpec, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - allErrs = append(allErrs, ValidateJobSpec(&spec, fldPath)...) - allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Completions, oldSpec.Completions, fldPath.Child("completions"))...) - allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Selector, oldSpec.Selector, fldPath.Child("selector"))...) - allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Template, oldSpec.Template, fldPath.Child("template"))...) - return allErrs -} - -func ValidateJobStatusUpdate(status, oldStatus extensions.JobStatus) field.ErrorList { - allErrs := field.ErrorList{} - allErrs = append(allErrs, ValidateJobStatus(&status, field.NewPath("status"))...) - return allErrs -} - -// ValidateIngress tests if required fields in the Ingress are set. -func ValidateIngress(ingress *extensions.Ingress) field.ErrorList { - allErrs := apivalidation.ValidateObjectMeta(&ingress.ObjectMeta, true, ValidateIngressName, field.NewPath("metadata")) - allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"))...) - return allErrs -} - -// ValidateIngressName validates that the given name can be used as an Ingress name. -func ValidateIngressName(name string, prefix bool) (bool, string) { - return apivalidation.NameIsDNSSubdomain(name, prefix) -} - -func validateIngressTLS(spec *extensions.IngressSpec, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - // Currently the Ingress only supports HTTP(S), so a secretName is required. - // This will not be the case if we support SSL routing at L4 via SNI. - for i, t := range spec.TLS { - if t.SecretName == "" { - allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("secretName"), spec.TLS[i].SecretName)) - } - } - // TODO: Perform a more thorough validation of spec.TLS.Hosts that takes - // the wildcard spec from RFC 6125 into account. - return allErrs -} - -// ValidateIngressSpec tests if required fields in the IngressSpec are set. -func ValidateIngressSpec(spec *extensions.IngressSpec, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - // TODO: Is a default backend mandatory? - if spec.Backend != nil { - allErrs = append(allErrs, validateIngressBackend(spec.Backend, fldPath.Child("backend"))...) - } else if len(spec.Rules) == 0 { - allErrs = append(allErrs, field.Invalid(fldPath, spec.Rules, "either `backend` or `rules` must be specified")) - } - if len(spec.Rules) > 0 { - allErrs = append(allErrs, validateIngressRules(spec.Rules, fldPath.Child("rules"))...) - } - if len(spec.TLS) > 0 { - allErrs = append(allErrs, validateIngressTLS(spec, fldPath.Child("tls"))...) - } - return allErrs -} - -// ValidateIngressUpdate tests if required fields in the Ingress are set. -func ValidateIngressUpdate(ingress, oldIngress *extensions.Ingress) field.ErrorList { - allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata")) - allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"))...) - return allErrs -} - -// ValidateIngressStatusUpdate tests if required fields in the Ingress are set when updating status. -func ValidateIngressStatusUpdate(ingress, oldIngress *extensions.Ingress) field.ErrorList { - allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata")) - allErrs = append(allErrs, apivalidation.ValidateLoadBalancerStatus(&ingress.Status.LoadBalancer, field.NewPath("status", "loadBalancer"))...) - return allErrs -} - -func validateIngressRules(IngressRules []extensions.IngressRule, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - if len(IngressRules) == 0 { - return append(allErrs, field.Required(fldPath, "")) - } - for i, ih := range IngressRules { - if len(ih.Host) > 0 { - // TODO: Ports and ips are allowed in the host part of a url - // according to RFC 3986, consider allowing them. - if valid, errMsg := apivalidation.NameIsDNSSubdomain(ih.Host, false); !valid { - allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, errMsg)) - } - if isIP := (net.ParseIP(ih.Host) != nil); isIP { - allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, "must be a DNS name, not an IP address")) - } - } - allErrs = append(allErrs, validateIngressRuleValue(&ih.IngressRuleValue, fldPath.Index(0))...) - } - return allErrs -} - -func validateIngressRuleValue(ingressRule *extensions.IngressRuleValue, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - if ingressRule.HTTP != nil { - allErrs = append(allErrs, validateHTTPIngressRuleValue(ingressRule.HTTP, fldPath.Child("http"))...) - } - return allErrs -} - -func validateHTTPIngressRuleValue(httpIngressRuleValue *extensions.HTTPIngressRuleValue, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - if len(httpIngressRuleValue.Paths) == 0 { - allErrs = append(allErrs, field.Required(fldPath.Child("paths"), "")) - } - for i, rule := range httpIngressRuleValue.Paths { - if len(rule.Path) > 0 { - if !strings.HasPrefix(rule.Path, "/") { - allErrs = append(allErrs, field.Invalid(fldPath.Child("paths").Index(i).Child("path"), rule.Path, "must be an absolute path")) - } - // TODO: More draconian path regex validation. - // Path must be a valid regex. This is the basic requirement. - // In addition to this any characters not allowed in a path per - // RFC 3986 section-3.3 cannot appear as a literal in the regex. - // Consider the example: http://host/valid?#bar, everything after - // the last '/' is a valid regex that matches valid#bar, which - // isn't a valid path, because the path terminates at the first ? - // or #. A more sophisticated form of validation would detect that - // the user is confusing url regexes with path regexes. - _, err := regexp.CompilePOSIX(rule.Path) - if err != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("paths").Index(i).Child("path"), rule.Path, "must be a valid regex")) - } - } - allErrs = append(allErrs, validateIngressBackend(&rule.Backend, fldPath.Child("backend"))...) - } - return allErrs -} - -// validateIngressBackend tests if a given backend is valid. -func validateIngressBackend(backend *extensions.IngressBackend, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - - // All backends must reference a single local service by name, and a single service port by name or number. - if len(backend.ServiceName) == 0 { - return append(allErrs, field.Required(fldPath.Child("serviceName"), "")) - } else if ok, errMsg := apivalidation.ValidateServiceName(backend.ServiceName, false); !ok { - allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceName"), backend.ServiceName, errMsg)) - } - if backend.ServicePort.Type == intstr.String { - if !validation.IsDNS1123Label(backend.ServicePort.StrVal) { - allErrs = append(allErrs, field.Invalid(fldPath.Child("servicePort"), backend.ServicePort.StrVal, apivalidation.DNS1123LabelErrorMsg)) - } - if !validation.IsValidPortName(backend.ServicePort.StrVal) { - allErrs = append(allErrs, field.Invalid(fldPath.Child("servicePort"), backend.ServicePort.StrVal, apivalidation.PortNameErrorMsg)) - } - } else if !validation.IsValidPortNum(backend.ServicePort.IntValue()) { - allErrs = append(allErrs, field.Invalid(fldPath.Child("servicePort"), backend.ServicePort, apivalidation.PortRangeErrorMsg)) - } - return allErrs -} - -func validateClusterAutoscalerSpec(spec extensions.ClusterAutoscalerSpec, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - if spec.MinNodes < 0 { - allErrs = append(allErrs, field.Invalid(fldPath.Child("minNodes"), spec.MinNodes, "must be greater than or equal to 0")) - } - if spec.MaxNodes < spec.MinNodes { - allErrs = append(allErrs, field.Invalid(fldPath.Child("maxNodes"), spec.MaxNodes, "must be greater than or equal to `minNodes`")) - } - if len(spec.TargetUtilization) == 0 { - allErrs = append(allErrs, field.Required(fldPath.Child("targetUtilization"), "")) - } - for _, target := range spec.TargetUtilization { - if len(target.Resource) == 0 { - allErrs = append(allErrs, field.Required(fldPath.Child("targetUtilization", "resource"), "")) - } - if target.Value <= 0 { - allErrs = append(allErrs, field.Invalid(fldPath.Child("targetUtilization", "value"), target.Value, "must be greater than 0")) - } - if target.Value > 1 { - allErrs = append(allErrs, field.Invalid(fldPath.Child("targetUtilization", "value"), target.Value, "must be less than or equal to 1")) - } - } - return allErrs -} - -func ValidateClusterAutoscaler(autoscaler *extensions.ClusterAutoscaler) field.ErrorList { - allErrs := field.ErrorList{} - if autoscaler.Name != "ClusterAutoscaler" { - allErrs = append(allErrs, field.Invalid(field.NewPath("metadata", "name"), autoscaler.Name, "must be 'ClusterAutoscaler'")) - } - if autoscaler.Namespace != api.NamespaceDefault { - allErrs = append(allErrs, field.Invalid(field.NewPath("metadata", "namespace"), autoscaler.Namespace, "must be 'default'")) - } - allErrs = append(allErrs, validateClusterAutoscalerSpec(autoscaler.Spec, field.NewPath("spec"))...) - return allErrs -} - -func ValidateScale(scale *extensions.Scale) field.ErrorList { - allErrs := field.ErrorList{} - allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&scale.ObjectMeta, true, apivalidation.NameIsDNSSubdomain, field.NewPath("metadata"))...) - - if scale.Spec.Replicas < 0 { - allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "replicas"), scale.Spec.Replicas, "must be greater than or equal to 0")) - } - - return allErrs -} - -// ValidateReplicaSetName can be used to check whether the given ReplicaSet -// name is valid. -// Prefix indicates this name will be used as part of generation, in which case -// trailing dashes are allowed. -func ValidateReplicaSetName(name string, prefix bool) (bool, string) { - return apivalidation.NameIsDNSSubdomain(name, prefix) -} - -// ValidateReplicaSet tests if required fields in the ReplicaSet are set. -func ValidateReplicaSet(rs *extensions.ReplicaSet) field.ErrorList { - allErrs := apivalidation.ValidateObjectMeta(&rs.ObjectMeta, true, ValidateReplicaSetName, field.NewPath("metadata")) - allErrs = append(allErrs, ValidateReplicaSetSpec(&rs.Spec, field.NewPath("spec"))...) - return allErrs -} - -// ValidateReplicaSetUpdate tests if required fields in the ReplicaSet are set. -func ValidateReplicaSetUpdate(rs, oldRs *extensions.ReplicaSet) field.ErrorList { - allErrs := field.ErrorList{} - allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&rs.ObjectMeta, &oldRs.ObjectMeta, field.NewPath("metadata"))...) - allErrs = append(allErrs, ValidateReplicaSetSpec(&rs.Spec, field.NewPath("spec"))...) - return allErrs -} - -// ValidateReplicaSetStatusUpdate tests if required fields in the ReplicaSet are set. -func ValidateReplicaSetStatusUpdate(rs, oldRs *extensions.ReplicaSet) field.ErrorList { - allErrs := field.ErrorList{} - allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&rs.ObjectMeta, &oldRs.ObjectMeta, field.NewPath("metadata"))...) - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(rs.Status.Replicas), field.NewPath("status", "replicas"))...) - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(rs.Status.ObservedGeneration), field.NewPath("status", "observedGeneration"))...) - return allErrs -} - -// ValidateReplicaSetSpec tests if required fields in the ReplicaSet spec are set. -func ValidateReplicaSetSpec(spec *extensions.ReplicaSetSpec, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...) - - if spec.Selector == nil { - allErrs = append(allErrs, field.Required(fldPath.Child("selector"), "")) - } else { - allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...) - if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 { - allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is not valid for deployment.")) - } - } - - selector, err := unversioned.LabelSelectorAsSelector(spec.Selector) - if err != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "invalid label selector.")) - } else { - allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(spec.Template, selector, spec.Replicas, fldPath.Child("template"))...) - } - return allErrs -} - -// Validates the given template and ensures that it is in accordance with the desired selector and replicas. -func ValidatePodTemplateSpecForReplicaSet(template *api.PodTemplateSpec, selector labels.Selector, replicas int, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - if template == nil { - allErrs = append(allErrs, field.Required(fldPath, "")) - } else { - if !selector.Empty() { - // Verify that the ReplicaSet selector matches the labels in template. - labels := labels.Set(template.Labels) - if !selector.Matches(labels) { - allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`")) - } - } - allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(template, fldPath)...) - if replicas > 1 { - allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(template.Spec.Volumes, fldPath.Child("spec", "volumes"))...) - } - // RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec(). - if template.Spec.RestartPolicy != api.RestartPolicyAlways { - allErrs = append(allErrs, field.NotSupported(fldPath.Child("spec", "restartPolicy"), template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)})) - } - } - return allErrs -} - -// ValidatePodSecurityPolicyName can be used to check whether the given -// pod security policy name is valid. -// Prefix indicates this name will be used as part of generation, in which case -// trailing dashes are allowed. -func ValidatePodSecurityPolicyName(name string, prefix bool) (bool, string) { - return apivalidation.NameIsDNSSubdomain(name, prefix) -} - -func ValidatePodSecurityPolicy(psp *extensions.PodSecurityPolicy) field.ErrorList { - allErrs := field.ErrorList{} - allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&psp.ObjectMeta, false, ValidatePodSecurityPolicyName, field.NewPath("metadata"))...) - allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&psp.Spec, field.NewPath("spec"))...) - return allErrs -} - -func ValidatePodSecurityPolicySpec(spec *extensions.PodSecurityPolicySpec, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - - allErrs = append(allErrs, validatePSPRunAsUser(fldPath.Child("runAsUser"), &spec.RunAsUser)...) - allErrs = append(allErrs, validatePSPSELinuxContext(fldPath.Child("seLinuxContext"), &spec.SELinuxContext)...) - allErrs = append(allErrs, validatePodSecurityPolicyVolumes(fldPath, spec.Volumes)...) - - return allErrs -} - -// validatePSPSELinuxContext validates the SELinuxContext fields of PodSecurityPolicy. -func validatePSPSELinuxContext(fldPath *field.Path, seLinuxContext *extensions.SELinuxContextStrategyOptions) field.ErrorList { - allErrs := field.ErrorList{} - - // ensure the selinux strategy has a valid type - supportedSELinuxContextTypes := sets.NewString(string(extensions.SELinuxStrategyMustRunAs), - string(extensions.SELinuxStrategyRunAsAny)) - if !supportedSELinuxContextTypes.Has(string(seLinuxContext.Type)) { - allErrs = append(allErrs, field.NotSupported(fldPath.Child("type"), seLinuxContext.Type, supportedSELinuxContextTypes.List())) - } - - return allErrs -} - -// validatePSPRunAsUser validates the RunAsUser fields of PodSecurityPolicy. -func validatePSPRunAsUser(fldPath *field.Path, runAsUser *extensions.RunAsUserStrategyOptions) field.ErrorList { - allErrs := field.ErrorList{} - - // ensure the user strategy has a valid type - supportedRunAsUserTypes := sets.NewString(string(extensions.RunAsUserStrategyMustRunAs), - string(extensions.RunAsUserStrategyMustRunAsNonRoot), - string(extensions.RunAsUserStrategyRunAsAny)) - if !supportedRunAsUserTypes.Has(string(runAsUser.Type)) { - allErrs = append(allErrs, field.NotSupported(fldPath.Child("type"), runAsUser.Type, supportedRunAsUserTypes.List())) - } - - // validate range settings - for idx, rng := range runAsUser.Ranges { - allErrs = append(allErrs, validateIDRanges(fldPath.Child("ranges").Index(idx), rng)...) - } - - return allErrs -} - -// validatePodSecurityPolicyVolumes validates the volume fields of PodSecurityPolicy. -func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []extensions.FSType) field.ErrorList { - allErrs := field.ErrorList{} - allowed := sets.NewString(string(extensions.HostPath), - string(extensions.EmptyDir), - string(extensions.GCEPersistentDisk), - string(extensions.AWSElasticBlockStore), - string(extensions.GitRepo), - string(extensions.Secret), - string(extensions.NFS), - string(extensions.ISCSI), - string(extensions.Glusterfs), - string(extensions.PersistentVolumeClaim), - string(extensions.RBD), - string(extensions.Cinder), - string(extensions.CephFS), - string(extensions.DownwardAPI), - string(extensions.FC)) - for _, v := range volumes { - if !allowed.Has(string(v)) { - allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumes"), v, allowed.List())) - } - } - - return allErrs -} - -// validateIDRanges ensures the range is valid. -func validateIDRanges(fldPath *field.Path, rng extensions.IDRange) field.ErrorList { - allErrs := field.ErrorList{} - - // if 0 <= Min <= Max then we do not need to validate max. It is always greater than or - // equal to 0 and Min. - if rng.Min < 0 { - allErrs = append(allErrs, field.Invalid(fldPath.Child("min"), rng.Min, "min cannot be negative")) - } - if rng.Max < 0 { - allErrs = append(allErrs, field.Invalid(fldPath.Child("max"), rng.Max, "max cannot be negative")) - } - if rng.Min > rng.Max { - allErrs = append(allErrs, field.Invalid(fldPath.Child("min"), rng.Min, "min cannot be greater than max")) - } - - return allErrs -} - -// ValidatePodSecurityPolicyUpdate validates a PSP for updates. -func ValidatePodSecurityPolicyUpdate(old *extensions.PodSecurityPolicy, new *extensions.PodSecurityPolicy) field.ErrorList { - allErrs := field.ErrorList{} - allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&old.ObjectMeta, &new.ObjectMeta, field.NewPath("metadata"))...) - allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&new.Spec, field.NewPath("spec"))...) - return allErrs -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/validation/validation_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/validation/validation_test.go deleted file mode 100644 index c421bdd40..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/apis/extensions/validation/validation_test.go +++ /dev/null @@ -1,2072 +0,0 @@ -/* -Copyright 2014 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 validation - -import ( - "fmt" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/controller/podautoscaler" - "k8s.io/kubernetes/pkg/util/intstr" -) - -func TestValidateHorizontalPodAutoscaler(t *testing.T) { - successCases := []extensions.HorizontalPodAutoscaler{ - { - ObjectMeta: api.ObjectMeta{ - Name: "myautoscaler", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{ - Kind: "ReplicationController", - Name: "myrc", - Subresource: "scale", - }, - MinReplicas: newInt(1), - MaxReplicas: 5, - CPUUtilization: &extensions.CPUTargetUtilization{TargetPercentage: 70}, - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "myautoscaler", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{ - Kind: "ReplicationController", - Name: "myrc", - Subresource: "scale", - }, - MinReplicas: newInt(1), - MaxReplicas: 5, - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "myautoscaler", - Namespace: api.NamespaceDefault, - Annotations: map[string]string{ - podautoscaler.HpaCustomMetricsTargetAnnotationName: "{\"items\":[{\"name\":\"qps\",\"value\":\"20\"}]}", - }, - }, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{ - Kind: "ReplicationController", - Name: "myrc", - Subresource: "scale", - }, - MinReplicas: newInt(1), - MaxReplicas: 5, - }, - }, - } - for _, successCase := range successCases { - if errs := ValidateHorizontalPodAutoscaler(&successCase); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := []struct { - horizontalPodAutoscaler extensions.HorizontalPodAutoscaler - msg string - }{ - { - horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{Name: "myautoscaler", Namespace: api.NamespaceDefault}, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{Name: "myrc", Subresource: "scale"}, - MinReplicas: newInt(1), - MaxReplicas: 5, - CPUUtilization: &extensions.CPUTargetUtilization{TargetPercentage: 70}, - }, - }, - msg: "scaleRef.kind: Required", - }, - { - horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{Name: "myautoscaler", Namespace: api.NamespaceDefault}, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{Kind: "..", Name: "myrc", Subresource: "scale"}, - MinReplicas: newInt(1), - MaxReplicas: 5, - CPUUtilization: &extensions.CPUTargetUtilization{TargetPercentage: 70}, - }, - }, - msg: "scaleRef.kind: Invalid", - }, - { - horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{Name: "myautoscaler", Namespace: api.NamespaceDefault}, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{Kind: "ReplicationController", Subresource: "scale"}, - MinReplicas: newInt(1), - MaxReplicas: 5, - CPUUtilization: &extensions.CPUTargetUtilization{TargetPercentage: 70}, - }, - }, - msg: "scaleRef.name: Required", - }, - { - horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{Name: "myautoscaler", Namespace: api.NamespaceDefault}, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{Kind: "ReplicationController", Name: "..", Subresource: "scale"}, - MinReplicas: newInt(1), - MaxReplicas: 5, - CPUUtilization: &extensions.CPUTargetUtilization{TargetPercentage: 70}, - }, - }, - msg: "scaleRef.name: Invalid", - }, - { - horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{Name: "myautoscaler", Namespace: api.NamespaceDefault}, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{Kind: "ReplicationController", Name: "myrc", Subresource: ""}, - MinReplicas: newInt(1), - MaxReplicas: 5, - CPUUtilization: &extensions.CPUTargetUtilization{TargetPercentage: 70}, - }, - }, - msg: "scaleRef.subresource: Required", - }, - { - horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{Name: "myautoscaler", Namespace: api.NamespaceDefault}, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{Kind: "ReplicationController", Name: "myrc", Subresource: ".."}, - MinReplicas: newInt(1), - MaxReplicas: 5, - CPUUtilization: &extensions.CPUTargetUtilization{TargetPercentage: 70}, - }, - }, - msg: "scaleRef.subresource: Invalid", - }, - { - horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{Name: "myautoscaler", Namespace: api.NamespaceDefault}, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{Kind: "ReplicationController", Name: "myrc", Subresource: "randomsubresource"}, - MinReplicas: newInt(1), - MaxReplicas: 5, - CPUUtilization: &extensions.CPUTargetUtilization{TargetPercentage: 70}, - }, - }, - msg: "scaleRef.subresource: Unsupported", - }, - { - horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{ - Name: "myautoscaler", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{ - Subresource: "scale", - }, - MinReplicas: newInt(-1), - MaxReplicas: 5, - }, - }, - msg: "must be greater than 0", - }, - { - horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{ - Name: "myautoscaler", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{ - Subresource: "scale", - }, - MinReplicas: newInt(7), - MaxReplicas: 5, - }, - }, - msg: "must be greater than or equal to `minReplicas`", - }, - { - horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{ - Name: "myautoscaler", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{ - Subresource: "scale", - }, - MinReplicas: newInt(1), - MaxReplicas: 5, - CPUUtilization: &extensions.CPUTargetUtilization{TargetPercentage: -70}, - }, - }, - msg: "must be greater than 0", - }, - { - horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{ - Name: "myautoscaler", - Namespace: api.NamespaceDefault, - Annotations: map[string]string{ - podautoscaler.HpaCustomMetricsTargetAnnotationName: "broken", - }, - }, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{ - Kind: "ReplicationController", - Name: "myrc", - Subresource: "scale", - }, - MinReplicas: newInt(1), - MaxReplicas: 5, - }, - }, - msg: "failed to parse custom metrics target annotation", - }, - { - horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{ - Name: "myautoscaler", - Namespace: api.NamespaceDefault, - Annotations: map[string]string{ - podautoscaler.HpaCustomMetricsTargetAnnotationName: "{}", - }, - }, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{ - Kind: "ReplicationController", - Name: "myrc", - Subresource: "scale", - }, - MinReplicas: newInt(1), - MaxReplicas: 5, - }, - }, - msg: "custom metrics target must not be empty", - }, - { - horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{ - Name: "myautoscaler", - Namespace: api.NamespaceDefault, - Annotations: map[string]string{ - podautoscaler.HpaCustomMetricsTargetAnnotationName: "{\"items\":[{\"value\":\"20\"}]}", - }, - }, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{ - Kind: "ReplicationController", - Name: "myrc", - Subresource: "scale", - }, - MinReplicas: newInt(1), - MaxReplicas: 5, - }, - }, - msg: "missing custom metric target name", - }, - { - horizontalPodAutoscaler: extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{ - Name: "myautoscaler", - Namespace: api.NamespaceDefault, - Annotations: map[string]string{ - podautoscaler.HpaCustomMetricsTargetAnnotationName: "{\"items\":[{\"name\":\"qps\",\"value\":\"0\"}]}", - }, - }, - Spec: extensions.HorizontalPodAutoscalerSpec{ - ScaleRef: extensions.SubresourceReference{ - Kind: "ReplicationController", - Name: "myrc", - Subresource: "scale", - }, - MinReplicas: newInt(1), - MaxReplicas: 5, - }, - }, - msg: "custom metric target value must be greater than 0", - }, - } - - for _, c := range errorCases { - errs := ValidateHorizontalPodAutoscaler(&c.horizontalPodAutoscaler) - if len(errs) == 0 { - t.Errorf("expected failure for %q", c.msg) - } else if !strings.Contains(errs[0].Error(), c.msg) { - t.Errorf("unexpected error: %q, expected: %q", errs[0], c.msg) - } - } -} - -func TestValidateDaemonSetStatusUpdate(t *testing.T) { - type dsUpdateTest struct { - old extensions.DaemonSet - update extensions.DaemonSet - } - - successCases := []dsUpdateTest{ - { - old: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Status: extensions.DaemonSetStatus{ - CurrentNumberScheduled: 1, - NumberMisscheduled: 2, - DesiredNumberScheduled: 3, - }, - }, - update: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Status: extensions.DaemonSetStatus{ - CurrentNumberScheduled: 1, - NumberMisscheduled: 1, - DesiredNumberScheduled: 3, - }, - }, - }, - } - - for _, successCase := range successCases { - successCase.old.ObjectMeta.ResourceVersion = "1" - successCase.update.ObjectMeta.ResourceVersion = "1" - if errs := ValidateDaemonSetStatusUpdate(&successCase.update, &successCase.old); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := map[string]dsUpdateTest{ - "negative values": { - old: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Status: extensions.DaemonSetStatus{ - CurrentNumberScheduled: 1, - NumberMisscheduled: 2, - DesiredNumberScheduled: 3, - }, - }, - update: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Status: extensions.DaemonSetStatus{ - CurrentNumberScheduled: -1, - NumberMisscheduled: -1, - DesiredNumberScheduled: -3, - }, - }, - }, - } - - for testName, errorCase := range errorCases { - if errs := ValidateDaemonSetStatusUpdate(&errorCase.old, &errorCase.update); len(errs) == 0 { - t.Errorf("expected failure: %s", testName) - } - } -} - -func TestValidateDaemonSetUpdate(t *testing.T) { - validSelector := map[string]string{"a": "b"} - validSelector2 := map[string]string{"c": "d"} - invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} - - validPodSpecAbc := api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - } - validPodSpecDef := api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "def", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - } - validPodSpecNodeSelector := api.PodSpec{ - NodeSelector: validSelector, - NodeName: "xyz", - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - } - validPodSpecVolume := api.PodSpec{ - Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - } - - validPodTemplateAbc := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validSelector, - }, - Spec: validPodSpecAbc, - }, - } - validPodTemplateNodeSelector := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validSelector, - }, - Spec: validPodSpecNodeSelector, - }, - } - validPodTemplateAbc2 := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validSelector2, - }, - Spec: validPodSpecAbc, - }, - } - validPodTemplateDef := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validSelector2, - }, - Spec: validPodSpecDef, - }, - } - invalidPodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - ObjectMeta: api.ObjectMeta{ - Labels: invalidSelector, - }, - }, - } - readWriteVolumePodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validSelector, - }, - Spec: validPodSpecVolume, - }, - } - - type dsUpdateTest struct { - old extensions.DaemonSet - update extensions.DaemonSet - } - successCases := []dsUpdateTest{ - { - old: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplateAbc.Template, - }, - }, - update: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplateAbc.Template, - }, - }, - }, - { - old: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplateAbc.Template, - }, - }, - update: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector2}, - Template: validPodTemplateAbc2.Template, - }, - }, - }, - { - old: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplateAbc.Template, - }, - }, - update: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplateNodeSelector.Template, - }, - }, - }, - } - for _, successCase := range successCases { - successCase.old.ObjectMeta.ResourceVersion = "1" - successCase.update.ObjectMeta.ResourceVersion = "1" - if errs := ValidateDaemonSetUpdate(&successCase.update, &successCase.old); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - errorCases := map[string]dsUpdateTest{ - "change daemon name": { - old: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplateAbc.Template, - }, - }, - update: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplateAbc.Template, - }, - }, - }, - "invalid selector": { - old: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplateAbc.Template, - }, - }, - update: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: invalidSelector}, - Template: validPodTemplateAbc.Template, - }, - }, - }, - "invalid pod": { - old: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplateAbc.Template, - }, - }, - update: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: invalidPodTemplate.Template, - }, - }, - }, - "change container image": { - old: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplateAbc.Template, - }, - }, - update: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplateDef.Template, - }, - }, - }, - "read-write volume": { - old: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplateAbc.Template, - }, - }, - update: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: readWriteVolumePodTemplate.Template, - }, - }, - }, - "invalid update strategy": { - old: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplateAbc.Template, - }, - }, - update: extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: invalidSelector}, - Template: validPodTemplateAbc.Template, - }, - }, - }, - } - for testName, errorCase := range errorCases { - if errs := ValidateDaemonSetUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 { - t.Errorf("expected failure: %s", testName) - } - } -} - -func TestValidateDaemonSet(t *testing.T) { - validSelector := map[string]string{"a": "b"} - validPodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validSelector, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - }, - } - invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} - invalidPodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - ObjectMeta: api.ObjectMeta{ - Labels: invalidSelector, - }, - }, - } - successCases := []extensions.DaemonSet{ - { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplate.Template, - }, - }, - { - ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplate.Template, - }, - }, - } - for _, successCase := range successCases { - if errs := ValidateDaemonSet(&successCase); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := map[string]extensions.DaemonSet{ - "zero-length ID": { - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplate.Template, - }, - }, - "missing-namespace": { - ObjectMeta: api.ObjectMeta{Name: "abc-123"}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplate.Template, - }, - }, - "empty selector": { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Template: validPodTemplate.Template, - }, - }, - "selector_doesnt_match": { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, - Template: validPodTemplate.Template, - }, - }, - "invalid template": { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - }, - }, - "invalid_label": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Namespace: api.NamespaceDefault, - Labels: map[string]string{ - "NoUppercaseOrSpecialCharsLike=Equals": "bar", - }, - }, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplate.Template, - }, - }, - "invalid_label 2": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Namespace: api.NamespaceDefault, - Labels: map[string]string{ - "NoUppercaseOrSpecialCharsLike=Equals": "bar", - }, - }, - Spec: extensions.DaemonSetSpec{ - Template: invalidPodTemplate.Template, - }, - }, - "invalid_annotation": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Namespace: api.NamespaceDefault, - Annotations: map[string]string{ - "NoUppercaseOrSpecialCharsLike=Equals": "bar", - }, - }, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplate.Template, - }, - }, - "invalid restart policy 1": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: api.PodTemplateSpec{ - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyOnFailure, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - ObjectMeta: api.ObjectMeta{ - Labels: validSelector, - }, - }, - }, - }, - "invalid restart policy 2": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validSelector}, - Template: api.PodTemplateSpec{ - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyNever, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - ObjectMeta: api.ObjectMeta{ - Labels: validSelector, - }, - }, - }, - }, - } - for k, v := range errorCases { - errs := ValidateDaemonSet(&v) - if len(errs) == 0 { - t.Errorf("expected failure for %s", k) - } - for i := range errs { - field := errs[i].Field - if !strings.HasPrefix(field, "spec.template.") && - !strings.HasPrefix(field, "spec.updateStrategy") && - field != "metadata.name" && - field != "metadata.namespace" && - field != "spec.selector" && - field != "spec.template" && - field != "GCEPersistentDisk.ReadOnly" && - field != "spec.template.labels" && - field != "metadata.annotations" && - field != "metadata.labels" { - t.Errorf("%s: missing prefix for: %v", k, errs[i]) - } - } - } -} - -func validDeployment() *extensions.Deployment { - return &extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.DeploymentSpec{ - Selector: &unversioned.LabelSelector{ - MatchLabels: map[string]string{ - "name": "abc", - }, - }, - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: api.NamespaceDefault, - Labels: map[string]string{ - "name": "abc", - }, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSDefault, - Containers: []api.Container{ - { - Name: "nginx", - Image: "image", - ImagePullPolicy: api.PullNever, - }, - }, - }, - }, - RollbackTo: &extensions.RollbackConfig{ - Revision: 1, - }, - }, - } -} - -func TestValidateDeployment(t *testing.T) { - successCases := []*extensions.Deployment{ - validDeployment(), - } - for _, successCase := range successCases { - if errs := ValidateDeployment(successCase); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := map[string]*extensions.Deployment{} - errorCases["metadata.name: Required value"] = &extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - Namespace: api.NamespaceDefault, - }, - } - // selector should match the labels in pod template. - invalidSelectorDeployment := validDeployment() - invalidSelectorDeployment.Spec.Selector = &unversioned.LabelSelector{ - MatchLabels: map[string]string{ - "name": "def", - }, - } - errorCases["`selector` does not match template `labels`"] = invalidSelectorDeployment - - // RestartPolicy should be always. - invalidRestartPolicyDeployment := validDeployment() - invalidRestartPolicyDeployment.Spec.Template.Spec.RestartPolicy = api.RestartPolicyNever - errorCases["Unsupported value: \"Never\""] = invalidRestartPolicyDeployment - - // rollingUpdate should be nil for recreate. - invalidRecreateDeployment := validDeployment() - invalidRecreateDeployment.Spec.Strategy = extensions.DeploymentStrategy{ - Type: extensions.RecreateDeploymentStrategyType, - RollingUpdate: &extensions.RollingUpdateDeployment{}, - } - errorCases["may not be specified when strategy `type` is 'Recreate'"] = invalidRecreateDeployment - - // MaxSurge should be in the form of 20%. - invalidMaxSurgeDeployment := validDeployment() - invalidMaxSurgeDeployment.Spec.Strategy = extensions.DeploymentStrategy{ - Type: extensions.RollingUpdateDeploymentStrategyType, - RollingUpdate: &extensions.RollingUpdateDeployment{ - MaxSurge: intstr.FromString("20Percent"), - }, - } - errorCases["must be an integer or percentage"] = invalidMaxSurgeDeployment - - // MaxSurge and MaxUnavailable cannot both be zero. - invalidRollingUpdateDeployment := validDeployment() - invalidRollingUpdateDeployment.Spec.Strategy = extensions.DeploymentStrategy{ - Type: extensions.RollingUpdateDeploymentStrategyType, - RollingUpdate: &extensions.RollingUpdateDeployment{ - MaxSurge: intstr.FromString("0%"), - MaxUnavailable: intstr.FromInt(0), - }, - } - errorCases["may not be 0 when `maxSurge` is 0"] = invalidRollingUpdateDeployment - - // MaxUnavailable should not be more than 100%. - invalidMaxUnavailableDeployment := validDeployment() - invalidMaxUnavailableDeployment.Spec.Strategy = extensions.DeploymentStrategy{ - Type: extensions.RollingUpdateDeploymentStrategyType, - RollingUpdate: &extensions.RollingUpdateDeployment{ - MaxUnavailable: intstr.FromString("110%"), - }, - } - errorCases["must not be greater than 100%"] = invalidMaxUnavailableDeployment - - // Rollback.Revision must be non-negative - invalidRollbackRevisionDeployment := validDeployment() - invalidRollbackRevisionDeployment.Spec.RollbackTo.Revision = -3 - errorCases["must be greater than or equal to 0"] = invalidRollbackRevisionDeployment - - for k, v := range errorCases { - errs := ValidateDeployment(v) - if len(errs) == 0 { - t.Errorf("[%s] expected failure", k) - } else if !strings.Contains(errs[0].Error(), k) { - t.Errorf("unexpected error: %q, expected: %q", errs[0].Error(), k) - } - } -} - -func validDeploymentRollback() *extensions.DeploymentRollback { - return &extensions.DeploymentRollback{ - Name: "abc", - UpdatedAnnotations: map[string]string{ - "created-by": "abc", - }, - RollbackTo: extensions.RollbackConfig{ - Revision: 1, - }, - } -} - -func TestValidateDeploymentRollback(t *testing.T) { - noAnnotation := validDeploymentRollback() - noAnnotation.UpdatedAnnotations = nil - successCases := []*extensions.DeploymentRollback{ - validDeploymentRollback(), - noAnnotation, - } - for _, successCase := range successCases { - if errs := ValidateDeploymentRollback(successCase); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := map[string]*extensions.DeploymentRollback{} - invalidNoName := validDeploymentRollback() - invalidNoName.Name = "" - errorCases["name: Required value"] = invalidNoName - - for k, v := range errorCases { - errs := ValidateDeploymentRollback(v) - if len(errs) == 0 { - t.Errorf("[%s] expected failure", k) - } else if !strings.Contains(errs[0].Error(), k) { - t.Errorf("unexpected error: %q, expected: %q", errs[0].Error(), k) - } - } -} - -func TestValidateJob(t *testing.T) { - validSelector := &unversioned.LabelSelector{ - MatchLabels: map[string]string{"a": "b"}, - } - validPodTemplateSpec := api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validSelector.MatchLabels, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyOnFailure, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - } - successCases := []extensions.Job{ - { - ObjectMeta: api.ObjectMeta{ - Name: "myjob", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.JobSpec{ - Selector: validSelector, - Template: validPodTemplateSpec, - }, - }, - } - for _, successCase := range successCases { - if errs := ValidateJob(&successCase); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - negative := -1 - negative64 := int64(-1) - errorCases := map[string]extensions.Job{ - "spec.parallelism:must be greater than or equal to 0": { - ObjectMeta: api.ObjectMeta{ - Name: "myjob", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.JobSpec{ - Parallelism: &negative, - Selector: validSelector, - Template: validPodTemplateSpec, - }, - }, - "spec.completions:must be greater than or equal to 0": { - ObjectMeta: api.ObjectMeta{ - Name: "myjob", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.JobSpec{ - Completions: &negative, - Selector: validSelector, - Template: validPodTemplateSpec, - }, - }, - "spec.activeDeadlineSeconds:must be greater than or equal to 0": { - ObjectMeta: api.ObjectMeta{ - Name: "myjob", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.JobSpec{ - ActiveDeadlineSeconds: &negative64, - Selector: validSelector, - Template: validPodTemplateSpec, - }, - }, - "spec.selector:Required value": { - ObjectMeta: api.ObjectMeta{ - Name: "myjob", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.JobSpec{ - Template: validPodTemplateSpec, - }, - }, - "spec.template.metadata.labels: Invalid value: {\"y\":\"z\"}: `selector` does not match template `labels`": { - ObjectMeta: api.ObjectMeta{ - Name: "myjob", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.JobSpec{ - Selector: validSelector, - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"y": "z"}, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyOnFailure, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - }, - }, - }, - "spec.template.spec.restartPolicy: Unsupported value": { - ObjectMeta: api.ObjectMeta{ - Name: "myjob", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.JobSpec{ - Selector: validSelector, - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validSelector.MatchLabels, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - }, - }, - }, - } - - for k, v := range errorCases { - errs := ValidateJob(&v) - if len(errs) == 0 { - t.Errorf("expected failure for %s", k) - } else { - s := strings.Split(k, ":") - err := errs[0] - if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) { - t.Errorf("unexpected error: %v, expected: %s", err, k) - } - } - } -} - -type ingressRules map[string]string - -func TestValidateIngress(t *testing.T) { - defaultBackend := extensions.IngressBackend{ - ServiceName: "default-backend", - ServicePort: intstr.FromInt(80), - } - - newValid := func() extensions.Ingress { - return extensions.Ingress{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.IngressSpec{ - Backend: &extensions.IngressBackend{ - ServiceName: "default-backend", - ServicePort: intstr.FromInt(80), - }, - Rules: []extensions.IngressRule{ - { - Host: "foo.bar.com", - IngressRuleValue: extensions.IngressRuleValue{ - HTTP: &extensions.HTTPIngressRuleValue{ - Paths: []extensions.HTTPIngressPath{ - { - Path: "/foo", - Backend: defaultBackend, - }, - }, - }, - }, - }, - }, - }, - Status: extensions.IngressStatus{ - LoadBalancer: api.LoadBalancerStatus{ - Ingress: []api.LoadBalancerIngress{ - {IP: "127.0.0.1"}, - }, - }, - }, - } - } - servicelessBackend := newValid() - servicelessBackend.Spec.Backend.ServiceName = "" - invalidNameBackend := newValid() - invalidNameBackend.Spec.Backend.ServiceName = "defaultBackend" - noPortBackend := newValid() - noPortBackend.Spec.Backend = &extensions.IngressBackend{ServiceName: defaultBackend.ServiceName} - noForwardSlashPath := newValid() - noForwardSlashPath.Spec.Rules[0].IngressRuleValue.HTTP.Paths = []extensions.HTTPIngressPath{ - { - Path: "invalid", - Backend: defaultBackend, - }, - } - noPaths := newValid() - noPaths.Spec.Rules[0].IngressRuleValue.HTTP.Paths = []extensions.HTTPIngressPath{} - badHost := newValid() - badHost.Spec.Rules[0].Host = "foobar:80" - badRegexPath := newValid() - badPathExpr := "/invalid[" - badRegexPath.Spec.Rules[0].IngressRuleValue.HTTP.Paths = []extensions.HTTPIngressPath{ - { - Path: badPathExpr, - Backend: defaultBackend, - }, - } - badPathErr := fmt.Sprintf("spec.rules[0].http.paths[0].path: Invalid value: '%v'", badPathExpr) - hostIP := "127.0.0.1" - badHostIP := newValid() - badHostIP.Spec.Rules[0].Host = hostIP - badHostIPErr := fmt.Sprintf("spec.rules[0].host: Invalid value: '%v'", hostIP) - noSecretName := newValid() - noSecretName.Spec.TLS = []extensions.IngressTLS{{SecretName: ""}} - - errorCases := map[string]extensions.Ingress{ - "spec.backend.serviceName: Required value": servicelessBackend, - "spec.backend.serviceName: Invalid value": invalidNameBackend, - "spec.backend.servicePort: Invalid value": noPortBackend, - "spec.rules[0].host: Invalid value": badHost, - "spec.rules[0].http.paths: Required value": noPaths, - "spec.rules[0].http.paths[0].path: Invalid value": noForwardSlashPath, - "spec.tls[0].secretName: Required value": noSecretName, - } - errorCases[badPathErr] = badRegexPath - errorCases[badHostIPErr] = badHostIP - - for k, v := range errorCases { - errs := ValidateIngress(&v) - if len(errs) == 0 { - t.Errorf("expected failure for %q", k) - } else { - s := strings.Split(k, ":") - err := errs[0] - if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) { - t.Errorf("unexpected error: %q, expected: %q", err, k) - } - } - } -} - -func TestValidateIngressStatusUpdate(t *testing.T) { - defaultBackend := extensions.IngressBackend{ - ServiceName: "default-backend", - ServicePort: intstr.FromInt(80), - } - - newValid := func() extensions.Ingress { - return extensions.Ingress{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: api.NamespaceDefault, - ResourceVersion: "9", - }, - Spec: extensions.IngressSpec{ - Backend: &extensions.IngressBackend{ - ServiceName: "default-backend", - ServicePort: intstr.FromInt(80), - }, - Rules: []extensions.IngressRule{ - { - Host: "foo.bar.com", - IngressRuleValue: extensions.IngressRuleValue{ - HTTP: &extensions.HTTPIngressRuleValue{ - Paths: []extensions.HTTPIngressPath{ - { - Path: "/foo", - Backend: defaultBackend, - }, - }, - }, - }, - }, - }, - }, - Status: extensions.IngressStatus{ - LoadBalancer: api.LoadBalancerStatus{ - Ingress: []api.LoadBalancerIngress{ - {IP: "127.0.0.1", Hostname: "foo.bar.com"}, - }, - }, - }, - } - } - oldValue := newValid() - newValue := newValid() - newValue.Status = extensions.IngressStatus{ - LoadBalancer: api.LoadBalancerStatus{ - Ingress: []api.LoadBalancerIngress{ - {IP: "127.0.0.2", Hostname: "foo.com"}, - }, - }, - } - invalidIP := newValid() - invalidIP.Status = extensions.IngressStatus{ - LoadBalancer: api.LoadBalancerStatus{ - Ingress: []api.LoadBalancerIngress{ - {IP: "abcd", Hostname: "foo.com"}, - }, - }, - } - invalidHostname := newValid() - invalidHostname.Status = extensions.IngressStatus{ - LoadBalancer: api.LoadBalancerStatus{ - Ingress: []api.LoadBalancerIngress{ - {IP: "127.0.0.1", Hostname: "127.0.0.1"}, - }, - }, - } - - errs := ValidateIngressStatusUpdate(&newValue, &oldValue) - if len(errs) != 0 { - t.Errorf("Unexpected error %v", errs) - } - - errorCases := map[string]extensions.Ingress{ - "status.loadBalancer.ingress[0].ip: Invalid value": invalidIP, - "status.loadBalancer.ingress[0].hostname: Invalid value": invalidHostname, - } - for k, v := range errorCases { - errs := ValidateIngressStatusUpdate(&v, &oldValue) - if len(errs) == 0 { - t.Errorf("expected failure for %s", k) - } else { - s := strings.Split(k, ":") - err := errs[0] - if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) { - t.Errorf("unexpected error: %q, expected: %q", err, k) - } - } - } -} - -func TestValidateClusterAutoscaler(t *testing.T) { - successCases := []extensions.ClusterAutoscaler{ - { - ObjectMeta: api.ObjectMeta{ - Name: "ClusterAutoscaler", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.ClusterAutoscalerSpec{ - MinNodes: 1, - MaxNodes: 5, - TargetUtilization: []extensions.NodeUtilization{ - { - Resource: extensions.CpuRequest, - Value: 0.7, - }, - }, - }, - }, - } - for _, successCase := range successCases { - if errs := ValidateClusterAutoscaler(&successCase); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := map[string]extensions.ClusterAutoscaler{ - "must be 'ClusterAutoscaler'": { - ObjectMeta: api.ObjectMeta{ - Name: "TestClusterAutoscaler", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.ClusterAutoscalerSpec{ - MinNodes: 1, - MaxNodes: 5, - TargetUtilization: []extensions.NodeUtilization{ - { - Resource: extensions.CpuRequest, - Value: 0.7, - }, - }, - }, - }, - "must be 'default'": { - ObjectMeta: api.ObjectMeta{ - Name: "ClusterAutoscaler", - Namespace: "test", - }, - Spec: extensions.ClusterAutoscalerSpec{ - MinNodes: 1, - MaxNodes: 5, - TargetUtilization: []extensions.NodeUtilization{ - { - Resource: extensions.CpuRequest, - Value: 0.7, - }, - }, - }, - }, - - `must be greater than or equal to 0`: { - ObjectMeta: api.ObjectMeta{ - Name: "ClusterAutoscaler", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.ClusterAutoscalerSpec{ - MinNodes: -1, - MaxNodes: 5, - TargetUtilization: []extensions.NodeUtilization{ - { - Resource: extensions.CpuRequest, - Value: 0.7, - }, - }, - }, - }, - "must be greater than or equal to `minNodes`": { - ObjectMeta: api.ObjectMeta{ - Name: "ClusterAutoscaler", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.ClusterAutoscalerSpec{ - MinNodes: 10, - MaxNodes: 5, - TargetUtilization: []extensions.NodeUtilization{ - { - Resource: extensions.CpuRequest, - Value: 0.7, - }, - }, - }, - }, - "Required value": { - ObjectMeta: api.ObjectMeta{ - Name: "ClusterAutoscaler", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.ClusterAutoscalerSpec{ - MinNodes: 1, - MaxNodes: 5, - TargetUtilization: []extensions.NodeUtilization{}, - }, - }, - } - - for k, v := range errorCases { - errs := ValidateClusterAutoscaler(&v) - if len(errs) == 0 { - t.Errorf("[%s] expected failure", k) - } else if !strings.Contains(errs[0].Error(), k) { - t.Errorf("unexpected error: %v, expected: %q", errs[0], k) - } - } -} - -func TestValidateScale(t *testing.T) { - successCases := []extensions.Scale{ - { - ObjectMeta: api.ObjectMeta{ - Name: "frontend", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.ScaleSpec{ - Replicas: 1, - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "frontend", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.ScaleSpec{ - Replicas: 10, - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "frontend", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.ScaleSpec{ - Replicas: 0, - }, - }, - } - - for _, successCase := range successCases { - if errs := ValidateScale(&successCase); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := []struct { - scale extensions.Scale - msg string - }{ - { - scale: extensions.Scale{ - ObjectMeta: api.ObjectMeta{ - Name: "frontend", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.ScaleSpec{ - Replicas: -1, - }, - }, - msg: "must be greater than or equal to 0", - }, - } - - for _, c := range errorCases { - if errs := ValidateScale(&c.scale); len(errs) == 0 { - t.Errorf("expected failure for %s", c.msg) - } else if !strings.Contains(errs[0].Error(), c.msg) { - t.Errorf("unexpected error: %v, expected: %s", errs[0], c.msg) - } - } -} - -func TestValidateReplicaSetStatusUpdate(t *testing.T) { - validLabels := map[string]string{"a": "b"} - validPodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validLabels, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - }, - } - type rcUpdateTest struct { - old extensions.ReplicaSet - update extensions.ReplicaSet - } - successCases := []rcUpdateTest{ - { - old: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - Status: extensions.ReplicaSetStatus{ - Replicas: 2, - }, - }, - update: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Replicas: 3, - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - Status: extensions.ReplicaSetStatus{ - Replicas: 4, - }, - }, - }, - } - for _, successCase := range successCases { - successCase.old.ObjectMeta.ResourceVersion = "1" - successCase.update.ObjectMeta.ResourceVersion = "1" - if errs := ValidateReplicaSetStatusUpdate(&successCase.update, &successCase.old); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - errorCases := map[string]rcUpdateTest{ - "negative replicas": { - old: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - Status: extensions.ReplicaSetStatus{ - Replicas: 3, - }, - }, - update: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Replicas: 2, - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - Status: extensions.ReplicaSetStatus{ - Replicas: -3, - }, - }, - }, - } - for testName, errorCase := range errorCases { - if errs := ValidateReplicaSetStatusUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 { - t.Errorf("expected failure: %s", testName) - } - } - -} - -func TestValidateReplicaSetUpdate(t *testing.T) { - validLabels := map[string]string{"a": "b"} - validPodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validLabels, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - }, - } - readWriteVolumePodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validLabels, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}}, - }, - }, - } - invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} - invalidPodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - ObjectMeta: api.ObjectMeta{ - Labels: invalidLabels, - }, - }, - } - type rcUpdateTest struct { - old extensions.ReplicaSet - update extensions.ReplicaSet - } - successCases := []rcUpdateTest{ - { - old: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - }, - update: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Replicas: 3, - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - }, - }, - { - old: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - }, - update: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Replicas: 1, - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &readWriteVolumePodTemplate.Template, - }, - }, - }, - } - for _, successCase := range successCases { - successCase.old.ObjectMeta.ResourceVersion = "1" - successCase.update.ObjectMeta.ResourceVersion = "1" - if errs := ValidateReplicaSetUpdate(&successCase.update, &successCase.old); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - errorCases := map[string]rcUpdateTest{ - "more than one read/write": { - old: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - }, - update: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Replicas: 2, - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &readWriteVolumePodTemplate.Template, - }, - }, - }, - "invalid selector": { - old: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - }, - update: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Replicas: 2, - Selector: &unversioned.LabelSelector{MatchLabels: invalidLabels}, - Template: &validPodTemplate.Template, - }, - }, - }, - "invalid pod": { - old: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - }, - update: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Replicas: 2, - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &invalidPodTemplate.Template, - }, - }, - }, - "negative replicas": { - old: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - }, - update: extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Replicas: -1, - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - }, - }, - } - for testName, errorCase := range errorCases { - if errs := ValidateReplicaSetUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 { - t.Errorf("expected failure: %s", testName) - } - } -} - -func TestValidateReplicaSet(t *testing.T) { - validLabels := map[string]string{"a": "b"} - validPodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validLabels, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - }, - } - readWriteVolumePodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: validLabels, - }, - Spec: api.PodSpec{ - Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}}, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - }, - } - invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} - invalidPodTemplate := api.PodTemplate{ - Template: api.PodTemplateSpec{ - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - }, - ObjectMeta: api.ObjectMeta{ - Labels: invalidLabels, - }, - }, - } - successCases := []extensions.ReplicaSet{ - { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - }, - { - ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - }, - { - ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Replicas: 1, - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &readWriteVolumePodTemplate.Template, - }, - }, - } - for _, successCase := range successCases { - if errs := ValidateReplicaSet(&successCase); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := map[string]extensions.ReplicaSet{ - "zero-length ID": { - ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - }, - "missing-namespace": { - ObjectMeta: api.ObjectMeta{Name: "abc-123"}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - }, - "empty selector": { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Template: &validPodTemplate.Template, - }, - }, - "selector_doesnt_match": { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, - Template: &validPodTemplate.Template, - }, - }, - "invalid manifest": { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - }, - }, - "read-write persistent disk with > 1 pod": { - ObjectMeta: api.ObjectMeta{Name: "abc"}, - Spec: extensions.ReplicaSetSpec{ - Replicas: 2, - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &readWriteVolumePodTemplate.Template, - }, - }, - "negative_replicas": { - ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - Spec: extensions.ReplicaSetSpec{ - Replicas: -1, - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - }, - }, - "invalid_label": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Namespace: api.NamespaceDefault, - Labels: map[string]string{ - "NoUppercaseOrSpecialCharsLike=Equals": "bar", - }, - }, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - }, - "invalid_label 2": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Namespace: api.NamespaceDefault, - Labels: map[string]string{ - "NoUppercaseOrSpecialCharsLike=Equals": "bar", - }, - }, - Spec: extensions.ReplicaSetSpec{ - Template: &invalidPodTemplate.Template, - }, - }, - "invalid_annotation": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Namespace: api.NamespaceDefault, - Annotations: map[string]string{ - "NoUppercaseOrSpecialCharsLike=Equals": "bar", - }, - }, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &validPodTemplate.Template, - }, - }, - "invalid restart policy 1": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &api.PodTemplateSpec{ - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyOnFailure, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - ObjectMeta: api.ObjectMeta{ - Labels: validLabels, - }, - }, - }, - }, - "invalid restart policy 2": { - ObjectMeta: api.ObjectMeta{ - Name: "abc-123", - Namespace: api.NamespaceDefault, - }, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: validLabels}, - Template: &api.PodTemplateSpec{ - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyNever, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, - }, - ObjectMeta: api.ObjectMeta{ - Labels: validLabels, - }, - }, - }, - }, - } - for k, v := range errorCases { - errs := ValidateReplicaSet(&v) - if len(errs) == 0 { - t.Errorf("expected failure for %s", k) - } - for i := range errs { - field := errs[i].Field - if !strings.HasPrefix(field, "spec.template.") && - field != "metadata.name" && - field != "metadata.namespace" && - field != "spec.selector" && - field != "spec.template" && - field != "GCEPersistentDisk.ReadOnly" && - field != "spec.replicas" && - field != "spec.template.labels" && - field != "metadata.annotations" && - field != "metadata.labels" && - field != "status.replicas" { - t.Errorf("%s: missing prefix for: %v", k, errs[i]) - } - } - } -} - -func newInt(val int) *int { - p := new(int) - *p = val - return p -} - -func TestValidatePodSecurityPolicy(t *testing.T) { - validSCC := func() *extensions.PodSecurityPolicy { - return &extensions.PodSecurityPolicy{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: extensions.PodSecurityPolicySpec{ - SELinuxContext: extensions.SELinuxContextStrategyOptions{ - Type: extensions.SELinuxStrategyRunAsAny, - }, - RunAsUser: extensions.RunAsUserStrategyOptions{ - Type: extensions.RunAsUserStrategyRunAsAny, - }, - }, - } - } - - noUserOptions := validSCC() - noUserOptions.Spec.RunAsUser.Type = "" - - noSELinuxOptions := validSCC() - noSELinuxOptions.Spec.SELinuxContext.Type = "" - - invalidUserStratType := validSCC() - invalidUserStratType.Spec.RunAsUser.Type = "invalid" - - invalidSELinuxStratType := validSCC() - invalidSELinuxStratType.Spec.SELinuxContext.Type = "invalid" - - missingObjectMetaName := validSCC() - missingObjectMetaName.ObjectMeta.Name = "" - - invalidRangeMinGreaterThanMax := validSCC() - invalidRangeMinGreaterThanMax.Spec.RunAsUser.Ranges = []extensions.IDRange{ - {Min: 2, Max: 1}, - } - - invalidRangeNegativeMin := validSCC() - invalidRangeNegativeMin.Spec.RunAsUser.Ranges = []extensions.IDRange{ - {Min: -1, Max: 10}, - } - - invalidRangeNegativeMax := validSCC() - invalidRangeNegativeMax.Spec.RunAsUser.Ranges = []extensions.IDRange{ - {Min: 1, Max: -10}, - } - - errorCases := map[string]struct { - scc *extensions.PodSecurityPolicy - errorDetail string - }{ - "no user options": { - scc: noUserOptions, - errorDetail: "supported values: MustRunAs, MustRunAsNonRoot, RunAsAny", - }, - "no selinux options": { - scc: noSELinuxOptions, - errorDetail: "supported values: MustRunAs, RunAsAny", - }, - "invalid user strategy type": { - scc: invalidUserStratType, - errorDetail: "supported values: MustRunAs, MustRunAsNonRoot, RunAsAny", - }, - "invalid selinux strategy type": { - scc: invalidSELinuxStratType, - errorDetail: "supported values: MustRunAs, RunAsAny", - }, - "missing object meta name": { - scc: missingObjectMetaName, - errorDetail: "name or generateName is required", - }, - "invalid range min greater than max": { - scc: invalidRangeMinGreaterThanMax, - errorDetail: "min cannot be greater than max", - }, - "invalid range negative min": { - scc: invalidRangeNegativeMin, - errorDetail: "min cannot be negative", - }, - "invalid range negative max": { - scc: invalidRangeNegativeMax, - errorDetail: "max cannot be negative", - }, - } - - for k, v := range errorCases { - if errs := ValidatePodSecurityPolicy(v.scc); len(errs) == 0 || errs[0].Detail != v.errorDetail { - t.Errorf("Expected error with detail %s for %s, got %v", v.errorDetail, k, errs[0].Detail) - } - } - - mustRunAs := validSCC() - mustRunAs.Spec.RunAsUser.Type = extensions.RunAsUserStrategyMustRunAs - mustRunAs.Spec.RunAsUser.Ranges = []extensions.IDRange{ - { - Min: 1, - Max: 1, - }, - } - mustRunAs.Spec.SELinuxContext.Type = extensions.SELinuxStrategyMustRunAs - - runAsNonRoot := validSCC() - runAsNonRoot.Spec.RunAsUser.Type = extensions.RunAsUserStrategyMustRunAsNonRoot - - successCases := map[string]struct { - scc *extensions.PodSecurityPolicy - }{ - "must run as": { - scc: mustRunAs, - }, - "run as any": { - scc: validSCC(), - }, - "run as non-root (user only)": { - scc: runAsNonRoot, - }, - } - - for k, v := range successCases { - if errs := ValidatePodSecurityPolicy(v.scc); len(errs) != 0 { - t.Errorf("Expected success for %s, got %v", k, errs) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/delta_fifo_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/delta_fifo_test.go deleted file mode 100644 index 8efd982b5..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/delta_fifo_test.go +++ /dev/null @@ -1,385 +0,0 @@ -/* -Copyright 2014 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 cache - -import ( - "reflect" - "testing" - "time" -) - -// helper function to reduce stuttering -func testPop(f *DeltaFIFO) testFifoObject { - return f.Pop().(Deltas).Newest().Object.(testFifoObject) -} - -// keyLookupFunc adapts a raw function to be a KeyLookup. -type keyLookupFunc func() []string - -// ListKeys just calls kl. -func (kl keyLookupFunc) ListKeys() []string { - return kl() -} - -// GetByKey returns the key if it exists in the list returned by kl. -func (kl keyLookupFunc) GetByKey(key string) (interface{}, bool, error) { - for _, v := range kl() { - if v == key { - return key, true, nil - } - } - return nil, false, nil -} - -func TestDeltaFIFO_basic(t *testing.T) { - f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil) - const amount = 500 - go func() { - for i := 0; i < amount; i++ { - f.Add(mkFifoObj(string([]rune{'a', rune(i)}), i+1)) - } - }() - go func() { - for u := uint64(0); u < amount; u++ { - f.Add(mkFifoObj(string([]rune{'b', rune(u)}), u+1)) - } - }() - - lastInt := int(0) - lastUint := uint64(0) - for i := 0; i < amount*2; i++ { - switch obj := testPop(f).val.(type) { - case int: - if obj <= lastInt { - t.Errorf("got %v (int) out of order, last was %v", obj, lastInt) - } - lastInt = obj - case uint64: - if obj <= lastUint { - t.Errorf("got %v (uint) out of order, last was %v", obj, lastUint) - } else { - lastUint = obj - } - default: - t.Fatalf("unexpected type %#v", obj) - } - } -} - -func TestDeltaFIFO_compressorWorks(t *testing.T) { - oldestTypes := []DeltaType{} - f := NewDeltaFIFO( - testFifoObjectKeyFunc, - // This function just keeps the most recent delta - // and puts deleted ones in the list. - DeltaCompressorFunc(func(d Deltas) Deltas { - if n := len(d); n > 1 { - oldestTypes = append(oldestTypes, d[0].Type) - d = d[1:] - } - return d - }), - nil, - ) - f.Add(mkFifoObj("foo", 10)) - f.Update(mkFifoObj("foo", 12)) - f.Replace([]interface{}{mkFifoObj("foo", 20)}, "0") - f.Delete(mkFifoObj("foo", 22)) - f.Add(mkFifoObj("foo", 25)) // flush the last one out - expect := []DeltaType{Added, Updated, Sync, Deleted} - if e, a := expect, oldestTypes; !reflect.DeepEqual(e, a) { - t.Errorf("Expected %#v, got %#v", e, a) - } - if e, a := (Deltas{{Added, mkFifoObj("foo", 25)}}), f.Pop().(Deltas); !reflect.DeepEqual(e, a) { - t.Fatalf("Expected %#v, got %#v", e, a) - } - -} - -func TestDeltaFIFO_addUpdate(t *testing.T) { - f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil) - f.Add(mkFifoObj("foo", 10)) - f.Update(mkFifoObj("foo", 12)) - f.Delete(mkFifoObj("foo", 15)) - - if e, a := []interface{}{mkFifoObj("foo", 15)}, f.List(); !reflect.DeepEqual(e, a) { - t.Errorf("Expected %+v, got %+v", e, a) - } - if e, a := []string{"foo"}, f.ListKeys(); !reflect.DeepEqual(e, a) { - t.Errorf("Expected %+v, got %+v", e, a) - } - - got := make(chan testFifoObject, 2) - go func() { - for { - obj := f.Pop().(Deltas).Newest().Object.(testFifoObject) - t.Logf("got a thing %#v", obj) - t.Logf("D len: %v", len(f.queue)) - got <- obj - } - }() - - first := <-got - if e, a := 15, first.val; e != a { - t.Errorf("Didn't get updated value (%v), got %v", e, a) - } - select { - case unexpected := <-got: - t.Errorf("Got second value %v", unexpected.val) - case <-time.After(50 * time.Millisecond): - } - _, exists, _ := f.Get(mkFifoObj("foo", "")) - if exists { - t.Errorf("item did not get removed") - } -} - -func TestDeltaFIFO_enqueueingNoLister(t *testing.T) { - f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil) - f.Add(mkFifoObj("foo", 10)) - f.Update(mkFifoObj("bar", 15)) - f.Add(mkFifoObj("qux", 17)) - f.Delete(mkFifoObj("qux", 18)) - - // This delete does not enqueue anything because baz doesn't exist. - f.Delete(mkFifoObj("baz", 20)) - - expectList := []int{10, 15, 18} - for _, expect := range expectList { - if e, a := expect, testPop(f).val; e != a { - t.Errorf("Didn't get updated value (%v), got %v", e, a) - } - } - if e, a := 0, len(f.items); e != a { - t.Errorf("queue unexpectedly not empty: %v != %v\n%#v", e, a, f.items) - } -} - -func TestDeltaFIFO_enqueueingWithLister(t *testing.T) { - f := NewDeltaFIFO( - testFifoObjectKeyFunc, - nil, - keyLookupFunc(func() []string { - return []string{"foo", "bar", "baz"} - }), - ) - f.Add(mkFifoObj("foo", 10)) - f.Update(mkFifoObj("bar", 15)) - - // This delete does enqueue the deletion, because "baz" is in the key lister. - f.Delete(mkFifoObj("baz", 20)) - - expectList := []int{10, 15, 20} - for _, expect := range expectList { - if e, a := expect, testPop(f).val; e != a { - t.Errorf("Didn't get updated value (%v), got %v", e, a) - } - } - if e, a := 0, len(f.items); e != a { - t.Errorf("queue unexpectedly not empty: %v != %v", e, a) - } -} - -func TestDeltaFIFO_addReplace(t *testing.T) { - f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil) - f.Add(mkFifoObj("foo", 10)) - f.Replace([]interface{}{mkFifoObj("foo", 15)}, "0") - got := make(chan testFifoObject, 2) - go func() { - for { - got <- testPop(f) - } - }() - - first := <-got - if e, a := 15, first.val; e != a { - t.Errorf("Didn't get updated value (%v), got %v", e, a) - } - select { - case unexpected := <-got: - t.Errorf("Got second value %v", unexpected.val) - case <-time.After(50 * time.Millisecond): - } - _, exists, _ := f.Get(mkFifoObj("foo", "")) - if exists { - t.Errorf("item did not get removed") - } -} - -func TestDeltaFIFO_ReplaceMakesDeletions(t *testing.T) { - f := NewDeltaFIFO( - testFifoObjectKeyFunc, - nil, - keyLookupFunc(func() []string { - return []string{"foo", "bar", "baz"} - }), - ) - f.Delete(mkFifoObj("baz", 10)) - f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0") - - expectedList := []Deltas{ - {{Deleted, mkFifoObj("baz", 10)}}, - {{Sync, mkFifoObj("foo", 5)}}, - // Since "bar" didn't have a delete event and wasn't in the Replace list - // it should get a tombstone key with the right Obj. - {{Deleted, DeletedFinalStateUnknown{Key: "bar", Obj: "bar"}}}, - } - - for _, expected := range expectedList { - cur := f.Pop().(Deltas) - if e, a := expected, cur; !reflect.DeepEqual(e, a) { - t.Errorf("Expected %#v, got %#v", e, a) - } - } -} - -func TestDeltaFIFO_detectLineJumpers(t *testing.T) { - f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil) - - f.Add(mkFifoObj("foo", 10)) - f.Add(mkFifoObj("bar", 1)) - f.Add(mkFifoObj("foo", 11)) - f.Add(mkFifoObj("foo", 13)) - f.Add(mkFifoObj("zab", 30)) - - if e, a := 13, testPop(f).val; a != e { - t.Fatalf("expected %d, got %d", e, a) - } - - f.Add(mkFifoObj("foo", 14)) // ensure foo doesn't jump back in line - - if e, a := 1, testPop(f).val; a != e { - t.Fatalf("expected %d, got %d", e, a) - } - - if e, a := 30, testPop(f).val; a != e { - t.Fatalf("expected %d, got %d", e, a) - } - - if e, a := 14, testPop(f).val; a != e { - t.Fatalf("expected %d, got %d", e, a) - } -} - -func TestDeltaFIFO_addIfNotPresent(t *testing.T) { - f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil) - - f.Add(mkFifoObj("b", 3)) - b3 := f.Pop() - f.Add(mkFifoObj("c", 4)) - c4 := f.Pop() - if e, a := 0, len(f.items); e != a { - t.Fatalf("Expected %v, got %v items in queue", e, a) - } - - f.Add(mkFifoObj("a", 1)) - f.Add(mkFifoObj("b", 2)) - f.AddIfNotPresent(b3) - f.AddIfNotPresent(c4) - - if e, a := 3, len(f.items); a != e { - t.Fatalf("expected queue length %d, got %d", e, a) - } - - expectedValues := []int{1, 2, 4} - for _, expected := range expectedValues { - if actual := testPop(f).val; actual != expected { - t.Fatalf("expected value %d, got %d", expected, actual) - } - } -} - -func TestDeltaFIFO_KeyOf(t *testing.T) { - f := DeltaFIFO{keyFunc: testFifoObjectKeyFunc} - - table := []struct { - obj interface{} - key string - }{ - {obj: testFifoObject{name: "A"}, key: "A"}, - {obj: DeletedFinalStateUnknown{Key: "B", Obj: nil}, key: "B"}, - {obj: Deltas{{Object: testFifoObject{name: "C"}}}, key: "C"}, - {obj: Deltas{{Object: DeletedFinalStateUnknown{Key: "D", Obj: nil}}}, key: "D"}, - } - - for _, item := range table { - got, err := f.KeyOf(item.obj) - if err != nil { - t.Errorf("Unexpected error for %q: %v", item.obj, err) - continue - } - if e, a := item.key, got; e != a { - t.Errorf("Expected %v, got %v", e, a) - } - } -} - -func TestDeltaFIFO_HasSynced(t *testing.T) { - tests := []struct { - actions []func(f *DeltaFIFO) - expectedSynced bool - }{ - { - actions: []func(f *DeltaFIFO){}, - expectedSynced: false, - }, - { - actions: []func(f *DeltaFIFO){ - func(f *DeltaFIFO) { f.Add(mkFifoObj("a", 1)) }, - }, - expectedSynced: true, - }, - { - actions: []func(f *DeltaFIFO){ - func(f *DeltaFIFO) { f.Replace([]interface{}{}, "0") }, - }, - expectedSynced: true, - }, - { - actions: []func(f *DeltaFIFO){ - func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") }, - }, - expectedSynced: false, - }, - { - actions: []func(f *DeltaFIFO){ - func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") }, - func(f *DeltaFIFO) { f.Pop() }, - }, - expectedSynced: false, - }, - { - actions: []func(f *DeltaFIFO){ - func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") }, - func(f *DeltaFIFO) { f.Pop() }, - func(f *DeltaFIFO) { f.Pop() }, - }, - expectedSynced: true, - }, - } - - for i, test := range tests { - f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil) - - for _, action := range test.actions { - action(f) - } - if e, a := test.expectedSynced, f.HasSynced(); a != e { - t.Errorf("test case %v failed, expected: %v , got %v", i, e, a) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/expiration_cache.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/expiration_cache.go index 5eb996b66..964deda07 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/expiration_cache.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/expiration_cache.go @@ -17,11 +17,11 @@ limitations under the License. package cache import ( + "sync" "time" "github.com/golang/glog" "k8s.io/kubernetes/pkg/util" - "k8s.io/kubernetes/pkg/util/runtime" ) // ExpirationCache implements the store interface @@ -29,12 +29,20 @@ import ( // a. The key is computed based off the original item/keyFunc // b. The value inserted under that key is the timestamped item // 2. Expiration happens lazily on read based on the expiration policy +// a. No item can be inserted into the store while we're expiring +// *any* item in the cache. // 3. Time-stamps are stripped off unexpired entries before return +// Note that the ExpirationCache is inherently slower than a normal +// threadSafeStore because it takes a write lock every time it checks if +// an item has expired. type ExpirationCache struct { cacheStorage ThreadSafeStore keyFunc KeyFunc clock util.Clock expirationPolicy ExpirationPolicy + // expirationLock is a write lock used to guarantee that we don't clobber + // newly inserted objects because of a stale expiration timestamp comparison + expirationLock sync.Mutex } // ExpirationPolicy dictates when an object expires. Currently only abstracted out @@ -68,7 +76,6 @@ type timestampedEntry struct { // getTimestampedEntry returnes the timestampedEntry stored under the given key. func (c *ExpirationCache) getTimestampedEntry(key string) (*timestampedEntry, bool) { item, _ := c.cacheStorage.Get(key) - // TODO: Check the cast instead if tsEntry, ok := item.(*timestampedEntry); ok { return tsEntry, true } @@ -76,24 +83,20 @@ func (c *ExpirationCache) getTimestampedEntry(key string) (*timestampedEntry, bo } // getOrExpire retrieves the object from the timestampedEntry if and only if it hasn't -// already expired. It kicks-off a go routine to delete expired objects from -// the store and sets exists=false. +// already expired. It holds a write lock across deletion. func (c *ExpirationCache) getOrExpire(key string) (interface{}, bool) { + // Prevent all inserts from the time we deem an item as "expired" to when we + // delete it, so an un-expired item doesn't sneak in under the same key, just + // before the Delete. + c.expirationLock.Lock() + defer c.expirationLock.Unlock() timestampedItem, exists := c.getTimestampedEntry(key) if !exists { return nil, false } if c.expirationPolicy.IsExpired(timestampedItem) { glog.V(4).Infof("Entry %v: %+v has expired", key, timestampedItem.obj) - // Since expiration happens lazily on read, don't hold up - // the reader trying to acquire a write lock for the delete. - // The next reader will retry the delete even if this one - // fails; as long as we only return un-expired entries a - // reader doesn't need to wait for the result of the delete. - go func() { - defer runtime.HandleCrash() - c.cacheStorage.Delete(key) - }() + c.cacheStorage.Delete(key) return nil, false } return timestampedItem.obj, true @@ -141,6 +144,8 @@ func (c *ExpirationCache) ListKeys() []string { // Add timestamps an item and inserts it into the cache, overwriting entries // that might exist under the same key. func (c *ExpirationCache) Add(obj interface{}) error { + c.expirationLock.Lock() + defer c.expirationLock.Unlock() key, err := c.keyFunc(obj) if err != nil { return KeyError{obj, err} @@ -157,6 +162,8 @@ func (c *ExpirationCache) Update(obj interface{}) error { // Delete removes an item from the cache. func (c *ExpirationCache) Delete(obj interface{}) error { + c.expirationLock.Lock() + defer c.expirationLock.Unlock() key, err := c.keyFunc(obj) if err != nil { return KeyError{obj, err} @@ -169,6 +176,8 @@ func (c *ExpirationCache) Delete(obj interface{}) error { // before attempting the replace operation. The replace operation will // delete the contents of the ExpirationCache `c`. func (c *ExpirationCache) Replace(list []interface{}, resourceVersion string) error { + c.expirationLock.Lock() + defer c.expirationLock.Unlock() items := map[string]interface{}{} ts := c.clock.Now() for _, item := range list { diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/expiration_cache_fakes.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/expiration_cache_fakes.go index 2e9a25d12..3b9597705 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/expiration_cache_fakes.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/expiration_cache_fakes.go @@ -28,6 +28,7 @@ type fakeThreadSafeMap struct { func (c *fakeThreadSafeMap) Delete(key string) { if c.deletedKeys != nil { + c.ThreadSafeStore.Delete(key) c.deletedKeys <- key } } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/expiration_cache_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/expiration_cache_test.go deleted file mode 100644 index 2e8cc5b57..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/expiration_cache_test.go +++ /dev/null @@ -1,136 +0,0 @@ -/* -Copyright 2014 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 cache - -import ( - "reflect" - "testing" - "time" - - "k8s.io/kubernetes/pkg/util" - "k8s.io/kubernetes/pkg/util/sets" - "k8s.io/kubernetes/pkg/util/wait" -) - -func TestTTLExpirationBasic(t *testing.T) { - testObj := testStoreObject{id: "foo", val: "bar"} - deleteChan := make(chan string) - ttlStore := NewFakeExpirationStore( - testStoreKeyFunc, deleteChan, - &FakeExpirationPolicy{ - NeverExpire: sets.NewString(), - RetrieveKeyFunc: func(obj interface{}) (string, error) { - return obj.(*timestampedEntry).obj.(testStoreObject).id, nil - }, - }, - util.RealClock{}, - ) - err := ttlStore.Add(testObj) - if err != nil { - t.Errorf("Unable to add obj %#v", testObj) - } - item, exists, err := ttlStore.Get(testObj) - if err != nil { - t.Errorf("Failed to get from store, %v", err) - } - if exists || item != nil { - t.Errorf("Got unexpected item %#v", item) - } - key, _ := testStoreKeyFunc(testObj) - select { - case delKey := <-deleteChan: - if delKey != key { - t.Errorf("Unexpected delete for key %s", key) - } - case <-time.After(wait.ForeverTestTimeout): - t.Errorf("Unexpected timeout waiting on delete") - } - close(deleteChan) -} - -func TestTTLList(t *testing.T) { - testObjs := []testStoreObject{ - {id: "foo", val: "bar"}, - {id: "foo1", val: "bar1"}, - {id: "foo2", val: "bar2"}, - } - expireKeys := sets.NewString(testObjs[0].id, testObjs[2].id) - deleteChan := make(chan string) - defer close(deleteChan) - - ttlStore := NewFakeExpirationStore( - testStoreKeyFunc, deleteChan, - &FakeExpirationPolicy{ - NeverExpire: sets.NewString(testObjs[1].id), - RetrieveKeyFunc: func(obj interface{}) (string, error) { - return obj.(*timestampedEntry).obj.(testStoreObject).id, nil - }, - }, - util.RealClock{}, - ) - for _, obj := range testObjs { - err := ttlStore.Add(obj) - if err != nil { - t.Errorf("Unable to add obj %#v", obj) - } - } - listObjs := ttlStore.List() - if len(listObjs) != 1 || !reflect.DeepEqual(listObjs[0], testObjs[1]) { - t.Errorf("List returned unexpected results %#v", listObjs) - } - - // Make sure all our deletes come through in an acceptable rate (1/100ms) - for expireKeys.Len() != 0 { - select { - case delKey := <-deleteChan: - if !expireKeys.Has(delKey) { - t.Errorf("Unexpected delete for key %s", delKey) - } - expireKeys.Delete(delKey) - case <-time.After(wait.ForeverTestTimeout): - t.Errorf("Unexpected timeout waiting on delete") - return - } - } -} - -func TestTTLPolicy(t *testing.T) { - fakeTime := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) - ttl := 30 * time.Second - exactlyOnTTL := fakeTime.Add(-ttl) - expiredTime := fakeTime.Add(-(ttl + 1)) - - policy := TTLPolicy{ttl, util.NewFakeClock(fakeTime)} - fakeTimestampedEntry := ×tampedEntry{obj: struct{}{}, timestamp: exactlyOnTTL} - if policy.IsExpired(fakeTimestampedEntry) { - t.Errorf("TTL cache should not expire entries exactly on ttl") - } - fakeTimestampedEntry.timestamp = fakeTime - if policy.IsExpired(fakeTimestampedEntry) { - t.Errorf("TTL Cache should not expire entries before ttl") - } - fakeTimestampedEntry.timestamp = expiredTime - if !policy.IsExpired(fakeTimestampedEntry) { - t.Errorf("TTL Cache should expire entries older than ttl") - } - for _, ttl = range []time.Duration{0, -1} { - policy.Ttl = ttl - if policy.IsExpired(fakeTimestampedEntry) { - t.Errorf("TTL policy should only expire entries when initialized with a ttl > 0") - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/fifo_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/fifo_test.go deleted file mode 100644 index 974fa6d3b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/fifo_test.go +++ /dev/null @@ -1,235 +0,0 @@ -/* -Copyright 2014 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 cache - -import ( - "reflect" - "testing" - "time" -) - -func testFifoObjectKeyFunc(obj interface{}) (string, error) { - return obj.(testFifoObject).name, nil -} - -type testFifoObject struct { - name string - val interface{} -} - -func mkFifoObj(name string, val interface{}) testFifoObject { - return testFifoObject{name: name, val: val} -} - -func TestFIFO_basic(t *testing.T) { - f := NewFIFO(testFifoObjectKeyFunc) - const amount = 500 - go func() { - for i := 0; i < amount; i++ { - f.Add(mkFifoObj(string([]rune{'a', rune(i)}), i+1)) - } - }() - go func() { - for u := uint64(0); u < amount; u++ { - f.Add(mkFifoObj(string([]rune{'b', rune(u)}), u+1)) - } - }() - - lastInt := int(0) - lastUint := uint64(0) - for i := 0; i < amount*2; i++ { - switch obj := f.Pop().(testFifoObject).val.(type) { - case int: - if obj <= lastInt { - t.Errorf("got %v (int) out of order, last was %v", obj, lastInt) - } - lastInt = obj - case uint64: - if obj <= lastUint { - t.Errorf("got %v (uint) out of order, last was %v", obj, lastUint) - } else { - lastUint = obj - } - default: - t.Fatalf("unexpected type %#v", obj) - } - } -} - -func TestFIFO_addUpdate(t *testing.T) { - f := NewFIFO(testFifoObjectKeyFunc) - f.Add(mkFifoObj("foo", 10)) - f.Update(mkFifoObj("foo", 15)) - - if e, a := []interface{}{mkFifoObj("foo", 15)}, f.List(); !reflect.DeepEqual(e, a) { - t.Errorf("Expected %+v, got %+v", e, a) - } - if e, a := []string{"foo"}, f.ListKeys(); !reflect.DeepEqual(e, a) { - t.Errorf("Expected %+v, got %+v", e, a) - } - - got := make(chan testFifoObject, 2) - go func() { - for { - got <- f.Pop().(testFifoObject) - } - }() - - first := <-got - if e, a := 15, first.val; e != a { - t.Errorf("Didn't get updated value (%v), got %v", e, a) - } - select { - case unexpected := <-got: - t.Errorf("Got second value %v", unexpected.val) - case <-time.After(50 * time.Millisecond): - } - _, exists, _ := f.Get(mkFifoObj("foo", "")) - if exists { - t.Errorf("item did not get removed") - } -} - -func TestFIFO_addReplace(t *testing.T) { - f := NewFIFO(testFifoObjectKeyFunc) - f.Add(mkFifoObj("foo", 10)) - f.Replace([]interface{}{mkFifoObj("foo", 15)}, "15") - got := make(chan testFifoObject, 2) - go func() { - for { - got <- f.Pop().(testFifoObject) - } - }() - - first := <-got - if e, a := 15, first.val; e != a { - t.Errorf("Didn't get updated value (%v), got %v", e, a) - } - select { - case unexpected := <-got: - t.Errorf("Got second value %v", unexpected.val) - case <-time.After(50 * time.Millisecond): - } - _, exists, _ := f.Get(mkFifoObj("foo", "")) - if exists { - t.Errorf("item did not get removed") - } -} - -func TestFIFO_detectLineJumpers(t *testing.T) { - f := NewFIFO(testFifoObjectKeyFunc) - - f.Add(mkFifoObj("foo", 10)) - f.Add(mkFifoObj("bar", 1)) - f.Add(mkFifoObj("foo", 11)) - f.Add(mkFifoObj("foo", 13)) - f.Add(mkFifoObj("zab", 30)) - - if e, a := 13, f.Pop().(testFifoObject).val; a != e { - t.Fatalf("expected %d, got %d", e, a) - } - - f.Add(mkFifoObj("foo", 14)) // ensure foo doesn't jump back in line - - if e, a := 1, f.Pop().(testFifoObject).val; a != e { - t.Fatalf("expected %d, got %d", e, a) - } - - if e, a := 30, f.Pop().(testFifoObject).val; a != e { - t.Fatalf("expected %d, got %d", e, a) - } - - if e, a := 14, f.Pop().(testFifoObject).val; a != e { - t.Fatalf("expected %d, got %d", e, a) - } -} - -func TestFIFO_addIfNotPresent(t *testing.T) { - f := NewFIFO(testFifoObjectKeyFunc) - - f.Add(mkFifoObj("a", 1)) - f.Add(mkFifoObj("b", 2)) - f.AddIfNotPresent(mkFifoObj("b", 3)) - f.AddIfNotPresent(mkFifoObj("c", 4)) - - if e, a := 3, len(f.items); a != e { - t.Fatalf("expected queue length %d, got %d", e, a) - } - - expectedValues := []int{1, 2, 4} - for _, expected := range expectedValues { - if actual := f.Pop().(testFifoObject).val; actual != expected { - t.Fatalf("expected value %d, got %d", expected, actual) - } - } -} - -func TestFIFO_HasSynced(t *testing.T) { - tests := []struct { - actions []func(f *FIFO) - expectedSynced bool - }{ - { - actions: []func(f *FIFO){}, - expectedSynced: false, - }, - { - actions: []func(f *FIFO){ - func(f *FIFO) { f.Add(mkFifoObj("a", 1)) }, - }, - expectedSynced: true, - }, - { - actions: []func(f *FIFO){ - func(f *FIFO) { f.Replace([]interface{}{}, "0") }, - }, - expectedSynced: true, - }, - { - actions: []func(f *FIFO){ - func(f *FIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") }, - }, - expectedSynced: false, - }, - { - actions: []func(f *FIFO){ - func(f *FIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") }, - func(f *FIFO) { f.Pop() }, - }, - expectedSynced: false, - }, - { - actions: []func(f *FIFO){ - func(f *FIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") }, - func(f *FIFO) { f.Pop() }, - func(f *FIFO) { f.Pop() }, - }, - expectedSynced: true, - }, - } - - for i, test := range tests { - f := NewFIFO(testFifoObjectKeyFunc) - - for _, action := range test.actions { - action(f) - } - if e, a := test.expectedSynced, f.HasSynced(); a != e { - t.Errorf("test case %v failed, expected: %v , got %v", i, e, a) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/index_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/index_test.go deleted file mode 100644 index 4b0d5ff4f..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/index_test.go +++ /dev/null @@ -1,135 +0,0 @@ -/* -Copyright 2015 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 cache - -import ( - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api" -) - -func testIndexFunc(obj interface{}) ([]string, error) { - pod := obj.(*api.Pod) - return []string{pod.Labels["foo"]}, nil -} - -func TestGetIndexFuncValues(t *testing.T) { - index := NewIndexer(MetaNamespaceKeyFunc, Indexers{"testmodes": testIndexFunc}) - - pod1 := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "one", Labels: map[string]string{"foo": "bar"}}} - pod2 := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "two", Labels: map[string]string{"foo": "bar"}}} - pod3 := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "tre", Labels: map[string]string{"foo": "biz"}}} - - index.Add(pod1) - index.Add(pod2) - index.Add(pod3) - - keys := index.ListIndexFuncValues("testmodes") - if len(keys) != 2 { - t.Errorf("Expected 2 keys but got %v", len(keys)) - } - - for _, key := range keys { - if key != "bar" && key != "biz" { - t.Errorf("Expected only 'bar' or 'biz' but got %s", key) - } - } -} - -func testUsersIndexFunc(obj interface{}) ([]string, error) { - pod := obj.(*api.Pod) - usersString := pod.Annotations["users"] - - return strings.Split(usersString, ","), nil -} - -func TestMultiIndexKeys(t *testing.T) { - index := NewIndexer(MetaNamespaceKeyFunc, Indexers{"byUser": testUsersIndexFunc}) - - pod1 := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "one", Annotations: map[string]string{"users": "ernie,bert"}}} - pod2 := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "two", Annotations: map[string]string{"users": "bert,oscar"}}} - pod3 := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "tre", Annotations: map[string]string{"users": "ernie,elmo"}}} - - index.Add(pod1) - index.Add(pod2) - index.Add(pod3) - - erniePods, err := index.ByIndex("byUser", "ernie") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if len(erniePods) != 2 { - t.Errorf("Expected 2 pods but got %v", len(erniePods)) - } - - bertPods, err := index.ByIndex("byUser", "bert") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if len(bertPods) != 2 { - t.Errorf("Expected 2 pods but got %v", len(bertPods)) - } - - oscarPods, err := index.ByIndex("byUser", "oscar") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if len(oscarPods) != 1 { - t.Errorf("Expected 1 pods but got %v", len(erniePods)) - } - - ernieAndBertKeys, err := index.Index("byUser", pod1) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if len(ernieAndBertKeys) != 3 { - t.Errorf("Expected 3 pods but got %v", len(ernieAndBertKeys)) - } - - index.Delete(pod3) - erniePods, err = index.ByIndex("byUser", "ernie") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if len(erniePods) != 1 { - t.Errorf("Expected 1 pods but got %v", len(erniePods)) - } - elmoPods, err := index.ByIndex("byUser", "elmo") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if len(elmoPods) != 0 { - t.Errorf("Expected 0 pods but got %v", len(elmoPods)) - } - - obj, err := api.Scheme.DeepCopy(pod2) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - copyOfPod2 := obj.(*api.Pod) - copyOfPod2.Annotations["users"] = "oscar" - index.Update(copyOfPod2) - bertPods, err = index.ByIndex("byUser", "bert") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if len(bertPods) != 1 { - t.Errorf("Expected 1 pods but got %v", len(bertPods)) - } - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/listers_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/listers_test.go deleted file mode 100644 index e95df1f21..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/listers_test.go +++ /dev/null @@ -1,721 +0,0 @@ -/* -Copyright 2015 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 cache - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/labels" - "k8s.io/kubernetes/pkg/util/sets" -) - -func TestStoreToNodeLister(t *testing.T) { - store := NewStore(MetaNamespaceKeyFunc) - ids := sets.NewString("foo", "bar", "baz") - for id := range ids { - store.Add(&api.Node{ObjectMeta: api.ObjectMeta{Name: id}}) - } - sml := StoreToNodeLister{store} - - gotNodes, err := sml.List() - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - got := make([]string, len(gotNodes.Items)) - for ix := range gotNodes.Items { - got[ix] = gotNodes.Items[ix].Name - } - if !ids.HasAll(got...) || len(got) != len(ids) { - t.Errorf("Expected %v, got %v", ids, got) - } -} - -func TestStoreToNodeConditionLister(t *testing.T) { - store := NewStore(MetaNamespaceKeyFunc) - nodes := []*api.Node{ - { - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Status: api.NodeStatus{ - Conditions: []api.NodeCondition{ - { - Type: api.NodeReady, - Status: api.ConditionTrue, - }, - { - Type: api.NodeOutOfDisk, - Status: api.ConditionFalse, - }, - }, - }, - }, - { - ObjectMeta: api.ObjectMeta{Name: "bar"}, - Status: api.NodeStatus{ - Conditions: []api.NodeCondition{ - { - Type: api.NodeOutOfDisk, - Status: api.ConditionTrue, - }, - }, - }, - }, - { - ObjectMeta: api.ObjectMeta{Name: "baz"}, - Status: api.NodeStatus{ - Conditions: []api.NodeCondition{ - { - Type: api.NodeReady, - Status: api.ConditionFalse, - }, - { - Type: api.NodeOutOfDisk, - Status: api.ConditionUnknown, - }, - }, - }, - }, - } - for _, n := range nodes { - store.Add(n) - } - - predicate := func(node api.Node) bool { - for _, cond := range node.Status.Conditions { - if cond.Type == api.NodeOutOfDisk && cond.Status == api.ConditionTrue { - return false - } - } - return true - } - - snl := StoreToNodeLister{store} - sncl := snl.NodeCondition(predicate) - - want := sets.NewString("foo", "baz") - gotNodes, err := sncl.List() - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - got := make([]string, len(gotNodes.Items)) - for ix := range gotNodes.Items { - got[ix] = gotNodes.Items[ix].Name - } - if !want.HasAll(got...) || len(got) != len(want) { - t.Errorf("Expected %v, got %v", want, got) - } -} - -func TestStoreToReplicationControllerLister(t *testing.T) { - store := NewStore(MetaNamespaceKeyFunc) - lister := StoreToReplicationControllerLister{store} - testCases := []struct { - inRCs []*api.ReplicationController - list func() ([]api.ReplicationController, error) - outRCNames sets.String - expectErr bool - }{ - // Basic listing with all labels and no selectors - { - inRCs: []*api.ReplicationController{ - {ObjectMeta: api.ObjectMeta{Name: "basic"}}, - }, - list: func() ([]api.ReplicationController, error) { - return lister.List() - }, - outRCNames: sets.NewString("basic"), - }, - // No pod labels - { - inRCs: []*api.ReplicationController{ - { - ObjectMeta: api.ObjectMeta{Name: "basic", Namespace: "ns"}, - Spec: api.ReplicationControllerSpec{ - Selector: map[string]string{"foo": "baz"}, - }, - }, - }, - list: func() ([]api.ReplicationController, error) { - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "pod1", Namespace: "ns"}, - } - return lister.GetPodControllers(pod) - }, - outRCNames: sets.NewString(), - expectErr: true, - }, - // No RC selectors - { - inRCs: []*api.ReplicationController{ - { - ObjectMeta: api.ObjectMeta{Name: "basic", Namespace: "ns"}, - }, - }, - list: func() ([]api.ReplicationController, error) { - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "pod1", - Namespace: "ns", - Labels: map[string]string{"foo": "bar"}, - }, - } - return lister.GetPodControllers(pod) - }, - outRCNames: sets.NewString(), - expectErr: true, - }, - // Matching labels to selectors and namespace - { - inRCs: []*api.ReplicationController{ - { - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: api.ReplicationControllerSpec{ - Selector: map[string]string{"foo": "bar"}, - }, - }, - { - ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "ns"}, - Spec: api.ReplicationControllerSpec{ - Selector: map[string]string{"foo": "bar"}, - }, - }, - }, - list: func() ([]api.ReplicationController, error) { - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "pod1", - Labels: map[string]string{"foo": "bar"}, - Namespace: "ns", - }, - } - return lister.GetPodControllers(pod) - }, - outRCNames: sets.NewString("bar"), - }, - } - for _, c := range testCases { - for _, r := range c.inRCs { - store.Add(r) - } - - gotControllers, err := c.list() - if err != nil && c.expectErr { - continue - } else if c.expectErr { - t.Error("Expected error, got none") - continue - } else if err != nil { - t.Errorf("Unexpected error %#v", err) - continue - } - gotNames := make([]string, len(gotControllers)) - for ix := range gotControllers { - gotNames[ix] = gotControllers[ix].Name - } - if !c.outRCNames.HasAll(gotNames...) || len(gotNames) != len(c.outRCNames) { - t.Errorf("Unexpected got controllers %+v expected %+v", gotNames, c.outRCNames) - } - } -} - -func TestStoreToReplicaSetLister(t *testing.T) { - store := NewStore(MetaNamespaceKeyFunc) - lister := StoreToReplicaSetLister{store} - testCases := []struct { - inRSs []*extensions.ReplicaSet - list func() ([]extensions.ReplicaSet, error) - outRSNames sets.String - expectErr bool - }{ - // Basic listing with all labels and no selectors - { - inRSs: []*extensions.ReplicaSet{ - {ObjectMeta: api.ObjectMeta{Name: "basic"}}, - }, - list: func() ([]extensions.ReplicaSet, error) { - return lister.List() - }, - outRSNames: sets.NewString("basic"), - }, - // No pod labels - { - inRSs: []*extensions.ReplicaSet{ - { - ObjectMeta: api.ObjectMeta{Name: "basic", Namespace: "ns"}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "baz"}}, - }, - }, - }, - list: func() ([]extensions.ReplicaSet, error) { - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "pod1", Namespace: "ns"}, - } - return lister.GetPodReplicaSets(pod) - }, - outRSNames: sets.NewString(), - expectErr: true, - }, - // No ReplicaSet selectors - { - inRSs: []*extensions.ReplicaSet{ - { - ObjectMeta: api.ObjectMeta{Name: "basic", Namespace: "ns"}, - }, - }, - list: func() ([]extensions.ReplicaSet, error) { - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "pod1", - Namespace: "ns", - Labels: map[string]string{"foo": "bar"}, - }, - } - return lister.GetPodReplicaSets(pod) - }, - outRSNames: sets.NewString(), - expectErr: true, - }, - // Matching labels to selectors and namespace - { - inRSs: []*extensions.ReplicaSet{ - { - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, - }, - }, - { - ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "ns"}, - Spec: extensions.ReplicaSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, - }, - }, - }, - list: func() ([]extensions.ReplicaSet, error) { - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "pod1", - Labels: map[string]string{"foo": "bar"}, - Namespace: "ns", - }, - } - return lister.GetPodReplicaSets(pod) - }, - outRSNames: sets.NewString("bar"), - }, - } - for _, c := range testCases { - for _, r := range c.inRSs { - store.Add(r) - } - - gotRSs, err := c.list() - if err != nil && c.expectErr { - continue - } else if c.expectErr { - t.Error("Expected error, got none") - continue - } else if err != nil { - t.Errorf("Unexpected error %#v", err) - continue - } - gotNames := make([]string, len(gotRSs)) - for ix := range gotRSs { - gotNames[ix] = gotRSs[ix].Name - } - if !c.outRSNames.HasAll(gotNames...) || len(gotNames) != len(c.outRSNames) { - t.Errorf("Unexpected got ReplicaSets %+v expected %+v", gotNames, c.outRSNames) - } - } -} - -func TestStoreToDaemonSetLister(t *testing.T) { - store := NewStore(MetaNamespaceKeyFunc) - lister := StoreToDaemonSetLister{store} - testCases := []struct { - inDSs []*extensions.DaemonSet - list func() ([]extensions.DaemonSet, error) - outDaemonSetNames sets.String - expectErr bool - }{ - // Basic listing - { - inDSs: []*extensions.DaemonSet{ - {ObjectMeta: api.ObjectMeta{Name: "basic"}}, - }, - list: func() ([]extensions.DaemonSet, error) { - list, err := lister.List() - return list.Items, err - }, - outDaemonSetNames: sets.NewString("basic"), - }, - // Listing multiple daemon sets - { - inDSs: []*extensions.DaemonSet{ - {ObjectMeta: api.ObjectMeta{Name: "basic"}}, - {ObjectMeta: api.ObjectMeta{Name: "complex"}}, - {ObjectMeta: api.ObjectMeta{Name: "complex2"}}, - }, - list: func() ([]extensions.DaemonSet, error) { - list, err := lister.List() - return list.Items, err - }, - outDaemonSetNames: sets.NewString("basic", "complex", "complex2"), - }, - // No pod labels - { - inDSs: []*extensions.DaemonSet{ - { - ObjectMeta: api.ObjectMeta{Name: "basic", Namespace: "ns"}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "baz"}}, - }, - }, - }, - list: func() ([]extensions.DaemonSet, error) { - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "pod1", Namespace: "ns"}, - } - return lister.GetPodDaemonSets(pod) - }, - outDaemonSetNames: sets.NewString(), - expectErr: true, - }, - // No DS selectors - { - inDSs: []*extensions.DaemonSet{ - { - ObjectMeta: api.ObjectMeta{Name: "basic", Namespace: "ns"}, - }, - }, - list: func() ([]extensions.DaemonSet, error) { - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "pod1", - Namespace: "ns", - Labels: map[string]string{"foo": "bar"}, - }, - } - return lister.GetPodDaemonSets(pod) - }, - outDaemonSetNames: sets.NewString(), - expectErr: true, - }, - // Matching labels to selectors and namespace - { - inDSs: []*extensions.DaemonSet{ - { - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, - }, - }, - { - ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "ns"}, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, - }, - }, - }, - list: func() ([]extensions.DaemonSet, error) { - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "pod1", - Labels: map[string]string{"foo": "bar"}, - Namespace: "ns", - }, - } - return lister.GetPodDaemonSets(pod) - }, - outDaemonSetNames: sets.NewString("bar"), - }, - } - for _, c := range testCases { - for _, r := range c.inDSs { - store.Add(r) - } - - daemonSets, err := c.list() - if err != nil && c.expectErr { - continue - } else if c.expectErr { - t.Error("Expected error, got none") - continue - } else if err != nil { - t.Errorf("Unexpected error %#v", err) - continue - } - daemonSetNames := make([]string, len(daemonSets)) - for ix := range daemonSets { - daemonSetNames[ix] = daemonSets[ix].Name - } - if !c.outDaemonSetNames.HasAll(daemonSetNames...) || len(daemonSetNames) != len(c.outDaemonSetNames) { - t.Errorf("Unexpected got controllers %+v expected %+v", daemonSetNames, c.outDaemonSetNames) - } - } -} - -func TestStoreToJobLister(t *testing.T) { - store := NewStore(MetaNamespaceKeyFunc) - lister := StoreToJobLister{store} - testCases := []struct { - inJobs []*extensions.Job - list func() ([]extensions.Job, error) - outJobNames sets.String - expectErr bool - msg string - }{ - // Basic listing - { - inJobs: []*extensions.Job{ - {ObjectMeta: api.ObjectMeta{Name: "basic"}}, - }, - list: func() ([]extensions.Job, error) { - list, err := lister.List() - return list.Items, err - }, - outJobNames: sets.NewString("basic"), - msg: "basic listing failed", - }, - // Listing multiple jobs - { - inJobs: []*extensions.Job{ - {ObjectMeta: api.ObjectMeta{Name: "basic"}}, - {ObjectMeta: api.ObjectMeta{Name: "complex"}}, - {ObjectMeta: api.ObjectMeta{Name: "complex2"}}, - }, - list: func() ([]extensions.Job, error) { - list, err := lister.List() - return list.Items, err - }, - outJobNames: sets.NewString("basic", "complex", "complex2"), - msg: "listing multiple jobs failed", - }, - // No pod labels - { - inJobs: []*extensions.Job{ - { - ObjectMeta: api.ObjectMeta{Name: "basic", Namespace: "ns"}, - Spec: extensions.JobSpec{ - Selector: &unversioned.LabelSelector{ - MatchLabels: map[string]string{"foo": "baz"}, - }, - }, - }, - }, - list: func() ([]extensions.Job, error) { - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "pod", Namespace: "ns"}, - } - return lister.GetPodJobs(pod) - }, - outJobNames: sets.NewString(), - expectErr: true, - msg: "listing jobs failed when pod has no labels: expected error, got none", - }, - // No Job selectors - { - inJobs: []*extensions.Job{ - { - ObjectMeta: api.ObjectMeta{Name: "basic", Namespace: "ns"}, - }, - }, - list: func() ([]extensions.Job, error) { - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "pod", - Namespace: "ns", - Labels: map[string]string{"foo": "bar"}, - }, - } - return lister.GetPodJobs(pod) - }, - outJobNames: sets.NewString(), - expectErr: true, - msg: "listing jobs failed when job has no selector: expected error, got none", - }, - // Matching labels to selectors and namespace - { - inJobs: []*extensions.Job{ - { - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: extensions.JobSpec{ - Selector: &unversioned.LabelSelector{ - MatchLabels: map[string]string{"foo": "bar"}, - }, - }, - }, - { - ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "ns"}, - Spec: extensions.JobSpec{ - Selector: &unversioned.LabelSelector{ - MatchLabels: map[string]string{"foo": "bar"}, - }, - }, - }, - }, - list: func() ([]extensions.Job, error) { - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "pod", - Labels: map[string]string{"foo": "bar"}, - Namespace: "ns", - }, - } - return lister.GetPodJobs(pod) - }, - outJobNames: sets.NewString("bar"), - msg: "listing jobs with namespace and selector failed", - }, - // Matching labels to selectors and namespace, error case - { - inJobs: []*extensions.Job{ - { - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "foo"}, - Spec: extensions.JobSpec{ - Selector: &unversioned.LabelSelector{ - MatchLabels: map[string]string{"foo": "bar"}, - }, - }, - }, - { - ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "bar"}, - Spec: extensions.JobSpec{ - Selector: &unversioned.LabelSelector{ - MatchLabels: map[string]string{"foo": "bar"}, - }, - }, - }, - }, - list: func() ([]extensions.Job, error) { - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "pod", - Labels: map[string]string{"foo": "bar"}, - Namespace: "baz", - }, - } - return lister.GetPodJobs(pod) - }, - expectErr: true, - msg: "listing jobs with namespace and selector failed: expected error, got none", - }, - } - for _, c := range testCases { - for _, r := range c.inJobs { - store.Add(r) - } - - Jobs, err := c.list() - if err != nil && c.expectErr { - continue - } else if c.expectErr { - t.Errorf("%v", c.msg) - continue - } else if err != nil { - t.Errorf("Unexpected error %#v", err) - continue - } - JobNames := make([]string, len(Jobs)) - for ix := range Jobs { - JobNames[ix] = Jobs[ix].Name - } - if !c.outJobNames.HasAll(JobNames...) || len(JobNames) != len(c.outJobNames) { - t.Errorf("%v : expected %v, got %v", c.msg, JobNames, c.outJobNames) - } - } -} - -func TestStoreToPodLister(t *testing.T) { - store := NewStore(MetaNamespaceKeyFunc) - ids := []string{"foo", "bar", "baz"} - for _, id := range ids { - store.Add(&api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: id, - Labels: map[string]string{"name": id}, - }, - }) - } - spl := StoreToPodLister{store} - - for _, id := range ids { - got, err := spl.List(labels.Set{"name": id}.AsSelector()) - if err != nil { - t.Errorf("Unexpected error: %v", err) - continue - } - if e, a := 1, len(got); e != a { - t.Errorf("Expected %v, got %v", e, a) - continue - } - if e, a := id, got[0].Name; e != a { - t.Errorf("Expected %v, got %v", e, a) - continue - } - - exists, err := spl.Exists(&api.Pod{ObjectMeta: api.ObjectMeta{Name: id}}) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !exists { - t.Errorf("exists returned false for %v", id) - } - } - - exists, err := spl.Exists(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "qux"}}) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if exists { - t.Error("Unexpected pod exists") - } -} - -func TestStoreToServiceLister(t *testing.T) { - store := NewStore(MetaNamespaceKeyFunc) - store.Add(&api.Service{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: api.ServiceSpec{ - Selector: map[string]string{}, - }, - }) - store.Add(&api.Service{ObjectMeta: api.ObjectMeta{Name: "bar"}}) - ssl := StoreToServiceLister{store} - - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foopod", - Labels: map[string]string{"role": "foo"}, - }, - } - - services, err := ssl.GetPodServices(pod) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - if len(services) != 1 { - t.Fatalf("Expected 1 service, got %v", len(services)) - } - if e, a := "foo", services[0].Name; e != a { - t.Errorf("Expected service %q, got %q", e, a) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/listwatch.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/listwatch.go index 18528d236..06c2f611b 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/listwatch.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/listwatch.go @@ -20,7 +20,7 @@ import ( "time" "k8s.io/kubernetes/pkg/api" - client "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/watch" @@ -42,7 +42,7 @@ type ListWatch struct { // Getter interface knows how to access Get method from RESTClient. type Getter interface { - Get() *client.Request + Get() *restclient.Request } // NewListWatchFromClient creates a new ListWatch from the specified client, resource, namespace and field selector. diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/listwatch_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/listwatch_test.go deleted file mode 100644 index a0a72f5d8..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/listwatch_test.go +++ /dev/null @@ -1,173 +0,0 @@ -/* -Copyright 2015 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 cache - -import ( - "net/http/httptest" - "net/url" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/fields" - utiltesting "k8s.io/kubernetes/pkg/util/testing" -) - -func parseSelectorOrDie(s string) fields.Selector { - selector, err := fields.ParseSelector(s) - if err != nil { - panic(err) - } - return selector -} - -// buildQueryValues is a convenience function for knowing if a namespace should be in a query param or not -func buildQueryValues(query url.Values) url.Values { - v := url.Values{} - if query != nil { - for key, values := range query { - for _, value := range values { - v.Add(key, value) - } - } - } - return v -} - -func buildLocation(resourcePath string, query url.Values) string { - return resourcePath + "?" + query.Encode() -} - -func TestListWatchesCanList(t *testing.T) { - fieldSelectorQueryParamName := unversioned.FieldSelectorQueryParam(testapi.Default.GroupVersion().String()) - table := []struct { - location string - resource string - namespace string - fieldSelector fields.Selector - }{ - // Node - { - location: testapi.Default.ResourcePath("nodes", api.NamespaceAll, ""), - resource: "nodes", - namespace: api.NamespaceAll, - fieldSelector: parseSelectorOrDie(""), - }, - // pod with "assigned" field selector. - { - location: buildLocation( - testapi.Default.ResourcePath("pods", api.NamespaceAll, ""), - buildQueryValues(url.Values{fieldSelectorQueryParamName: []string{"spec.host="}})), - resource: "pods", - namespace: api.NamespaceAll, - fieldSelector: fields.Set{"spec.host": ""}.AsSelector(), - }, - // pod in namespace "foo" - { - location: buildLocation( - testapi.Default.ResourcePath("pods", "foo", ""), - buildQueryValues(url.Values{fieldSelectorQueryParamName: []string{"spec.host="}})), - resource: "pods", - namespace: "foo", - fieldSelector: fields.Set{"spec.host": ""}.AsSelector(), - }, - } - for _, item := range table { - handler := utiltesting.FakeHandler{ - StatusCode: 500, - ResponseBody: "", - T: t, - } - server := httptest.NewServer(&handler) - // TODO: Uncomment when fix #19254 - // defer server.Close() - client := client.NewOrDie(&client.Config{Host: server.URL, ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}) - lw := NewListWatchFromClient(client, item.resource, item.namespace, item.fieldSelector) - // This test merely tests that the correct request is made. - lw.List(api.ListOptions{}) - handler.ValidateRequest(t, item.location, "GET", nil) - } -} - -func TestListWatchesCanWatch(t *testing.T) { - fieldSelectorQueryParamName := unversioned.FieldSelectorQueryParam(testapi.Default.GroupVersion().String()) - table := []struct { - rv string - location string - resource string - namespace string - fieldSelector fields.Selector - }{ - // Node - { - location: buildLocation( - testapi.Default.ResourcePathWithPrefix("watch", "nodes", api.NamespaceAll, ""), - buildQueryValues(url.Values{})), - rv: "", - resource: "nodes", - namespace: api.NamespaceAll, - fieldSelector: parseSelectorOrDie(""), - }, - { - location: buildLocation( - testapi.Default.ResourcePathWithPrefix("watch", "nodes", api.NamespaceAll, ""), - buildQueryValues(url.Values{"resourceVersion": []string{"42"}})), - rv: "42", - resource: "nodes", - namespace: api.NamespaceAll, - fieldSelector: parseSelectorOrDie(""), - }, - // pod with "assigned" field selector. - { - location: buildLocation( - testapi.Default.ResourcePathWithPrefix("watch", "pods", api.NamespaceAll, ""), - buildQueryValues(url.Values{fieldSelectorQueryParamName: []string{"spec.host="}, "resourceVersion": []string{"0"}})), - rv: "0", - resource: "pods", - namespace: api.NamespaceAll, - fieldSelector: fields.Set{"spec.host": ""}.AsSelector(), - }, - // pod with namespace foo and assigned field selector - { - location: buildLocation( - testapi.Default.ResourcePathWithPrefix("watch", "pods", "foo", ""), - buildQueryValues(url.Values{fieldSelectorQueryParamName: []string{"spec.host="}, "resourceVersion": []string{"0"}})), - rv: "0", - resource: "pods", - namespace: "foo", - fieldSelector: fields.Set{"spec.host": ""}.AsSelector(), - }, - } - - for _, item := range table { - handler := utiltesting.FakeHandler{ - StatusCode: 500, - ResponseBody: "", - T: t, - } - server := httptest.NewServer(&handler) - // TODO: Uncomment when fix #19254 - // defer server.Close() - client := client.NewOrDie(&client.Config{Host: server.URL, ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}) - lw := NewListWatchFromClient(client, item.resource, item.namespace, item.fieldSelector) - // This test merely tests that the correct request is made. - lw.Watch(api.ListOptions{ResourceVersion: item.rv}) - handler.ValidateRequest(t, item.location, "GET", nil) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/reflector_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/reflector_test.go deleted file mode 100644 index 223769ade..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/reflector_test.go +++ /dev/null @@ -1,404 +0,0 @@ -/* -Copyright 2014 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 cache - -import ( - "fmt" - "math/rand" - "strconv" - "testing" - "time" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/wait" - "k8s.io/kubernetes/pkg/watch" -) - -type testLW struct { - ListFunc func() (runtime.Object, error) - WatchFunc func(resourceVersion string) (watch.Interface, error) -} - -func (t *testLW) List(options api.ListOptions) (runtime.Object, error) { - return t.ListFunc() -} -func (t *testLW) Watch(options api.ListOptions) (watch.Interface, error) { - return t.WatchFunc(options.ResourceVersion) -} - -func TestCloseWatchChannelOnError(t *testing.T) { - r := NewReflector(&testLW{}, &api.Pod{}, NewStore(MetaNamespaceKeyFunc), 0) - pod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "bar"}} - fw := watch.NewFake() - r.listerWatcher = &testLW{ - WatchFunc: func(rv string) (watch.Interface, error) { - return fw, nil - }, - ListFunc: func() (runtime.Object, error) { - return &api.PodList{ListMeta: unversioned.ListMeta{ResourceVersion: "1"}}, nil - }, - } - go r.ListAndWatch(wait.NeverStop) - fw.Error(pod) - select { - case _, ok := <-fw.ResultChan(): - if ok { - t.Errorf("Watch channel left open after cancellation") - } - case <-time.After(wait.ForeverTestTimeout): - t.Errorf("the cancellation is at least %s late", wait.ForeverTestTimeout.String()) - break - } -} - -func TestRunUntil(t *testing.T) { - stopCh := make(chan struct{}) - store := NewStore(MetaNamespaceKeyFunc) - r := NewReflector(&testLW{}, &api.Pod{}, store, 0) - fw := watch.NewFake() - r.listerWatcher = &testLW{ - WatchFunc: func(rv string) (watch.Interface, error) { - return fw, nil - }, - ListFunc: func() (runtime.Object, error) { - return &api.PodList{ListMeta: unversioned.ListMeta{ResourceVersion: "1"}}, nil - }, - } - r.RunUntil(stopCh) - // Synchronously add a dummy pod into the watch channel so we - // know the RunUntil go routine is in the watch handler. - fw.Add(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "bar"}}) - stopCh <- struct{}{} - select { - case _, ok := <-fw.ResultChan(): - if ok { - t.Errorf("Watch channel left open after stopping the watch") - } - case <-time.After(wait.ForeverTestTimeout): - t.Errorf("the cancellation is at least %s late", wait.ForeverTestTimeout.String()) - break - } -} - -func TestReflectorResyncChan(t *testing.T) { - s := NewStore(MetaNamespaceKeyFunc) - g := NewReflector(&testLW{}, &api.Pod{}, s, time.Millisecond) - a, _ := g.resyncChan() - b := time.After(wait.ForeverTestTimeout) - select { - case <-a: - t.Logf("got timeout as expected") - case <-b: - t.Errorf("resyncChan() is at least 99 milliseconds late??") - } -} - -func BenchmarkReflectorResyncChanMany(b *testing.B) { - s := NewStore(MetaNamespaceKeyFunc) - g := NewReflector(&testLW{}, &api.Pod{}, s, 25*time.Millisecond) - // The improvement to this (calling the timer's Stop() method) makes - // this benchmark about 40% faster. - for i := 0; i < b.N; i++ { - g.resyncPeriod = time.Duration(rand.Float64() * float64(time.Millisecond) * 25) - _, stop := g.resyncChan() - stop() - } -} - -func TestReflectorWatchHandlerError(t *testing.T) { - s := NewStore(MetaNamespaceKeyFunc) - g := NewReflector(&testLW{}, &api.Pod{}, s, 0) - fw := watch.NewFake() - go func() { - fw.Stop() - }() - var resumeRV string - err := g.watchHandler(fw, &resumeRV, neverExitWatch, wait.NeverStop) - if err == nil { - t.Errorf("unexpected non-error") - } -} - -func TestReflectorWatchHandler(t *testing.T) { - s := NewStore(MetaNamespaceKeyFunc) - g := NewReflector(&testLW{}, &api.Pod{}, s, 0) - fw := watch.NewFake() - s.Add(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}) - s.Add(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "bar"}}) - go func() { - fw.Add(&api.Service{ObjectMeta: api.ObjectMeta{Name: "rejected"}}) - fw.Delete(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}) - fw.Modify(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "55"}}) - fw.Add(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "32"}}) - fw.Stop() - }() - var resumeRV string - err := g.watchHandler(fw, &resumeRV, neverExitWatch, wait.NeverStop) - if err != nil { - t.Errorf("unexpected error %v", err) - } - - mkPod := func(id string, rv string) *api.Pod { - return &api.Pod{ObjectMeta: api.ObjectMeta{Name: id, ResourceVersion: rv}} - } - - table := []struct { - Pod *api.Pod - exists bool - }{ - {mkPod("foo", ""), false}, - {mkPod("rejected", ""), false}, - {mkPod("bar", "55"), true}, - {mkPod("baz", "32"), true}, - } - for _, item := range table { - obj, exists, _ := s.Get(item.Pod) - if e, a := item.exists, exists; e != a { - t.Errorf("%v: expected %v, got %v", item.Pod, e, a) - } - if !exists { - continue - } - if e, a := item.Pod.ResourceVersion, obj.(*api.Pod).ResourceVersion; e != a { - t.Errorf("%v: expected %v, got %v", item.Pod, e, a) - } - } - - // RV should send the last version we see. - if e, a := "32", resumeRV; e != a { - t.Errorf("expected %v, got %v", e, a) - } - - // last sync resource version should be the last version synced with store - if e, a := "32", g.LastSyncResourceVersion(); e != a { - t.Errorf("expected %v, got %v", e, a) - } -} - -func TestReflectorWatchHandlerTimeout(t *testing.T) { - s := NewStore(MetaNamespaceKeyFunc) - g := NewReflector(&testLW{}, &api.Pod{}, s, 0) - fw := watch.NewFake() - var resumeRV string - exit := make(chan time.Time, 1) - exit <- time.Now() - err := g.watchHandler(fw, &resumeRV, exit, wait.NeverStop) - if err != errorResyncRequested { - t.Errorf("expected timeout error, but got %q", err) - } -} - -func TestReflectorStopWatch(t *testing.T) { - s := NewStore(MetaNamespaceKeyFunc) - g := NewReflector(&testLW{}, &api.Pod{}, s, 0) - fw := watch.NewFake() - var resumeRV string - stopWatch := make(chan struct{}, 1) - stopWatch <- struct{}{} - err := g.watchHandler(fw, &resumeRV, neverExitWatch, stopWatch) - if err != errorStopRequested { - t.Errorf("expected stop error, got %q", err) - } -} - -func TestReflectorListAndWatch(t *testing.T) { - createdFakes := make(chan *watch.FakeWatcher) - - // The ListFunc says that it's at revision 1. Therefore, we expect our WatchFunc - // to get called at the beginning of the watch with 1, and again with 3 when we - // inject an error. - expectedRVs := []string{"1", "3"} - lw := &testLW{ - WatchFunc: func(rv string) (watch.Interface, error) { - fw := watch.NewFake() - if e, a := expectedRVs[0], rv; e != a { - t.Errorf("Expected rv %v, but got %v", e, a) - } - expectedRVs = expectedRVs[1:] - // channel is not buffered because the for loop below needs to block. But - // we don't want to block here, so report the new fake via a go routine. - go func() { createdFakes <- fw }() - return fw, nil - }, - ListFunc: func() (runtime.Object, error) { - return &api.PodList{ListMeta: unversioned.ListMeta{ResourceVersion: "1"}}, nil - }, - } - s := NewFIFO(MetaNamespaceKeyFunc) - r := NewReflector(lw, &api.Pod{}, s, 0) - go r.ListAndWatch(wait.NeverStop) - - ids := []string{"foo", "bar", "baz", "qux", "zoo"} - var fw *watch.FakeWatcher - for i, id := range ids { - if fw == nil { - fw = <-createdFakes - } - sendingRV := strconv.FormatUint(uint64(i+2), 10) - fw.Add(&api.Pod{ObjectMeta: api.ObjectMeta{Name: id, ResourceVersion: sendingRV}}) - if sendingRV == "3" { - // Inject a failure. - fw.Stop() - fw = nil - } - } - - // Verify we received the right ids with the right resource versions. - for i, id := range ids { - pod := s.Pop().(*api.Pod) - if e, a := id, pod.Name; e != a { - t.Errorf("%v: Expected %v, got %v", i, e, a) - } - if e, a := strconv.FormatUint(uint64(i+2), 10), pod.ResourceVersion; e != a { - t.Errorf("%v: Expected %v, got %v", i, e, a) - } - } - - if len(expectedRVs) != 0 { - t.Error("called watchStarter an unexpected number of times") - } -} - -func TestReflectorListAndWatchWithErrors(t *testing.T) { - mkPod := func(id string, rv string) *api.Pod { - return &api.Pod{ObjectMeta: api.ObjectMeta{Name: id, ResourceVersion: rv}} - } - mkList := func(rv string, pods ...*api.Pod) *api.PodList { - list := &api.PodList{ListMeta: unversioned.ListMeta{ResourceVersion: rv}} - for _, pod := range pods { - list.Items = append(list.Items, *pod) - } - return list - } - table := []struct { - list *api.PodList - listErr error - events []watch.Event - watchErr error - }{ - { - list: mkList("1"), - events: []watch.Event{ - {watch.Added, mkPod("foo", "2")}, - {watch.Added, mkPod("bar", "3")}, - }, - }, { - list: mkList("3", mkPod("foo", "2"), mkPod("bar", "3")), - events: []watch.Event{ - {watch.Deleted, mkPod("foo", "4")}, - {watch.Added, mkPod("qux", "5")}, - }, - }, { - listErr: fmt.Errorf("a list error"), - }, { - list: mkList("5", mkPod("bar", "3"), mkPod("qux", "5")), - watchErr: fmt.Errorf("a watch error"), - }, { - list: mkList("5", mkPod("bar", "3"), mkPod("qux", "5")), - events: []watch.Event{ - {watch.Added, mkPod("baz", "6")}, - }, - }, { - list: mkList("6", mkPod("bar", "3"), mkPod("qux", "5"), mkPod("baz", "6")), - }, - } - - s := NewFIFO(MetaNamespaceKeyFunc) - for line, item := range table { - if item.list != nil { - // Test that the list is what currently exists in the store. - current := s.List() - checkMap := map[string]string{} - for _, item := range current { - pod := item.(*api.Pod) - checkMap[pod.Name] = pod.ResourceVersion - } - for _, pod := range item.list.Items { - if e, a := pod.ResourceVersion, checkMap[pod.Name]; e != a { - t.Errorf("%v: expected %v, got %v for pod %v", line, e, a, pod.Name) - } - } - if e, a := len(item.list.Items), len(checkMap); e != a { - t.Errorf("%v: expected %v, got %v", line, e, a) - } - } - watchRet, watchErr := item.events, item.watchErr - lw := &testLW{ - WatchFunc: func(rv string) (watch.Interface, error) { - if watchErr != nil { - return nil, watchErr - } - watchErr = fmt.Errorf("second watch") - fw := watch.NewFake() - go func() { - for _, e := range watchRet { - fw.Action(e.Type, e.Object) - } - fw.Stop() - }() - return fw, nil - }, - ListFunc: func() (runtime.Object, error) { - return item.list, item.listErr - }, - } - r := NewReflector(lw, &api.Pod{}, s, 0) - r.ListAndWatch(wait.NeverStop) - } -} - -func TestReflectorResync(t *testing.T) { - s := NewStore(MetaNamespaceKeyFunc) - - currentTime := time.Time{} - iteration := 0 - - lw := &testLW{ - WatchFunc: func(rv string) (watch.Interface, error) { - if iteration == 0 { - // Move time, but do not force resync. - currentTime = currentTime.Add(30 * time.Second) - } else if iteration == 1 { - // Move time to force resync. - currentTime = currentTime.Add(28 * time.Second) - } else if iteration >= 2 { - t.Fatalf("should have forced resync earlier") - } - iteration++ - fw := watch.NewFake() - // Send something to the watcher to avoid "watch too short" errors. - go func() { - fw.Add(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: strconv.Itoa(iteration)}}) - fw.Stop() - }() - return fw, nil - }, - ListFunc: func() (runtime.Object, error) { - return &api.PodList{ListMeta: unversioned.ListMeta{ResourceVersion: "0"}}, nil - }, - } - resyncPeriod := time.Minute - r := NewReflector(lw, &api.Pod{}, s, resyncPeriod) - r.now = func() time.Time { return currentTime } - - r.ListAndWatch(wait.NeverStop) - if iteration != 2 { - t.Errorf("exactly 2 iterations were expected, got: %v", iteration) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/store_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/store_test.go deleted file mode 100644 index 07275f493..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/store_test.go +++ /dev/null @@ -1,156 +0,0 @@ -/* -Copyright 2014 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 cache - -import ( - "testing" - - "k8s.io/kubernetes/pkg/util/sets" -) - -// Test public interface -func doTestStore(t *testing.T, store Store) { - mkObj := func(id string, val string) testStoreObject { - return testStoreObject{id: id, val: val} - } - - store.Add(mkObj("foo", "bar")) - if item, ok, _ := store.Get(mkObj("foo", "")); !ok { - t.Errorf("didn't find inserted item") - } else { - if e, a := "bar", item.(testStoreObject).val; e != a { - t.Errorf("expected %v, got %v", e, a) - } - } - store.Update(mkObj("foo", "baz")) - if item, ok, _ := store.Get(mkObj("foo", "")); !ok { - t.Errorf("didn't find inserted item") - } else { - if e, a := "baz", item.(testStoreObject).val; e != a { - t.Errorf("expected %v, got %v", e, a) - } - } - store.Delete(mkObj("foo", "")) - if _, ok, _ := store.Get(mkObj("foo", "")); ok { - t.Errorf("found deleted item??") - } - - // Test List. - store.Add(mkObj("a", "b")) - store.Add(mkObj("c", "d")) - store.Add(mkObj("e", "e")) - { - found := sets.String{} - for _, item := range store.List() { - found.Insert(item.(testStoreObject).val) - } - if !found.HasAll("b", "d", "e") { - t.Errorf("missing items, found: %v", found) - } - if len(found) != 3 { - t.Errorf("extra items") - } - } - - // Test Replace. - store.Replace([]interface{}{ - mkObj("foo", "foo"), - mkObj("bar", "bar"), - }, "0") - - { - found := sets.String{} - for _, item := range store.List() { - found.Insert(item.(testStoreObject).val) - } - if !found.HasAll("foo", "bar") { - t.Errorf("missing items") - } - if len(found) != 2 { - t.Errorf("extra items") - } - } -} - -// Test public interface -func doTestIndex(t *testing.T, indexer Indexer) { - mkObj := func(id string, val string) testStoreObject { - return testStoreObject{id: id, val: val} - } - - // Test Index - expected := map[string]sets.String{} - expected["b"] = sets.NewString("a", "c") - expected["f"] = sets.NewString("e") - expected["h"] = sets.NewString("g") - indexer.Add(mkObj("a", "b")) - indexer.Add(mkObj("c", "b")) - indexer.Add(mkObj("e", "f")) - indexer.Add(mkObj("g", "h")) - { - for k, v := range expected { - found := sets.String{} - indexResults, err := indexer.Index("by_val", mkObj("", k)) - if err != nil { - t.Errorf("Unexpected error %v", err) - } - for _, item := range indexResults { - found.Insert(item.(testStoreObject).id) - } - items := v.List() - if !found.HasAll(items...) { - t.Errorf("missing items, index %s, expected %v but found %v", k, items, found.List()) - } - } - } -} - -func testStoreKeyFunc(obj interface{}) (string, error) { - return obj.(testStoreObject).id, nil -} - -func testStoreIndexFunc(obj interface{}) ([]string, error) { - return []string{obj.(testStoreObject).val}, nil -} - -func testStoreIndexers() Indexers { - indexers := Indexers{} - indexers["by_val"] = testStoreIndexFunc - return indexers -} - -type testStoreObject struct { - id string - val string -} - -func TestCache(t *testing.T) { - doTestStore(t, NewStore(testStoreKeyFunc)) -} - -func TestFIFOCache(t *testing.T) { - doTestStore(t, NewFIFO(testStoreKeyFunc)) -} - -func TestUndeltaStore(t *testing.T) { - nop := func([]interface{}) {} - doTestStore(t, NewUndeltaStore(nop, testStoreKeyFunc)) -} - -func TestIndex(t *testing.T) { - doTestIndex(t, NewIndexer(testStoreKeyFunc, testStoreIndexers())) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/undelta_store_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/undelta_store_test.go deleted file mode 100644 index c14b7a800..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/undelta_store_test.go +++ /dev/null @@ -1,131 +0,0 @@ -/* -Copyright 2015 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 cache - -import ( - "reflect" - "testing" -) - -// store_test.go checks that UndeltaStore conforms to the Store interface -// behavior. This test just tests that it calls the push func in addition. - -type testUndeltaObject struct { - name string - val interface{} -} - -func testUndeltaKeyFunc(obj interface{}) (string, error) { - return obj.(testUndeltaObject).name, nil -} - -/* -var ( - o1 interface{} = t{1} - o2 interface{} = t{2} - l1 []interface{} = []interface{}{t{1}} -) -*/ - -func TestUpdateCallsPush(t *testing.T) { - mkObj := func(name string, val interface{}) testUndeltaObject { - return testUndeltaObject{name: name, val: val} - } - - var got []interface{} - var callcount int = 0 - push := func(m []interface{}) { - callcount++ - got = m - } - - u := NewUndeltaStore(push, testUndeltaKeyFunc) - - u.Add(mkObj("a", 2)) - u.Update(mkObj("a", 1)) - if callcount != 2 { - t.Errorf("Expected 2 calls, got %d", callcount) - } - - l := []interface{}{mkObj("a", 1)} - if !reflect.DeepEqual(l, got) { - t.Errorf("Expected %#v, Got %#v", l, got) - } -} - -func TestDeleteCallsPush(t *testing.T) { - mkObj := func(name string, val interface{}) testUndeltaObject { - return testUndeltaObject{name: name, val: val} - } - - var got []interface{} - var callcount int = 0 - push := func(m []interface{}) { - callcount++ - got = m - } - - u := NewUndeltaStore(push, testUndeltaKeyFunc) - - u.Add(mkObj("a", 2)) - u.Delete(mkObj("a", "")) - if callcount != 2 { - t.Errorf("Expected 2 calls, got %d", callcount) - } - expected := []interface{}{} - if !reflect.DeepEqual(expected, got) { - t.Errorf("Expected %#v, Got %#v", expected, got) - } -} - -func TestReadsDoNotCallPush(t *testing.T) { - push := func(m []interface{}) { - t.Errorf("Unexpected call to push!") - } - - u := NewUndeltaStore(push, testUndeltaKeyFunc) - - // These should not call push. - _ = u.List() - _, _, _ = u.Get(testUndeltaObject{"a", ""}) -} - -func TestReplaceCallsPush(t *testing.T) { - mkObj := func(name string, val interface{}) testUndeltaObject { - return testUndeltaObject{name: name, val: val} - } - - var got []interface{} - var callcount int = 0 - push := func(m []interface{}) { - callcount++ - got = m - } - - u := NewUndeltaStore(push, testUndeltaKeyFunc) - - m := []interface{}{mkObj("a", 1)} - - u.Replace(m, "0") - if callcount != 1 { - t.Errorf("Expected 1 calls, got %d", callcount) - } - expected := []interface{}{mkObj("a", 1)} - if !reflect.DeepEqual(expected, got) { - t.Errorf("Expected %#v, Got %#v", expected, got) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/clientset.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/clientset.go index 66a2e6863..599a0c40b 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/clientset.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/clientset.go @@ -18,13 +18,14 @@ package internalclientset import ( "github.com/golang/glog" + restclient "k8s.io/kubernetes/pkg/client/restclient" + discovery "k8s.io/kubernetes/pkg/client/typed/discovery" unversionedcore "k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned" unversionedextensions "k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned" - unversioned "k8s.io/kubernetes/pkg/client/unversioned" ) type Interface interface { - Discovery() unversioned.DiscoveryInterface + Discovery() discovery.DiscoveryInterface Core() unversionedcore.CoreInterface Extensions() unversionedextensions.ExtensionsInterface } @@ -32,7 +33,7 @@ type Interface interface { // Clientset contains the clients for groups. Each group has exactly one // version included in a Clientset. type Clientset struct { - *unversioned.DiscoveryClient + *discovery.DiscoveryClient *unversionedcore.CoreClient *unversionedextensions.ExtensionsClient } @@ -48,12 +49,12 @@ func (c *Clientset) Extensions() unversionedextensions.ExtensionsInterface { } // Discovery retrieves the DiscoveryClient -func (c *Clientset) Discovery() unversioned.DiscoveryInterface { +func (c *Clientset) Discovery() discovery.DiscoveryInterface { return c.DiscoveryClient } // NewForConfig creates a new Clientset for the given config. -func NewForConfig(c *unversioned.Config) (*Clientset, error) { +func NewForConfig(c *restclient.Config) (*Clientset, error) { var clientset Clientset var err error clientset.CoreClient, err = unversionedcore.NewForConfig(c) @@ -65,7 +66,7 @@ func NewForConfig(c *unversioned.Config) (*Clientset, error) { return &clientset, err } - clientset.DiscoveryClient, err = unversioned.NewDiscoveryClientForConfig(c) + clientset.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(c) if err != nil { glog.Errorf("failed to create the DiscoveryClient: %v", err) } @@ -74,21 +75,21 @@ func NewForConfig(c *unversioned.Config) (*Clientset, error) { // NewForConfigOrDie creates a new Clientset for the given config and // panics if there is an error in the config. -func NewForConfigOrDie(c *unversioned.Config) *Clientset { +func NewForConfigOrDie(c *restclient.Config) *Clientset { var clientset Clientset clientset.CoreClient = unversionedcore.NewForConfigOrDie(c) clientset.ExtensionsClient = unversionedextensions.NewForConfigOrDie(c) - clientset.DiscoveryClient = unversioned.NewDiscoveryClientForConfigOrDie(c) + clientset.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) return &clientset } // New creates a new Clientset for the given RESTClient. -func New(c *unversioned.RESTClient) *Clientset { +func New(c *restclient.RESTClient) *Clientset { var clientset Clientset clientset.CoreClient = unversionedcore.New(c) clientset.ExtensionsClient = unversionedextensions.New(c) - clientset.DiscoveryClient = unversioned.NewDiscoveryClient(c) + clientset.DiscoveryClient = discovery.NewDiscoveryClient(c) return &clientset } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/clientset_adaption.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/clientset_adaption.go index 1b721ad62..2e1214ac1 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/clientset_adaption.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/clientset_adaption.go @@ -17,6 +17,7 @@ limitations under the License. package internalclientset import ( + "k8s.io/kubernetes/pkg/client/typed/discovery" unversionedcore "k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned" unversionedextensions "k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned" "k8s.io/kubernetes/pkg/client/unversioned" @@ -38,5 +39,11 @@ func FromUnversionedClient(c *unversioned.Client) *Clientset { clientset.ExtensionsClient = unversionedextensions.New(nil) } + if c != nil && c.DiscoveryClient != nil { + clientset.DiscoveryClient = discovery.NewDiscoveryClient(c.DiscoveryClient.RESTClient) + } else { + clientset.DiscoveryClient = discovery.NewDiscoveryClient(nil) + } + return &clientset } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake/clientset_generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake/clientset_generated.go deleted file mode 100644 index 574c65ba1..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake/clientset_generated.go +++ /dev/null @@ -1,70 +0,0 @@ -/* -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 fake - -import ( - "k8s.io/kubernetes/pkg/api" - clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" - "k8s.io/kubernetes/pkg/client/testing/core" - unversionedcore "k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned" - fakeunversionedcore "k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake" - unversionedextensions "k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned" - fakeunversionedextensions "k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake" - "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/watch" -) - -// Clientset returns a clientset that will respond with the provided objects -func NewSimpleClientset(objects ...runtime.Object) *Clientset { - o := core.NewObjects(api.Scheme, api.Codecs.UniversalDecoder()) - for _, obj := range objects { - if err := o.Add(obj); err != nil { - panic(err) - } - } - - fakePtr := core.Fake{} - fakePtr.AddReactor("*", "*", core.ObjectReaction(o, api.RESTMapper)) - - fakePtr.AddWatchReactor("*", core.DefaultWatchReactor(watch.NewFake(), nil)) - - return &Clientset{fakePtr} -} - -// Clientset implements clientset.Interface. Meant to be embedded into a -// struct to get a default implementation. This makes faking out just the method -// you want to test easier. -type Clientset struct { - core.Fake -} - -func (c *Clientset) Discovery() unversioned.DiscoveryInterface { - return &FakeDiscovery{&c.Fake} -} - -var _ clientset.Interface = &Clientset{} - -// Core retrieves the CoreClient -func (c *Clientset) Core() unversionedcore.CoreInterface { - return &fakeunversionedcore.FakeCore{&c.Fake} -} - -// Extensions retrieves the ExtensionsClient -func (c *Clientset) Extensions() unversionedextensions.ExtensionsInterface { - return &fakeunversionedextensions.FakeExtensions{&c.Fake} -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake/discovery.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake/discovery.go deleted file mode 100644 index 655fa41e0..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake/discovery.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -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. -*/ - -// TODO: the fake discovery client should live in pkg/client/discovery/, rather -// than being copied in every fake clientset. -package fake - -import ( - "github.com/emicklei/go-restful/swagger" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/v1" - "k8s.io/kubernetes/pkg/client/testing/core" - "k8s.io/kubernetes/pkg/version" -) - -type FakeDiscovery struct { - *core.Fake -} - -func (c *FakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*unversioned.APIResourceList, error) { - action := core.ActionImpl{ - Verb: "get", - Resource: "resource", - } - c.Invokes(action, nil) - return c.Resources[groupVersion], nil -} - -func (c *FakeDiscovery) ServerResources() (map[string]*unversioned.APIResourceList, error) { - action := core.ActionImpl{ - Verb: "get", - Resource: "resource", - } - c.Invokes(action, nil) - return c.Resources, nil -} - -func (c *FakeDiscovery) ServerGroups() (*unversioned.APIGroupList, error) { - return nil, nil -} - -func (c *FakeDiscovery) ServerVersion() (*version.Info, error) { - action := core.ActionImpl{} - action.Verb = "get" - action.Resource = "version" - - c.Invokes(action, nil) - versionInfo := version.Get() - return &versionInfo, nil -} - -func (c *FakeDiscovery) SwaggerSchema(version unversioned.GroupVersion) (*swagger.ApiDeclaration, error) { - action := core.ActionImpl{} - action.Verb = "get" - if version == v1.SchemeGroupVersion { - action.Resource = "/swaggerapi/api/" + version.Version - } else { - action.Resource = "/swaggerapi/apis/" + version.Group + "/" + version.Version - } - - c.Invokes(action, nil) - return &swagger.ApiDeclaration{}, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/record/event.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/record/event.go index 221a8cc34..a47868247 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/record/event.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/record/event.go @@ -24,7 +24,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/unversioned" - client "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" utilruntime "k8s.io/kubernetes/pkg/util/runtime" @@ -182,7 +182,7 @@ func recordEvent(sink EventSink, event *api.Event, patch []byte, updateExistingE // If we can't contact the server, then hold everything while we keep trying. // Otherwise, something about the event is malformed and we should abandon it. switch err.(type) { - case *client.RequestConstructionError: + case *restclient.RequestConstructionError: // We will construct the request the same next time, so don't keep trying. glog.Errorf("Unable to construct event '%#v': '%v' (will not retry!)", event, err) return true diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/record/event_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/record/event_test.go deleted file mode 100644 index 61597e0d5..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/record/event_test.go +++ /dev/null @@ -1,885 +0,0 @@ -/* -Copyright 2014 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 record - -import ( - "encoding/json" - "fmt" - "math/rand" - "strconv" - "testing" - "time" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/errors" - client "k8s.io/kubernetes/pkg/client/unversioned" - k8sruntime "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util" - "k8s.io/kubernetes/pkg/util/strategicpatch" -) - -func init() { - // Don't bother sleeping between retries. - sleepDuration = 0 -} - -type testEventSink struct { - OnCreate func(e *api.Event) (*api.Event, error) - OnUpdate func(e *api.Event) (*api.Event, error) - OnPatch func(e *api.Event, p []byte) (*api.Event, error) -} - -// CreateEvent records the event for testing. -func (t *testEventSink) Create(e *api.Event) (*api.Event, error) { - if t.OnCreate != nil { - return t.OnCreate(e) - } - return e, nil -} - -// UpdateEvent records the event for testing. -func (t *testEventSink) Update(e *api.Event) (*api.Event, error) { - if t.OnUpdate != nil { - return t.OnUpdate(e) - } - return e, nil -} - -// PatchEvent records the event for testing. -func (t *testEventSink) Patch(e *api.Event, p []byte) (*api.Event, error) { - if t.OnPatch != nil { - return t.OnPatch(e, p) - } - return e, nil -} - -type OnCreateFunc func(*api.Event) (*api.Event, error) - -func OnCreateFactory(testCache map[string]*api.Event, createEvent chan<- *api.Event) OnCreateFunc { - return func(event *api.Event) (*api.Event, error) { - testCache[getEventKey(event)] = event - createEvent <- event - return event, nil - } -} - -type OnPatchFunc func(*api.Event, []byte) (*api.Event, error) - -func OnPatchFactory(testCache map[string]*api.Event, patchEvent chan<- *api.Event) OnPatchFunc { - return func(event *api.Event, patch []byte) (*api.Event, error) { - cachedEvent, found := testCache[getEventKey(event)] - if !found { - return nil, fmt.Errorf("unexpected error: couldn't find Event in testCache.") - } - originalData, err := json.Marshal(cachedEvent) - if err != nil { - return nil, fmt.Errorf("unexpected error: %v", err) - } - patched, err := strategicpatch.StrategicMergePatch(originalData, patch, event) - if err != nil { - return nil, fmt.Errorf("unexpected error: %v", err) - } - patchedObj := &api.Event{} - err = json.Unmarshal(patched, patchedObj) - if err != nil { - return nil, fmt.Errorf("unexpected error: %v", err) - } - patchEvent <- patchedObj - return patchedObj, nil - } -} - -func TestEventf(t *testing.T) { - testPod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - SelfLink: "/api/version/pods/foo", - Name: "foo", - Namespace: "baz", - UID: "bar", - }, - } - testPod2 := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - SelfLink: "/api/version/pods/foo", - Name: "foo", - Namespace: "baz", - UID: "differentUid", - }, - } - testRef, err := api.GetPartialReference(testPod, "spec.containers[2]") - testRef2, err := api.GetPartialReference(testPod2, "spec.containers[3]") - if err != nil { - t.Fatal(err) - } - table := []struct { - obj k8sruntime.Object - eventtype string - reason string - messageFmt string - elements []interface{} - expect *api.Event - expectLog string - expectUpdate bool - }{ - { - obj: testRef, - eventtype: api.EventTypeNormal, - reason: "Started", - messageFmt: "some verbose message: %v", - elements: []interface{}{1}, - expect: &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "baz", - }, - InvolvedObject: api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "baz", - UID: "bar", - APIVersion: "version", - FieldPath: "spec.containers[2]", - }, - Reason: "Started", - Message: "some verbose message: 1", - Source: api.EventSource{Component: "eventTest"}, - Count: 1, - Type: api.EventTypeNormal, - }, - expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`, - expectUpdate: false, - }, - { - obj: testPod, - eventtype: api.EventTypeNormal, - reason: "Killed", - messageFmt: "some other verbose message: %v", - elements: []interface{}{1}, - expect: &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "baz", - }, - InvolvedObject: api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "baz", - UID: "bar", - APIVersion: "version", - }, - Reason: "Killed", - Message: "some other verbose message: 1", - Source: api.EventSource{Component: "eventTest"}, - Count: 1, - Type: api.EventTypeNormal, - }, - expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:""}): type: 'Normal' reason: 'Killed' some other verbose message: 1`, - expectUpdate: false, - }, - { - obj: testRef, - eventtype: api.EventTypeNormal, - reason: "Started", - messageFmt: "some verbose message: %v", - elements: []interface{}{1}, - expect: &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "baz", - }, - InvolvedObject: api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "baz", - UID: "bar", - APIVersion: "version", - FieldPath: "spec.containers[2]", - }, - Reason: "Started", - Message: "some verbose message: 1", - Source: api.EventSource{Component: "eventTest"}, - Count: 2, - Type: api.EventTypeNormal, - }, - expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`, - expectUpdate: true, - }, - { - obj: testRef2, - eventtype: api.EventTypeNormal, - reason: "Started", - messageFmt: "some verbose message: %v", - elements: []interface{}{1}, - expect: &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "baz", - }, - InvolvedObject: api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "baz", - UID: "differentUid", - APIVersion: "version", - FieldPath: "spec.containers[3]", - }, - Reason: "Started", - Message: "some verbose message: 1", - Source: api.EventSource{Component: "eventTest"}, - Count: 1, - Type: api.EventTypeNormal, - }, - expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Started' some verbose message: 1`, - expectUpdate: false, - }, - { - obj: testRef, - eventtype: api.EventTypeNormal, - reason: "Started", - messageFmt: "some verbose message: %v", - elements: []interface{}{1}, - expect: &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "baz", - }, - InvolvedObject: api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "baz", - UID: "bar", - APIVersion: "version", - FieldPath: "spec.containers[2]", - }, - Reason: "Started", - Message: "some verbose message: 1", - Source: api.EventSource{Component: "eventTest"}, - Count: 3, - Type: api.EventTypeNormal, - }, - expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`, - expectUpdate: true, - }, - { - obj: testRef2, - eventtype: api.EventTypeNormal, - reason: "Stopped", - messageFmt: "some verbose message: %v", - elements: []interface{}{1}, - expect: &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "baz", - }, - InvolvedObject: api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "baz", - UID: "differentUid", - APIVersion: "version", - FieldPath: "spec.containers[3]", - }, - Reason: "Stopped", - Message: "some verbose message: 1", - Source: api.EventSource{Component: "eventTest"}, - Count: 1, - Type: api.EventTypeNormal, - }, - expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Stopped' some verbose message: 1`, - expectUpdate: false, - }, - { - obj: testRef2, - eventtype: api.EventTypeNormal, - reason: "Stopped", - messageFmt: "some verbose message: %v", - elements: []interface{}{1}, - expect: &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "baz", - }, - InvolvedObject: api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "baz", - UID: "differentUid", - APIVersion: "version", - FieldPath: "spec.containers[3]", - }, - Reason: "Stopped", - Message: "some verbose message: 1", - Source: api.EventSource{Component: "eventTest"}, - Count: 2, - Type: api.EventTypeNormal, - }, - expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Stopped' some verbose message: 1`, - expectUpdate: true, - }, - } - - testCache := map[string]*api.Event{} - logCalled := make(chan struct{}) - createEvent := make(chan *api.Event) - updateEvent := make(chan *api.Event) - patchEvent := make(chan *api.Event) - testEvents := testEventSink{ - OnCreate: OnCreateFactory(testCache, createEvent), - OnUpdate: func(event *api.Event) (*api.Event, error) { - updateEvent <- event - return event, nil - }, - OnPatch: OnPatchFactory(testCache, patchEvent), - } - eventBroadcaster := NewBroadcaster() - sinkWatcher := eventBroadcaster.StartRecordingToSink(&testEvents) - - clock := util.NewFakeClock(time.Now()) - recorder := recorderWithFakeClock(api.EventSource{Component: "eventTest"}, eventBroadcaster, clock) - for index, item := range table { - clock.Step(1 * time.Second) - logWatcher1 := eventBroadcaster.StartLogging(t.Logf) // Prove that it is useful - logWatcher2 := eventBroadcaster.StartLogging(func(formatter string, args ...interface{}) { - if e, a := item.expectLog, fmt.Sprintf(formatter, args...); e != a { - t.Errorf("Expected '%v', got '%v'", e, a) - } - logCalled <- struct{}{} - }) - recorder.Eventf(item.obj, item.eventtype, item.reason, item.messageFmt, item.elements...) - - <-logCalled - - // validate event - if item.expectUpdate { - actualEvent := <-patchEvent - validateEvent(string(index), actualEvent, item.expect, t) - } else { - actualEvent := <-createEvent - validateEvent(string(index), actualEvent, item.expect, t) - } - logWatcher1.Stop() - logWatcher2.Stop() - } - sinkWatcher.Stop() -} - -func recorderWithFakeClock(eventSource api.EventSource, eventBroadcaster EventBroadcaster, clock util.Clock) EventRecorder { - return &recorderImpl{eventSource, eventBroadcaster.(*eventBroadcasterImpl).Broadcaster, clock} -} - -func TestWriteEventError(t *testing.T) { - type entry struct { - timesToSendError int - attemptsWanted int - err error - } - table := map[string]*entry{ - "giveUp1": { - timesToSendError: 1000, - attemptsWanted: 1, - err: &client.RequestConstructionError{}, - }, - "giveUp2": { - timesToSendError: 1000, - attemptsWanted: 1, - err: &errors.StatusError{}, - }, - "retry1": { - timesToSendError: 1000, - attemptsWanted: 12, - err: &errors.UnexpectedObjectError{}, - }, - "retry2": { - timesToSendError: 1000, - attemptsWanted: 12, - err: fmt.Errorf("A weird error"), - }, - "succeedEventually": { - timesToSendError: 2, - attemptsWanted: 2, - err: fmt.Errorf("A weird error"), - }, - } - - eventCorrelator := NewEventCorrelator(util.RealClock{}) - randGen := rand.New(rand.NewSource(time.Now().UnixNano())) - - for caseName, ent := range table { - attempts := 0 - sink := &testEventSink{ - OnCreate: func(event *api.Event) (*api.Event, error) { - attempts++ - if attempts < ent.timesToSendError { - return nil, ent.err - } - return event, nil - }, - } - ev := &api.Event{} - recordToSink(sink, ev, eventCorrelator, randGen) - if attempts != ent.attemptsWanted { - t.Errorf("case %v: wanted %d, got %d attempts", caseName, ent.attemptsWanted, attempts) - } - } -} - -func TestLotsOfEvents(t *testing.T) { - recorderCalled := make(chan struct{}) - loggerCalled := make(chan struct{}) - - // Fail each event a few times to ensure there's some load on the tested code. - var counts [1000]int - testEvents := testEventSink{ - OnCreate: func(event *api.Event) (*api.Event, error) { - num, err := strconv.Atoi(event.Message) - if err != nil { - t.Error(err) - return event, nil - } - counts[num]++ - if counts[num] < 5 { - return nil, fmt.Errorf("fake error") - } - recorderCalled <- struct{}{} - return event, nil - }, - } - - eventBroadcaster := NewBroadcaster() - sinkWatcher := eventBroadcaster.StartRecordingToSink(&testEvents) - logWatcher := eventBroadcaster.StartLogging(func(formatter string, args ...interface{}) { - loggerCalled <- struct{}{} - }) - recorder := eventBroadcaster.NewRecorder(api.EventSource{Component: "eventTest"}) - ref := &api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "baz", - UID: "bar", - APIVersion: "version", - } - for i := 0; i < maxQueuedEvents; i++ { - // we need to vary the reason to prevent aggregation - go recorder.Eventf(ref, api.EventTypeNormal, "Reason-"+string(i), strconv.Itoa(i)) - } - // Make sure no events were dropped by either of the listeners. - for i := 0; i < maxQueuedEvents; i++ { - <-recorderCalled - <-loggerCalled - } - // Make sure that every event was attempted 5 times - for i := 0; i < maxQueuedEvents; i++ { - if counts[i] < 5 { - t.Errorf("Only attempted to record event '%d' %d times.", i, counts[i]) - } - } - sinkWatcher.Stop() - logWatcher.Stop() -} - -func TestEventfNoNamespace(t *testing.T) { - testPod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - SelfLink: "/api/version/pods/foo", - Name: "foo", - UID: "bar", - }, - } - testRef, err := api.GetPartialReference(testPod, "spec.containers[2]") - if err != nil { - t.Fatal(err) - } - table := []struct { - obj k8sruntime.Object - eventtype string - reason string - messageFmt string - elements []interface{} - expect *api.Event - expectLog string - expectUpdate bool - }{ - { - obj: testRef, - eventtype: api.EventTypeNormal, - reason: "Started", - messageFmt: "some verbose message: %v", - elements: []interface{}{1}, - expect: &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "default", - }, - InvolvedObject: api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "", - UID: "bar", - APIVersion: "version", - FieldPath: "spec.containers[2]", - }, - Reason: "Started", - Message: "some verbose message: 1", - Source: api.EventSource{Component: "eventTest"}, - Count: 1, - Type: api.EventTypeNormal, - }, - expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`, - expectUpdate: false, - }, - } - - testCache := map[string]*api.Event{} - logCalled := make(chan struct{}) - createEvent := make(chan *api.Event) - updateEvent := make(chan *api.Event) - patchEvent := make(chan *api.Event) - testEvents := testEventSink{ - OnCreate: OnCreateFactory(testCache, createEvent), - OnUpdate: func(event *api.Event) (*api.Event, error) { - updateEvent <- event - return event, nil - }, - OnPatch: OnPatchFactory(testCache, patchEvent), - } - eventBroadcaster := NewBroadcaster() - sinkWatcher := eventBroadcaster.StartRecordingToSink(&testEvents) - - clock := util.NewFakeClock(time.Now()) - recorder := recorderWithFakeClock(api.EventSource{Component: "eventTest"}, eventBroadcaster, clock) - - for index, item := range table { - clock.Step(1 * time.Second) - logWatcher1 := eventBroadcaster.StartLogging(t.Logf) // Prove that it is useful - logWatcher2 := eventBroadcaster.StartLogging(func(formatter string, args ...interface{}) { - if e, a := item.expectLog, fmt.Sprintf(formatter, args...); e != a { - t.Errorf("Expected '%v', got '%v'", e, a) - } - logCalled <- struct{}{} - }) - recorder.Eventf(item.obj, item.eventtype, item.reason, item.messageFmt, item.elements...) - - <-logCalled - - // validate event - if item.expectUpdate { - actualEvent := <-patchEvent - validateEvent(string(index), actualEvent, item.expect, t) - } else { - actualEvent := <-createEvent - validateEvent(string(index), actualEvent, item.expect, t) - } - - logWatcher1.Stop() - logWatcher2.Stop() - } - sinkWatcher.Stop() -} - -func TestMultiSinkCache(t *testing.T) { - testPod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - SelfLink: "/api/version/pods/foo", - Name: "foo", - Namespace: "baz", - UID: "bar", - }, - } - testPod2 := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - SelfLink: "/api/version/pods/foo", - Name: "foo", - Namespace: "baz", - UID: "differentUid", - }, - } - testRef, err := api.GetPartialReference(testPod, "spec.containers[2]") - testRef2, err := api.GetPartialReference(testPod2, "spec.containers[3]") - if err != nil { - t.Fatal(err) - } - table := []struct { - obj k8sruntime.Object - eventtype string - reason string - messageFmt string - elements []interface{} - expect *api.Event - expectLog string - expectUpdate bool - }{ - { - obj: testRef, - eventtype: api.EventTypeNormal, - reason: "Started", - messageFmt: "some verbose message: %v", - elements: []interface{}{1}, - expect: &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "baz", - }, - InvolvedObject: api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "baz", - UID: "bar", - APIVersion: "version", - FieldPath: "spec.containers[2]", - }, - Reason: "Started", - Message: "some verbose message: 1", - Source: api.EventSource{Component: "eventTest"}, - Count: 1, - Type: api.EventTypeNormal, - }, - expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`, - expectUpdate: false, - }, - { - obj: testPod, - eventtype: api.EventTypeNormal, - reason: "Killed", - messageFmt: "some other verbose message: %v", - elements: []interface{}{1}, - expect: &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "baz", - }, - InvolvedObject: api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "baz", - UID: "bar", - APIVersion: "version", - }, - Reason: "Killed", - Message: "some other verbose message: 1", - Source: api.EventSource{Component: "eventTest"}, - Count: 1, - Type: api.EventTypeNormal, - }, - expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:""}): type: 'Normal' reason: 'Killed' some other verbose message: 1`, - expectUpdate: false, - }, - { - obj: testRef, - eventtype: api.EventTypeNormal, - reason: "Started", - messageFmt: "some verbose message: %v", - elements: []interface{}{1}, - expect: &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "baz", - }, - InvolvedObject: api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "baz", - UID: "bar", - APIVersion: "version", - FieldPath: "spec.containers[2]", - }, - Reason: "Started", - Message: "some verbose message: 1", - Source: api.EventSource{Component: "eventTest"}, - Count: 2, - Type: api.EventTypeNormal, - }, - expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`, - expectUpdate: true, - }, - { - obj: testRef2, - eventtype: api.EventTypeNormal, - reason: "Started", - messageFmt: "some verbose message: %v", - elements: []interface{}{1}, - expect: &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "baz", - }, - InvolvedObject: api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "baz", - UID: "differentUid", - APIVersion: "version", - FieldPath: "spec.containers[3]", - }, - Reason: "Started", - Message: "some verbose message: 1", - Source: api.EventSource{Component: "eventTest"}, - Count: 1, - Type: api.EventTypeNormal, - }, - expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Started' some verbose message: 1`, - expectUpdate: false, - }, - { - obj: testRef, - eventtype: api.EventTypeNormal, - reason: "Started", - messageFmt: "some verbose message: %v", - elements: []interface{}{1}, - expect: &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "baz", - }, - InvolvedObject: api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "baz", - UID: "bar", - APIVersion: "version", - FieldPath: "spec.containers[2]", - }, - Reason: "Started", - Message: "some verbose message: 1", - Source: api.EventSource{Component: "eventTest"}, - Count: 3, - Type: api.EventTypeNormal, - }, - expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`, - expectUpdate: true, - }, - { - obj: testRef2, - eventtype: api.EventTypeNormal, - reason: "Stopped", - messageFmt: "some verbose message: %v", - elements: []interface{}{1}, - expect: &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "baz", - }, - InvolvedObject: api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "baz", - UID: "differentUid", - APIVersion: "version", - FieldPath: "spec.containers[3]", - }, - Reason: "Stopped", - Message: "some verbose message: 1", - Source: api.EventSource{Component: "eventTest"}, - Count: 1, - Type: api.EventTypeNormal, - }, - expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Stopped' some verbose message: 1`, - expectUpdate: false, - }, - { - obj: testRef2, - eventtype: api.EventTypeNormal, - reason: "Stopped", - messageFmt: "some verbose message: %v", - elements: []interface{}{1}, - expect: &api.Event{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "baz", - }, - InvolvedObject: api.ObjectReference{ - Kind: "Pod", - Name: "foo", - Namespace: "baz", - UID: "differentUid", - APIVersion: "version", - FieldPath: "spec.containers[3]", - }, - Reason: "Stopped", - Message: "some verbose message: 1", - Source: api.EventSource{Component: "eventTest"}, - Count: 2, - Type: api.EventTypeNormal, - }, - expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Stopped' some verbose message: 1`, - expectUpdate: true, - }, - } - - testCache := map[string]*api.Event{} - createEvent := make(chan *api.Event) - updateEvent := make(chan *api.Event) - patchEvent := make(chan *api.Event) - testEvents := testEventSink{ - OnCreate: OnCreateFactory(testCache, createEvent), - OnUpdate: func(event *api.Event) (*api.Event, error) { - updateEvent <- event - return event, nil - }, - OnPatch: OnPatchFactory(testCache, patchEvent), - } - - testCache2 := map[string]*api.Event{} - createEvent2 := make(chan *api.Event) - updateEvent2 := make(chan *api.Event) - patchEvent2 := make(chan *api.Event) - testEvents2 := testEventSink{ - OnCreate: OnCreateFactory(testCache2, createEvent2), - OnUpdate: func(event *api.Event) (*api.Event, error) { - updateEvent2 <- event - return event, nil - }, - OnPatch: OnPatchFactory(testCache2, patchEvent2), - } - - eventBroadcaster := NewBroadcaster() - clock := util.NewFakeClock(time.Now()) - recorder := recorderWithFakeClock(api.EventSource{Component: "eventTest"}, eventBroadcaster, clock) - - sinkWatcher := eventBroadcaster.StartRecordingToSink(&testEvents) - for index, item := range table { - clock.Step(1 * time.Second) - recorder.Eventf(item.obj, item.eventtype, item.reason, item.messageFmt, item.elements...) - - // validate event - if item.expectUpdate { - actualEvent := <-patchEvent - validateEvent(string(index), actualEvent, item.expect, t) - } else { - actualEvent := <-createEvent - validateEvent(string(index), actualEvent, item.expect, t) - } - } - - // Another StartRecordingToSink call should start to record events with new clean cache. - sinkWatcher2 := eventBroadcaster.StartRecordingToSink(&testEvents2) - for index, item := range table { - clock.Step(1 * time.Second) - recorder.Eventf(item.obj, item.eventtype, item.reason, item.messageFmt, item.elements...) - - // validate event - if item.expectUpdate { - actualEvent := <-patchEvent2 - validateEvent(string(index), actualEvent, item.expect, t) - } else { - actualEvent := <-createEvent2 - validateEvent(string(index), actualEvent, item.expect, t) - } - } - - sinkWatcher.Stop() - sinkWatcher2.Stop() -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/record/events_cache_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/record/events_cache_test.go deleted file mode 100644 index 08699d9a6..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/record/events_cache_test.go +++ /dev/null @@ -1,253 +0,0 @@ -/* -Copyright 2015 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 record - -import ( - "reflect" - "strings" - "testing" - "time" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/util" -) - -func makeObjectReference(kind, name, namespace string) api.ObjectReference { - return api.ObjectReference{ - Kind: kind, - Name: name, - Namespace: namespace, - UID: "C934D34AFB20242", - APIVersion: "version", - } -} - -func makeEvent(reason, message string, involvedObject api.ObjectReference) api.Event { - eventTime := unversioned.Now() - event := api.Event{ - Reason: reason, - Message: message, - InvolvedObject: involvedObject, - Source: api.EventSource{ - Component: "kubelet", - Host: "kublet.node1", - }, - Count: 1, - FirstTimestamp: eventTime, - LastTimestamp: eventTime, - Type: api.EventTypeNormal, - } - return event -} - -func makeEvents(num int, template api.Event) []api.Event { - events := []api.Event{} - for i := 0; i < num; i++ { - events = append(events, template) - } - return events -} - -func makeUniqueEvents(num int) []api.Event { - events := []api.Event{} - kind := "Pod" - for i := 0; i < num; i++ { - reason := strings.Join([]string{"reason", string(i)}, "-") - message := strings.Join([]string{"message", string(i)}, "-") - name := strings.Join([]string{"pod", string(i)}, "-") - namespace := strings.Join([]string{"ns", string(i)}, "-") - involvedObject := makeObjectReference(kind, name, namespace) - events = append(events, makeEvent(reason, message, involvedObject)) - } - return events -} - -func makeSimilarEvents(num int, template api.Event, messagePrefix string) []api.Event { - events := makeEvents(num, template) - for i := range events { - events[i].Message = strings.Join([]string{messagePrefix, string(i), events[i].Message}, "-") - } - return events -} - -func setCount(event api.Event, count int) api.Event { - event.Count = count - return event -} - -func validateEvent(messagePrefix string, actualEvent *api.Event, expectedEvent *api.Event, t *testing.T) (*api.Event, error) { - recvEvent := *actualEvent - expectCompression := expectedEvent.Count > 1 - t.Logf("%v - expectedEvent.Count is %d\n", messagePrefix, expectedEvent.Count) - // Just check that the timestamp was set. - if recvEvent.FirstTimestamp.IsZero() || recvEvent.LastTimestamp.IsZero() { - t.Errorf("%v - timestamp wasn't set: %#v", messagePrefix, recvEvent) - } - actualFirstTimestamp := recvEvent.FirstTimestamp - actualLastTimestamp := recvEvent.LastTimestamp - if actualFirstTimestamp.Equal(actualLastTimestamp) { - if expectCompression { - t.Errorf("%v - FirstTimestamp (%q) and LastTimestamp (%q) must be different to indicate event compression happened, but were the same. Actual Event: %#v", messagePrefix, actualFirstTimestamp, actualLastTimestamp, recvEvent) - } - } else { - if expectedEvent.Count == 1 { - t.Errorf("%v - FirstTimestamp (%q) and LastTimestamp (%q) must be equal to indicate only one occurrence of the event, but were different. Actual Event: %#v", messagePrefix, actualFirstTimestamp, actualLastTimestamp, recvEvent) - } - } - // Temp clear time stamps for comparison because actual values don't matter for comparison - recvEvent.FirstTimestamp = expectedEvent.FirstTimestamp - recvEvent.LastTimestamp = expectedEvent.LastTimestamp - // Check that name has the right prefix. - if n, en := recvEvent.Name, expectedEvent.Name; !strings.HasPrefix(n, en) { - t.Errorf("%v - Name '%v' does not contain prefix '%v'", messagePrefix, n, en) - } - recvEvent.Name = expectedEvent.Name - if e, a := expectedEvent, &recvEvent; !reflect.DeepEqual(e, a) { - t.Errorf("%v - diff: %s", messagePrefix, util.ObjectGoPrintDiff(e, a)) - } - recvEvent.FirstTimestamp = actualFirstTimestamp - recvEvent.LastTimestamp = actualLastTimestamp - return actualEvent, nil -} - -// TestDefaultEventFilterFunc ensures that no events are filtered -func TestDefaultEventFilterFunc(t *testing.T) { - event := makeEvent("end-of-world", "it was fun", makeObjectReference("Pod", "pod1", "other")) - if DefaultEventFilterFunc(&event) { - t.Fatalf("DefaultEventFilterFunc should always return false") - } -} - -// TestEventAggregatorByReasonFunc ensures that two events are aggregated if they vary only by event.message -func TestEventAggregatorByReasonFunc(t *testing.T) { - event1 := makeEvent("end-of-world", "it was fun", makeObjectReference("Pod", "pod1", "other")) - event2 := makeEvent("end-of-world", "it was awful", makeObjectReference("Pod", "pod1", "other")) - event3 := makeEvent("nevermind", "it was a bug", makeObjectReference("Pod", "pod1", "other")) - - aggKey1, localKey1 := EventAggregatorByReasonFunc(&event1) - aggKey2, localKey2 := EventAggregatorByReasonFunc(&event2) - aggKey3, _ := EventAggregatorByReasonFunc(&event3) - - if aggKey1 != aggKey2 { - t.Errorf("Expected %v equal %v", aggKey1, aggKey2) - } - if localKey1 == localKey2 { - t.Errorf("Expected %v to not equal %v", aggKey1, aggKey3) - } - if aggKey1 == aggKey3 { - t.Errorf("Expected %v to not equal %v", aggKey1, aggKey3) - } -} - -// TestEventAggregatorByReasonMessageFunc validates the proper output for an aggregate message -func TestEventAggregatorByReasonMessageFunc(t *testing.T) { - expected := "(events with common reason combined)" - event1 := makeEvent("end-of-world", "it was fun", makeObjectReference("Pod", "pod1", "other")) - if actual := EventAggregatorByReasonMessageFunc(&event1); expected != actual { - t.Errorf("Expected %v got %v", expected, actual) - } -} - -// TestEventCorrelator validates proper counting, aggregation of events -func TestEventCorrelator(t *testing.T) { - firstEvent := makeEvent("first", "i am first", makeObjectReference("Pod", "my-pod", "my-ns")) - duplicateEvent := makeEvent("duplicate", "me again", makeObjectReference("Pod", "my-pod", "my-ns")) - uniqueEvent := makeEvent("unique", "snowflake", makeObjectReference("Pod", "my-pod", "my-ns")) - similarEvent := makeEvent("similar", "similar message", makeObjectReference("Pod", "my-pod", "my-ns")) - aggregateEvent := makeEvent(similarEvent.Reason, EventAggregatorByReasonMessageFunc(&similarEvent), similarEvent.InvolvedObject) - scenario := map[string]struct { - previousEvents []api.Event - newEvent api.Event - expectedEvent api.Event - intervalSeconds int - }{ - "create-a-single-event": { - previousEvents: []api.Event{}, - newEvent: firstEvent, - expectedEvent: setCount(firstEvent, 1), - intervalSeconds: 5, - }, - "the-same-event-should-just-count": { - previousEvents: makeEvents(1, duplicateEvent), - newEvent: duplicateEvent, - expectedEvent: setCount(duplicateEvent, 2), - intervalSeconds: 5, - }, - "the-same-event-should-just-count-even-if-more-than-aggregate": { - previousEvents: makeEvents(defaultAggregateMaxEvents, duplicateEvent), - newEvent: duplicateEvent, - expectedEvent: setCount(duplicateEvent, defaultAggregateMaxEvents+1), - intervalSeconds: 5, - }, - "create-many-unique-events": { - previousEvents: makeUniqueEvents(30), - newEvent: uniqueEvent, - expectedEvent: setCount(uniqueEvent, 1), - intervalSeconds: 5, - }, - "similar-events-should-aggregate-event": { - previousEvents: makeSimilarEvents(defaultAggregateMaxEvents-1, similarEvent, similarEvent.Message), - newEvent: similarEvent, - expectedEvent: setCount(aggregateEvent, 1), - intervalSeconds: 5, - }, - "similar-events-many-times-should-count-the-aggregate": { - previousEvents: makeSimilarEvents(defaultAggregateMaxEvents, similarEvent, similarEvent.Message), - newEvent: similarEvent, - expectedEvent: setCount(aggregateEvent, 2), - intervalSeconds: 5, - }, - "similar-events-whose-interval-is-greater-than-aggregate-interval-do-not-aggregate": { - previousEvents: makeSimilarEvents(defaultAggregateMaxEvents-1, similarEvent, similarEvent.Message), - newEvent: similarEvent, - expectedEvent: setCount(similarEvent, 1), - intervalSeconds: defaultAggregateIntervalInSeconds, - }, - } - - for testScenario, testInput := range scenario { - eventInterval := time.Duration(testInput.intervalSeconds) * time.Second - clock := util.IntervalClock{Time: time.Now(), Duration: eventInterval} - correlator := NewEventCorrelator(&clock) - for i := range testInput.previousEvents { - event := testInput.previousEvents[i] - now := unversioned.NewTime(clock.Now()) - event.FirstTimestamp = now - event.LastTimestamp = now - result, err := correlator.EventCorrelate(&event) - if err != nil { - t.Errorf("scenario %v: unexpected error playing back prevEvents %v", testScenario, err) - } - correlator.UpdateState(result.Event) - } - - // update the input to current clock value - now := unversioned.NewTime(clock.Now()) - testInput.newEvent.FirstTimestamp = now - testInput.newEvent.LastTimestamp = now - result, err := correlator.EventCorrelate(&testInput.newEvent) - if err != nil { - t.Errorf("scenario %v: unexpected error correlating input event %v", testScenario, err) - } - - _, err = validateEvent(testScenario, result.Event, &testInput.expectedEvent, t) - if err != nil { - t.Errorf("scenario %v: unexpected error validating result %v", testScenario, err) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/restclient.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/client.go similarity index 99% rename from Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/restclient.go rename to Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/client.go index 3946754f3..2e378a922 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/restclient.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/client.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package unversioned +package restclient import ( "net/http" diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/config.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/config.go new file mode 100644 index 000000000..b084a8eb2 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/config.go @@ -0,0 +1,309 @@ +/* +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 restclient + +import ( + "fmt" + "io/ioutil" + "net" + "net/http" + "os" + "path" + gruntime "runtime" + "strings" + + "github.com/golang/glog" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util" + "k8s.io/kubernetes/pkg/version" +) + +// Config holds the common attributes that can be passed to a Kubernetes client on +// initialization. +type Config struct { + // Host must be a host string, a host:port pair, or a URL to the base of the apiserver. + // If a URL is given then the (optional) Path of that URL represents a prefix that must + // be appended to all request URIs used to access the apiserver. This allows a frontend + // proxy to easily relocate all of the apiserver endpoints. + Host string + // APIPath is a sub-path that points to an API root. + APIPath string + // Prefix is the sub path of the server. If not specified, the client will set + // a default value. Use "/" to indicate the server root should be used + Prefix string + + // ContentConfig contains settings that affect how objects are transformed when + // sent to the server. + ContentConfig + + // Server requires Basic authentication + Username string + Password string + + // Server requires Bearer authentication. This client will not attempt to use + // refresh tokens for an OAuth2 flow. + // TODO: demonstrate an OAuth2 compatible client. + BearerToken string + + // TLSClientConfig contains settings to enable transport layer security + TLSClientConfig + + // Server should be accessed without verifying the TLS + // certificate. For testing only. + Insecure bool + + // UserAgent is an optional field that specifies the caller of this request. + UserAgent string + + // Transport may be used for custom HTTP behavior. This attribute may not + // be specified with the TLS client certificate options. Use WrapTransport + // for most client level operations. + Transport http.RoundTripper + // WrapTransport will be invoked for custom HTTP behavior after the underlying + // transport is initialized (either the transport created from TLSClientConfig, + // Transport, or http.DefaultTransport). The config may layer other RoundTrippers + // on top of the returned RoundTripper. + WrapTransport func(rt http.RoundTripper) http.RoundTripper + + // QPS indicates the maximum QPS to the master from this client. If zero, QPS is unlimited. + QPS float32 + + // Maximum burst for throttle + Burst int +} + +// TLSClientConfig contains settings to enable transport layer security +type TLSClientConfig struct { + // Server requires TLS client certificate authentication + CertFile string + // Server requires TLS client certificate authentication + KeyFile string + // Trusted root certificates for server + CAFile string + + // CertData holds PEM-encoded bytes (typically read from a client certificate file). + // CertData takes precedence over CertFile + CertData []byte + // KeyData holds PEM-encoded bytes (typically read from a client certificate key file). + // KeyData takes precedence over KeyFile + KeyData []byte + // CAData holds PEM-encoded bytes (typically read from a root certificates bundle). + // CAData takes precedence over CAFile + CAData []byte +} + +type ContentConfig struct { + // ContentType specifies the wire format used to communicate with the server. + // This value will be set as the Accept header on requests made to the server, and + // as the default content type on any object sent to the server. If not set, + // "application/json" is used. + ContentType string + // GroupVersion is the API version to talk to. Must be provided when initializing + // a RESTClient directly. When initializing a Client, will be set with the default + // code version. + GroupVersion *unversioned.GroupVersion + // Codec specifies the encoding and decoding behavior for runtime.Objects passed + // to a RESTClient or Client. Required when initializing a RESTClient, optional + // when initializing a Client. + Codec runtime.Codec +} + +// RESTClientFor returns a RESTClient that satisfies the requested attributes on a client Config +// object. Note that a RESTClient may require fields that are optional when initializing a Client. +// A RESTClient created by this method is generic - it expects to operate on an API that follows +// the Kubernetes conventions, but may not be the Kubernetes API. +func RESTClientFor(config *Config) (*RESTClient, error) { + if config.GroupVersion == nil { + return nil, fmt.Errorf("GroupVersion is required when initializing a RESTClient") + } + if config.Codec == nil { + return nil, fmt.Errorf("Codec is required when initializing a RESTClient") + } + + baseURL, versionedAPIPath, err := defaultServerUrlFor(config) + if err != nil { + return nil, err + } + + transport, err := TransportFor(config) + if err != nil { + return nil, err + } + + var httpClient *http.Client + if transport != http.DefaultTransport { + httpClient = &http.Client{Transport: transport} + } + + client := NewRESTClient(baseURL, versionedAPIPath, config.ContentConfig, config.QPS, config.Burst, httpClient) + + return client, nil +} + +// UnversionedRESTClientFor is the same as RESTClientFor, except that it allows +// the config.Version to be empty. +func UnversionedRESTClientFor(config *Config) (*RESTClient, error) { + if config.Codec == nil { + return nil, fmt.Errorf("Codec is required when initializing a RESTClient") + } + + baseURL, versionedAPIPath, err := defaultServerUrlFor(config) + if err != nil { + return nil, err + } + + transport, err := TransportFor(config) + if err != nil { + return nil, err + } + + var httpClient *http.Client + if transport != http.DefaultTransport { + httpClient = &http.Client{Transport: transport} + } + + versionConfig := config.ContentConfig + if versionConfig.GroupVersion == nil { + v := unversioned.SchemeGroupVersion + versionConfig.GroupVersion = &v + } + + client := NewRESTClient(baseURL, versionedAPIPath, versionConfig, config.QPS, config.Burst, httpClient) + return client, nil +} + +// SetKubernetesDefaults sets default values on the provided client config for accessing the +// Kubernetes API or returns an error if any of the defaults are impossible or invalid. +func SetKubernetesDefaults(config *Config) error { + if len(config.UserAgent) == 0 { + config.UserAgent = DefaultKubernetesUserAgent() + } + if config.QPS == 0.0 { + config.QPS = 5.0 + } + if config.Burst == 0 { + config.Burst = 10 + } + return nil +} + +// DefaultKubernetesUserAgent returns the default user agent that clients can use. +func DefaultKubernetesUserAgent() string { + commit := version.Get().GitCommit + if len(commit) > 7 { + commit = commit[:7] + } + if len(commit) == 0 { + commit = "unknown" + } + version := version.Get().GitVersion + seg := strings.SplitN(version, "-", 2) + version = seg[0] + return fmt.Sprintf("%s/%s (%s/%s) kubernetes/%s", path.Base(os.Args[0]), version, gruntime.GOOS, gruntime.GOARCH, commit) +} + +// InClusterConfig returns a config object which uses the service account +// kubernetes gives to pods. It's intended for clients that expect to be +// running inside a pod running on kuberenetes. It will return an error if +// called from a process not running in a kubernetes environment. +func InClusterConfig() (*Config, error) { + host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT") + if len(host) == 0 || len(port) == 0 { + return nil, fmt.Errorf("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined") + } + + token, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/" + api.ServiceAccountTokenKey) + if err != nil { + return nil, err + } + tlsClientConfig := TLSClientConfig{} + rootCAFile := "/var/run/secrets/kubernetes.io/serviceaccount/" + api.ServiceAccountRootCAKey + if _, err := util.CertPoolFromFile(rootCAFile); err != nil { + glog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err) + } else { + tlsClientConfig.CAFile = rootCAFile + } + + return &Config{ + // TODO: switch to using cluster DNS. + Host: "https://" + net.JoinHostPort(host, port), + BearerToken: string(token), + TLSClientConfig: tlsClientConfig, + }, nil +} + +// IsConfigTransportTLS returns true if and only if the provided +// config will result in a protected connection to the server when it +// is passed to restclient.RESTClientFor(). Use to determine when to +// send credentials over the wire. +// +// Note: the Insecure flag is ignored when testing for this value, so MITM attacks are +// still possible. +func IsConfigTransportTLS(config Config) bool { + baseURL, _, err := defaultServerUrlFor(&config) + if err != nil { + return false + } + return baseURL.Scheme == "https" +} + +// LoadTLSFiles copies the data from the CertFile, KeyFile, and CAFile fields into the CertData, +// KeyData, and CAFile fields, or returns an error. If no error is returned, all three fields are +// either populated or were empty to start. +func LoadTLSFiles(c *Config) error { + var err error + c.CAData, err = dataFromSliceOrFile(c.CAData, c.CAFile) + if err != nil { + return err + } + + c.CertData, err = dataFromSliceOrFile(c.CertData, c.CertFile) + if err != nil { + return err + } + + c.KeyData, err = dataFromSliceOrFile(c.KeyData, c.KeyFile) + if err != nil { + return err + } + return nil +} + +// dataFromSliceOrFile returns data from the slice (if non-empty), or from the file, +// or an error if an error occurred reading the file +func dataFromSliceOrFile(data []byte, file string) ([]byte, error) { + if len(data) > 0 { + return data, nil + } + if len(file) > 0 { + fileData, err := ioutil.ReadFile(file) + if err != nil { + return []byte{}, err + } + return fileData, nil + } + return nil, nil +} + +func AddUserAgent(config *Config, userAgent string) *Config { + fullUserAgent := DefaultKubernetesUserAgent() + "/" + userAgent + config.UserAgent = fullUserAgent + return config +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/request.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/request.go similarity index 94% rename from Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/request.go rename to Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/request.go index 24c4bd195..0c9a10b7d 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/request.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/request.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package unversioned +package restclient import ( "bytes" @@ -292,22 +292,22 @@ func (r *Request) RequestURI(uri string) *Request { const ( // A constant that clients can use to refer in a field selector to the object name field. // Will be automatically emitted as the correct name for the API version. - NodeUnschedulable = "spec.unschedulable" - ObjectNameField = "metadata.name" - PodHost = "spec.nodeName" - PodStatus = "status.phase" - SecretType = "type" + nodeUnschedulable = "spec.unschedulable" + objectNameField = "metadata.name" + podHost = "spec.nodeName" + podStatus = "status.phase" + secretType = "type" - EventReason = "reason" - EventSource = "source" - EventType = "type" - EventInvolvedKind = "involvedObject.kind" - EventInvolvedNamespace = "involvedObject.namespace" - EventInvolvedName = "involvedObject.name" - EventInvolvedUID = "involvedObject.uid" - EventInvolvedAPIVersion = "involvedObject.apiVersion" - EventInvolvedResourceVersion = "involvedObject.resourceVersion" - EventInvolvedFieldPath = "involvedObject.fieldPath" + eventReason = "reason" + eventSource = "source" + eventType = "type" + eventInvolvedKind = "involvedObject.kind" + eventInvolvedNamespace = "involvedObject.namespace" + eventInvolvedName = "involvedObject.name" + eventInvolvedUID = "involvedObject.uid" + eventInvolvedAPIVersion = "involvedObject.apiVersion" + eventInvolvedResourceVersion = "involvedObject.resourceVersion" + eventInvolvedFieldPath = "involvedObject.fieldPath" ) type clientFieldNameToAPIVersionFieldName map[string]string @@ -350,34 +350,34 @@ func (v versionToResourceToFieldMapping) filterField(groupVersion *unversioned.G var fieldMappings = versionToResourceToFieldMapping{ v1.SchemeGroupVersion: resourceTypeToFieldMapping{ "nodes": clientFieldNameToAPIVersionFieldName{ - ObjectNameField: ObjectNameField, - NodeUnschedulable: NodeUnschedulable, + objectNameField: objectNameField, + nodeUnschedulable: nodeUnschedulable, }, "pods": clientFieldNameToAPIVersionFieldName{ - PodHost: PodHost, - PodStatus: PodStatus, + podHost: podHost, + podStatus: podStatus, }, "secrets": clientFieldNameToAPIVersionFieldName{ - SecretType: SecretType, + secretType: secretType, }, "serviceAccounts": clientFieldNameToAPIVersionFieldName{ - ObjectNameField: ObjectNameField, + objectNameField: objectNameField, }, "endpoints": clientFieldNameToAPIVersionFieldName{ - ObjectNameField: ObjectNameField, + objectNameField: objectNameField, }, "events": clientFieldNameToAPIVersionFieldName{ - ObjectNameField: ObjectNameField, - EventReason: EventReason, - EventSource: EventSource, - EventType: EventType, - EventInvolvedKind: EventInvolvedKind, - EventInvolvedNamespace: EventInvolvedNamespace, - EventInvolvedName: EventInvolvedName, - EventInvolvedUID: EventInvolvedUID, - EventInvolvedAPIVersion: EventInvolvedAPIVersion, - EventInvolvedResourceVersion: EventInvolvedResourceVersion, - EventInvolvedFieldPath: EventInvolvedFieldPath, + objectNameField: objectNameField, + eventReason: eventReason, + eventSource: eventSource, + eventType: eventType, + eventInvolvedKind: eventInvolvedKind, + eventInvolvedNamespace: eventInvolvedNamespace, + eventInvolvedName: eventInvolvedName, + eventInvolvedUID: eventInvolvedUID, + eventInvolvedAPIVersion: eventInvolvedAPIVersion, + eventInvolvedResourceVersion: eventInvolvedResourceVersion, + eventInvolvedFieldPath: eventInvolvedFieldPath, }, }, } @@ -624,7 +624,7 @@ func (r *Request) tryThrottle() { r.throttle.Accept() } if latency := time.Since(now); latency > longThrottleLatency { - glog.Warningf("Throttling request took %v, request: %s", latency, r.URL().String()) + glog.Warningf("Throttling request took %v, request: %s:%s", latency, r.verb, r.URL().String()) } } @@ -645,7 +645,7 @@ func (r *Request) Watch() (watch.Interface, error) { if client == nil { client = http.DefaultClient } - time.Sleep(r.backoffMgr.CalculateBackoff(r.URL())) + r.backoffMgr.Sleep(r.backoffMgr.CalculateBackoff(r.URL())) resp, err := client.Do(req) updateURLMetrics(r, resp, err) if r.baseURL != nil { @@ -710,7 +710,7 @@ func (r *Request) Stream() (io.ReadCloser, error) { if client == nil { client = http.DefaultClient } - time.Sleep(r.backoffMgr.CalculateBackoff(r.URL())) + r.backoffMgr.Sleep(r.backoffMgr.CalculateBackoff(r.URL())) resp, err := client.Do(req) updateURLMetrics(r, resp, err) if r.baseURL != nil { @@ -792,7 +792,7 @@ func (r *Request) request(fn func(*http.Request, *http.Response)) error { } req.Header = r.headers - time.Sleep(r.backoffMgr.CalculateBackoff(r.URL())) + r.backoffMgr.Sleep(r.backoffMgr.CalculateBackoff(r.URL())) resp, err := client.Do(req) updateURLMetrics(r, resp, err) if err != nil { @@ -812,7 +812,7 @@ func (r *Request) request(fn func(*http.Request, *http.Response)) error { retries++ if seconds, wait := checkWait(resp); wait && retries < maxRetries { glog.V(4).Infof("Got a Retry-After %s response for attempt %d to %v", seconds, retries, url) - time.Sleep(time.Duration(seconds) * time.Second) + r.backoffMgr.Sleep(time.Duration(seconds) * time.Second) return false } fn(req, resp) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/transport.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/transport.go similarity index 99% rename from Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/transport.go rename to Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/transport.go index 74469cae0..7d4b497c3 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/transport.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/transport.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package unversioned +package restclient import ( "crypto/tls" diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/url_utils.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/url_utils.go new file mode 100644 index 000000000..9a83d7874 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/url_utils.go @@ -0,0 +1,93 @@ +/* +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 restclient + +import ( + "fmt" + "net/url" + "path" + + "k8s.io/kubernetes/pkg/api/unversioned" +) + +// DefaultServerURL converts a host, host:port, or URL string to the default base server API path +// to use with a Client at a given API version following the standard conventions for a +// Kubernetes API. +func DefaultServerURL(host, apiPath string, groupVersion unversioned.GroupVersion, defaultTLS bool) (*url.URL, string, error) { + if host == "" { + return nil, "", fmt.Errorf("host must be a URL or a host:port pair") + } + base := host + hostURL, err := url.Parse(base) + if err != nil { + return nil, "", err + } + if hostURL.Scheme == "" { + scheme := "http://" + if defaultTLS { + scheme = "https://" + } + hostURL, err = url.Parse(scheme + base) + if err != nil { + return nil, "", err + } + if hostURL.Path != "" && hostURL.Path != "/" { + return nil, "", fmt.Errorf("host must be a URL or a host:port pair: %q", base) + } + } + + // hostURL.Path is optional; a non-empty Path is treated as a prefix that is to be applied to + // all URIs used to access the host. this is useful when there's a proxy in front of the + // apiserver that has relocated the apiserver endpoints, forwarding all requests from, for + // example, /a/b/c to the apiserver. in this case the Path should be /a/b/c. + // + // if running without a frontend proxy (that changes the location of the apiserver), then + // hostURL.Path should be blank. + // + // versionedAPIPath, a path relative to baseURL.Path, points to a versioned API base + versionedAPIPath := path.Join("/", apiPath) + + // Add the version to the end of the path + if len(groupVersion.Group) > 0 { + versionedAPIPath = path.Join(versionedAPIPath, groupVersion.Group, groupVersion.Version) + + } else { + versionedAPIPath = path.Join(versionedAPIPath, groupVersion.Version) + + } + + return hostURL, versionedAPIPath, nil +} + +// defaultServerUrlFor is shared between IsConfigTransportTLS and RESTClientFor. It +// requires Host and Version to be set prior to being called. +func defaultServerUrlFor(config *Config) (*url.URL, string, error) { + // TODO: move the default to secure when the apiserver supports TLS by default + // config.Insecure is taken to mean "I want HTTPS but don't bother checking the certs against a CA." + hasCA := len(config.CAFile) != 0 || len(config.CAData) != 0 + hasCert := len(config.CertFile) != 0 || len(config.CertData) != 0 + defaultTLS := hasCA || hasCert || config.Insecure + host := config.Host + if host == "" { + host = "localhost" + } + + if config.GroupVersion != nil { + return DefaultServerURL(host, config.APIPath, *config.GroupVersion, defaultTLS) + } + return DefaultServerURL(host, config.APIPath, unversioned.GroupVersion{}, defaultTLS) +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/urlbackoff.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/urlbackoff.go similarity index 93% rename from Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/urlbackoff.go rename to Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/urlbackoff.go index 331079bd7..7baba5c1f 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/urlbackoff.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/urlbackoff.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package unversioned +package restclient import ( "net/url" @@ -35,6 +35,7 @@ var maxResponseCode = 499 type BackoffManager interface { UpdateBackoff(actualUrl *url.URL, err error, responseCode int) CalculateBackoff(actualUrl *url.URL) time.Duration + Sleep(d time.Duration) } // URLBackoff struct implements the semantics on top of Backoff which @@ -54,6 +55,9 @@ func (n *NoBackoff) UpdateBackoff(actualUrl *url.URL, err error, responseCode in func (n *NoBackoff) CalculateBackoff(actualUrl *url.URL) time.Duration { return 0 * time.Second } +func (n *NoBackoff) Sleep(d time.Duration) { + return +} // Disable makes the backoff trivial, i.e., sets it to zero. This might be used // by tests which want to run 1000s of mock requests without slowing down. @@ -80,7 +84,7 @@ func (b *URLBackoff) baseUrlKey(rawurl *url.URL) string { func (b *URLBackoff) UpdateBackoff(actualUrl *url.URL, err error, responseCode int) { // range for retry counts that we store is [0,13] if responseCode > maxResponseCode || serverIsOverloadedSet.Has(responseCode) { - b.Backoff.Next(b.baseUrlKey(actualUrl), time.Now()) + b.Backoff.Next(b.baseUrlKey(actualUrl), b.Backoff.Clock.Now()) return } else if responseCode >= 300 || err != nil { glog.V(4).Infof("Client is returning errors: code %v, error %v", responseCode, err) @@ -95,3 +99,7 @@ func (b *URLBackoff) UpdateBackoff(actualUrl *url.URL, err error, responseCode i func (b *URLBackoff) CalculateBackoff(actualUrl *url.URL) time.Duration { return b.Backoff.Get(b.baseUrlKey(actualUrl)) } + +func (b *URLBackoff) Sleep(d time.Duration) { + b.Backoff.Clock.Sleep(d) +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/versions.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/versions.go new file mode 100644 index 000000000..e12c05c10 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/restclient/versions.go @@ -0,0 +1,88 @@ +/* +Copyright 2014 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 restclient + +import ( + "encoding/json" + "fmt" + "net/http" + "path" + + "k8s.io/kubernetes/pkg/api/unversioned" +) + +const ( + legacyAPIPath = "/api" + defaultAPIPath = "/apis" +) + +// TODO: Is this obsoleted by the discovery client? + +// ServerAPIVersions returns the GroupVersions supported by the API server. +// It creates a RESTClient based on the passed in config, but it doesn't rely +// on the Version and Codec of the config, because it uses AbsPath and +// takes the raw response. +func ServerAPIVersions(c *Config) (groupVersions []string, err error) { + transport, err := TransportFor(c) + if err != nil { + return nil, err + } + client := http.Client{Transport: transport} + + configCopy := *c + configCopy.GroupVersion = nil + configCopy.APIPath = "" + baseURL, _, err := defaultServerUrlFor(&configCopy) + if err != nil { + return nil, err + } + // Get the groupVersions exposed at /api + originalPath := baseURL.Path + baseURL.Path = path.Join(originalPath, legacyAPIPath) + resp, err := client.Get(baseURL.String()) + if err != nil { + return nil, err + } + var v unversioned.APIVersions + defer resp.Body.Close() + err = json.NewDecoder(resp.Body).Decode(&v) + if err != nil { + return nil, fmt.Errorf("unexpected error: %v", err) + } + + groupVersions = append(groupVersions, v.Versions...) + // Get the groupVersions exposed at /apis + baseURL.Path = path.Join(originalPath, defaultAPIPath) + resp2, err := client.Get(baseURL.String()) + if err != nil { + return nil, err + } + var apiGroupList unversioned.APIGroupList + defer resp2.Body.Close() + err = json.NewDecoder(resp2.Body).Decode(&apiGroupList) + if err != nil { + return nil, fmt.Errorf("unexpected error: %v", err) + } + + for _, g := range apiGroupList.Groups { + for _, gv := range g.Versions { + groupVersions = append(groupVersions, gv.GroupVersion) + } + } + + return groupVersions, nil +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/transport/cache_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/transport/cache_test.go deleted file mode 100644 index 8a602c147..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/transport/cache_test.go +++ /dev/null @@ -1,114 +0,0 @@ -/* -Copyright 2015 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 transport - -import ( - "net/http" - "testing" -) - -func TestTLSConfigKey(t *testing.T) { - // Make sure config fields that don't affect the tls config don't affect the cache key - identicalConfigurations := map[string]*Config{ - "empty": {}, - "basic": {Username: "bob", Password: "password"}, - "bearer": {BearerToken: "token"}, - "user agent": {UserAgent: "useragent"}, - "transport": {Transport: http.DefaultTransport}, - "wrap transport": {WrapTransport: func(http.RoundTripper) http.RoundTripper { return nil }}, - } - for nameA, valueA := range identicalConfigurations { - for nameB, valueB := range identicalConfigurations { - keyA, err := tlsConfigKey(valueA) - if err != nil { - t.Errorf("Unexpected error for %q: %v", nameA, err) - continue - } - keyB, err := tlsConfigKey(valueB) - if err != nil { - t.Errorf("Unexpected error for %q: %v", nameB, err) - continue - } - if keyA != keyB { - t.Errorf("Expected identical cache keys for %q and %q, got:\n\t%s\n\t%s", nameA, nameB, keyA, keyB) - continue - } - } - } - - // Make sure config fields that affect the tls config affect the cache key - uniqueConfigurations := map[string]*Config{ - "no tls": {}, - "insecure": {TLS: TLSConfig{Insecure: true}}, - "cadata 1": {TLS: TLSConfig{CAData: []byte{1}}}, - "cadata 2": {TLS: TLSConfig{CAData: []byte{2}}}, - "cert 1, key 1": { - TLS: TLSConfig{ - CertData: []byte{1}, - KeyData: []byte{1}, - }, - }, - "cert 1, key 2": { - TLS: TLSConfig{ - CertData: []byte{1}, - KeyData: []byte{2}, - }, - }, - "cert 2, key 1": { - TLS: TLSConfig{ - CertData: []byte{2}, - KeyData: []byte{1}, - }, - }, - "cert 2, key 2": { - TLS: TLSConfig{ - CertData: []byte{2}, - KeyData: []byte{2}, - }, - }, - "cadata 1, cert 1, key 1": { - TLS: TLSConfig{ - CAData: []byte{1}, - CertData: []byte{1}, - KeyData: []byte{1}, - }, - }, - } - for nameA, valueA := range uniqueConfigurations { - for nameB, valueB := range uniqueConfigurations { - // Don't compare to ourselves - if nameA == nameB { - continue - } - - keyA, err := tlsConfigKey(valueA) - if err != nil { - t.Errorf("Unexpected error for %q: %v", nameA, err) - continue - } - keyB, err := tlsConfigKey(valueB) - if err != nil { - t.Errorf("Unexpected error for %q: %v", nameB, err) - continue - } - if keyA == keyB { - t.Errorf("Expected unique cache keys for %q and %q, got:\n\t%s\n\t%s", nameA, nameB, keyA, keyB) - continue - } - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/transport/round_trippers_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/transport/round_trippers_test.go deleted file mode 100644 index 6e8e52f7d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/transport/round_trippers_test.go +++ /dev/null @@ -1,101 +0,0 @@ -/* -Copyright 2014 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 transport - -import ( - "net/http" - "testing" -) - -type testRoundTripper struct { - Request *http.Request - Response *http.Response - Err error -} - -func (rt *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - rt.Request = req - return rt.Response, rt.Err -} - -func TestBearerAuthRoundTripper(t *testing.T) { - rt := &testRoundTripper{} - req := &http.Request{} - NewBearerAuthRoundTripper("test", rt).RoundTrip(req) - if rt.Request == nil { - t.Fatalf("unexpected nil request: %v", rt) - } - if rt.Request == req { - t.Fatalf("round tripper should have copied request object: %#v", rt.Request) - } - if rt.Request.Header.Get("Authorization") != "Bearer test" { - t.Errorf("unexpected authorization header: %#v", rt.Request) - } -} - -func TestBasicAuthRoundTripper(t *testing.T) { - for n, tc := range map[string]struct { - user string - pass string - }{ - "basic": {user: "user", pass: "pass"}, - "no pass": {user: "user"}, - } { - rt := &testRoundTripper{} - req := &http.Request{} - NewBasicAuthRoundTripper(tc.user, tc.pass, rt).RoundTrip(req) - if rt.Request == nil { - t.Fatalf("%s: unexpected nil request: %v", n, rt) - } - if rt.Request == req { - t.Fatalf("%s: round tripper should have copied request object: %#v", n, rt.Request) - } - if user, pass, found := rt.Request.BasicAuth(); !found || user != tc.user || pass != tc.pass { - t.Errorf("%s: unexpected authorization header: %#v", n, rt.Request) - } - } -} - -func TestUserAgentRoundTripper(t *testing.T) { - rt := &testRoundTripper{} - req := &http.Request{ - Header: make(http.Header), - } - req.Header.Set("User-Agent", "other") - NewUserAgentRoundTripper("test", rt).RoundTrip(req) - if rt.Request == nil { - t.Fatalf("unexpected nil request: %v", rt) - } - if rt.Request != req { - t.Fatalf("round tripper should not have copied request object: %#v", rt.Request) - } - if rt.Request.Header.Get("User-Agent") != "other" { - t.Errorf("unexpected user agent header: %#v", rt.Request) - } - - req = &http.Request{} - NewUserAgentRoundTripper("test", rt).RoundTrip(req) - if rt.Request == nil { - t.Fatalf("unexpected nil request: %v", rt) - } - if rt.Request == req { - t.Fatalf("round tripper should have copied request object: %#v", rt.Request) - } - if rt.Request.Header.Get("User-Agent") != "test" { - t.Errorf("unexpected user agent header: %#v", rt.Request) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/transport/transport_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/transport/transport_test.go deleted file mode 100644 index ca04172d2..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/transport/transport_test.go +++ /dev/null @@ -1,204 +0,0 @@ -/* -Copyright 2015 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 transport - -import ( - "net/http" - "testing" -) - -const ( - rootCACert = `-----BEGIN CERTIFICATE----- -MIIC4DCCAcqgAwIBAgIBATALBgkqhkiG9w0BAQswIzEhMB8GA1UEAwwYMTAuMTMu -MTI5LjEwNkAxNDIxMzU5MDU4MB4XDTE1MDExNTIxNTczN1oXDTE2MDExNTIxNTcz -OFowIzEhMB8GA1UEAwwYMTAuMTMuMTI5LjEwNkAxNDIxMzU5MDU4MIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAunDRXGwsiYWGFDlWH6kjGun+PshDGeZX -xtx9lUnL8pIRWH3wX6f13PO9sktaOWW0T0mlo6k2bMlSLlSZgG9H6og0W6gLS3vq -s4VavZ6DbXIwemZG2vbRwsvR+t4G6Nbwelm6F8RFnA1Fwt428pavmNQ/wgYzo+T1 -1eS+HiN4ACnSoDSx3QRWcgBkB1g6VReofVjx63i0J+w8Q/41L9GUuLqquFxu6ZnH -60vTB55lHgFiDLjA1FkEz2dGvGh/wtnFlRvjaPC54JH2K1mPYAUXTreoeJtLJKX0 -ycoiyB24+zGCniUmgIsmQWRPaOPircexCp1BOeze82BT1LCZNTVaxQIDAQABoyMw -ITAOBgNVHQ8BAf8EBAMCAKQwDwYDVR0TAQH/BAUwAwEB/zALBgkqhkiG9w0BAQsD -ggEBADMxsUuAFlsYDpF4fRCzXXwrhbtj4oQwcHpbu+rnOPHCZupiafzZpDu+rw4x -YGPnCb594bRTQn4pAu3Ac18NbLD5pV3uioAkv8oPkgr8aUhXqiv7KdDiaWm6sbAL -EHiXVBBAFvQws10HMqMoKtO8f1XDNAUkWduakR/U6yMgvOPwS7xl0eUTqyRB6zGb -K55q2dejiFWaFqB/y78txzvz6UlOZKE44g2JAVoJVM6kGaxh33q8/FmrL4kuN3ut -W+MmJCVDvd4eEqPwbp7146ZWTqpIJ8lvA6wuChtqV8lhAPka2hD/LMqY8iXNmfXD -uml0obOEy+ON91k+SWTJ3ggmF/U= ------END CERTIFICATE-----` - - certData = `-----BEGIN CERTIFICATE----- -MIIC6jCCAdSgAwIBAgIBCzALBgkqhkiG9w0BAQswIzEhMB8GA1UEAwwYMTAuMTMu -MTI5LjEwNkAxNDIxMzU5MDU4MB4XDTE1MDExNTIyMDEzMVoXDTE2MDExNTIyMDEz -MlowGzEZMBcGA1UEAxMQb3BlbnNoaWZ0LWNsaWVudDCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBAKtdhz0+uCLXw5cSYns9rU/XifFSpb/x24WDdrm72S/v -b9BPYsAStiP148buylr1SOuNi8sTAZmlVDDIpIVwMLff+o2rKYDicn9fjbrTxTOj -lI4pHJBH+JU3AJ0tbajupioh70jwFS0oYpwtneg2zcnE2Z4l6mhrj2okrc5Q1/X2 -I2HChtIU4JYTisObtin10QKJX01CLfYXJLa8upWzKZ4/GOcHG+eAV3jXWoXidtjb -1Usw70amoTZ6mIVCkiu1QwCoa8+ycojGfZhvqMsAp1536ZcCul+Na+AbCv4zKS7F -kQQaImVrXdUiFansIoofGlw/JNuoKK6ssVpS5Ic3pgcCAwEAAaM1MDMwDgYDVR0P -AQH/BAQDAgCgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwCwYJ -KoZIhvcNAQELA4IBAQCKLREH7bXtXtZ+8vI6cjD7W3QikiArGqbl36bAhhWsJLp/ -p/ndKz39iFNaiZ3GlwIURWOOKx3y3GA0x9m8FR+Llthf0EQ8sUjnwaknWs0Y6DQ3 -jjPFZOpV3KPCFrdMJ3++E3MgwFC/Ih/N2ebFX9EcV9Vcc6oVWMdwT0fsrhu683rq -6GSR/3iVX1G/pmOiuaR0fNUaCyCfYrnI4zHBDgSfnlm3vIvN2lrsR/DQBakNL8DJ -HBgKxMGeUPoneBv+c8DMXIL0EhaFXRlBv9QW45/GiAIOuyFJ0i6hCtGZpJjq4OpQ -BRjCI+izPzFTjsxD4aORE+WOkyWFCGPWKfNejfw0 ------END CERTIFICATE-----` - - keyData = `-----BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAq12HPT64ItfDlxJiez2tT9eJ8VKlv/HbhYN2ubvZL+9v0E9i -wBK2I/Xjxu7KWvVI642LyxMBmaVUMMikhXAwt9/6jaspgOJyf1+NutPFM6OUjikc -kEf4lTcAnS1tqO6mKiHvSPAVLShinC2d6DbNycTZniXqaGuPaiStzlDX9fYjYcKG -0hTglhOKw5u2KfXRAolfTUIt9hcktry6lbMpnj8Y5wcb54BXeNdaheJ22NvVSzDv -RqahNnqYhUKSK7VDAKhrz7JyiMZ9mG+oywCnXnfplwK6X41r4BsK/jMpLsWRBBoi -ZWtd1SIVqewiih8aXD8k26gorqyxWlLkhzemBwIDAQABAoIBAD2XYRs3JrGHQUpU -FkdbVKZkvrSY0vAZOqBTLuH0zUv4UATb8487anGkWBjRDLQCgxH+jucPTrztekQK -aW94clo0S3aNtV4YhbSYIHWs1a0It0UdK6ID7CmdWkAj6s0T8W8lQT7C46mWYVLm -5mFnCTHi6aB42jZrqmEpC7sivWwuU0xqj3Ml8kkxQCGmyc9JjmCB4OrFFC8NNt6M -ObvQkUI6Z3nO4phTbpxkE1/9dT0MmPIF7GhHVzJMS+EyyRYUDllZ0wvVSOM3qZT0 -JMUaBerkNwm9foKJ1+dv2nMKZZbJajv7suUDCfU44mVeaEO+4kmTKSGCGjjTBGkr -7L1ySDECgYEA5ElIMhpdBzIivCuBIH8LlUeuzd93pqssO1G2Xg0jHtfM4tz7fyeI -cr90dc8gpli24dkSxzLeg3Tn3wIj/Bu64m2TpZPZEIlukYvgdgArmRIPQVxerYey -OkrfTNkxU1HXsYjLCdGcGXs5lmb+K/kuTcFxaMOs7jZi7La+jEONwf8CgYEAwCs/ -rUOOA0klDsWWisbivOiNPII79c9McZCNBqncCBfMUoiGe8uWDEO4TFHN60vFuVk9 -8PkwpCfvaBUX+ajvbafIfHxsnfk1M04WLGCeqQ/ym5Q4sQoQOcC1b1y9qc/xEWfg -nIUuia0ukYRpl7qQa3tNg+BNFyjypW8zukUAC/kCgYB1/Kojuxx5q5/oQVPrx73k -2bevD+B3c+DYh9MJqSCNwFtUpYIWpggPxoQan4LwdsmO0PKzocb/ilyNFj4i/vII -NToqSc/WjDFpaDIKyuu9oWfhECye45NqLWhb/6VOuu4QA/Nsj7luMhIBehnEAHW+ -GkzTKM8oD1PxpEG3nPKXYQKBgQC6AuMPRt3XBl1NkCrpSBy/uObFlFaP2Enpf39S -3OZ0Gv0XQrnSaL1kP8TMcz68rMrGX8DaWYsgytstR4W+jyy7WvZwsUu+GjTJ5aMG -77uEcEBpIi9CBzivfn7hPccE8ZgqPf+n4i6q66yxBJflW5xhvafJqDtW2LcPNbW/ -bvzdmQKBgExALRUXpq+5dbmkdXBHtvXdRDZ6rVmrnjy4nI5bPw+1GqQqk6uAR6B/ -F6NmLCQOO4PDG/cuatNHIr2FrwTmGdEL6ObLUGWn9Oer9gJhHVqqsY5I4sEPo4XX -stR0Yiw0buV6DL/moUO0HIM9Bjh96HJp+LxiIS6UCdIhMPp5HoQa ------END RSA PRIVATE KEY-----` -) - -func TestNew(t *testing.T) { - testCases := map[string]struct { - Config *Config - Err bool - TLS bool - Default bool - }{ - "default transport": { - Default: true, - Config: &Config{}, - }, - - "ca transport": { - TLS: true, - Config: &Config{ - TLS: TLSConfig{ - CAData: []byte(rootCACert), - }, - }, - }, - "bad ca file transport": { - Err: true, - Config: &Config{ - TLS: TLSConfig{ - CAFile: "invalid file", - }, - }, - }, - "ca data overriding bad ca file transport": { - TLS: true, - Config: &Config{ - TLS: TLSConfig{ - CAData: []byte(rootCACert), - CAFile: "invalid file", - }, - }, - }, - - "cert transport": { - TLS: true, - Config: &Config{ - TLS: TLSConfig{ - CAData: []byte(rootCACert), - CertData: []byte(certData), - KeyData: []byte(keyData), - }, - }, - }, - "bad cert data transport": { - Err: true, - Config: &Config{ - TLS: TLSConfig{ - CAData: []byte(rootCACert), - CertData: []byte(certData), - KeyData: []byte("bad key data"), - }, - }, - }, - "bad file cert transport": { - Err: true, - Config: &Config{ - TLS: TLSConfig{ - CAData: []byte(rootCACert), - CertData: []byte(certData), - KeyFile: "invalid file", - }, - }, - }, - "key data overriding bad file cert transport": { - TLS: true, - Config: &Config{ - TLS: TLSConfig{ - CAData: []byte(rootCACert), - CertData: []byte(certData), - KeyData: []byte(keyData), - KeyFile: "invalid file", - }, - }, - }, - } - for k, testCase := range testCases { - transport, err := New(testCase.Config) - switch { - case testCase.Err && err == nil: - t.Errorf("%s: unexpected non-error", k) - continue - case !testCase.Err && err != nil: - t.Errorf("%s: unexpected error: %v", k, err) - continue - } - - switch { - case testCase.Default && transport != http.DefaultTransport: - t.Errorf("%s: expected the default transport, got %#v", k, transport) - continue - case !testCase.Default && transport == http.DefaultTransport: - t.Errorf("%s: expected non-default transport, got %#v", k, transport) - continue - } - - // We only know how to check TLSConfig on http.Transports - if transport, ok := transport.(*http.Transport); ok { - switch { - case testCase.TLS && transport.TLSClientConfig == nil: - t.Errorf("%s: expected TLSClientConfig, got %#v", k, transport) - continue - case !testCase.TLS && transport.TLSClientConfig != nil: - t.Errorf("%s: expected no TLSClientConfig, got %#v", k, transport) - continue - } - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/discovery_client.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/discovery/discovery_client.go similarity index 90% rename from Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/discovery_client.go rename to Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/discovery/discovery_client.go index f43d728fb..fa4d94a34 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/discovery_client.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/discovery/discovery_client.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package unversioned +package discovery import ( "encoding/json" @@ -27,6 +27,7 @@ import ( "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/version" ) @@ -70,7 +71,7 @@ type SwaggerSchemaInterface interface { // DiscoveryClient implements the functions that discover server-supported API groups, // versions and resources. type DiscoveryClient struct { - *RESTClient + *restclient.RESTClient } // Convert unversioned.APIVersions to unversioned.APIGroup. APIVersions is used by legacy v1, so @@ -147,7 +148,7 @@ func (d *DiscoveryClient) ServerResources() (map[string]*unversioned.APIResource if err != nil { return nil, err } - groupVersions := ExtractGroupVersions(apiGroups) + groupVersions := unversioned.ExtractGroupVersions(apiGroups) result := map[string]*unversioned.APIResourceList{} for _, groupVersion := range groupVersions { resources, err := d.ServerResourcesForGroupVersion(groupVersion) @@ -183,7 +184,7 @@ func (d *DiscoveryClient) SwaggerSchema(version unversioned.GroupVersion) (*swag if err != nil { return nil, err } - groupVersions := ExtractGroupVersions(groupList) + groupVersions := unversioned.ExtractGroupVersions(groupList) // This check also takes care the case that kubectl is newer than the running endpoint if stringDoesntExistIn(version.String(), groupVersions) { return nil, fmt.Errorf("API version: %v is not supported by the server. Use one of: %v", version, groupVersions) @@ -207,27 +208,30 @@ func (d *DiscoveryClient) SwaggerSchema(version unversioned.GroupVersion) (*swag return &schema, nil } -func setDiscoveryDefaults(config *Config) error { +func setDiscoveryDefaults(config *restclient.Config) error { config.APIPath = "" config.GroupVersion = nil config.Codec = runtime.NoopEncoder{api.Codecs.UniversalDecoder()} + if len(config.UserAgent) == 0 { + config.UserAgent = restclient.DefaultKubernetesUserAgent() + } return nil } // NewDiscoveryClientForConfig creates a new DiscoveryClient for the given config. This client // can be used to discover supported resources in the API server. -func NewDiscoveryClientForConfig(c *Config) (*DiscoveryClient, error) { +func NewDiscoveryClientForConfig(c *restclient.Config) (*DiscoveryClient, error) { config := *c if err := setDiscoveryDefaults(&config); err != nil { return nil, err } - client, err := UnversionedRESTClientFor(&config) + client, err := restclient.UnversionedRESTClientFor(&config) return &DiscoveryClient{client}, err } // NewDiscoveryClientForConfig creates a new DiscoveryClient for the given config. If // there is an error, it panics. -func NewDiscoveryClientForConfigOrDie(c *Config) *DiscoveryClient { +func NewDiscoveryClientForConfigOrDie(c *restclient.Config) *DiscoveryClient { client, err := NewDiscoveryClientForConfig(c) if err != nil { panic(err) @@ -237,6 +241,15 @@ func NewDiscoveryClientForConfigOrDie(c *Config) *DiscoveryClient { } // New creates a new DiscoveryClient for the given RESTClient. -func NewDiscoveryClient(c *RESTClient) *DiscoveryClient { +func NewDiscoveryClient(c *restclient.RESTClient) *DiscoveryClient { return &DiscoveryClient{c} } + +func stringDoesntExistIn(str string, slice []string) bool { + for _, s := range slice { + if s == str { + return false + } + } + return true +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/core_client.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/core_client.go index c8119031e..dc3561c0b 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/core_client.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/core_client.go @@ -19,7 +19,7 @@ package unversioned import ( api "k8s.io/kubernetes/pkg/api" registered "k8s.io/kubernetes/pkg/apimachinery/registered" - unversioned "k8s.io/kubernetes/pkg/client/unversioned" + restclient "k8s.io/kubernetes/pkg/client/restclient" ) type CoreInterface interface { @@ -43,7 +43,7 @@ type CoreInterface interface { // CoreClient is used to interact with features provided by the Core group. type CoreClient struct { - *unversioned.RESTClient + *restclient.RESTClient } func (c *CoreClient) ComponentStatuses() ComponentStatusInterface { @@ -111,12 +111,12 @@ func (c *CoreClient) ServiceAccounts(namespace string) ServiceAccountInterface { } // NewForConfig creates a new CoreClient for the given config. -func NewForConfig(c *unversioned.Config) (*CoreClient, error) { +func NewForConfig(c *restclient.Config) (*CoreClient, error) { config := *c if err := setConfigDefaults(&config); err != nil { return nil, err } - client, err := unversioned.RESTClientFor(&config) + client, err := restclient.RESTClientFor(&config) if err != nil { return nil, err } @@ -125,7 +125,7 @@ func NewForConfig(c *unversioned.Config) (*CoreClient, error) { // NewForConfigOrDie creates a new CoreClient for the given config and // panics if there is an error in the config. -func NewForConfigOrDie(c *unversioned.Config) *CoreClient { +func NewForConfigOrDie(c *restclient.Config) *CoreClient { client, err := NewForConfig(c) if err != nil { panic(err) @@ -134,11 +134,11 @@ func NewForConfigOrDie(c *unversioned.Config) *CoreClient { } // New creates a new CoreClient for the given RESTClient. -func New(c *unversioned.RESTClient) *CoreClient { +func New(c *restclient.RESTClient) *CoreClient { return &CoreClient{c} } -func setConfigDefaults(config *unversioned.Config) error { +func setConfigDefaults(config *restclient.Config) error { // if core group is not registered, return an error g, err := registered.Group("") if err != nil { @@ -146,7 +146,7 @@ func setConfigDefaults(config *unversioned.Config) error { } config.APIPath = "/api" if config.UserAgent == "" { - config.UserAgent = unversioned.DefaultKubernetesUserAgent() + config.UserAgent = restclient.DefaultKubernetesUserAgent() } // TODO: Unconditionally set the config.Version, until we fix the config. //if config.Version == "" { diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/doc.go deleted file mode 100644 index dd6d4da71..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -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 fake has the automatically generated clients. -package fake diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_componentstatus.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_componentstatus.go deleted file mode 100644 index 478dd9dbf..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_componentstatus.go +++ /dev/null @@ -1,95 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeComponentStatuses implements ComponentStatusInterface -type FakeComponentStatuses struct { - Fake *FakeCore -} - -func (c *FakeComponentStatuses) Create(componentStatus *api.ComponentStatus) (result *api.ComponentStatus, err error) { - obj, err := c.Fake. - Invokes(core.NewRootCreateAction("componentstatuses", componentStatus), &api.ComponentStatus{}) - if obj == nil { - return nil, err - } - return obj.(*api.ComponentStatus), err -} - -func (c *FakeComponentStatuses) Update(componentStatus *api.ComponentStatus) (result *api.ComponentStatus, err error) { - obj, err := c.Fake. - Invokes(core.NewRootUpdateAction("componentstatuses", componentStatus), &api.ComponentStatus{}) - if obj == nil { - return nil, err - } - return obj.(*api.ComponentStatus), err -} - -func (c *FakeComponentStatuses) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewRootDeleteAction("componentstatuses", name), &api.ComponentStatus{}) - return err -} - -func (c *FakeComponentStatuses) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewRootDeleteCollectionAction("componentstatuses", listOptions) - - _, err := c.Fake.Invokes(action, &api.ComponentStatusList{}) - return err -} - -func (c *FakeComponentStatuses) Get(name string) (result *api.ComponentStatus, err error) { - obj, err := c.Fake. - Invokes(core.NewRootGetAction("componentstatuses", name), &api.ComponentStatus{}) - if obj == nil { - return nil, err - } - return obj.(*api.ComponentStatus), err -} - -func (c *FakeComponentStatuses) List(opts api.ListOptions) (result *api.ComponentStatusList, err error) { - obj, err := c.Fake. - Invokes(core.NewRootListAction("componentstatuses", opts), &api.ComponentStatusList{}) - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.ComponentStatusList{} - for _, item := range obj.(*api.ComponentStatusList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested componentStatuses. -func (c *FakeComponentStatuses) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewRootWatchAction("componentstatuses", opts)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_configmap.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_configmap.go deleted file mode 100644 index 34fa0b229..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_configmap.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeConfigMaps implements ConfigMapInterface -type FakeConfigMaps struct { - Fake *FakeCore - ns string -} - -func (c *FakeConfigMaps) Create(configMap *api.ConfigMap) (result *api.ConfigMap, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("configmaps", c.ns, configMap), &api.ConfigMap{}) - - if obj == nil { - return nil, err - } - return obj.(*api.ConfigMap), err -} - -func (c *FakeConfigMaps) Update(configMap *api.ConfigMap) (result *api.ConfigMap, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("configmaps", c.ns, configMap), &api.ConfigMap{}) - - if obj == nil { - return nil, err - } - return obj.(*api.ConfigMap), err -} - -func (c *FakeConfigMaps) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("configmaps", c.ns, name), &api.ConfigMap{}) - - return err -} - -func (c *FakeConfigMaps) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("configmaps", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &api.ConfigMapList{}) - return err -} - -func (c *FakeConfigMaps) Get(name string) (result *api.ConfigMap, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("configmaps", c.ns, name), &api.ConfigMap{}) - - if obj == nil { - return nil, err - } - return obj.(*api.ConfigMap), err -} - -func (c *FakeConfigMaps) List(opts api.ListOptions) (result *api.ConfigMapList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("configmaps", c.ns, opts), &api.ConfigMapList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.ConfigMapList{} - for _, item := range obj.(*api.ConfigMapList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested configMaps. -func (c *FakeConfigMaps) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("configmaps", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_core_client.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_core_client.go deleted file mode 100644 index afc6cf6f9..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_core_client.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -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 fake - -import ( - core "k8s.io/kubernetes/pkg/client/testing/core" - unversioned "k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned" -) - -type FakeCore struct { - *core.Fake -} - -func (c *FakeCore) ComponentStatuses() unversioned.ComponentStatusInterface { - return &FakeComponentStatuses{c} -} - -func (c *FakeCore) ConfigMaps(namespace string) unversioned.ConfigMapInterface { - return &FakeConfigMaps{c, namespace} -} - -func (c *FakeCore) Endpoints(namespace string) unversioned.EndpointsInterface { - return &FakeEndpoints{c, namespace} -} - -func (c *FakeCore) Events(namespace string) unversioned.EventInterface { - return &FakeEvents{c, namespace} -} - -func (c *FakeCore) LimitRanges(namespace string) unversioned.LimitRangeInterface { - return &FakeLimitRanges{c, namespace} -} - -func (c *FakeCore) Namespaces() unversioned.NamespaceInterface { - return &FakeNamespaces{c} -} - -func (c *FakeCore) Nodes() unversioned.NodeInterface { - return &FakeNodes{c} -} - -func (c *FakeCore) PersistentVolumes() unversioned.PersistentVolumeInterface { - return &FakePersistentVolumes{c} -} - -func (c *FakeCore) PersistentVolumeClaims(namespace string) unversioned.PersistentVolumeClaimInterface { - return &FakePersistentVolumeClaims{c, namespace} -} - -func (c *FakeCore) Pods(namespace string) unversioned.PodInterface { - return &FakePods{c, namespace} -} - -func (c *FakeCore) PodTemplates(namespace string) unversioned.PodTemplateInterface { - return &FakePodTemplates{c, namespace} -} - -func (c *FakeCore) ReplicationControllers(namespace string) unversioned.ReplicationControllerInterface { - return &FakeReplicationControllers{c, namespace} -} - -func (c *FakeCore) ResourceQuotas(namespace string) unversioned.ResourceQuotaInterface { - return &FakeResourceQuotas{c, namespace} -} - -func (c *FakeCore) Secrets(namespace string) unversioned.SecretInterface { - return &FakeSecrets{c, namespace} -} - -func (c *FakeCore) Services(namespace string) unversioned.ServiceInterface { - return &FakeServices{c, namespace} -} - -func (c *FakeCore) ServiceAccounts(namespace string) unversioned.ServiceAccountInterface { - return &FakeServiceAccounts{c, namespace} -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_endpoints.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_endpoints.go deleted file mode 100644 index cc25b6e06..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_endpoints.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeEndpoints implements EndpointsInterface -type FakeEndpoints struct { - Fake *FakeCore - ns string -} - -func (c *FakeEndpoints) Create(endpoints *api.Endpoints) (result *api.Endpoints, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("endpoints", c.ns, endpoints), &api.Endpoints{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Endpoints), err -} - -func (c *FakeEndpoints) Update(endpoints *api.Endpoints) (result *api.Endpoints, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("endpoints", c.ns, endpoints), &api.Endpoints{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Endpoints), err -} - -func (c *FakeEndpoints) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("endpoints", c.ns, name), &api.Endpoints{}) - - return err -} - -func (c *FakeEndpoints) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("endpoints", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &api.EndpointsList{}) - return err -} - -func (c *FakeEndpoints) Get(name string) (result *api.Endpoints, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("endpoints", c.ns, name), &api.Endpoints{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Endpoints), err -} - -func (c *FakeEndpoints) List(opts api.ListOptions) (result *api.EndpointsList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("endpoints", c.ns, opts), &api.EndpointsList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.EndpointsList{} - for _, item := range obj.(*api.EndpointsList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested endpoints. -func (c *FakeEndpoints) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("endpoints", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_event.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_event.go deleted file mode 100644 index a9f88153b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_event.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeEvents implements EventInterface -type FakeEvents struct { - Fake *FakeCore - ns string -} - -func (c *FakeEvents) Create(event *api.Event) (result *api.Event, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("events", c.ns, event), &api.Event{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Event), err -} - -func (c *FakeEvents) Update(event *api.Event) (result *api.Event, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("events", c.ns, event), &api.Event{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Event), err -} - -func (c *FakeEvents) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("events", c.ns, name), &api.Event{}) - - return err -} - -func (c *FakeEvents) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("events", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &api.EventList{}) - return err -} - -func (c *FakeEvents) Get(name string) (result *api.Event, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("events", c.ns, name), &api.Event{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Event), err -} - -func (c *FakeEvents) List(opts api.ListOptions) (result *api.EventList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("events", c.ns, opts), &api.EventList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.EventList{} - for _, item := range obj.(*api.EventList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested events. -func (c *FakeEvents) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("events", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_event_expansion.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_event_expansion.go deleted file mode 100644 index bc514d505..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_event_expansion.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2014 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 fake - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/testing/core" - "k8s.io/kubernetes/pkg/fields" - "k8s.io/kubernetes/pkg/runtime" -) - -func (c *FakeEvents) CreateWithEventNamespace(event *api.Event) (*api.Event, error) { - action := core.NewRootCreateAction("events", event) - if c.ns != "" { - action = core.NewCreateAction("events", c.ns, event) - } - obj, err := c.Fake.Invokes(action, event) - if obj == nil { - return nil, err - } - - return obj.(*api.Event), err -} - -// Update replaces an existing event. Returns the copy of the event the server returns, or an error. -func (c *FakeEvents) UpdateWithEventNamespace(event *api.Event) (*api.Event, error) { - action := core.NewRootUpdateAction("events", event) - if c.ns != "" { - action = core.NewUpdateAction("events", c.ns, event) - } - obj, err := c.Fake.Invokes(action, event) - if obj == nil { - return nil, err - } - - return obj.(*api.Event), err -} - -// Patch patches an existing event. Returns the copy of the event the server returns, or an error. -func (c *FakeEvents) Patch(event *api.Event, data []byte) (*api.Event, error) { - action := core.NewRootPatchAction("events", event) - if c.ns != "" { - action = core.NewPatchAction("events", c.ns, event) - } - obj, err := c.Fake.Invokes(action, event) - if obj == nil { - return nil, err - } - - return obj.(*api.Event), err -} - -// Search returns a list of events matching the specified object. -func (c *FakeEvents) Search(objOrRef runtime.Object) (*api.EventList, error) { - action := core.NewRootListAction("events", api.ListOptions{}) - if c.ns != "" { - action = core.NewListAction("events", c.ns, api.ListOptions{}) - } - obj, err := c.Fake.Invokes(action, &api.EventList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.EventList), err -} - -func (c *FakeEvents) GetFieldSelector(involvedObjectName, involvedObjectNamespace, involvedObjectKind, involvedObjectUID *string) fields.Selector { - action := core.GenericActionImpl{} - action.Verb = "get-field-selector" - action.Resource = "events" - - c.Fake.Invokes(action, nil) - return fields.Everything() -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_limitrange.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_limitrange.go deleted file mode 100644 index cab44ce4e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_limitrange.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeLimitRanges implements LimitRangeInterface -type FakeLimitRanges struct { - Fake *FakeCore - ns string -} - -func (c *FakeLimitRanges) Create(limitRange *api.LimitRange) (result *api.LimitRange, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("limitranges", c.ns, limitRange), &api.LimitRange{}) - - if obj == nil { - return nil, err - } - return obj.(*api.LimitRange), err -} - -func (c *FakeLimitRanges) Update(limitRange *api.LimitRange) (result *api.LimitRange, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("limitranges", c.ns, limitRange), &api.LimitRange{}) - - if obj == nil { - return nil, err - } - return obj.(*api.LimitRange), err -} - -func (c *FakeLimitRanges) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("limitranges", c.ns, name), &api.LimitRange{}) - - return err -} - -func (c *FakeLimitRanges) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("limitranges", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &api.LimitRangeList{}) - return err -} - -func (c *FakeLimitRanges) Get(name string) (result *api.LimitRange, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("limitranges", c.ns, name), &api.LimitRange{}) - - if obj == nil { - return nil, err - } - return obj.(*api.LimitRange), err -} - -func (c *FakeLimitRanges) List(opts api.ListOptions) (result *api.LimitRangeList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("limitranges", c.ns, opts), &api.LimitRangeList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.LimitRangeList{} - for _, item := range obj.(*api.LimitRangeList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested limitRanges. -func (c *FakeLimitRanges) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("limitranges", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_namespace.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_namespace.go deleted file mode 100644 index 78933814f..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_namespace.go +++ /dev/null @@ -1,104 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeNamespaces implements NamespaceInterface -type FakeNamespaces struct { - Fake *FakeCore -} - -func (c *FakeNamespaces) Create(namespace *api.Namespace) (result *api.Namespace, err error) { - obj, err := c.Fake. - Invokes(core.NewRootCreateAction("namespaces", namespace), &api.Namespace{}) - if obj == nil { - return nil, err - } - return obj.(*api.Namespace), err -} - -func (c *FakeNamespaces) Update(namespace *api.Namespace) (result *api.Namespace, err error) { - obj, err := c.Fake. - Invokes(core.NewRootUpdateAction("namespaces", namespace), &api.Namespace{}) - if obj == nil { - return nil, err - } - return obj.(*api.Namespace), err -} - -func (c *FakeNamespaces) UpdateStatus(namespace *api.Namespace) (*api.Namespace, error) { - obj, err := c.Fake. - Invokes(core.NewRootUpdateSubresourceAction("namespaces", "status", namespace), &api.Namespace{}) - if obj == nil { - return nil, err - } - return obj.(*api.Namespace), err -} - -func (c *FakeNamespaces) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewRootDeleteAction("namespaces", name), &api.Namespace{}) - return err -} - -func (c *FakeNamespaces) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewRootDeleteCollectionAction("namespaces", listOptions) - - _, err := c.Fake.Invokes(action, &api.NamespaceList{}) - return err -} - -func (c *FakeNamespaces) Get(name string) (result *api.Namespace, err error) { - obj, err := c.Fake. - Invokes(core.NewRootGetAction("namespaces", name), &api.Namespace{}) - if obj == nil { - return nil, err - } - return obj.(*api.Namespace), err -} - -func (c *FakeNamespaces) List(opts api.ListOptions) (result *api.NamespaceList, err error) { - obj, err := c.Fake. - Invokes(core.NewRootListAction("namespaces", opts), &api.NamespaceList{}) - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.NamespaceList{} - for _, item := range obj.(*api.NamespaceList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested namespaces. -func (c *FakeNamespaces) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewRootWatchAction("namespaces", opts)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_namespace_expansion.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_namespace_expansion.go deleted file mode 100644 index 8bb49ff2b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_namespace_expansion.go +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright 2014 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 fake - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/testing/core" -) - -func (c *FakeNamespaces) Finalize(namespace *api.Namespace) (*api.Namespace, error) { - action := core.CreateActionImpl{} - action.Verb = "create" - action.Resource = "namespaces" - action.Subresource = "finalize" - action.Object = namespace - - obj, err := c.Fake.Invokes(action, namespace) - if obj == nil { - return nil, err - } - - return obj.(*api.Namespace), err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_node.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_node.go deleted file mode 100644 index 8761c8772..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_node.go +++ /dev/null @@ -1,104 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeNodes implements NodeInterface -type FakeNodes struct { - Fake *FakeCore -} - -func (c *FakeNodes) Create(node *api.Node) (result *api.Node, err error) { - obj, err := c.Fake. - Invokes(core.NewRootCreateAction("nodes", node), &api.Node{}) - if obj == nil { - return nil, err - } - return obj.(*api.Node), err -} - -func (c *FakeNodes) Update(node *api.Node) (result *api.Node, err error) { - obj, err := c.Fake. - Invokes(core.NewRootUpdateAction("nodes", node), &api.Node{}) - if obj == nil { - return nil, err - } - return obj.(*api.Node), err -} - -func (c *FakeNodes) UpdateStatus(node *api.Node) (*api.Node, error) { - obj, err := c.Fake. - Invokes(core.NewRootUpdateSubresourceAction("nodes", "status", node), &api.Node{}) - if obj == nil { - return nil, err - } - return obj.(*api.Node), err -} - -func (c *FakeNodes) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewRootDeleteAction("nodes", name), &api.Node{}) - return err -} - -func (c *FakeNodes) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewRootDeleteCollectionAction("nodes", listOptions) - - _, err := c.Fake.Invokes(action, &api.NodeList{}) - return err -} - -func (c *FakeNodes) Get(name string) (result *api.Node, err error) { - obj, err := c.Fake. - Invokes(core.NewRootGetAction("nodes", name), &api.Node{}) - if obj == nil { - return nil, err - } - return obj.(*api.Node), err -} - -func (c *FakeNodes) List(opts api.ListOptions) (result *api.NodeList, err error) { - obj, err := c.Fake. - Invokes(core.NewRootListAction("nodes", opts), &api.NodeList{}) - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.NodeList{} - for _, item := range obj.(*api.NodeList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested nodes. -func (c *FakeNodes) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewRootWatchAction("nodes", opts)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_persistentvolume.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_persistentvolume.go deleted file mode 100644 index d3d8c79f5..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_persistentvolume.go +++ /dev/null @@ -1,104 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakePersistentVolumes implements PersistentVolumeInterface -type FakePersistentVolumes struct { - Fake *FakeCore -} - -func (c *FakePersistentVolumes) Create(persistentVolume *api.PersistentVolume) (result *api.PersistentVolume, err error) { - obj, err := c.Fake. - Invokes(core.NewRootCreateAction("persistentvolumes", persistentVolume), &api.PersistentVolume{}) - if obj == nil { - return nil, err - } - return obj.(*api.PersistentVolume), err -} - -func (c *FakePersistentVolumes) Update(persistentVolume *api.PersistentVolume) (result *api.PersistentVolume, err error) { - obj, err := c.Fake. - Invokes(core.NewRootUpdateAction("persistentvolumes", persistentVolume), &api.PersistentVolume{}) - if obj == nil { - return nil, err - } - return obj.(*api.PersistentVolume), err -} - -func (c *FakePersistentVolumes) UpdateStatus(persistentVolume *api.PersistentVolume) (*api.PersistentVolume, error) { - obj, err := c.Fake. - Invokes(core.NewRootUpdateSubresourceAction("persistentvolumes", "status", persistentVolume), &api.PersistentVolume{}) - if obj == nil { - return nil, err - } - return obj.(*api.PersistentVolume), err -} - -func (c *FakePersistentVolumes) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewRootDeleteAction("persistentvolumes", name), &api.PersistentVolume{}) - return err -} - -func (c *FakePersistentVolumes) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewRootDeleteCollectionAction("persistentvolumes", listOptions) - - _, err := c.Fake.Invokes(action, &api.PersistentVolumeList{}) - return err -} - -func (c *FakePersistentVolumes) Get(name string) (result *api.PersistentVolume, err error) { - obj, err := c.Fake. - Invokes(core.NewRootGetAction("persistentvolumes", name), &api.PersistentVolume{}) - if obj == nil { - return nil, err - } - return obj.(*api.PersistentVolume), err -} - -func (c *FakePersistentVolumes) List(opts api.ListOptions) (result *api.PersistentVolumeList, err error) { - obj, err := c.Fake. - Invokes(core.NewRootListAction("persistentvolumes", opts), &api.PersistentVolumeList{}) - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.PersistentVolumeList{} - for _, item := range obj.(*api.PersistentVolumeList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested persistentVolumes. -func (c *FakePersistentVolumes) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewRootWatchAction("persistentvolumes", opts)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_persistentvolumeclaim.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_persistentvolumeclaim.go deleted file mode 100644 index ba674f269..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_persistentvolumeclaim.go +++ /dev/null @@ -1,112 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakePersistentVolumeClaims implements PersistentVolumeClaimInterface -type FakePersistentVolumeClaims struct { - Fake *FakeCore - ns string -} - -func (c *FakePersistentVolumeClaims) Create(persistentVolumeClaim *api.PersistentVolumeClaim) (result *api.PersistentVolumeClaim, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("persistentvolumeclaims", c.ns, persistentVolumeClaim), &api.PersistentVolumeClaim{}) - - if obj == nil { - return nil, err - } - return obj.(*api.PersistentVolumeClaim), err -} - -func (c *FakePersistentVolumeClaims) Update(persistentVolumeClaim *api.PersistentVolumeClaim) (result *api.PersistentVolumeClaim, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("persistentvolumeclaims", c.ns, persistentVolumeClaim), &api.PersistentVolumeClaim{}) - - if obj == nil { - return nil, err - } - return obj.(*api.PersistentVolumeClaim), err -} - -func (c *FakePersistentVolumeClaims) UpdateStatus(persistentVolumeClaim *api.PersistentVolumeClaim) (*api.PersistentVolumeClaim, error) { - obj, err := c.Fake. - Invokes(core.NewUpdateSubresourceAction("persistentvolumeclaims", "status", c.ns, persistentVolumeClaim), &api.PersistentVolumeClaim{}) - - if obj == nil { - return nil, err - } - return obj.(*api.PersistentVolumeClaim), err -} - -func (c *FakePersistentVolumeClaims) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("persistentvolumeclaims", c.ns, name), &api.PersistentVolumeClaim{}) - - return err -} - -func (c *FakePersistentVolumeClaims) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("persistentvolumeclaims", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &api.PersistentVolumeClaimList{}) - return err -} - -func (c *FakePersistentVolumeClaims) Get(name string) (result *api.PersistentVolumeClaim, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("persistentvolumeclaims", c.ns, name), &api.PersistentVolumeClaim{}) - - if obj == nil { - return nil, err - } - return obj.(*api.PersistentVolumeClaim), err -} - -func (c *FakePersistentVolumeClaims) List(opts api.ListOptions) (result *api.PersistentVolumeClaimList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("persistentvolumeclaims", c.ns, opts), &api.PersistentVolumeClaimList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.PersistentVolumeClaimList{} - for _, item := range obj.(*api.PersistentVolumeClaimList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested persistentVolumeClaims. -func (c *FakePersistentVolumeClaims) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("persistentvolumeclaims", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_pod.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_pod.go deleted file mode 100644 index 6488c021d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_pod.go +++ /dev/null @@ -1,112 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakePods implements PodInterface -type FakePods struct { - Fake *FakeCore - ns string -} - -func (c *FakePods) Create(pod *api.Pod) (result *api.Pod, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("pods", c.ns, pod), &api.Pod{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Pod), err -} - -func (c *FakePods) Update(pod *api.Pod) (result *api.Pod, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("pods", c.ns, pod), &api.Pod{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Pod), err -} - -func (c *FakePods) UpdateStatus(pod *api.Pod) (*api.Pod, error) { - obj, err := c.Fake. - Invokes(core.NewUpdateSubresourceAction("pods", "status", c.ns, pod), &api.Pod{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Pod), err -} - -func (c *FakePods) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("pods", c.ns, name), &api.Pod{}) - - return err -} - -func (c *FakePods) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("pods", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &api.PodList{}) - return err -} - -func (c *FakePods) Get(name string) (result *api.Pod, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("pods", c.ns, name), &api.Pod{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Pod), err -} - -func (c *FakePods) List(opts api.ListOptions) (result *api.PodList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("pods", c.ns, opts), &api.PodList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.PodList{} - for _, item := range obj.(*api.PodList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested pods. -func (c *FakePods) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("pods", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_pod_expansion.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_pod_expansion.go deleted file mode 100644 index 034ecf2b2..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_pod_expansion.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2014 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 fake - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/testing/core" - client "k8s.io/kubernetes/pkg/client/unversioned" -) - -func (c *FakePods) Bind(binding *api.Binding) error { - action := core.CreateActionImpl{} - action.Verb = "create" - action.Resource = "pods" - action.Subresource = "bindings" - action.Object = binding - - _, err := c.Fake.Invokes(action, binding) - return err -} - -func (c *FakePods) GetLogs(name string, opts *api.PodLogOptions) *client.Request { - action := core.GenericActionImpl{} - action.Verb = "get" - action.Namespace = c.ns - action.Resource = "pod" - action.Subresource = "logs" - action.Value = opts - - _, _ = c.Fake.Invokes(action, &api.Pod{}) - return &client.Request{} -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_podtemplate.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_podtemplate.go deleted file mode 100644 index b900a113c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_podtemplate.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakePodTemplates implements PodTemplateInterface -type FakePodTemplates struct { - Fake *FakeCore - ns string -} - -func (c *FakePodTemplates) Create(podTemplate *api.PodTemplate) (result *api.PodTemplate, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("podtemplates", c.ns, podTemplate), &api.PodTemplate{}) - - if obj == nil { - return nil, err - } - return obj.(*api.PodTemplate), err -} - -func (c *FakePodTemplates) Update(podTemplate *api.PodTemplate) (result *api.PodTemplate, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("podtemplates", c.ns, podTemplate), &api.PodTemplate{}) - - if obj == nil { - return nil, err - } - return obj.(*api.PodTemplate), err -} - -func (c *FakePodTemplates) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("podtemplates", c.ns, name), &api.PodTemplate{}) - - return err -} - -func (c *FakePodTemplates) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("podtemplates", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &api.PodTemplateList{}) - return err -} - -func (c *FakePodTemplates) Get(name string) (result *api.PodTemplate, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("podtemplates", c.ns, name), &api.PodTemplate{}) - - if obj == nil { - return nil, err - } - return obj.(*api.PodTemplate), err -} - -func (c *FakePodTemplates) List(opts api.ListOptions) (result *api.PodTemplateList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("podtemplates", c.ns, opts), &api.PodTemplateList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.PodTemplateList{} - for _, item := range obj.(*api.PodTemplateList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested podTemplates. -func (c *FakePodTemplates) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("podtemplates", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_replicationcontroller.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_replicationcontroller.go deleted file mode 100644 index 205f09456..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_replicationcontroller.go +++ /dev/null @@ -1,112 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeReplicationControllers implements ReplicationControllerInterface -type FakeReplicationControllers struct { - Fake *FakeCore - ns string -} - -func (c *FakeReplicationControllers) Create(replicationController *api.ReplicationController) (result *api.ReplicationController, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("replicationcontrollers", c.ns, replicationController), &api.ReplicationController{}) - - if obj == nil { - return nil, err - } - return obj.(*api.ReplicationController), err -} - -func (c *FakeReplicationControllers) Update(replicationController *api.ReplicationController) (result *api.ReplicationController, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("replicationcontrollers", c.ns, replicationController), &api.ReplicationController{}) - - if obj == nil { - return nil, err - } - return obj.(*api.ReplicationController), err -} - -func (c *FakeReplicationControllers) UpdateStatus(replicationController *api.ReplicationController) (*api.ReplicationController, error) { - obj, err := c.Fake. - Invokes(core.NewUpdateSubresourceAction("replicationcontrollers", "status", c.ns, replicationController), &api.ReplicationController{}) - - if obj == nil { - return nil, err - } - return obj.(*api.ReplicationController), err -} - -func (c *FakeReplicationControllers) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("replicationcontrollers", c.ns, name), &api.ReplicationController{}) - - return err -} - -func (c *FakeReplicationControllers) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("replicationcontrollers", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &api.ReplicationControllerList{}) - return err -} - -func (c *FakeReplicationControllers) Get(name string) (result *api.ReplicationController, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("replicationcontrollers", c.ns, name), &api.ReplicationController{}) - - if obj == nil { - return nil, err - } - return obj.(*api.ReplicationController), err -} - -func (c *FakeReplicationControllers) List(opts api.ListOptions) (result *api.ReplicationControllerList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("replicationcontrollers", c.ns, opts), &api.ReplicationControllerList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.ReplicationControllerList{} - for _, item := range obj.(*api.ReplicationControllerList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested replicationControllers. -func (c *FakeReplicationControllers) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("replicationcontrollers", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_resourcequota.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_resourcequota.go deleted file mode 100644 index 056e61ed5..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_resourcequota.go +++ /dev/null @@ -1,112 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeResourceQuotas implements ResourceQuotaInterface -type FakeResourceQuotas struct { - Fake *FakeCore - ns string -} - -func (c *FakeResourceQuotas) Create(resourceQuota *api.ResourceQuota) (result *api.ResourceQuota, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("resourcequotas", c.ns, resourceQuota), &api.ResourceQuota{}) - - if obj == nil { - return nil, err - } - return obj.(*api.ResourceQuota), err -} - -func (c *FakeResourceQuotas) Update(resourceQuota *api.ResourceQuota) (result *api.ResourceQuota, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("resourcequotas", c.ns, resourceQuota), &api.ResourceQuota{}) - - if obj == nil { - return nil, err - } - return obj.(*api.ResourceQuota), err -} - -func (c *FakeResourceQuotas) UpdateStatus(resourceQuota *api.ResourceQuota) (*api.ResourceQuota, error) { - obj, err := c.Fake. - Invokes(core.NewUpdateSubresourceAction("resourcequotas", "status", c.ns, resourceQuota), &api.ResourceQuota{}) - - if obj == nil { - return nil, err - } - return obj.(*api.ResourceQuota), err -} - -func (c *FakeResourceQuotas) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("resourcequotas", c.ns, name), &api.ResourceQuota{}) - - return err -} - -func (c *FakeResourceQuotas) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("resourcequotas", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &api.ResourceQuotaList{}) - return err -} - -func (c *FakeResourceQuotas) Get(name string) (result *api.ResourceQuota, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("resourcequotas", c.ns, name), &api.ResourceQuota{}) - - if obj == nil { - return nil, err - } - return obj.(*api.ResourceQuota), err -} - -func (c *FakeResourceQuotas) List(opts api.ListOptions) (result *api.ResourceQuotaList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("resourcequotas", c.ns, opts), &api.ResourceQuotaList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.ResourceQuotaList{} - for _, item := range obj.(*api.ResourceQuotaList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested resourceQuotas. -func (c *FakeResourceQuotas) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("resourcequotas", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_secret.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_secret.go deleted file mode 100644 index 2f09be6e5..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_secret.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeSecrets implements SecretInterface -type FakeSecrets struct { - Fake *FakeCore - ns string -} - -func (c *FakeSecrets) Create(secret *api.Secret) (result *api.Secret, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("secrets", c.ns, secret), &api.Secret{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Secret), err -} - -func (c *FakeSecrets) Update(secret *api.Secret) (result *api.Secret, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("secrets", c.ns, secret), &api.Secret{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Secret), err -} - -func (c *FakeSecrets) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("secrets", c.ns, name), &api.Secret{}) - - return err -} - -func (c *FakeSecrets) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("secrets", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &api.SecretList{}) - return err -} - -func (c *FakeSecrets) Get(name string) (result *api.Secret, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("secrets", c.ns, name), &api.Secret{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Secret), err -} - -func (c *FakeSecrets) List(opts api.ListOptions) (result *api.SecretList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("secrets", c.ns, opts), &api.SecretList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.SecretList{} - for _, item := range obj.(*api.SecretList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested secrets. -func (c *FakeSecrets) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("secrets", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_service.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_service.go deleted file mode 100644 index 2cf38901c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_service.go +++ /dev/null @@ -1,112 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeServices implements ServiceInterface -type FakeServices struct { - Fake *FakeCore - ns string -} - -func (c *FakeServices) Create(service *api.Service) (result *api.Service, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("services", c.ns, service), &api.Service{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Service), err -} - -func (c *FakeServices) Update(service *api.Service) (result *api.Service, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("services", c.ns, service), &api.Service{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Service), err -} - -func (c *FakeServices) UpdateStatus(service *api.Service) (*api.Service, error) { - obj, err := c.Fake. - Invokes(core.NewUpdateSubresourceAction("services", "status", c.ns, service), &api.Service{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Service), err -} - -func (c *FakeServices) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("services", c.ns, name), &api.Service{}) - - return err -} - -func (c *FakeServices) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("services", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &api.ServiceList{}) - return err -} - -func (c *FakeServices) Get(name string) (result *api.Service, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("services", c.ns, name), &api.Service{}) - - if obj == nil { - return nil, err - } - return obj.(*api.Service), err -} - -func (c *FakeServices) List(opts api.ListOptions) (result *api.ServiceList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("services", c.ns, opts), &api.ServiceList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.ServiceList{} - for _, item := range obj.(*api.ServiceList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested services. -func (c *FakeServices) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("services", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_service_expansion.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_service_expansion.go deleted file mode 100644 index 7d72d1dfe..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_service_expansion.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2014 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 fake - -import ( - "k8s.io/kubernetes/pkg/client/testing/core" - client "k8s.io/kubernetes/pkg/client/unversioned" -) - -func (c *FakeServices) ProxyGet(scheme, name, port, path string, params map[string]string) client.ResponseWrapper { - return c.Fake.InvokesProxy(core.NewProxyGetAction("services", c.ns, scheme, name, port, path, params)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_serviceaccount.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_serviceaccount.go deleted file mode 100644 index 61d7a04f5..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/fake/fake_serviceaccount.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeServiceAccounts implements ServiceAccountInterface -type FakeServiceAccounts struct { - Fake *FakeCore - ns string -} - -func (c *FakeServiceAccounts) Create(serviceAccount *api.ServiceAccount) (result *api.ServiceAccount, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("serviceaccounts", c.ns, serviceAccount), &api.ServiceAccount{}) - - if obj == nil { - return nil, err - } - return obj.(*api.ServiceAccount), err -} - -func (c *FakeServiceAccounts) Update(serviceAccount *api.ServiceAccount) (result *api.ServiceAccount, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("serviceaccounts", c.ns, serviceAccount), &api.ServiceAccount{}) - - if obj == nil { - return nil, err - } - return obj.(*api.ServiceAccount), err -} - -func (c *FakeServiceAccounts) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("serviceaccounts", c.ns, name), &api.ServiceAccount{}) - - return err -} - -func (c *FakeServiceAccounts) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("serviceaccounts", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &api.ServiceAccountList{}) - return err -} - -func (c *FakeServiceAccounts) Get(name string) (result *api.ServiceAccount, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("serviceaccounts", c.ns, name), &api.ServiceAccount{}) - - if obj == nil { - return nil, err - } - return obj.(*api.ServiceAccount), err -} - -func (c *FakeServiceAccounts) List(opts api.ListOptions) (result *api.ServiceAccountList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("serviceaccounts", c.ns, opts), &api.ServiceAccountList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.ServiceAccountList{} - for _, item := range obj.(*api.ServiceAccountList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested serviceAccounts. -func (c *FakeServiceAccounts) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("serviceaccounts", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/pod_expansion.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/pod_expansion.go index 0a46431d5..8ebd29d30 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/pod_expansion.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/pod_expansion.go @@ -18,13 +18,13 @@ package unversioned import ( "k8s.io/kubernetes/pkg/api" - unversioned "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/client/restclient" ) // The PodExpansion interface allows manually adding extra methods to the PodInterface. type PodExpansion interface { Bind(binding *api.Binding) error - GetLogs(name string, opts *api.PodLogOptions) *unversioned.Request + GetLogs(name string, opts *api.PodLogOptions) *restclient.Request } // Bind applies the provided binding to the named pod in the current namespace (binding.Namespace is ignored). @@ -33,6 +33,6 @@ func (c *pods) Bind(binding *api.Binding) error { } // Get constructs a request for getting the logs for a pod -func (c *pods) GetLogs(name string, opts *api.PodLogOptions) *unversioned.Request { +func (c *pods) GetLogs(name string, opts *api.PodLogOptions) *restclient.Request { return c.client.Get().Namespace(c.ns).Name(name).Resource("pods").SubResource("log").VersionedParams(opts, api.ParameterCodec) } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/service_expansion.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/service_expansion.go index b9fb6af86..89266e6cd 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/service_expansion.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned/service_expansion.go @@ -17,17 +17,17 @@ limitations under the License. package unversioned import ( - "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/util/net" ) // The ServiceExpansion interface allows manually adding extra methods to the ServiceInterface. type ServiceExpansion interface { - ProxyGet(scheme, name, port, path string, params map[string]string) unversioned.ResponseWrapper + ProxyGet(scheme, name, port, path string, params map[string]string) restclient.ResponseWrapper } // ProxyGet returns a response of the service by calling it through the proxy. -func (c *services) ProxyGet(scheme, name, port, path string, params map[string]string) unversioned.ResponseWrapper { +func (c *services) ProxyGet(scheme, name, port, path string, params map[string]string) restclient.ResponseWrapper { request := c.client.Get(). Prefix("proxy"). Namespace(c.ns). diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/extensions_client.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/extensions_client.go index 6780a5c03..220dd69b5 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/extensions_client.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/extensions_client.go @@ -19,7 +19,7 @@ package unversioned import ( api "k8s.io/kubernetes/pkg/api" registered "k8s.io/kubernetes/pkg/apimachinery/registered" - unversioned "k8s.io/kubernetes/pkg/client/unversioned" + restclient "k8s.io/kubernetes/pkg/client/restclient" ) type ExtensionsInterface interface { @@ -35,7 +35,7 @@ type ExtensionsInterface interface { // ExtensionsClient is used to interact with features provided by the Extensions group. type ExtensionsClient struct { - *unversioned.RESTClient + *restclient.RESTClient } func (c *ExtensionsClient) DaemonSets(namespace string) DaemonSetInterface { @@ -71,12 +71,12 @@ func (c *ExtensionsClient) ThirdPartyResources(namespace string) ThirdPartyResou } // NewForConfig creates a new ExtensionsClient for the given config. -func NewForConfig(c *unversioned.Config) (*ExtensionsClient, error) { +func NewForConfig(c *restclient.Config) (*ExtensionsClient, error) { config := *c if err := setConfigDefaults(&config); err != nil { return nil, err } - client, err := unversioned.RESTClientFor(&config) + client, err := restclient.RESTClientFor(&config) if err != nil { return nil, err } @@ -85,7 +85,7 @@ func NewForConfig(c *unversioned.Config) (*ExtensionsClient, error) { // NewForConfigOrDie creates a new ExtensionsClient for the given config and // panics if there is an error in the config. -func NewForConfigOrDie(c *unversioned.Config) *ExtensionsClient { +func NewForConfigOrDie(c *restclient.Config) *ExtensionsClient { client, err := NewForConfig(c) if err != nil { panic(err) @@ -94,11 +94,11 @@ func NewForConfigOrDie(c *unversioned.Config) *ExtensionsClient { } // New creates a new ExtensionsClient for the given RESTClient. -func New(c *unversioned.RESTClient) *ExtensionsClient { +func New(c *restclient.RESTClient) *ExtensionsClient { return &ExtensionsClient{c} } -func setConfigDefaults(config *unversioned.Config) error { +func setConfigDefaults(config *restclient.Config) error { // if extensions group is not registered, return an error g, err := registered.Group("extensions") if err != nil { @@ -106,7 +106,7 @@ func setConfigDefaults(config *unversioned.Config) error { } config.APIPath = "/apis" if config.UserAgent == "" { - config.UserAgent = unversioned.DefaultKubernetesUserAgent() + config.UserAgent = restclient.DefaultKubernetesUserAgent() } // TODO: Unconditionally set the config.Version, until we fix the config. //if config.Version == "" { diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/doc.go deleted file mode 100644 index dd6d4da71..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -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 fake has the automatically generated clients. -package fake diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_daemonset.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_daemonset.go deleted file mode 100644 index 7de9f927f..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_daemonset.go +++ /dev/null @@ -1,113 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - extensions "k8s.io/kubernetes/pkg/apis/extensions" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeDaemonSets implements DaemonSetInterface -type FakeDaemonSets struct { - Fake *FakeExtensions - ns string -} - -func (c *FakeDaemonSets) Create(daemonSet *extensions.DaemonSet) (result *extensions.DaemonSet, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("daemonsets", c.ns, daemonSet), &extensions.DaemonSet{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.DaemonSet), err -} - -func (c *FakeDaemonSets) Update(daemonSet *extensions.DaemonSet) (result *extensions.DaemonSet, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("daemonsets", c.ns, daemonSet), &extensions.DaemonSet{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.DaemonSet), err -} - -func (c *FakeDaemonSets) UpdateStatus(daemonSet *extensions.DaemonSet) (*extensions.DaemonSet, error) { - obj, err := c.Fake. - Invokes(core.NewUpdateSubresourceAction("daemonsets", "status", c.ns, daemonSet), &extensions.DaemonSet{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.DaemonSet), err -} - -func (c *FakeDaemonSets) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("daemonsets", c.ns, name), &extensions.DaemonSet{}) - - return err -} - -func (c *FakeDaemonSets) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("daemonsets", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &extensions.DaemonSetList{}) - return err -} - -func (c *FakeDaemonSets) Get(name string) (result *extensions.DaemonSet, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("daemonsets", c.ns, name), &extensions.DaemonSet{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.DaemonSet), err -} - -func (c *FakeDaemonSets) List(opts api.ListOptions) (result *extensions.DaemonSetList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("daemonsets", c.ns, opts), &extensions.DaemonSetList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &extensions.DaemonSetList{} - for _, item := range obj.(*extensions.DaemonSetList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested daemonSets. -func (c *FakeDaemonSets) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("daemonsets", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_deployment.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_deployment.go deleted file mode 100644 index 748968a9d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_deployment.go +++ /dev/null @@ -1,113 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - extensions "k8s.io/kubernetes/pkg/apis/extensions" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeDeployments implements DeploymentInterface -type FakeDeployments struct { - Fake *FakeExtensions - ns string -} - -func (c *FakeDeployments) Create(deployment *extensions.Deployment) (result *extensions.Deployment, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("deployments", c.ns, deployment), &extensions.Deployment{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.Deployment), err -} - -func (c *FakeDeployments) Update(deployment *extensions.Deployment) (result *extensions.Deployment, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("deployments", c.ns, deployment), &extensions.Deployment{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.Deployment), err -} - -func (c *FakeDeployments) UpdateStatus(deployment *extensions.Deployment) (*extensions.Deployment, error) { - obj, err := c.Fake. - Invokes(core.NewUpdateSubresourceAction("deployments", "status", c.ns, deployment), &extensions.Deployment{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.Deployment), err -} - -func (c *FakeDeployments) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("deployments", c.ns, name), &extensions.Deployment{}) - - return err -} - -func (c *FakeDeployments) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("deployments", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &extensions.DeploymentList{}) - return err -} - -func (c *FakeDeployments) Get(name string) (result *extensions.Deployment, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("deployments", c.ns, name), &extensions.Deployment{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.Deployment), err -} - -func (c *FakeDeployments) List(opts api.ListOptions) (result *extensions.DeploymentList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("deployments", c.ns, opts), &extensions.DeploymentList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &extensions.DeploymentList{} - for _, item := range obj.(*extensions.DeploymentList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested deployments. -func (c *FakeDeployments) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("deployments", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_deployment_expansion.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_deployment_expansion.go deleted file mode 100644 index 3edc64c01..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_deployment_expansion.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -Copyright 2014 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 fake - -import ( - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/client/testing/core" -) - -func (c *FakeDeployments) Rollback(deploymentRollback *extensions.DeploymentRollback) error { - action := core.CreateActionImpl{} - action.Verb = "create" - action.Resource = "deployments" - action.Subresource = "rollback" - action.Object = deploymentRollback - - _, err := c.Fake.Invokes(action, deploymentRollback) - return err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_extensions_client.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_extensions_client.go deleted file mode 100644 index 2f7fb0423..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_extensions_client.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -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 fake - -import ( - core "k8s.io/kubernetes/pkg/client/testing/core" - unversioned "k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned" -) - -type FakeExtensions struct { - *core.Fake -} - -func (c *FakeExtensions) DaemonSets(namespace string) unversioned.DaemonSetInterface { - return &FakeDaemonSets{c, namespace} -} - -func (c *FakeExtensions) Deployments(namespace string) unversioned.DeploymentInterface { - return &FakeDeployments{c, namespace} -} - -func (c *FakeExtensions) HorizontalPodAutoscalers(namespace string) unversioned.HorizontalPodAutoscalerInterface { - return &FakeHorizontalPodAutoscalers{c, namespace} -} - -func (c *FakeExtensions) Ingresses(namespace string) unversioned.IngressInterface { - return &FakeIngresses{c, namespace} -} - -func (c *FakeExtensions) Jobs(namespace string) unversioned.JobInterface { - return &FakeJobs{c, namespace} -} - -func (c *FakeExtensions) ReplicaSets(namespace string) unversioned.ReplicaSetInterface { - return &FakeReplicaSets{c, namespace} -} - -func (c *FakeExtensions) Scales(namespace string) unversioned.ScaleInterface { - return &FakeScales{c, namespace} -} - -func (c *FakeExtensions) ThirdPartyResources(namespace string) unversioned.ThirdPartyResourceInterface { - return &FakeThirdPartyResources{c, namespace} -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_horizontalpodautoscaler.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_horizontalpodautoscaler.go deleted file mode 100644 index 71b5cf322..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_horizontalpodautoscaler.go +++ /dev/null @@ -1,113 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - extensions "k8s.io/kubernetes/pkg/apis/extensions" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeHorizontalPodAutoscalers implements HorizontalPodAutoscalerInterface -type FakeHorizontalPodAutoscalers struct { - Fake *FakeExtensions - ns string -} - -func (c *FakeHorizontalPodAutoscalers) Create(horizontalPodAutoscaler *extensions.HorizontalPodAutoscaler) (result *extensions.HorizontalPodAutoscaler, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("horizontalpodautoscalers", c.ns, horizontalPodAutoscaler), &extensions.HorizontalPodAutoscaler{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.HorizontalPodAutoscaler), err -} - -func (c *FakeHorizontalPodAutoscalers) Update(horizontalPodAutoscaler *extensions.HorizontalPodAutoscaler) (result *extensions.HorizontalPodAutoscaler, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("horizontalpodautoscalers", c.ns, horizontalPodAutoscaler), &extensions.HorizontalPodAutoscaler{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.HorizontalPodAutoscaler), err -} - -func (c *FakeHorizontalPodAutoscalers) UpdateStatus(horizontalPodAutoscaler *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error) { - obj, err := c.Fake. - Invokes(core.NewUpdateSubresourceAction("horizontalpodautoscalers", "status", c.ns, horizontalPodAutoscaler), &extensions.HorizontalPodAutoscaler{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.HorizontalPodAutoscaler), err -} - -func (c *FakeHorizontalPodAutoscalers) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("horizontalpodautoscalers", c.ns, name), &extensions.HorizontalPodAutoscaler{}) - - return err -} - -func (c *FakeHorizontalPodAutoscalers) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("horizontalpodautoscalers", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &extensions.HorizontalPodAutoscalerList{}) - return err -} - -func (c *FakeHorizontalPodAutoscalers) Get(name string) (result *extensions.HorizontalPodAutoscaler, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("horizontalpodautoscalers", c.ns, name), &extensions.HorizontalPodAutoscaler{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.HorizontalPodAutoscaler), err -} - -func (c *FakeHorizontalPodAutoscalers) List(opts api.ListOptions) (result *extensions.HorizontalPodAutoscalerList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("horizontalpodautoscalers", c.ns, opts), &extensions.HorizontalPodAutoscalerList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &extensions.HorizontalPodAutoscalerList{} - for _, item := range obj.(*extensions.HorizontalPodAutoscalerList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested horizontalPodAutoscalers. -func (c *FakeHorizontalPodAutoscalers) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("horizontalpodautoscalers", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_ingress.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_ingress.go deleted file mode 100644 index a331644e4..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_ingress.go +++ /dev/null @@ -1,113 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - extensions "k8s.io/kubernetes/pkg/apis/extensions" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeIngresses implements IngressInterface -type FakeIngresses struct { - Fake *FakeExtensions - ns string -} - -func (c *FakeIngresses) Create(ingress *extensions.Ingress) (result *extensions.Ingress, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("ingresses", c.ns, ingress), &extensions.Ingress{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.Ingress), err -} - -func (c *FakeIngresses) Update(ingress *extensions.Ingress) (result *extensions.Ingress, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("ingresses", c.ns, ingress), &extensions.Ingress{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.Ingress), err -} - -func (c *FakeIngresses) UpdateStatus(ingress *extensions.Ingress) (*extensions.Ingress, error) { - obj, err := c.Fake. - Invokes(core.NewUpdateSubresourceAction("ingresses", "status", c.ns, ingress), &extensions.Ingress{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.Ingress), err -} - -func (c *FakeIngresses) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("ingresses", c.ns, name), &extensions.Ingress{}) - - return err -} - -func (c *FakeIngresses) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("ingresses", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &extensions.IngressList{}) - return err -} - -func (c *FakeIngresses) Get(name string) (result *extensions.Ingress, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("ingresses", c.ns, name), &extensions.Ingress{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.Ingress), err -} - -func (c *FakeIngresses) List(opts api.ListOptions) (result *extensions.IngressList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("ingresses", c.ns, opts), &extensions.IngressList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &extensions.IngressList{} - for _, item := range obj.(*extensions.IngressList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested ingresses. -func (c *FakeIngresses) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("ingresses", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_job.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_job.go deleted file mode 100644 index c1875c006..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_job.go +++ /dev/null @@ -1,113 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - extensions "k8s.io/kubernetes/pkg/apis/extensions" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeJobs implements JobInterface -type FakeJobs struct { - Fake *FakeExtensions - ns string -} - -func (c *FakeJobs) Create(job *extensions.Job) (result *extensions.Job, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("jobs", c.ns, job), &extensions.Job{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.Job), err -} - -func (c *FakeJobs) Update(job *extensions.Job) (result *extensions.Job, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("jobs", c.ns, job), &extensions.Job{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.Job), err -} - -func (c *FakeJobs) UpdateStatus(job *extensions.Job) (*extensions.Job, error) { - obj, err := c.Fake. - Invokes(core.NewUpdateSubresourceAction("jobs", "status", c.ns, job), &extensions.Job{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.Job), err -} - -func (c *FakeJobs) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("jobs", c.ns, name), &extensions.Job{}) - - return err -} - -func (c *FakeJobs) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("jobs", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &extensions.JobList{}) - return err -} - -func (c *FakeJobs) Get(name string) (result *extensions.Job, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("jobs", c.ns, name), &extensions.Job{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.Job), err -} - -func (c *FakeJobs) List(opts api.ListOptions) (result *extensions.JobList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("jobs", c.ns, opts), &extensions.JobList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &extensions.JobList{} - for _, item := range obj.(*extensions.JobList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested jobs. -func (c *FakeJobs) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("jobs", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_replicaset.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_replicaset.go deleted file mode 100644 index d861326b7..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_replicaset.go +++ /dev/null @@ -1,113 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - extensions "k8s.io/kubernetes/pkg/apis/extensions" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeReplicaSets implements ReplicaSetInterface -type FakeReplicaSets struct { - Fake *FakeExtensions - ns string -} - -func (c *FakeReplicaSets) Create(replicaSet *extensions.ReplicaSet) (result *extensions.ReplicaSet, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("replicasets", c.ns, replicaSet), &extensions.ReplicaSet{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.ReplicaSet), err -} - -func (c *FakeReplicaSets) Update(replicaSet *extensions.ReplicaSet) (result *extensions.ReplicaSet, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("replicasets", c.ns, replicaSet), &extensions.ReplicaSet{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.ReplicaSet), err -} - -func (c *FakeReplicaSets) UpdateStatus(replicaSet *extensions.ReplicaSet) (*extensions.ReplicaSet, error) { - obj, err := c.Fake. - Invokes(core.NewUpdateSubresourceAction("replicasets", "status", c.ns, replicaSet), &extensions.ReplicaSet{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.ReplicaSet), err -} - -func (c *FakeReplicaSets) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("replicasets", c.ns, name), &extensions.ReplicaSet{}) - - return err -} - -func (c *FakeReplicaSets) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("replicasets", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &extensions.ReplicaSetList{}) - return err -} - -func (c *FakeReplicaSets) Get(name string) (result *extensions.ReplicaSet, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("replicasets", c.ns, name), &extensions.ReplicaSet{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.ReplicaSet), err -} - -func (c *FakeReplicaSets) List(opts api.ListOptions) (result *extensions.ReplicaSetList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("replicasets", c.ns, opts), &extensions.ReplicaSetList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &extensions.ReplicaSetList{} - for _, item := range obj.(*extensions.ReplicaSetList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested replicaSets. -func (c *FakeReplicaSets) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("replicasets", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_scale_expansion.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_scale_expansion.go deleted file mode 100644 index 8c52e409b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_scale_expansion.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2015 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 fake - -import ( - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/client/testing/core" -) - -func (c *FakeScales) Get(kind string, name string) (result *extensions.Scale, err error) { - action := core.GetActionImpl{} - action.Verb = "get" - action.Namespace = c.ns - action.Resource = kind - action.Subresource = "scale" - action.Name = name - obj, err := c.Fake.Invokes(action, &extensions.Scale{}) - result = obj.(*extensions.Scale) - return -} - -func (c *FakeScales) Update(kind string, scale *extensions.Scale) (result *extensions.Scale, err error) { - action := core.UpdateActionImpl{} - action.Verb = "update" - action.Namespace = c.ns - action.Resource = kind - action.Subresource = "scale" - action.Object = scale - obj, err := c.Fake.Invokes(action, scale) - result = obj.(*extensions.Scale) - return -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_thirdpartyresource.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_thirdpartyresource.go deleted file mode 100644 index 9a005d570..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_thirdpartyresource.go +++ /dev/null @@ -1,103 +0,0 @@ -/* -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 fake - -import ( - api "k8s.io/kubernetes/pkg/api" - extensions "k8s.io/kubernetes/pkg/apis/extensions" - core "k8s.io/kubernetes/pkg/client/testing/core" - labels "k8s.io/kubernetes/pkg/labels" - watch "k8s.io/kubernetes/pkg/watch" -) - -// FakeThirdPartyResources implements ThirdPartyResourceInterface -type FakeThirdPartyResources struct { - Fake *FakeExtensions - ns string -} - -func (c *FakeThirdPartyResources) Create(thirdPartyResource *extensions.ThirdPartyResource) (result *extensions.ThirdPartyResource, err error) { - obj, err := c.Fake. - Invokes(core.NewCreateAction("thirdpartyresources", c.ns, thirdPartyResource), &extensions.ThirdPartyResource{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.ThirdPartyResource), err -} - -func (c *FakeThirdPartyResources) Update(thirdPartyResource *extensions.ThirdPartyResource) (result *extensions.ThirdPartyResource, err error) { - obj, err := c.Fake. - Invokes(core.NewUpdateAction("thirdpartyresources", c.ns, thirdPartyResource), &extensions.ThirdPartyResource{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.ThirdPartyResource), err -} - -func (c *FakeThirdPartyResources) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake. - Invokes(core.NewDeleteAction("thirdpartyresources", c.ns, name), &extensions.ThirdPartyResource{}) - - return err -} - -func (c *FakeThirdPartyResources) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := core.NewDeleteCollectionAction("thirdpartyresources", c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &extensions.ThirdPartyResourceList{}) - return err -} - -func (c *FakeThirdPartyResources) Get(name string) (result *extensions.ThirdPartyResource, err error) { - obj, err := c.Fake. - Invokes(core.NewGetAction("thirdpartyresources", c.ns, name), &extensions.ThirdPartyResource{}) - - if obj == nil { - return nil, err - } - return obj.(*extensions.ThirdPartyResource), err -} - -func (c *FakeThirdPartyResources) List(opts api.ListOptions) (result *extensions.ThirdPartyResourceList, err error) { - obj, err := c.Fake. - Invokes(core.NewListAction("thirdpartyresources", c.ns, opts), &extensions.ThirdPartyResourceList{}) - - if obj == nil { - return nil, err - } - - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &extensions.ThirdPartyResourceList{} - for _, item := range obj.(*extensions.ThirdPartyResourceList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested thirdPartyResources. -func (c *FakeThirdPartyResources) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(core.NewWatchAction("thirdpartyresources", c.ns, opts)) - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/auth/clientauth.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/auth/clientauth.go index 16acc00ce..64b3ef6be 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/auth/clientauth.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/auth/clientauth.go @@ -68,7 +68,7 @@ import ( "io/ioutil" "os" - client "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/client/restclient" ) // Info holds Kubernetes API authorization config. It is intended @@ -104,8 +104,8 @@ func LoadFromFile(path string) (*Info, error) { // MergeWithConfig returns a copy of a client.Config with values from the Info. // The fields of client.Config with a corresponding field in the Info are set // with the value from the Info. -func (info Info) MergeWithConfig(c client.Config) (client.Config, error) { - var config client.Config = c +func (info Info) MergeWithConfig(c restclient.Config) (restclient.Config, error) { + var config restclient.Config = c config.Username = info.User config.Password = info.Password config.CAFile = info.CAFile diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/auth/clientauth_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/auth/clientauth_test.go deleted file mode 100644 index a99c5d94a..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/auth/clientauth_test.go +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright 2014 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 auth_test - -import ( - "io/ioutil" - "os" - "reflect" - "testing" - - clientauth "k8s.io/kubernetes/pkg/client/unversioned/auth" -) - -func TestLoadFromFile(t *testing.T) { - loadAuthInfoTests := []struct { - authData string - authInfo *clientauth.Info - expectErr bool - }{ - { - `{"user": "user", "password": "pass"}`, - &clientauth.Info{User: "user", Password: "pass"}, - false, - }, - { - "", nil, true, - }, - } - for _, loadAuthInfoTest := range loadAuthInfoTests { - tt := loadAuthInfoTest - aifile, err := ioutil.TempFile("", "testAuthInfo") - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - if tt.authData != "missing" { - defer os.Remove(aifile.Name()) - defer aifile.Close() - _, err = aifile.WriteString(tt.authData) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - } else { - aifile.Close() - os.Remove(aifile.Name()) - } - authInfo, err := clientauth.LoadFromFile(aifile.Name()) - gotErr := err != nil - if gotErr != tt.expectErr { - t.Errorf("expected errorness: %v, actual errorness: %v", tt.expectErr, gotErr) - } - if !reflect.DeepEqual(authInfo, tt.authInfo) { - t.Errorf("Expected %v, got %v", tt.authInfo, authInfo) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/autoscaling.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/autoscaling.go index 9445c0c6c..c3ec19810 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/autoscaling.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/autoscaling.go @@ -20,6 +20,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/autoscaling" + "k8s.io/kubernetes/pkg/client/restclient" ) type AutoscalingInterface interface { @@ -28,26 +29,26 @@ type AutoscalingInterface interface { // AutoscalingClient is used to interact with Kubernetes autoscaling features. type AutoscalingClient struct { - *RESTClient + *restclient.RESTClient } func (c *AutoscalingClient) HorizontalPodAutoscalers(namespace string) HorizontalPodAutoscalerInterface { return newHorizontalPodAutoscalersV1(c, namespace) } -func NewAutoscaling(c *Config) (*AutoscalingClient, error) { +func NewAutoscaling(c *restclient.Config) (*AutoscalingClient, error) { config := *c if err := setAutoscalingDefaults(&config); err != nil { return nil, err } - client, err := RESTClientFor(&config) + client, err := restclient.RESTClientFor(&config) if err != nil { return nil, err } return &AutoscalingClient{client}, nil } -func NewAutoscalingOrDie(c *Config) *AutoscalingClient { +func NewAutoscalingOrDie(c *restclient.Config) *AutoscalingClient { client, err := NewAutoscaling(c) if err != nil { panic(err) @@ -55,7 +56,7 @@ func NewAutoscalingOrDie(c *Config) *AutoscalingClient { return client } -func setAutoscalingDefaults(config *Config) error { +func setAutoscalingDefaults(config *restclient.Config) error { // if autoscaling group is not registered, return an error g, err := registered.Group(autoscaling.GroupName) if err != nil { @@ -63,7 +64,7 @@ func setAutoscalingDefaults(config *Config) error { } config.APIPath = defaultAPIPath if config.UserAgent == "" { - config.UserAgent = DefaultKubernetesUserAgent() + config.UserAgent = restclient.DefaultKubernetesUserAgent() } // TODO: Unconditionally set the config.Version, until we fix the config. //if config.Version == "" { diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/batch.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/batch.go new file mode 100644 index 000000000..a432e4c78 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/batch.go @@ -0,0 +1,83 @@ +/* +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 unversioned + +import ( + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/apis/batch" + "k8s.io/kubernetes/pkg/client/restclient" +) + +type BatchInterface interface { + JobsNamespacer +} + +// BatchClient is used to interact with Kubernetes batch features. +type BatchClient struct { + *restclient.RESTClient +} + +func (c *BatchClient) Jobs(namespace string) JobInterface { + return newJobsV1(c, namespace) +} + +func NewBatch(c *restclient.Config) (*BatchClient, error) { + config := *c + if err := setBatchDefaults(&config); err != nil { + return nil, err + } + client, err := restclient.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &BatchClient{client}, nil +} + +func NewBatchOrDie(c *restclient.Config) *BatchClient { + client, err := NewBatch(c) + if err != nil { + panic(err) + } + return client +} + +func setBatchDefaults(config *restclient.Config) error { + // if batch group is not registered, return an error + g, err := registered.Group(batch.GroupName) + if err != nil { + return err + } + config.APIPath = defaultAPIPath + if config.UserAgent == "" { + config.UserAgent = restclient.DefaultKubernetesUserAgent() + } + // TODO: Unconditionally set the config.Version, until we fix the config. + //if config.Version == "" { + copyGroupVersion := g.GroupVersion + config.GroupVersion = ©GroupVersion + //} + + config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion) + if config.QPS == 0 { + config.QPS = 5 + } + if config.Burst == 0 { + config.Burst = 10 + } + return nil +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/client.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/client.go index 8a891c44a..b897bc230 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/client.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/client.go @@ -20,6 +20,9 @@ import ( "net" "net/url" "strings" + + "k8s.io/kubernetes/pkg/client/restclient" + "k8s.io/kubernetes/pkg/client/typed/discovery" ) // Interface holds the methods for clients of Kubernetes, @@ -42,8 +45,9 @@ type Interface interface { ComponentStatusesInterface ConfigMapsNamespacer Autoscaling() AutoscalingInterface + Batch() BatchInterface Extensions() ExtensionsInterface - Discovery() DiscoveryInterface + Discovery() discovery.DiscoveryInterface } func (c *Client) ReplicationControllers(namespace string) ReplicationControllerInterface { @@ -111,19 +115,11 @@ func (c *Client) ConfigMaps(namespace string) ConfigMapsInterface { // Client is the implementation of a Kubernetes client. type Client struct { - *RESTClient + *restclient.RESTClient *AutoscalingClient + *BatchClient *ExtensionsClient - *DiscoveryClient -} - -func stringDoesntExistIn(str string, slice []string) bool { - for _, s := range slice { - if s == str { - return false - } - } - return true + *discovery.DiscoveryClient } // IsTimeout tests if this is a timeout error in the underlying transport. @@ -152,10 +148,14 @@ func (c *Client) Autoscaling() AutoscalingInterface { return c.AutoscalingClient } +func (c *Client) Batch() BatchInterface { + return c.BatchClient +} + func (c *Client) Extensions() ExtensionsInterface { return c.ExtensionsClient } -func (c *Client) Discovery() DiscoveryInterface { +func (c *Client) Discovery() discovery.DiscoveryInterface { return c.DiscoveryClient } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/client_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/client_test.go deleted file mode 100644 index 487591c5d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/client_test.go +++ /dev/null @@ -1,308 +0,0 @@ -/* -Copyright 2014 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 unversioned - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "reflect" - "testing" - - "github.com/emicklei/go-restful/swagger" - - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/v1" - "k8s.io/kubernetes/pkg/version" -) - -func TestGetServerVersion(t *testing.T) { - expect := version.Info{ - Major: "foo", - Minor: "bar", - GitCommit: "baz", - } - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - output, err := json.Marshal(expect) - if err != nil { - t.Errorf("unexpected encoding error: %v", err) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write(output) - })) - // TODO: Uncomment when fix #19254 - // defer server.Close() - client := NewOrDie(&Config{Host: server.URL}) - - got, err := client.Discovery().ServerVersion() - if err != nil { - t.Fatalf("unexpected encoding error: %v", err) - } - if e, a := expect, *got; !reflect.DeepEqual(e, a) { - t.Errorf("expected %v, got %v", e, a) - } -} - -func TestGetServerGroupsWithV1Server(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - var obj interface{} - switch req.URL.Path { - case "/api": - obj = &unversioned.APIVersions{ - Versions: []string{ - "v1", - }, - } - default: - w.WriteHeader(http.StatusNotFound) - return - } - output, err := json.Marshal(obj) - if err != nil { - t.Fatalf("unexpected encoding error: %v", err) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write(output) - })) - // TODO: Uncomment when fix #19254 - // defer server.Close() - client := NewOrDie(&Config{Host: server.URL}) - // ServerGroups should not return an error even if server returns error at /api and /apis - apiGroupList, err := client.Discovery().ServerGroups() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - groupVersions := ExtractGroupVersions(apiGroupList) - if !reflect.DeepEqual(groupVersions, []string{"v1"}) { - t.Errorf("expected: %q, got: %q", []string{"v1"}, groupVersions) - } -} - -func TestGetServerResourcesWithV1Server(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - var obj interface{} - switch req.URL.Path { - case "/api": - obj = &unversioned.APIVersions{ - Versions: []string{ - "v1", - }, - } - default: - w.WriteHeader(http.StatusNotFound) - return - } - output, err := json.Marshal(obj) - if err != nil { - t.Errorf("unexpected encoding error: %v", err) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write(output) - })) - // TODO: Uncomment when fix #19254 - // defer server.Close() - client := NewOrDie(&Config{Host: server.URL}) - // ServerResources should not return an error even if server returns error at /api/v1. - resourceMap, err := client.Discovery().ServerResources() - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if _, found := resourceMap["v1"]; !found { - t.Errorf("missing v1 in resource map") - } - -} - -func TestGetServerResources(t *testing.T) { - stable := unversioned.APIResourceList{ - GroupVersion: "v1", - APIResources: []unversioned.APIResource{ - {"pods", true, "Pod"}, - {"services", true, "Service"}, - {"namespaces", false, "Namespace"}, - }, - } - beta := unversioned.APIResourceList{ - GroupVersion: "extensions/v1", - APIResources: []unversioned.APIResource{ - {"deployments", true, "Deployment"}, - {"ingresses", true, "Ingress"}, - {"jobs", true, "Job"}, - }, - } - tests := []struct { - resourcesList *unversioned.APIResourceList - path string - request string - expectErr bool - }{ - { - resourcesList: &stable, - path: "/api/v1", - request: "v1", - expectErr: false, - }, - { - resourcesList: &beta, - path: "/apis/extensions/v1beta1", - request: "extensions/v1beta1", - expectErr: false, - }, - { - resourcesList: &stable, - path: "/api/v1", - request: "foobar", - expectErr: true, - }, - } - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - var list interface{} - switch req.URL.Path { - case "/api/v1": - list = &stable - case "/apis/extensions/v1beta1": - list = &beta - case "/api": - list = &unversioned.APIVersions{ - Versions: []string{ - "v1", - }, - } - case "/apis": - list = &unversioned.APIGroupList{ - Groups: []unversioned.APIGroup{ - { - Versions: []unversioned.GroupVersionForDiscovery{ - {GroupVersion: "extensions/v1beta1"}, - }, - }, - }, - } - default: - t.Logf("unexpected request: %s", req.URL.Path) - w.WriteHeader(http.StatusNotFound) - return - } - output, err := json.Marshal(list) - if err != nil { - t.Errorf("unexpected encoding error: %v", err) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write(output) - })) - // TODO: Uncomment when fix #19254 - // defer server.Close() - client := NewOrDie(&Config{Host: server.URL}) - for _, test := range tests { - got, err := client.Discovery().ServerResourcesForGroupVersion(test.request) - if test.expectErr { - if err == nil { - t.Error("unexpected non-error") - } - continue - } - if err != nil { - t.Errorf("unexpected error: %v", err) - continue - } - if !reflect.DeepEqual(got, test.resourcesList) { - t.Errorf("expected:\n%v\ngot:\n%v\n", test.resourcesList, got) - } - } - - resourceMap, err := client.Discovery().ServerResources() - if err != nil { - t.Errorf("unexpected error: %v", err) - } - for _, api := range []string{"v1", "extensions/v1beta1"} { - if _, found := resourceMap[api]; !found { - t.Errorf("missing expected api: %s", api) - } - } -} - -func swaggerSchemaFakeServer() (*httptest.Server, error) { - request := 1 - var sErr error - - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - var resp interface{} - if request == 1 { - resp = unversioned.APIVersions{Versions: []string{"v1", "v2", "v3"}} - request++ - } else { - resp = swagger.ApiDeclaration{} - } - output, err := json.Marshal(resp) - if err != nil { - sErr = err - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write(output) - })) - return server, sErr -} - -func TestGetSwaggerSchema(t *testing.T) { - expect := swagger.ApiDeclaration{} - - server, err := swaggerSchemaFakeServer() - if err != nil { - t.Errorf("unexpected encoding error: %v", err) - } - // TODO: Uncomment when fix #19254 - // defer server.Close() - - client := NewOrDie(&Config{Host: server.URL}) - got, err := client.Discovery().SwaggerSchema(v1.SchemeGroupVersion) - if err != nil { - t.Fatalf("unexpected encoding error: %v", err) - } - if e, a := expect, *got; !reflect.DeepEqual(e, a) { - t.Errorf("expected %v, got %v", e, a) - } -} - -func TestGetSwaggerSchemaFail(t *testing.T) { - expErr := "API version: api.group/v4 is not supported by the server. Use one of: [v1 v2 v3]" - - server, err := swaggerSchemaFakeServer() - if err != nil { - t.Errorf("unexpected encoding error: %v", err) - } - // TODO: Uncomment when fix #19254 - // defer server.Close() - - client := NewOrDie(&Config{Host: server.URL}) - got, err := client.Discovery().SwaggerSchema(unversioned.GroupVersion{Group: "api.group", Version: "v4"}) - if got != nil { - t.Fatalf("unexpected response: %v", got) - } - if err.Error() != expErr { - t.Errorf("expected an error, got %v", err) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/helpers_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/helpers_test.go deleted file mode 100644 index ca970f3b4..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/helpers_test.go +++ /dev/null @@ -1,301 +0,0 @@ -/* -Copyright 2015 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 api - -import ( - "fmt" - "io/ioutil" - "os" - "reflect" - "testing" - - "github.com/ghodss/yaml" -) - -func newMergedConfig(certFile, certContent, keyFile, keyContent, caFile, caContent string, t *testing.T) Config { - if err := ioutil.WriteFile(certFile, []byte(certContent), 0644); err != nil { - t.Errorf("unexpected error: %v", err) - } - if err := ioutil.WriteFile(keyFile, []byte(keyContent), 0600); err != nil { - t.Errorf("unexpected error: %v", err) - } - if err := ioutil.WriteFile(caFile, []byte(caContent), 0644); err != nil { - t.Errorf("unexpected error: %v", err) - } - - return Config{ - AuthInfos: map[string]*AuthInfo{ - "red-user": {Token: "red-token", ClientCertificateData: []byte(certContent), ClientKeyData: []byte(keyContent)}, - "blue-user": {Token: "blue-token", ClientCertificate: certFile, ClientKey: keyFile}}, - Clusters: map[string]*Cluster{ - "cow-cluster": {Server: "http://cow.org:8080", CertificateAuthorityData: []byte(caContent)}, - "chicken-cluster": {Server: "http://chicken.org:8080", CertificateAuthority: caFile}}, - Contexts: map[string]*Context{ - "federal-context": {AuthInfo: "red-user", Cluster: "cow-cluster"}, - "shaker-context": {AuthInfo: "blue-user", Cluster: "chicken-cluster"}}, - CurrentContext: "federal-context", - } -} - -func TestMinifySuccess(t *testing.T) { - certFile, _ := ioutil.TempFile("", "") - defer os.Remove(certFile.Name()) - keyFile, _ := ioutil.TempFile("", "") - defer os.Remove(keyFile.Name()) - caFile, _ := ioutil.TempFile("", "") - defer os.Remove(caFile.Name()) - - mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t) - - if err := MinifyConfig(&mutatingConfig); err != nil { - t.Errorf("unexpected error: %v", err) - } - - if len(mutatingConfig.Contexts) > 1 { - t.Errorf("unexpected contexts: %v", mutatingConfig.Contexts) - } - if _, exists := mutatingConfig.Contexts["federal-context"]; !exists { - t.Errorf("missing context") - } - - if len(mutatingConfig.Clusters) > 1 { - t.Errorf("unexpected clusters: %v", mutatingConfig.Clusters) - } - if _, exists := mutatingConfig.Clusters["cow-cluster"]; !exists { - t.Errorf("missing cluster") - } - - if len(mutatingConfig.AuthInfos) > 1 { - t.Errorf("unexpected users: %v", mutatingConfig.AuthInfos) - } - if _, exists := mutatingConfig.AuthInfos["red-user"]; !exists { - t.Errorf("missing user") - } -} - -func TestMinifyMissingContext(t *testing.T) { - certFile, _ := ioutil.TempFile("", "") - defer os.Remove(certFile.Name()) - keyFile, _ := ioutil.TempFile("", "") - defer os.Remove(keyFile.Name()) - caFile, _ := ioutil.TempFile("", "") - defer os.Remove(caFile.Name()) - - mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t) - mutatingConfig.CurrentContext = "missing" - - errMsg := "cannot locate context missing" - - if err := MinifyConfig(&mutatingConfig); err == nil || err.Error() != errMsg { - t.Errorf("expected %v, got %v", errMsg, err) - } -} - -func TestMinifyMissingCluster(t *testing.T) { - certFile, _ := ioutil.TempFile("", "") - defer os.Remove(certFile.Name()) - keyFile, _ := ioutil.TempFile("", "") - defer os.Remove(keyFile.Name()) - caFile, _ := ioutil.TempFile("", "") - defer os.Remove(caFile.Name()) - - mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t) - delete(mutatingConfig.Clusters, mutatingConfig.Contexts[mutatingConfig.CurrentContext].Cluster) - - errMsg := "cannot locate cluster cow-cluster" - - if err := MinifyConfig(&mutatingConfig); err == nil || err.Error() != errMsg { - t.Errorf("expected %v, got %v", errMsg, err) - } -} - -func TestMinifyMissingAuthInfo(t *testing.T) { - certFile, _ := ioutil.TempFile("", "") - defer os.Remove(certFile.Name()) - keyFile, _ := ioutil.TempFile("", "") - defer os.Remove(keyFile.Name()) - caFile, _ := ioutil.TempFile("", "") - defer os.Remove(caFile.Name()) - - mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t) - delete(mutatingConfig.AuthInfos, mutatingConfig.Contexts[mutatingConfig.CurrentContext].AuthInfo) - - errMsg := "cannot locate user red-user" - - if err := MinifyConfig(&mutatingConfig); err == nil || err.Error() != errMsg { - t.Errorf("expected %v, got %v", errMsg, err) - } -} - -func TestFlattenSuccess(t *testing.T) { - certFile, _ := ioutil.TempFile("", "") - defer os.Remove(certFile.Name()) - keyFile, _ := ioutil.TempFile("", "") - defer os.Remove(keyFile.Name()) - caFile, _ := ioutil.TempFile("", "") - defer os.Remove(caFile.Name()) - - certData := "cert" - keyData := "key" - caData := "ca" - - unchangingCluster := "cow-cluster" - unchangingAuthInfo := "red-user" - changingCluster := "chicken-cluster" - changingAuthInfo := "blue-user" - - startingConfig := newMergedConfig(certFile.Name(), certData, keyFile.Name(), keyData, caFile.Name(), caData, t) - mutatingConfig := startingConfig - - if err := FlattenConfig(&mutatingConfig); err != nil { - t.Errorf("unexpected error: %v", err) - } - - if len(mutatingConfig.Contexts) != 2 { - t.Errorf("unexpected contexts: %v", mutatingConfig.Contexts) - } - if !reflect.DeepEqual(startingConfig.Contexts, mutatingConfig.Contexts) { - t.Errorf("expected %v, got %v", startingConfig.Contexts, mutatingConfig.Contexts) - } - - if len(mutatingConfig.Clusters) != 2 { - t.Errorf("unexpected clusters: %v", mutatingConfig.Clusters) - } - if !reflect.DeepEqual(startingConfig.Clusters[unchangingCluster], mutatingConfig.Clusters[unchangingCluster]) { - t.Errorf("expected %v, got %v", startingConfig.Clusters[unchangingCluster], mutatingConfig.Clusters[unchangingCluster]) - } - if len(mutatingConfig.Clusters[changingCluster].CertificateAuthority) != 0 { - t.Errorf("unexpected caFile") - } - if string(mutatingConfig.Clusters[changingCluster].CertificateAuthorityData) != caData { - t.Errorf("expected %v, got %v", caData, string(mutatingConfig.Clusters[changingCluster].CertificateAuthorityData)) - } - - if len(mutatingConfig.AuthInfos) != 2 { - t.Errorf("unexpected users: %v", mutatingConfig.AuthInfos) - } - if !reflect.DeepEqual(startingConfig.AuthInfos[unchangingAuthInfo], mutatingConfig.AuthInfos[unchangingAuthInfo]) { - t.Errorf("expected %v, got %v", startingConfig.AuthInfos[unchangingAuthInfo], mutatingConfig.AuthInfos[unchangingAuthInfo]) - } - if len(mutatingConfig.AuthInfos[changingAuthInfo].ClientCertificate) != 0 { - t.Errorf("unexpected caFile") - } - if string(mutatingConfig.AuthInfos[changingAuthInfo].ClientCertificateData) != certData { - t.Errorf("expected %v, got %v", certData, string(mutatingConfig.AuthInfos[changingAuthInfo].ClientCertificateData)) - } - if len(mutatingConfig.AuthInfos[changingAuthInfo].ClientKey) != 0 { - t.Errorf("unexpected caFile") - } - if string(mutatingConfig.AuthInfos[changingAuthInfo].ClientKeyData) != keyData { - t.Errorf("expected %v, got %v", keyData, string(mutatingConfig.AuthInfos[changingAuthInfo].ClientKeyData)) - } - -} - -func ExampleMinifyAndShorten() { - certFile, _ := ioutil.TempFile("", "") - defer os.Remove(certFile.Name()) - keyFile, _ := ioutil.TempFile("", "") - defer os.Remove(keyFile.Name()) - caFile, _ := ioutil.TempFile("", "") - defer os.Remove(caFile.Name()) - - certData := "cert" - keyData := "key" - caData := "ca" - - config := newMergedConfig(certFile.Name(), certData, keyFile.Name(), keyData, caFile.Name(), caData, nil) - - MinifyConfig(&config) - ShortenConfig(&config) - - output, _ := yaml.Marshal(config) - fmt.Printf("%s", string(output)) - // Output: - // clusters: - // cow-cluster: - // LocationOfOrigin: "" - // certificate-authority-data: REDACTED - // server: http://cow.org:8080 - // contexts: - // federal-context: - // LocationOfOrigin: "" - // cluster: cow-cluster - // user: red-user - // current-context: federal-context - // preferences: {} - // users: - // red-user: - // LocationOfOrigin: "" - // client-certificate-data: REDACTED - // client-key-data: REDACTED - // token: red-token -} - -func TestShortenSuccess(t *testing.T) { - certFile, _ := ioutil.TempFile("", "") - defer os.Remove(certFile.Name()) - keyFile, _ := ioutil.TempFile("", "") - defer os.Remove(keyFile.Name()) - caFile, _ := ioutil.TempFile("", "") - defer os.Remove(caFile.Name()) - - certData := "cert" - keyData := "key" - caData := "ca" - - unchangingCluster := "chicken-cluster" - unchangingAuthInfo := "blue-user" - changingCluster := "cow-cluster" - changingAuthInfo := "red-user" - - startingConfig := newMergedConfig(certFile.Name(), certData, keyFile.Name(), keyData, caFile.Name(), caData, t) - mutatingConfig := startingConfig - - ShortenConfig(&mutatingConfig) - - if len(mutatingConfig.Contexts) != 2 { - t.Errorf("unexpected contexts: %v", mutatingConfig.Contexts) - } - if !reflect.DeepEqual(startingConfig.Contexts, mutatingConfig.Contexts) { - t.Errorf("expected %v, got %v", startingConfig.Contexts, mutatingConfig.Contexts) - } - - redacted := string(redactedBytes) - if len(mutatingConfig.Clusters) != 2 { - t.Errorf("unexpected clusters: %v", mutatingConfig.Clusters) - } - if !reflect.DeepEqual(startingConfig.Clusters[unchangingCluster], mutatingConfig.Clusters[unchangingCluster]) { - t.Errorf("expected %v, got %v", startingConfig.Clusters[unchangingCluster], mutatingConfig.Clusters[unchangingCluster]) - } - if string(mutatingConfig.Clusters[changingCluster].CertificateAuthorityData) != redacted { - t.Errorf("expected %v, got %v", redacted, string(mutatingConfig.Clusters[changingCluster].CertificateAuthorityData)) - } - - if len(mutatingConfig.AuthInfos) != 2 { - t.Errorf("unexpected users: %v", mutatingConfig.AuthInfos) - } - if !reflect.DeepEqual(startingConfig.AuthInfos[unchangingAuthInfo], mutatingConfig.AuthInfos[unchangingAuthInfo]) { - t.Errorf("expected %v, got %v", startingConfig.AuthInfos[unchangingAuthInfo], mutatingConfig.AuthInfos[unchangingAuthInfo]) - } - if string(mutatingConfig.AuthInfos[changingAuthInfo].ClientCertificateData) != redacted { - t.Errorf("expected %v, got %v", redacted, string(mutatingConfig.AuthInfos[changingAuthInfo].ClientCertificateData)) - } - if string(mutatingConfig.AuthInfos[changingAuthInfo].ClientKeyData) != redacted { - t.Errorf("expected %v, got %v", redacted, string(mutatingConfig.AuthInfos[changingAuthInfo].ClientKeyData)) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/types.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/types.go index 4a1dc364f..7e2bfcfa8 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/types.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/types.go @@ -29,7 +29,11 @@ type Config struct { // Legacy field from pkg/api/types.go TypeMeta. // TODO(jlowdermilk): remove this after eliminating downstream dependencies. Kind string `json:"kind,omitempty"` - // Version of the schema for this config object. + // DEPRECATED: APIVersion is the preferred api version for communicating with the kubernetes cluster (v1, v2, etc). + // Because a cluster can run multiple API groups and potentially multiple versions of each, it no longer makes sense to specify + // a single value for the cluster version. + // This field isnt really needed anyway, so we are deprecating it without replacement. + // It will be ignored if it is present. APIVersion string `json:"apiVersion,omitempty"` // Preferences holds general information to be use for cli interactions Preferences Preferences `json:"preferences"` diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/types_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/types_test.go deleted file mode 100644 index 8059fe21f..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/types_test.go +++ /dev/null @@ -1,123 +0,0 @@ -/* -Copyright 2014 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 api - -import ( - "fmt" - - "github.com/ghodss/yaml" -) - -func ExampleEmptyConfig() { - defaultConfig := NewConfig() - - output, err := yaml.Marshal(defaultConfig) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - - fmt.Printf("%v", string(output)) - // Output: - // clusters: {} - // contexts: {} - // current-context: "" - // preferences: {} - // users: {} -} - -func ExampleOfOptionsConfig() { - defaultConfig := NewConfig() - defaultConfig.Preferences.Colors = true - defaultConfig.Clusters["alfa"] = &Cluster{ - Server: "https://alfa.org:8080", - APIVersion: "v1", - InsecureSkipTLSVerify: true, - CertificateAuthority: "path/to/my/cert-ca-filename", - } - defaultConfig.Clusters["bravo"] = &Cluster{ - Server: "https://bravo.org:8080", - APIVersion: "v1", - InsecureSkipTLSVerify: false, - } - defaultConfig.AuthInfos["white-mage-via-cert"] = &AuthInfo{ - ClientCertificate: "path/to/my/client-cert-filename", - ClientKey: "path/to/my/client-key-filename", - } - defaultConfig.AuthInfos["red-mage-via-token"] = &AuthInfo{ - Token: "my-secret-token", - } - defaultConfig.Contexts["bravo-as-black-mage"] = &Context{ - Cluster: "bravo", - AuthInfo: "black-mage-via-file", - Namespace: "yankee", - } - defaultConfig.Contexts["alfa-as-black-mage"] = &Context{ - Cluster: "alfa", - AuthInfo: "black-mage-via-file", - Namespace: "zulu", - } - defaultConfig.Contexts["alfa-as-white-mage"] = &Context{ - Cluster: "alfa", - AuthInfo: "white-mage-via-cert", - } - defaultConfig.CurrentContext = "alfa-as-white-mage" - - output, err := yaml.Marshal(defaultConfig) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - - fmt.Printf("%v", string(output)) - // Output: - // clusters: - // alfa: - // LocationOfOrigin: "" - // api-version: v1 - // certificate-authority: path/to/my/cert-ca-filename - // insecure-skip-tls-verify: true - // server: https://alfa.org:8080 - // bravo: - // LocationOfOrigin: "" - // api-version: v1 - // server: https://bravo.org:8080 - // contexts: - // alfa-as-black-mage: - // LocationOfOrigin: "" - // cluster: alfa - // namespace: zulu - // user: black-mage-via-file - // alfa-as-white-mage: - // LocationOfOrigin: "" - // cluster: alfa - // user: white-mage-via-cert - // bravo-as-black-mage: - // LocationOfOrigin: "" - // cluster: bravo - // namespace: yankee - // user: black-mage-via-file - // current-context: alfa-as-white-mage - // preferences: - // colors: true - // users: - // red-mage-via-token: - // LocationOfOrigin: "" - // token: my-secret-token - // white-mage-via-cert: - // LocationOfOrigin: "" - // client-certificate: path/to/my/client-cert-filename - // client-key: path/to/my/client-key-filename -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/v1/types.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/v1/types.go index e04f1311e..c9b4ab56b 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/v1/types.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/v1/types.go @@ -28,7 +28,11 @@ type Config struct { // Legacy field from pkg/api/types.go TypeMeta. // TODO(jlowdermilk): remove this after eliminating downstream dependencies. Kind string `json:"kind,omitempty"` - // Version of the schema for this config object. + // DEPRECATED: APIVersion is the preferred api version for communicating with the kubernetes cluster (v1, v2, etc). + // Because a cluster can run multiple API groups and potentially multiple versions of each, it no longer makes sense to specify + // a single value for the cluster version. + // This field isnt really needed anyway, so we are deprecating it without replacement. + // It will be ignored if it is present. APIVersion string `json:"apiVersion,omitempty"` // Preferences holds general information to be use for cli interactions Preferences Preferences `json:"preferences"` diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/client_config.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/client_config.go index e5af51b32..9ff259edf 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/client_config.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/client_config.go @@ -28,8 +28,7 @@ import ( "github.com/imdario/mergo" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - client "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/client/restclient" clientauth "k8s.io/kubernetes/pkg/client/unversioned/auth" clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" ) @@ -50,7 +49,7 @@ type ClientConfig interface { // RawConfig returns the merged result of all overrides RawConfig() (clientcmdapi.Config, error) // ClientConfig returns a complete client config - ClientConfig() (*client.Config, error) + ClientConfig() (*restclient.Config, error) // Namespace returns the namespace resulting from the merged // result of all overrides and a boolean indicating if it was // overridden @@ -85,7 +84,7 @@ func (config *DirectClientConfig) RawConfig() (clientcmdapi.Config, error) { } // ClientConfig implements ClientConfig -func (config *DirectClientConfig) ClientConfig() (*client.Config, error) { +func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) { if err := config.ConfirmUsable(); err != nil { return nil, err } @@ -93,26 +92,21 @@ func (config *DirectClientConfig) ClientConfig() (*client.Config, error) { configAuthInfo := config.getAuthInfo() configClusterInfo := config.getCluster() - clientConfig := &client.Config{} + clientConfig := &restclient.Config{} clientConfig.Host = configClusterInfo.Server if u, err := url.ParseRequestURI(clientConfig.Host); err == nil && u.Opaque == "" && len(u.Path) > 1 { u.RawQuery = "" u.Fragment = "" clientConfig.Host = u.String() } - if len(configClusterInfo.APIVersion) != 0 { - gv, err := unversioned.ParseGroupVersion(configClusterInfo.APIVersion) - if err != nil { - return nil, err - } - clientConfig.GroupVersion = &gv - } // only try to read the auth information if we are secure - if client.IsConfigTransportTLS(*clientConfig) { + if restclient.IsConfigTransportTLS(*clientConfig) { var err error // mergo is a first write wins for map value and a last writing wins for interface values + // NOTE: This behavior changed with https://github.com/imdario/mergo/commit/d304790b2ed594794496464fadd89d2bb266600a. + // Our mergo.Merge version is older than this change. userAuthPartialConfig, err := getUserIdentificationPartialConfig(configAuthInfo, config.fallbackReader) if err != nil { return nil, err @@ -135,11 +129,11 @@ func (config *DirectClientConfig) ClientConfig() (*client.Config, error) { // 1. configClusterInfo (the final result of command line flags and merged .kubeconfig files) // 2. configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority) // 3. load the ~/.kubernetes_auth file as a default -func getServerIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, configClusterInfo clientcmdapi.Cluster) (*client.Config, error) { - mergedConfig := &client.Config{} +func getServerIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, configClusterInfo clientcmdapi.Cluster) (*restclient.Config, error) { + mergedConfig := &restclient.Config{} // configClusterInfo holds the information identify the server provided by .kubeconfig - configClientConfig := &client.Config{} + configClientConfig := &restclient.Config{} configClientConfig.CAFile = configClusterInfo.CertificateAuthority configClientConfig.CAData = configClusterInfo.CertificateAuthorityData configClientConfig.Insecure = configClusterInfo.InsecureSkipTLSVerify @@ -155,8 +149,8 @@ func getServerIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, // 2. configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority) // 3. if there is not enough information to idenfity the user, load try the ~/.kubernetes_auth file // 4. if there is not enough information to identify the user, prompt if possible -func getUserIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, fallbackReader io.Reader) (*client.Config, error) { - mergedConfig := &client.Config{} +func getUserIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, fallbackReader io.Reader) (*restclient.Config, error) { + mergedConfig := &restclient.Config{} // blindly overwrite existing values based on precedence if len(configAuthInfo.Token) > 0 { @@ -180,7 +174,7 @@ func getUserIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, fa promptedConfig := makeUserIdentificationConfig(*promptedAuthInfo) previouslyMergedConfig := mergedConfig - mergedConfig = &client.Config{} + mergedConfig = &restclient.Config{} mergo.Merge(mergedConfig, promptedConfig) mergo.Merge(mergedConfig, previouslyMergedConfig) } @@ -189,8 +183,8 @@ func getUserIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, fa } // makeUserIdentificationFieldsConfig returns a client.Config capable of being merged using mergo for only user identification information -func makeUserIdentificationConfig(info clientauth.Info) *client.Config { - config := &client.Config{} +func makeUserIdentificationConfig(info clientauth.Info) *restclient.Config { + config := &restclient.Config{} config.Username = info.User config.Password = info.Password config.CertFile = info.CertFile @@ -200,8 +194,8 @@ func makeUserIdentificationConfig(info clientauth.Info) *client.Config { } // makeUserIdentificationFieldsConfig returns a client.Config capable of being merged using mergo for only server identification information -func makeServerIdentificationConfig(info clientauth.Info) client.Config { - config := client.Config{} +func makeServerIdentificationConfig(info clientauth.Info) restclient.Config { + config := restclient.Config{} config.CAFile = info.CAFile if info.Insecure != nil { config.Insecure = *info.Insecure @@ -209,7 +203,7 @@ func makeServerIdentificationConfig(info clientauth.Info) client.Config { return config } -func canIdentifyUser(config client.Config) bool { +func canIdentifyUser(config restclient.Config) bool { return len(config.Username) > 0 || (len(config.CertFile) > 0 || len(config.CertData) > 0) || len(config.BearerToken) > 0 @@ -322,8 +316,8 @@ func (inClusterClientConfig) RawConfig() (clientcmdapi.Config, error) { return clientcmdapi.Config{}, fmt.Errorf("inCluster environment config doesn't support multiple clusters") } -func (inClusterClientConfig) ClientConfig() (*client.Config, error) { - return client.InClusterConfig() +func (inClusterClientConfig) ClientConfig() (*restclient.Config, error) { + return restclient.InClusterConfig() } func (inClusterClientConfig) Namespace() (string, error) { @@ -356,10 +350,10 @@ func (inClusterClientConfig) Possible() bool { // components. Warnings should reflect this usage. If neither masterUrl or kubeconfigPath // are passed in we fallback to inClusterConfig. If inClusterConfig fails, we fallback // to the default config. -func BuildConfigFromFlags(masterUrl, kubeconfigPath string) (*client.Config, error) { +func BuildConfigFromFlags(masterUrl, kubeconfigPath string) (*restclient.Config, error) { if kubeconfigPath == "" && masterUrl == "" { glog.Warningf("Neither --kubeconfig nor --master was specified. Using the inClusterConfig. This might not work.") - kubeconfig, err := client.InClusterConfig() + kubeconfig, err := restclient.InClusterConfig() if err == nil { return kubeconfig, nil } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/client_config_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/client_config_test.go deleted file mode 100644 index 834869677..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/client_config_test.go +++ /dev/null @@ -1,252 +0,0 @@ -/* -Copyright 2014 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 clientcmd - -import ( - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api/testapi" - client "k8s.io/kubernetes/pkg/client/unversioned" - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" -) - -func createValidTestConfig() *clientcmdapi.Config { - const ( - server = "https://anything.com:8080" - token = "the-token" - ) - - config := clientcmdapi.NewConfig() - config.Clusters["clean"] = &clientcmdapi.Cluster{ - Server: server, - APIVersion: testapi.Default.GroupVersion().String(), - } - config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ - Token: token, - } - config.Contexts["clean"] = &clientcmdapi.Context{ - Cluster: "clean", - AuthInfo: "clean", - } - config.CurrentContext = "clean" - - return config -} - -func TestMergeContext(t *testing.T) { - const namespace = "overriden-namespace" - - config := createValidTestConfig() - clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}) - - _, overridden, err := clientBuilder.Namespace() - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - if overridden { - t.Error("Expected namespace to not be overridden") - } - - clientBuilder = NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{ - Context: clientcmdapi.Context{ - Namespace: namespace, - }, - }) - - actual, overridden, err := clientBuilder.Namespace() - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - if !overridden { - t.Error("Expected namespace to be overridden") - } - - matchStringArg(namespace, actual, t) -} - -func TestCertificateData(t *testing.T) { - caData := []byte("ca-data") - certData := []byte("cert-data") - keyData := []byte("key-data") - - config := clientcmdapi.NewConfig() - config.Clusters["clean"] = &clientcmdapi.Cluster{ - Server: "https://localhost:8443", - APIVersion: testapi.Default.GroupVersion().String(), - CertificateAuthorityData: caData, - } - config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ - ClientCertificateData: certData, - ClientKeyData: keyData, - } - config.Contexts["clean"] = &clientcmdapi.Context{ - Cluster: "clean", - AuthInfo: "clean", - } - config.CurrentContext = "clean" - - clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}) - - clientConfig, err := clientBuilder.ClientConfig() - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - // Make sure cert data gets into config (will override file paths) - matchByteArg(caData, clientConfig.TLSClientConfig.CAData, t) - matchByteArg(certData, clientConfig.TLSClientConfig.CertData, t) - matchByteArg(keyData, clientConfig.TLSClientConfig.KeyData, t) -} - -func TestBasicAuthData(t *testing.T) { - username := "myuser" - password := "mypass" - - config := clientcmdapi.NewConfig() - config.Clusters["clean"] = &clientcmdapi.Cluster{ - Server: "https://localhost:8443", - APIVersion: testapi.Default.GroupVersion().String(), - } - config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ - Username: username, - Password: password, - } - config.Contexts["clean"] = &clientcmdapi.Context{ - Cluster: "clean", - AuthInfo: "clean", - } - config.CurrentContext = "clean" - - clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}) - - clientConfig, err := clientBuilder.ClientConfig() - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - // Make sure basic auth data gets into config - matchStringArg(username, clientConfig.Username, t) - matchStringArg(password, clientConfig.Password, t) -} - -func TestCreateClean(t *testing.T) { - config := createValidTestConfig() - clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}) - - clientConfig, err := clientBuilder.ClientConfig() - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t) - matchStringArg("", clientConfig.APIPath, t) - matchStringArg(config.Clusters["clean"].APIVersion, clientConfig.GroupVersion.String(), t) - matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t) - matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t) -} - -func TestCreateCleanWithPrefix(t *testing.T) { - tt := []struct { - server string - host string - }{ - {"https://anything.com:8080/foo/bar", "https://anything.com:8080/foo/bar"}, - {"http://anything.com:8080/foo/bar", "http://anything.com:8080/foo/bar"}, - {"http://anything.com:8080/foo/bar/", "http://anything.com:8080/foo/bar/"}, - {"http://anything.com:8080/", "http://anything.com:8080/"}, - {"http://anything.com:8080//", "http://anything.com:8080//"}, - {"anything.com:8080/foo/bar", "anything.com:8080/foo/bar"}, - {"anything.com:8080", "anything.com:8080"}, - {"anything.com", "anything.com"}, - {"anything", "anything"}, - } - - // WARNING: EnvVarCluster.Server is set during package loading time and can not be overriden by os.Setenv inside this test - EnvVarCluster.Server = "" - tt = append(tt, struct{ server, host string }{"", "http://localhost:8080"}) - - for _, tc := range tt { - config := createValidTestConfig() - - cleanConfig := config.Clusters["clean"] - cleanConfig.Server = tc.server - config.Clusters["clean"] = cleanConfig - - clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}) - - clientConfig, err := clientBuilder.ClientConfig() - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - matchStringArg(tc.host, clientConfig.Host, t) - } -} - -func TestCreateCleanDefault(t *testing.T) { - config := createValidTestConfig() - clientBuilder := NewDefaultClientConfig(*config, &ConfigOverrides{}) - - clientConfig, err := clientBuilder.ClientConfig() - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t) - matchStringArg(config.Clusters["clean"].APIVersion, clientConfig.GroupVersion.String(), t) - matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t) - matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t) -} - -func TestCreateMissingContext(t *testing.T) { - const expectedErrorContains = "Context was not found for specified context" - config := createValidTestConfig() - clientBuilder := NewNonInteractiveClientConfig(*config, "not-present", &ConfigOverrides{}) - - clientConfig, err := clientBuilder.ClientConfig() - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - expectedConfig := &client.Config{Host: clientConfig.Host} - - if !reflect.DeepEqual(expectedConfig, clientConfig) { - t.Errorf("Expected %#v, got %#v", expectedConfig, clientConfig) - } - -} - -func matchBoolArg(expected, got bool, t *testing.T) { - if expected != got { - t.Errorf("Expected %v, got %v", expected, got) - } -} - -func matchStringArg(expected, got string, t *testing.T) { - if expected != got { - t.Errorf("Expected %q, got %q", expected, got) - } -} - -func matchByteArg(expected, got []byte, t *testing.T) { - if !reflect.DeepEqual(expected, got) { - t.Errorf("Expected %v, got %v", expected, got) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/loader_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/loader_test.go deleted file mode 100644 index 0921f7043..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/loader_test.go +++ /dev/null @@ -1,562 +0,0 @@ -/* -Copyright 2014 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 clientcmd - -import ( - "fmt" - "io/ioutil" - "os" - "path" - "path/filepath" - "reflect" - "strings" - "testing" - - "github.com/ghodss/yaml" - - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" - clientcmdlatest "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/latest" - "k8s.io/kubernetes/pkg/runtime" -) - -var ( - testConfigAlfa = clientcmdapi.Config{ - AuthInfos: map[string]*clientcmdapi.AuthInfo{ - "red-user": {Token: "red-token"}}, - Clusters: map[string]*clientcmdapi.Cluster{ - "cow-cluster": {Server: "http://cow.org:8080"}}, - Contexts: map[string]*clientcmdapi.Context{ - "federal-context": {AuthInfo: "red-user", Cluster: "cow-cluster", Namespace: "hammer-ns"}}, - } - testConfigBravo = clientcmdapi.Config{ - AuthInfos: map[string]*clientcmdapi.AuthInfo{ - "black-user": {Token: "black-token"}}, - Clusters: map[string]*clientcmdapi.Cluster{ - "pig-cluster": {Server: "http://pig.org:8080"}}, - Contexts: map[string]*clientcmdapi.Context{ - "queen-anne-context": {AuthInfo: "black-user", Cluster: "pig-cluster", Namespace: "saw-ns"}}, - } - testConfigCharlie = clientcmdapi.Config{ - AuthInfos: map[string]*clientcmdapi.AuthInfo{ - "green-user": {Token: "green-token"}}, - Clusters: map[string]*clientcmdapi.Cluster{ - "horse-cluster": {Server: "http://horse.org:8080"}}, - Contexts: map[string]*clientcmdapi.Context{ - "shaker-context": {AuthInfo: "green-user", Cluster: "horse-cluster", Namespace: "chisel-ns"}}, - } - testConfigDelta = clientcmdapi.Config{ - AuthInfos: map[string]*clientcmdapi.AuthInfo{ - "blue-user": {Token: "blue-token"}}, - Clusters: map[string]*clientcmdapi.Cluster{ - "chicken-cluster": {Server: "http://chicken.org:8080"}}, - Contexts: map[string]*clientcmdapi.Context{ - "gothic-context": {AuthInfo: "blue-user", Cluster: "chicken-cluster", Namespace: "plane-ns"}}, - } - - testConfigConflictAlfa = clientcmdapi.Config{ - AuthInfos: map[string]*clientcmdapi.AuthInfo{ - "red-user": {Token: "a-different-red-token"}, - "yellow-user": {Token: "yellow-token"}}, - Clusters: map[string]*clientcmdapi.Cluster{ - "cow-cluster": {Server: "http://a-different-cow.org:8080", InsecureSkipTLSVerify: true}, - "donkey-cluster": {Server: "http://donkey.org:8080", InsecureSkipTLSVerify: true}}, - CurrentContext: "federal-context", - } -) - -func TestNonExistentCommandLineFile(t *testing.T) { - loadingRules := ClientConfigLoadingRules{ - ExplicitPath: "bogus_file", - } - - _, err := loadingRules.Load() - if err == nil { - t.Fatalf("Expected error for missing command-line file, got none") - } - if !strings.Contains(err.Error(), "bogus_file") { - t.Fatalf("Expected error about 'bogus_file', got %s", err.Error()) - } -} - -func TestToleratingMissingFiles(t *testing.T) { - loadingRules := ClientConfigLoadingRules{ - Precedence: []string{"bogus1", "bogus2", "bogus3"}, - } - - _, err := loadingRules.Load() - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } -} - -func TestErrorReadingFile(t *testing.T) { - commandLineFile, _ := ioutil.TempFile("", "") - defer os.Remove(commandLineFile.Name()) - - if err := ioutil.WriteFile(commandLineFile.Name(), []byte("bogus value"), 0644); err != nil { - t.Fatalf("Error creating tempfile: %v", err) - } - - loadingRules := ClientConfigLoadingRules{ - ExplicitPath: commandLineFile.Name(), - } - - _, err := loadingRules.Load() - if err == nil { - t.Fatalf("Expected error for unloadable file, got none") - } - if !strings.Contains(err.Error(), commandLineFile.Name()) { - t.Fatalf("Expected error about '%s', got %s", commandLineFile.Name(), err.Error()) - } -} - -func TestErrorReadingNonFile(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatalf("Couldn't create tmpdir") - } - defer os.Remove(tmpdir) - - loadingRules := ClientConfigLoadingRules{ - ExplicitPath: tmpdir, - } - - _, err = loadingRules.Load() - if err == nil { - t.Fatalf("Expected error for non-file, got none") - } - if !strings.Contains(err.Error(), tmpdir) { - t.Fatalf("Expected error about '%s', got %s", tmpdir, err.Error()) - } -} - -func TestConflictingCurrentContext(t *testing.T) { - commandLineFile, _ := ioutil.TempFile("", "") - defer os.Remove(commandLineFile.Name()) - envVarFile, _ := ioutil.TempFile("", "") - defer os.Remove(envVarFile.Name()) - - mockCommandLineConfig := clientcmdapi.Config{ - CurrentContext: "any-context-value", - } - mockEnvVarConfig := clientcmdapi.Config{ - CurrentContext: "a-different-context", - } - - WriteToFile(mockCommandLineConfig, commandLineFile.Name()) - WriteToFile(mockEnvVarConfig, envVarFile.Name()) - - loadingRules := ClientConfigLoadingRules{ - ExplicitPath: commandLineFile.Name(), - Precedence: []string{envVarFile.Name()}, - } - - mergedConfig, err := loadingRules.Load() - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - if mergedConfig.CurrentContext != mockCommandLineConfig.CurrentContext { - t.Errorf("expected %v, got %v", mockCommandLineConfig.CurrentContext, mergedConfig.CurrentContext) - } -} - -func TestLoadingEmptyMaps(t *testing.T) { - configFile, _ := ioutil.TempFile("", "") - defer os.Remove(configFile.Name()) - - mockConfig := clientcmdapi.Config{ - CurrentContext: "any-context-value", - } - - WriteToFile(mockConfig, configFile.Name()) - - config, err := LoadFromFile(configFile.Name()) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - if config.Clusters == nil { - t.Error("expected config.Clusters to be non-nil") - } - if config.AuthInfos == nil { - t.Error("expected config.AuthInfos to be non-nil") - } - if config.Contexts == nil { - t.Error("expected config.Contexts to be non-nil") - } -} - -func TestResolveRelativePaths(t *testing.T) { - pathResolutionConfig1 := clientcmdapi.Config{ - AuthInfos: map[string]*clientcmdapi.AuthInfo{ - "relative-user-1": {ClientCertificate: "relative/client/cert", ClientKey: "../relative/client/key"}, - "absolute-user-1": {ClientCertificate: "/absolute/client/cert", ClientKey: "/absolute/client/key"}, - }, - Clusters: map[string]*clientcmdapi.Cluster{ - "relative-server-1": {CertificateAuthority: "../relative/ca"}, - "absolute-server-1": {CertificateAuthority: "/absolute/ca"}, - }, - } - pathResolutionConfig2 := clientcmdapi.Config{ - AuthInfos: map[string]*clientcmdapi.AuthInfo{ - "relative-user-2": {ClientCertificate: "relative/client/cert2", ClientKey: "../relative/client/key2"}, - "absolute-user-2": {ClientCertificate: "/absolute/client/cert2", ClientKey: "/absolute/client/key2"}, - }, - Clusters: map[string]*clientcmdapi.Cluster{ - "relative-server-2": {CertificateAuthority: "../relative/ca2"}, - "absolute-server-2": {CertificateAuthority: "/absolute/ca2"}, - }, - } - - configDir1, _ := ioutil.TempDir("", "") - configFile1 := path.Join(configDir1, ".kubeconfig") - configDir1, _ = filepath.Abs(configDir1) - defer os.Remove(configFile1) - configDir2, _ := ioutil.TempDir("", "") - configDir2, _ = ioutil.TempDir(configDir2, "") - configFile2 := path.Join(configDir2, ".kubeconfig") - configDir2, _ = filepath.Abs(configDir2) - defer os.Remove(configFile2) - - WriteToFile(pathResolutionConfig1, configFile1) - WriteToFile(pathResolutionConfig2, configFile2) - - loadingRules := ClientConfigLoadingRules{ - Precedence: []string{configFile1, configFile2}, - } - - mergedConfig, err := loadingRules.Load() - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - foundClusterCount := 0 - for key, cluster := range mergedConfig.Clusters { - if key == "relative-server-1" { - foundClusterCount++ - matchStringArg(path.Join(configDir1, pathResolutionConfig1.Clusters["relative-server-1"].CertificateAuthority), cluster.CertificateAuthority, t) - } - if key == "relative-server-2" { - foundClusterCount++ - matchStringArg(path.Join(configDir2, pathResolutionConfig2.Clusters["relative-server-2"].CertificateAuthority), cluster.CertificateAuthority, t) - } - if key == "absolute-server-1" { - foundClusterCount++ - matchStringArg(pathResolutionConfig1.Clusters["absolute-server-1"].CertificateAuthority, cluster.CertificateAuthority, t) - } - if key == "absolute-server-2" { - foundClusterCount++ - matchStringArg(pathResolutionConfig2.Clusters["absolute-server-2"].CertificateAuthority, cluster.CertificateAuthority, t) - } - } - if foundClusterCount != 4 { - t.Errorf("Expected 4 clusters, found %v: %v", foundClusterCount, mergedConfig.Clusters) - } - - foundAuthInfoCount := 0 - for key, authInfo := range mergedConfig.AuthInfos { - if key == "relative-user-1" { - foundAuthInfoCount++ - matchStringArg(path.Join(configDir1, pathResolutionConfig1.AuthInfos["relative-user-1"].ClientCertificate), authInfo.ClientCertificate, t) - matchStringArg(path.Join(configDir1, pathResolutionConfig1.AuthInfos["relative-user-1"].ClientKey), authInfo.ClientKey, t) - } - if key == "relative-user-2" { - foundAuthInfoCount++ - matchStringArg(path.Join(configDir2, pathResolutionConfig2.AuthInfos["relative-user-2"].ClientCertificate), authInfo.ClientCertificate, t) - matchStringArg(path.Join(configDir2, pathResolutionConfig2.AuthInfos["relative-user-2"].ClientKey), authInfo.ClientKey, t) - } - if key == "absolute-user-1" { - foundAuthInfoCount++ - matchStringArg(pathResolutionConfig1.AuthInfos["absolute-user-1"].ClientCertificate, authInfo.ClientCertificate, t) - matchStringArg(pathResolutionConfig1.AuthInfos["absolute-user-1"].ClientKey, authInfo.ClientKey, t) - } - if key == "absolute-user-2" { - foundAuthInfoCount++ - matchStringArg(pathResolutionConfig2.AuthInfos["absolute-user-2"].ClientCertificate, authInfo.ClientCertificate, t) - matchStringArg(pathResolutionConfig2.AuthInfos["absolute-user-2"].ClientKey, authInfo.ClientKey, t) - } - } - if foundAuthInfoCount != 4 { - t.Errorf("Expected 4 users, found %v: %v", foundAuthInfoCount, mergedConfig.AuthInfos) - } - -} - -func TestMigratingFile(t *testing.T) { - sourceFile, _ := ioutil.TempFile("", "") - defer os.Remove(sourceFile.Name()) - destinationFile, _ := ioutil.TempFile("", "") - // delete the file so that we'll write to it - os.Remove(destinationFile.Name()) - - WriteToFile(testConfigAlfa, sourceFile.Name()) - - loadingRules := ClientConfigLoadingRules{ - MigrationRules: map[string]string{destinationFile.Name(): sourceFile.Name()}, - } - - if _, err := loadingRules.Load(); err != nil { - t.Errorf("unexpected error %v", err) - } - - // the load should have recreated this file - defer os.Remove(destinationFile.Name()) - - sourceContent, err := ioutil.ReadFile(sourceFile.Name()) - if err != nil { - t.Errorf("unexpected error %v", err) - } - destinationContent, err := ioutil.ReadFile(destinationFile.Name()) - if err != nil { - t.Errorf("unexpected error %v", err) - } - - if !reflect.DeepEqual(sourceContent, destinationContent) { - t.Errorf("source and destination do not match") - } -} - -func TestMigratingFileLeaveExistingFileAlone(t *testing.T) { - sourceFile, _ := ioutil.TempFile("", "") - defer os.Remove(sourceFile.Name()) - destinationFile, _ := ioutil.TempFile("", "") - defer os.Remove(destinationFile.Name()) - - WriteToFile(testConfigAlfa, sourceFile.Name()) - - loadingRules := ClientConfigLoadingRules{ - MigrationRules: map[string]string{destinationFile.Name(): sourceFile.Name()}, - } - - if _, err := loadingRules.Load(); err != nil { - t.Errorf("unexpected error %v", err) - } - - destinationContent, err := ioutil.ReadFile(destinationFile.Name()) - if err != nil { - t.Errorf("unexpected error %v", err) - } - - if len(destinationContent) > 0 { - t.Errorf("destination should not have been touched") - } -} - -func TestMigratingFileSourceMissingSkip(t *testing.T) { - sourceFilename := "some-missing-file" - destinationFile, _ := ioutil.TempFile("", "") - // delete the file so that we'll write to it - os.Remove(destinationFile.Name()) - - loadingRules := ClientConfigLoadingRules{ - MigrationRules: map[string]string{destinationFile.Name(): sourceFilename}, - } - - if _, err := loadingRules.Load(); err != nil { - t.Errorf("unexpected error %v", err) - } - - if _, err := os.Stat(destinationFile.Name()); !os.IsNotExist(err) { - t.Errorf("destination should not exist") - } -} - -func ExampleNoMergingOnExplicitPaths() { - commandLineFile, _ := ioutil.TempFile("", "") - defer os.Remove(commandLineFile.Name()) - envVarFile, _ := ioutil.TempFile("", "") - defer os.Remove(envVarFile.Name()) - - WriteToFile(testConfigAlfa, commandLineFile.Name()) - WriteToFile(testConfigConflictAlfa, envVarFile.Name()) - - loadingRules := ClientConfigLoadingRules{ - ExplicitPath: commandLineFile.Name(), - Precedence: []string{envVarFile.Name()}, - } - - mergedConfig, err := loadingRules.Load() - - json, err := runtime.Encode(clientcmdlatest.Codec, mergedConfig) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - output, err := yaml.JSONToYAML(json) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - - fmt.Printf("%v", string(output)) - // Output: - // apiVersion: v1 - // clusters: - // - cluster: - // server: http://cow.org:8080 - // name: cow-cluster - // contexts: - // - context: - // cluster: cow-cluster - // namespace: hammer-ns - // user: red-user - // name: federal-context - // current-context: "" - // kind: Config - // preferences: {} - // users: - // - name: red-user - // user: - // token: red-token -} - -func ExampleMergingSomeWithConflict() { - commandLineFile, _ := ioutil.TempFile("", "") - defer os.Remove(commandLineFile.Name()) - envVarFile, _ := ioutil.TempFile("", "") - defer os.Remove(envVarFile.Name()) - - WriteToFile(testConfigAlfa, commandLineFile.Name()) - WriteToFile(testConfigConflictAlfa, envVarFile.Name()) - - loadingRules := ClientConfigLoadingRules{ - Precedence: []string{commandLineFile.Name(), envVarFile.Name()}, - } - - mergedConfig, err := loadingRules.Load() - - json, err := runtime.Encode(clientcmdlatest.Codec, mergedConfig) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - output, err := yaml.JSONToYAML(json) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - - fmt.Printf("%v", string(output)) - // Output: - // apiVersion: v1 - // clusters: - // - cluster: - // server: http://cow.org:8080 - // name: cow-cluster - // - cluster: - // insecure-skip-tls-verify: true - // server: http://donkey.org:8080 - // name: donkey-cluster - // contexts: - // - context: - // cluster: cow-cluster - // namespace: hammer-ns - // user: red-user - // name: federal-context - // current-context: federal-context - // kind: Config - // preferences: {} - // users: - // - name: red-user - // user: - // token: red-token - // - name: yellow-user - // user: - // token: yellow-token -} - -func ExampleMergingEverythingNoConflicts() { - commandLineFile, _ := ioutil.TempFile("", "") - defer os.Remove(commandLineFile.Name()) - envVarFile, _ := ioutil.TempFile("", "") - defer os.Remove(envVarFile.Name()) - currentDirFile, _ := ioutil.TempFile("", "") - defer os.Remove(currentDirFile.Name()) - homeDirFile, _ := ioutil.TempFile("", "") - defer os.Remove(homeDirFile.Name()) - - WriteToFile(testConfigAlfa, commandLineFile.Name()) - WriteToFile(testConfigBravo, envVarFile.Name()) - WriteToFile(testConfigCharlie, currentDirFile.Name()) - WriteToFile(testConfigDelta, homeDirFile.Name()) - - loadingRules := ClientConfigLoadingRules{ - Precedence: []string{commandLineFile.Name(), envVarFile.Name(), currentDirFile.Name(), homeDirFile.Name()}, - } - - mergedConfig, err := loadingRules.Load() - - json, err := runtime.Encode(clientcmdlatest.Codec, mergedConfig) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - output, err := yaml.JSONToYAML(json) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - - fmt.Printf("%v", string(output)) - // Output: - // apiVersion: v1 - // clusters: - // - cluster: - // server: http://chicken.org:8080 - // name: chicken-cluster - // - cluster: - // server: http://cow.org:8080 - // name: cow-cluster - // - cluster: - // server: http://horse.org:8080 - // name: horse-cluster - // - cluster: - // server: http://pig.org:8080 - // name: pig-cluster - // contexts: - // - context: - // cluster: cow-cluster - // namespace: hammer-ns - // user: red-user - // name: federal-context - // - context: - // cluster: chicken-cluster - // namespace: plane-ns - // user: blue-user - // name: gothic-context - // - context: - // cluster: pig-cluster - // namespace: saw-ns - // user: black-user - // name: queen-anne-context - // - context: - // cluster: horse-cluster - // namespace: chisel-ns - // user: green-user - // name: shaker-context - // current-context: "" - // kind: Config - // preferences: {} - // users: - // - name: black-user - // user: - // token: black-token - // - name: blue-user - // user: - // token: blue-token - // - name: green-user - // user: - // token: green-token - // - name: red-user - // user: - // token: red-token -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/merged_client_builder.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/merged_client_builder.go index 8fee4b9ec..321eae9e8 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/merged_client_builder.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/merged_client_builder.go @@ -23,7 +23,7 @@ import ( "github.com/golang/glog" - client "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/client/restclient" clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" ) @@ -86,7 +86,7 @@ func (config *DeferredLoadingClientConfig) RawConfig() (clientcmdapi.Config, err } // ClientConfig implements ClientConfig -func (config *DeferredLoadingClientConfig) ClientConfig() (*client.Config, error) { +func (config *DeferredLoadingClientConfig) ClientConfig() (*restclient.Config, error) { mergedClientConfig, err := config.createClientConfig() if err != nil { return nil, err diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/overrides.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/overrides.go index d6bc496a2..9996d2f44 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/overrides.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/overrides.go @@ -134,7 +134,7 @@ func RecommendedAuthOverrideFlags(prefix string) AuthOverrideFlags { func RecommendedClusterOverrideFlags(prefix string) ClusterOverrideFlags { return ClusterOverrideFlags{ APIServer: FlagInfo{prefix + FlagAPIServer, "", "", "The address and port of the Kubernetes API server"}, - APIVersion: FlagInfo{prefix + FlagAPIVersion, "", "", "The API version to use when talking to the server"}, + APIVersion: FlagInfo{prefix + FlagAPIVersion, "", "", "DEPRECATED: The API version to use when talking to the server"}, CertificateAuthority: FlagInfo{prefix + FlagCAFile, "", "", "Path to a cert. file for the certificate authority."}, InsecureSkipTLSVerify: FlagInfo{prefix + FlagInsecure, "", "false", "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure."}, } @@ -171,7 +171,9 @@ func BindAuthInfoFlags(authInfo *clientcmdapi.AuthInfo, flags *pflag.FlagSet, fl // BindClusterFlags is a convenience method to bind the specified flags to their associated variables func BindClusterFlags(clusterInfo *clientcmdapi.Cluster, flags *pflag.FlagSet, flagNames ClusterOverrideFlags) { flagNames.APIServer.BindStringFlag(flags, &clusterInfo.Server) + // TODO: remove --api-version flag in 1.3. flagNames.APIVersion.BindStringFlag(flags, &clusterInfo.APIVersion) + flags.MarkDeprecated(FlagAPIVersion, "flag is no longer respected and will be deleted in the next release") flagNames.CertificateAuthority.BindStringFlag(flags, &clusterInfo.CertificateAuthority) flagNames.InsecureSkipTLSVerify.BindBoolFlag(flags, &clusterInfo.InsecureSkipTLSVerify) } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/validation_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/validation_test.go deleted file mode 100644 index ca4843a87..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/clientcmd/validation_test.go +++ /dev/null @@ -1,432 +0,0 @@ -/* -Copyright 2014 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 clientcmd - -import ( - "io/ioutil" - "os" - "strings" - "testing" - - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" - utilerrors "k8s.io/kubernetes/pkg/util/errors" -) - -func TestConfirmUsableBadInfoButOkConfig(t *testing.T) { - config := clientcmdapi.NewConfig() - config.Clusters["missing ca"] = &clientcmdapi.Cluster{ - Server: "anything", - CertificateAuthority: "missing", - } - config.AuthInfos["error"] = &clientcmdapi.AuthInfo{ - Username: "anything", - Token: "here", - } - config.Contexts["dirty"] = &clientcmdapi.Context{ - Cluster: "missing ca", - AuthInfo: "error", - } - config.Clusters["clean"] = &clientcmdapi.Cluster{ - Server: "anything", - } - config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ - Token: "here", - } - config.Contexts["clean"] = &clientcmdapi.Context{ - Cluster: "clean", - AuthInfo: "clean", - } - - badValidation := configValidationTest{ - config: config, - expectedErrorSubstring: []string{"unable to read certificate-authority"}, - } - okTest := configValidationTest{ - config: config, - } - - okTest.testConfirmUsable("clean", t) - badValidation.testConfig(t) -} -func TestConfirmUsableBadInfoConfig(t *testing.T) { - config := clientcmdapi.NewConfig() - config.Clusters["missing ca"] = &clientcmdapi.Cluster{ - Server: "anything", - CertificateAuthority: "missing", - } - config.AuthInfos["error"] = &clientcmdapi.AuthInfo{ - Username: "anything", - Token: "here", - } - config.Contexts["first"] = &clientcmdapi.Context{ - Cluster: "missing ca", - AuthInfo: "error", - } - test := configValidationTest{ - config: config, - expectedErrorSubstring: []string{"unable to read certificate-authority"}, - } - - test.testConfirmUsable("first", t) -} -func TestConfirmUsableEmptyConfig(t *testing.T) { - config := clientcmdapi.NewConfig() - test := configValidationTest{ - config: config, - expectedErrorSubstring: []string{"invalid configuration: no configuration has been provided"}, - } - - test.testConfirmUsable("", t) -} -func TestConfirmUsableMissingConfig(t *testing.T) { - config := clientcmdapi.NewConfig() - test := configValidationTest{ - config: config, - expectedErrorSubstring: []string{"invalid configuration: no configuration has been provided"}, - } - - test.testConfirmUsable("not-here", t) -} -func TestValidateEmptyConfig(t *testing.T) { - config := clientcmdapi.NewConfig() - test := configValidationTest{ - config: config, - expectedErrorSubstring: []string{"invalid configuration: no configuration has been provided"}, - } - - test.testConfig(t) -} -func TestValidateMissingCurrentContextConfig(t *testing.T) { - config := clientcmdapi.NewConfig() - config.CurrentContext = "anything" - test := configValidationTest{ - config: config, - expectedErrorSubstring: []string{"context was not found for specified "}, - } - - test.testConfig(t) -} -func TestIsContextNotFound(t *testing.T) { - config := clientcmdapi.NewConfig() - config.CurrentContext = "anything" - - err := Validate(*config) - if !IsContextNotFound(err) { - t.Errorf("Expected context not found, but got %v", err) - } - if !IsConfigurationInvalid(err) { - t.Errorf("Expected configuration invalid, but got %v", err) - } -} - -func TestIsEmptyConfig(t *testing.T) { - config := clientcmdapi.NewConfig() - - err := Validate(*config) - if !IsEmptyConfig(err) { - t.Errorf("Expected context not found, but got %v", err) - } - if !IsConfigurationInvalid(err) { - t.Errorf("Expected configuration invalid, but got %v", err) - } -} - -func TestIsConfigurationInvalid(t *testing.T) { - if newErrConfigurationInvalid([]error{}) != nil { - t.Errorf("unexpected error") - } - if newErrConfigurationInvalid([]error{ErrNoContext}) == ErrNoContext { - t.Errorf("unexpected error") - } - if newErrConfigurationInvalid([]error{ErrNoContext, ErrNoContext}) == nil { - t.Errorf("unexpected error") - } - if !IsConfigurationInvalid(newErrConfigurationInvalid([]error{ErrNoContext, ErrNoContext})) { - t.Errorf("unexpected error") - } -} - -func TestValidateMissingReferencesConfig(t *testing.T) { - config := clientcmdapi.NewConfig() - config.CurrentContext = "anything" - config.Contexts["anything"] = &clientcmdapi.Context{Cluster: "missing", AuthInfo: "missing"} - test := configValidationTest{ - config: config, - expectedErrorSubstring: []string{"user \"missing\" was not found for context \"anything\"", "cluster \"missing\" was not found for context \"anything\""}, - } - - test.testContext("anything", t) - test.testConfig(t) -} -func TestValidateEmptyContext(t *testing.T) { - config := clientcmdapi.NewConfig() - config.CurrentContext = "anything" - config.Contexts["anything"] = &clientcmdapi.Context{} - test := configValidationTest{ - config: config, - expectedErrorSubstring: []string{"user was not specified for context \"anything\"", "cluster was not specified for context \"anything\""}, - } - - test.testContext("anything", t) - test.testConfig(t) -} - -func TestValidateEmptyClusterInfo(t *testing.T) { - config := clientcmdapi.NewConfig() - config.Clusters["empty"] = &clientcmdapi.Cluster{} - test := configValidationTest{ - config: config, - expectedErrorSubstring: []string{"cluster has no server defined"}, - } - - test.testCluster("empty", t) - test.testConfig(t) -} -func TestValidateMissingCAFileClusterInfo(t *testing.T) { - config := clientcmdapi.NewConfig() - config.Clusters["missing ca"] = &clientcmdapi.Cluster{ - Server: "anything", - CertificateAuthority: "missing", - } - test := configValidationTest{ - config: config, - expectedErrorSubstring: []string{"unable to read certificate-authority"}, - } - - test.testCluster("missing ca", t) - test.testConfig(t) -} -func TestValidateCleanClusterInfo(t *testing.T) { - config := clientcmdapi.NewConfig() - config.Clusters["clean"] = &clientcmdapi.Cluster{ - Server: "anything", - } - test := configValidationTest{ - config: config, - } - - test.testCluster("clean", t) - test.testConfig(t) -} -func TestValidateCleanWithCAClusterInfo(t *testing.T) { - tempFile, _ := ioutil.TempFile("", "") - defer os.Remove(tempFile.Name()) - - config := clientcmdapi.NewConfig() - config.Clusters["clean"] = &clientcmdapi.Cluster{ - Server: "anything", - CertificateAuthority: tempFile.Name(), - } - test := configValidationTest{ - config: config, - } - - test.testCluster("clean", t) - test.testConfig(t) -} - -func TestValidateEmptyAuthInfo(t *testing.T) { - config := clientcmdapi.NewConfig() - config.AuthInfos["error"] = &clientcmdapi.AuthInfo{} - test := configValidationTest{ - config: config, - } - - test.testAuthInfo("error", t) - test.testConfig(t) -} -func TestValidateCertFilesNotFoundAuthInfo(t *testing.T) { - config := clientcmdapi.NewConfig() - config.AuthInfos["error"] = &clientcmdapi.AuthInfo{ - ClientCertificate: "missing", - ClientKey: "missing", - } - test := configValidationTest{ - config: config, - expectedErrorSubstring: []string{"unable to read client-cert", "unable to read client-key"}, - } - - test.testAuthInfo("error", t) - test.testConfig(t) -} -func TestValidateCertDataOverridesFiles(t *testing.T) { - tempFile, _ := ioutil.TempFile("", "") - defer os.Remove(tempFile.Name()) - - config := clientcmdapi.NewConfig() - config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ - ClientCertificate: tempFile.Name(), - ClientCertificateData: []byte("certdata"), - ClientKey: tempFile.Name(), - ClientKeyData: []byte("keydata"), - } - test := configValidationTest{ - config: config, - expectedErrorSubstring: []string{"client-cert-data and client-cert are both specified", "client-key-data and client-key are both specified"}, - } - - test.testAuthInfo("clean", t) - test.testConfig(t) -} -func TestValidateCleanCertFilesAuthInfo(t *testing.T) { - tempFile, _ := ioutil.TempFile("", "") - defer os.Remove(tempFile.Name()) - - config := clientcmdapi.NewConfig() - config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ - ClientCertificate: tempFile.Name(), - ClientKey: tempFile.Name(), - } - test := configValidationTest{ - config: config, - } - - test.testAuthInfo("clean", t) - test.testConfig(t) -} -func TestValidateCleanTokenAuthInfo(t *testing.T) { - config := clientcmdapi.NewConfig() - config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ - Token: "any-value", - } - test := configValidationTest{ - config: config, - } - - test.testAuthInfo("clean", t) - test.testConfig(t) -} - -func TestValidateMultipleMethodsAuthInfo(t *testing.T) { - config := clientcmdapi.NewConfig() - config.AuthInfos["error"] = &clientcmdapi.AuthInfo{ - Token: "token", - Username: "username", - } - test := configValidationTest{ - config: config, - expectedErrorSubstring: []string{"more than one authentication method", "token", "basicAuth"}, - } - - test.testAuthInfo("error", t) - test.testConfig(t) -} - -type configValidationTest struct { - config *clientcmdapi.Config - expectedErrorSubstring []string -} - -func (c configValidationTest) testContext(contextName string, t *testing.T) { - errs := validateContext(contextName, *c.config.Contexts[contextName], *c.config) - - if len(c.expectedErrorSubstring) != 0 { - if len(errs) == 0 { - t.Errorf("Expected error containing: %v", c.expectedErrorSubstring) - } - for _, curr := range c.expectedErrorSubstring { - if len(errs) != 0 && !strings.Contains(utilerrors.NewAggregate(errs).Error(), curr) { - t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, utilerrors.NewAggregate(errs)) - } - } - - } else { - if len(errs) != 0 { - t.Errorf("Unexpected error: %v", utilerrors.NewAggregate(errs)) - } - } -} -func (c configValidationTest) testConfirmUsable(contextName string, t *testing.T) { - err := ConfirmUsable(*c.config, contextName) - - if len(c.expectedErrorSubstring) != 0 { - if err == nil { - t.Errorf("Expected error containing: %v", c.expectedErrorSubstring) - } else { - for _, curr := range c.expectedErrorSubstring { - if err != nil && !strings.Contains(err.Error(), curr) { - t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, err) - } - } - } - } else { - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - } -} -func (c configValidationTest) testConfig(t *testing.T) { - err := Validate(*c.config) - - if len(c.expectedErrorSubstring) != 0 { - if err == nil { - t.Errorf("Expected error containing: %v", c.expectedErrorSubstring) - } else { - for _, curr := range c.expectedErrorSubstring { - if err != nil && !strings.Contains(err.Error(), curr) { - t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, err) - } - } - if !IsConfigurationInvalid(err) { - t.Errorf("all errors should be configuration invalid: %v", err) - } - } - } else { - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - } -} -func (c configValidationTest) testCluster(clusterName string, t *testing.T) { - errs := validateClusterInfo(clusterName, *c.config.Clusters[clusterName]) - - if len(c.expectedErrorSubstring) != 0 { - if len(errs) == 0 { - t.Errorf("Expected error containing: %v", c.expectedErrorSubstring) - } - for _, curr := range c.expectedErrorSubstring { - if len(errs) != 0 && !strings.Contains(utilerrors.NewAggregate(errs).Error(), curr) { - t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, utilerrors.NewAggregate(errs)) - } - } - - } else { - if len(errs) != 0 { - t.Errorf("Unexpected error: %v", utilerrors.NewAggregate(errs)) - } - } -} - -func (c configValidationTest) testAuthInfo(authInfoName string, t *testing.T) { - errs := validateAuthInfo(authInfoName, *c.config.AuthInfos[authInfoName]) - - if len(c.expectedErrorSubstring) != 0 { - if len(errs) == 0 { - t.Errorf("Expected error containing: %v", c.expectedErrorSubstring) - } - for _, curr := range c.expectedErrorSubstring { - if len(errs) != 0 && !strings.Contains(utilerrors.NewAggregate(errs).Error(), curr) { - t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, utilerrors.NewAggregate(errs)) - } - } - - } else { - if len(errs) != 0 { - t.Errorf("Unexpected error: %v", utilerrors.NewAggregate(errs)) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/conditions.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/conditions.go index 7102c8116..5087baa80 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/conditions.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/conditions.go @@ -153,12 +153,18 @@ func JobHasDesiredParallelism(c ExtensionsInterface, job *extensions.Job) wait.C // the desired replica count for a deployment equals its updated replicas count. // (non-terminated pods that have the desired template spec). func DeploymentHasDesiredReplicas(c ExtensionsInterface, deployment *extensions.Deployment) wait.ConditionFunc { + // If we're given a deployment where the status lags the spec, it either + // means that the deployment is stale, or that the deployment manager hasn't + // noticed the update yet. Polling status.Replicas is not safe in the latter + // case. + desiredGeneration := deployment.Generation return func() (bool, error) { deployment, err := c.Deployments(deployment.Namespace).Get(deployment.Name) if err != nil { return false, err } - return deployment.Status.UpdatedReplicas == deployment.Spec.Replicas, nil + return deployment.Status.ObservedGeneration >= desiredGeneration && + deployment.Status.UpdatedReplicas == deployment.Spec.Replicas, nil } } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/conditions_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/conditions_test.go deleted file mode 100644 index 1042461c0..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/conditions_test.go +++ /dev/null @@ -1,71 +0,0 @@ -/* -Copyright 2014 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 unversioned - -import ( - "fmt" - "testing" - - "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/util/wait" -) - -func TestRetryOnConflict(t *testing.T) { - opts := wait.Backoff{Factor: 1.0, Steps: 3} - conflictErr := errors.NewConflict(unversioned.GroupResource{Resource: "test"}, "other", nil) - - // never returns - err := RetryOnConflict(opts, func() error { - return conflictErr - }) - if err != conflictErr { - t.Errorf("unexpected error: %v", err) - } - - // returns immediately - i := 0 - err = RetryOnConflict(opts, func() error { - i++ - return nil - }) - if err != nil || i != 1 { - t.Errorf("unexpected error: %v", err) - } - - // returns immediately on error - testErr := fmt.Errorf("some other error") - err = RetryOnConflict(opts, func() error { - return testErr - }) - if err != testErr { - t.Errorf("unexpected error: %v", err) - } - - // keeps retrying - i = 0 - err = RetryOnConflict(opts, func() error { - if i < 2 { - i++ - return errors.NewConflict(unversioned.GroupResource{Resource: "test"}, "other", nil) - } - return nil - }) - if err != nil || i != 2 { - t.Errorf("unexpected error: %v", err) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/containerinfo_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/containerinfo_test.go deleted file mode 100644 index 5e7e9768d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/containerinfo_test.go +++ /dev/null @@ -1,200 +0,0 @@ -/* -Copyright 2014 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 unversioned - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "net/url" - "path" - "reflect" - "strconv" - "strings" - "testing" - "time" - - cadvisorapi "github.com/google/cadvisor/info/v1" - cadvisorapitest "github.com/google/cadvisor/info/v1/test" -) - -func testHTTPContainerInfoGetter( - req *cadvisorapi.ContainerInfoRequest, - cinfo *cadvisorapi.ContainerInfo, - podID string, - containerID string, - status int, - t *testing.T, -) { - expectedPath := "/stats" - if len(podID) > 0 && len(containerID) > 0 { - expectedPath = path.Join(expectedPath, podID, containerID) - } - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if status != 0 { - w.WriteHeader(status) - } - if strings.TrimRight(r.URL.Path, "/") != strings.TrimRight(expectedPath, "/") { - t.Fatalf("Received request to an invalid path. Should be %v. got %v", - expectedPath, r.URL.Path) - } - - var receivedReq cadvisorapi.ContainerInfoRequest - err := json.NewDecoder(r.Body).Decode(&receivedReq) - if err != nil { - t.Fatal(err) - } - // Note: This will not make a deep copy of req. - // So changing req after Get*Info would be a race. - expectedReq := req - // Fill any empty fields with default value - if !expectedReq.Equals(receivedReq) { - t.Errorf("received wrong request") - } - err = json.NewEncoder(w).Encode(cinfo) - if err != nil { - t.Fatal(err) - } - })) - // TODO: Uncomment when fix #19254 - // defer ts.Close() - hostURL, err := url.Parse(ts.URL) - if err != nil { - t.Fatal(err) - } - parts := strings.Split(hostURL.Host, ":") - - port, err := strconv.Atoi(parts[1]) - if err != nil { - t.Fatal(err) - } - - containerInfoGetter := &HTTPContainerInfoGetter{ - Client: http.DefaultClient, - Port: port, - } - - var receivedContainerInfo *cadvisorapi.ContainerInfo - if len(podID) > 0 && len(containerID) > 0 { - receivedContainerInfo, err = containerInfoGetter.GetContainerInfo(parts[0], podID, containerID, req) - } else { - receivedContainerInfo, err = containerInfoGetter.GetRootInfo(parts[0], req) - } - if status == 0 || status == http.StatusOK { - if err != nil { - t.Errorf("received unexpected error: %v", err) - } - - if !receivedContainerInfo.Eq(cinfo) { - t.Error("received unexpected container info") - } - } else { - if err == nil { - t.Error("did not receive expected error.") - } - } -} - -func TestHTTPContainerInfoGetterGetContainerInfoSuccessfully(t *testing.T) { - req := &cadvisorapi.ContainerInfoRequest{ - NumStats: 10, - } - cinfo := cadvisorapitest.GenerateRandomContainerInfo( - "dockerIDWhichWillNotBeChecked", // docker ID - 2, // Number of cores - req, - 1*time.Second, - ) - testHTTPContainerInfoGetter(req, cinfo, "somePodID", "containerNameInK8S", 0, t) -} - -func TestHTTPContainerInfoGetterGetRootInfoSuccessfully(t *testing.T) { - req := &cadvisorapi.ContainerInfoRequest{ - NumStats: 10, - } - cinfo := cadvisorapitest.GenerateRandomContainerInfo( - "dockerIDWhichWillNotBeChecked", // docker ID - 2, // Number of cores - req, - 1*time.Second, - ) - testHTTPContainerInfoGetter(req, cinfo, "", "", 0, t) -} - -func TestHTTPContainerInfoGetterGetContainerInfoWithError(t *testing.T) { - req := &cadvisorapi.ContainerInfoRequest{ - NumStats: 10, - } - cinfo := cadvisorapitest.GenerateRandomContainerInfo( - "dockerIDWhichWillNotBeChecked", // docker ID - 2, // Number of cores - req, - 1*time.Second, - ) - testHTTPContainerInfoGetter(req, cinfo, "somePodID", "containerNameInK8S", http.StatusNotFound, t) -} - -func TestHTTPContainerInfoGetterGetRootInfoWithError(t *testing.T) { - req := &cadvisorapi.ContainerInfoRequest{ - NumStats: 10, - } - cinfo := cadvisorapitest.GenerateRandomContainerInfo( - "dockerIDWhichWillNotBeChecked", // docker ID - 2, // Number of cores - req, - 1*time.Second, - ) - testHTTPContainerInfoGetter(req, cinfo, "", "", http.StatusNotFound, t) -} - -func TestHTTPGetMachineInfo(t *testing.T) { - mspec := &cadvisorapi.MachineInfo{ - NumCores: 4, - MemoryCapacity: 2048, - } - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - err := json.NewEncoder(w).Encode(mspec) - if err != nil { - t.Fatal(err) - } - })) - // TODO: Uncomment when fix #19254 - // defer ts.Close() - hostURL, err := url.Parse(ts.URL) - if err != nil { - t.Fatal(err) - } - parts := strings.Split(hostURL.Host, ":") - - port, err := strconv.Atoi(parts[1]) - if err != nil { - t.Fatal(err) - } - - containerInfoGetter := &HTTPContainerInfoGetter{ - Client: http.DefaultClient, - Port: port, - } - - received, err := containerInfoGetter.GetMachineInfo(parts[0]) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(received, mspec) { - t.Errorf("received wrong machine spec") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/daemon_sets_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/daemon_sets_test.go deleted file mode 100644 index 2bfbb44b0..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/daemon_sets_test.go +++ /dev/null @@ -1,199 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/apis/extensions" -) - -func getDSResourceName() string { - return "daemonsets" -} - -func TestListDaemonSets(t *testing.T) { - ns := api.NamespaceAll - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Extensions.ResourcePath(getDSResourceName(), ns, ""), - }, - Response: simple.Response{StatusCode: 200, - Body: &extensions.DaemonSetList{ - Items: []extensions.DaemonSet{ - { - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.DaemonSetSpec{ - Template: api.PodTemplateSpec{}, - }, - }, - }, - }, - }, - } - receivedDSs, err := c.Setup(t).Extensions().DaemonSets(ns).List(api.ListOptions{}) - defer c.Close() - c.Validate(t, receivedDSs, err) - -} - -func TestGetDaemonSet(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "GET", Path: testapi.Extensions.ResourcePath(getDSResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.DaemonSetSpec{ - Template: api.PodTemplateSpec{}, - }, - }, - }, - } - receivedDaemonSet, err := c.Setup(t).Extensions().DaemonSets(ns).Get("foo") - defer c.Close() - c.Validate(t, receivedDaemonSet, err) -} - -func TestGetDaemonSetWithNoName(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{Error: true} - receivedPod, err := c.Setup(t).Extensions().DaemonSets(ns).Get("") - defer c.Close() - if (err != nil) && (err.Error() != simple.NameRequiredError) { - t.Errorf("Expected error: %v, but got %v", simple.NameRequiredError, err) - } - - c.Validate(t, receivedPod, err) -} - -func TestUpdateDaemonSet(t *testing.T) { - ns := api.NamespaceDefault - requestDaemonSet := &extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Extensions.ResourcePath(getDSResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.DaemonSetSpec{ - Template: api.PodTemplateSpec{}, - }, - }, - }, - } - receivedDaemonSet, err := c.Setup(t).Extensions().DaemonSets(ns).Update(requestDaemonSet) - defer c.Close() - c.Validate(t, receivedDaemonSet, err) -} - -func TestUpdateDaemonSetUpdateStatus(t *testing.T) { - ns := api.NamespaceDefault - requestDaemonSet := &extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Extensions.ResourcePath(getDSResourceName(), ns, "foo") + "/status", Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.DaemonSetSpec{ - Template: api.PodTemplateSpec{}, - }, - Status: extensions.DaemonSetStatus{}, - }, - }, - } - receivedDaemonSet, err := c.Setup(t).Extensions().DaemonSets(ns).UpdateStatus(requestDaemonSet) - defer c.Close() - c.Validate(t, receivedDaemonSet, err) -} - -func TestDeleteDaemon(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: testapi.Extensions.ResourcePath(getDSResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).Extensions().DaemonSets(ns).Delete("foo") - defer c.Close() - c.Validate(t, nil, err) -} - -func TestCreateDaemonSet(t *testing.T) { - ns := api.NamespaceDefault - requestDaemonSet := &extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - } - c := &simple.Client{ - Request: simple.Request{Method: "POST", Path: testapi.Extensions.ResourcePath(getDSResourceName(), ns, ""), Body: requestDaemonSet, Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.DaemonSetSpec{ - Template: api.PodTemplateSpec{}, - }, - }, - }, - } - receivedDaemonSet, err := c.Setup(t).Extensions().DaemonSets(ns).Create(requestDaemonSet) - defer c.Close() - c.Validate(t, receivedDaemonSet, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/deployment_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/deployment_test.go deleted file mode 100644 index 2480c3a25..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/deployment_test.go +++ /dev/null @@ -1,240 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "net/http" - "net/url" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/labels" -) - -func getDeploymentsResourceName() string { - return "deployments" -} - -func TestDeploymentCreate(t *testing.T) { - ns := api.NamespaceDefault - deployment := extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: ns, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: testapi.Extensions.ResourcePath(getDeploymentsResourceName(), ns, ""), - Query: simple.BuildQueryValues(nil), - Body: &deployment, - }, - Response: simple.Response{StatusCode: 200, Body: &deployment}, - } - - response, err := c.Setup(t).Deployments(ns).Create(&deployment) - defer c.Close() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - c.Validate(t, response, err) -} - -func TestDeploymentGet(t *testing.T) { - ns := api.NamespaceDefault - deployment := &extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: ns, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Extensions.ResourcePath(getDeploymentsResourceName(), ns, "abc"), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: deployment}, - } - - response, err := c.Setup(t).Deployments(ns).Get("abc") - defer c.Close() - c.Validate(t, response, err) -} - -func TestDeploymentList(t *testing.T) { - ns := api.NamespaceDefault - deploymentList := &extensions.DeploymentList{ - Items: []extensions.Deployment{ - { - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: ns, - }, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Extensions.ResourcePath(getDeploymentsResourceName(), ns, ""), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: deploymentList}, - } - response, err := c.Setup(t).Deployments(ns).List(api.ListOptions{}) - defer c.Close() - c.Validate(t, response, err) -} - -func TestDeploymentUpdate(t *testing.T) { - ns := api.NamespaceDefault - deployment := &extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: ns, - ResourceVersion: "1", - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "PUT", - Path: testapi.Extensions.ResourcePath(getDeploymentsResourceName(), ns, "abc"), - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{StatusCode: 200, Body: deployment}, - } - response, err := c.Setup(t).Deployments(ns).Update(deployment) - defer c.Close() - c.Validate(t, response, err) -} - -func TestDeploymentUpdateStatus(t *testing.T) { - ns := api.NamespaceDefault - deployment := &extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: ns, - ResourceVersion: "1", - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "PUT", - Path: testapi.Extensions.ResourcePath(getDeploymentsResourceName(), ns, "abc") + "/status", - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{StatusCode: 200, Body: deployment}, - } - response, err := c.Setup(t).Deployments(ns).UpdateStatus(deployment) - defer c.Close() - c.Validate(t, response, err) -} - -func TestDeploymentDelete(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{ - Method: "DELETE", - Path: testapi.Extensions.ResourcePath(getDeploymentsResourceName(), ns, "foo"), - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).Deployments(ns).Delete("foo", nil) - defer c.Close() - c.Validate(t, nil, err) -} - -func TestDeploymentWatch(t *testing.T) { - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Extensions.ResourcePathWithPrefix("watch", getDeploymentsResourceName(), "", ""), - Query: url.Values{"resourceVersion": []string{}}, - }, - Response: simple.Response{StatusCode: 200}, - } - _, err := c.Setup(t).Deployments(api.NamespaceAll).Watch(api.ListOptions{}) - defer c.Close() - c.Validate(t, nil, err) -} - -func TestListDeploymentsLabels(t *testing.T) { - ns := api.NamespaceDefault - labelSelectorQueryParamName := unversioned.LabelSelectorQueryParam(testapi.Extensions.GroupVersion().String()) - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Extensions.ResourcePath("deployments", ns, ""), - Query: simple.BuildQueryValues(url.Values{labelSelectorQueryParamName: []string{"foo=bar,name=baz"}})}, - Response: simple.Response{ - StatusCode: http.StatusOK, - Body: &extensions.DeploymentList{ - Items: []extensions.Deployment{ - { - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - }, - }, - }, - }, - } - c.Setup(t) - defer c.Close() - c.QueryValidator[labelSelectorQueryParamName] = simple.ValidateLabels - selector := labels.Set{"foo": "bar", "name": "baz"}.AsSelector() - options := api.ListOptions{LabelSelector: selector} - receivedPodList, err := c.Deployments(ns).List(options) - c.Validate(t, receivedPodList, err) -} - -func TestDeploymentRollback(t *testing.T) { - ns := api.NamespaceDefault - deploymentRollback := &extensions.DeploymentRollback{ - Name: "abc", - UpdatedAnnotations: map[string]string{}, - RollbackTo: extensions.RollbackConfig{Revision: 1}, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: testapi.Extensions.ResourcePath(getDeploymentsResourceName(), ns, "abc") + "/rollback", - Query: simple.BuildQueryValues(nil), - Body: deploymentRollback, - }, - Response: simple.Response{StatusCode: http.StatusOK}, - } - err := c.Setup(t).Deployments(ns).Rollback(deploymentRollback) - defer c.Close() - c.ValidateCommon(t, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/endpoints_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/endpoints_test.go deleted file mode 100644 index ef1701585..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/endpoints_test.go +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" -) - -func TestListEndpoints(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "GET", Path: testapi.Default.ResourcePath("endpoints", ns, ""), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, - Body: &api.EndpointsList{ - Items: []api.Endpoints{ - { - ObjectMeta: api.ObjectMeta{Name: "endpoint-1"}, - Subsets: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "10.245.1.2"}, {IP: "10.245.1.3"}}, - Ports: []api.EndpointPort{{Port: 8080}}, - }}, - }, - }, - }, - }, - } - receivedEndpointsList, err := c.Setup(t).Endpoints(ns).List(api.ListOptions{}) - defer c.Close() - c.Validate(t, receivedEndpointsList, err) -} - -func TestGetEndpoints(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "GET", Path: testapi.Default.ResourcePath("endpoints", ns, "endpoint-1"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: &api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "endpoint-1"}}}, - } - response, err := c.Setup(t).Endpoints(ns).Get("endpoint-1") - defer c.Close() - c.Validate(t, response, err) -} - -func TestGetEndpointWithNoName(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{Error: true} - receivedPod, err := c.Setup(t).Endpoints(ns).Get("") - defer c.Close() - if (err != nil) && (err.Error() != simple.NameRequiredError) { - t.Errorf("Expected error: %v, but got %v", simple.NameRequiredError, err) - } - - c.Validate(t, receivedPod, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/events_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/events_test.go deleted file mode 100644 index 371c7544e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/events_test.go +++ /dev/null @@ -1,205 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "net/url" - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" -) - -func TestEventSearch(t *testing.T) { - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath("events", "baz", ""), - Query: url.Values{ - unversioned.FieldSelectorQueryParam(testapi.Default.GroupVersion().String()): []string{ - GetInvolvedObjectNameFieldLabel(testapi.Default.GroupVersion().String()) + "=foo,", - "involvedObject.namespace=baz,", - "involvedObject.kind=Pod", - }, - unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String()): []string{}, - }, - }, - Response: simple.Response{StatusCode: 200, Body: &api.EventList{}}, - } - eventList, err := c.Setup(t).Events("baz").Search( - &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "baz", - SelfLink: testapi.Default.SelfLink("pods", ""), - }, - }, - ) - defer c.Close() - c.Validate(t, eventList, err) -} - -func TestEventCreate(t *testing.T) { - objReference := &api.ObjectReference{ - Kind: "foo", - Namespace: "nm", - Name: "objref1", - UID: "uid", - APIVersion: "apiv1", - ResourceVersion: "1", - } - timeStamp := unversioned.Now() - event := &api.Event{ - ObjectMeta: api.ObjectMeta{ - Namespace: api.NamespaceDefault, - }, - InvolvedObject: *objReference, - FirstTimestamp: timeStamp, - LastTimestamp: timeStamp, - Count: 1, - Type: api.EventTypeNormal, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: testapi.Default.ResourcePath("events", api.NamespaceDefault, ""), - Body: event, - }, - Response: simple.Response{StatusCode: 200, Body: event}, - } - - response, err := c.Setup(t).Events(api.NamespaceDefault).Create(event) - defer c.Close() - - if err != nil { - t.Fatalf("%v should be nil.", err) - } - - if e, a := *objReference, response.InvolvedObject; !reflect.DeepEqual(e, a) { - t.Errorf("%#v != %#v.", e, a) - } -} - -func TestEventGet(t *testing.T) { - objReference := &api.ObjectReference{ - Kind: "foo", - Namespace: "nm", - Name: "objref1", - UID: "uid", - APIVersion: "apiv1", - ResourceVersion: "1", - } - timeStamp := unversioned.Now() - event := &api.Event{ - ObjectMeta: api.ObjectMeta{ - Namespace: "other", - }, - InvolvedObject: *objReference, - FirstTimestamp: timeStamp, - LastTimestamp: timeStamp, - Count: 1, - Type: api.EventTypeNormal, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath("events", "other", "1"), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: event}, - } - - response, err := c.Setup(t).Events("other").Get("1") - defer c.Close() - - if err != nil { - t.Fatalf("%v should be nil.", err) - } - - if e, r := event.InvolvedObject, response.InvolvedObject; !reflect.DeepEqual(e, r) { - t.Errorf("%#v != %#v.", e, r) - } -} - -func TestEventList(t *testing.T) { - ns := api.NamespaceDefault - objReference := &api.ObjectReference{ - Kind: "foo", - Namespace: ns, - Name: "objref1", - UID: "uid", - APIVersion: "apiv1", - ResourceVersion: "1", - } - timeStamp := unversioned.Now() - eventList := &api.EventList{ - Items: []api.Event{ - { - InvolvedObject: *objReference, - FirstTimestamp: timeStamp, - LastTimestamp: timeStamp, - Count: 1, - Type: api.EventTypeNormal, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath("events", ns, ""), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: eventList}, - } - response, err := c.Setup(t).Events(ns).List(api.ListOptions{}) - defer c.Close() - - if err != nil { - t.Errorf("%#v should be nil.", err) - } - - if len(response.Items) != 1 { - t.Errorf("%#v response.Items should have len 1.", response.Items) - } - - responseEvent := response.Items[0] - if e, r := eventList.Items[0].InvolvedObject, - responseEvent.InvolvedObject; !reflect.DeepEqual(e, r) { - t.Errorf("%#v != %#v.", e, r) - } -} - -func TestEventDelete(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{ - Method: "DELETE", - Path: testapi.Default.ResourcePath("events", ns, "foo"), - }, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).Events(ns).Delete("foo") - defer c.Close() - c.Validate(t, nil, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/extensions.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/extensions.go index a95bd1b89..5db86dbb8 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/extensions.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/extensions.go @@ -20,6 +20,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/client/restclient" ) // Interface holds the experimental methods for clients of Kubernetes @@ -42,7 +43,7 @@ type ExtensionsInterface interface { // Features of Extensions group are not supported and may be changed or removed in // incompatible ways at any time. type ExtensionsClient struct { - *RESTClient + *restclient.RESTClient } func (c *ExtensionsClient) PodSecurityPolicies() PodSecurityPolicyInterface { @@ -85,12 +86,12 @@ func (c *ExtensionsClient) ReplicaSets(namespace string) ReplicaSetInterface { // provides access to experimental Kubernetes features. // Features of Extensions group are not supported and may be changed or removed in // incompatible ways at any time. -func NewExtensions(c *Config) (*ExtensionsClient, error) { +func NewExtensions(c *restclient.Config) (*ExtensionsClient, error) { config := *c if err := setExtensionsDefaults(&config); err != nil { return nil, err } - client, err := RESTClientFor(&config) + client, err := restclient.RESTClientFor(&config) if err != nil { return nil, err } @@ -101,7 +102,7 @@ func NewExtensions(c *Config) (*ExtensionsClient, error) { // panics if there is an error in the config. // Features of Extensions group are not supported and may be changed or removed in // incompatible ways at any time. -func NewExtensionsOrDie(c *Config) *ExtensionsClient { +func NewExtensionsOrDie(c *restclient.Config) *ExtensionsClient { client, err := NewExtensions(c) if err != nil { panic(err) @@ -109,7 +110,7 @@ func NewExtensionsOrDie(c *Config) *ExtensionsClient { return client } -func setExtensionsDefaults(config *Config) error { +func setExtensionsDefaults(config *restclient.Config) error { // if experimental group is not registered, return an error g, err := registered.Group(extensions.GroupName) if err != nil { @@ -117,7 +118,7 @@ func setExtensionsDefaults(config *Config) error { } config.APIPath = defaultAPIPath if config.UserAgent == "" { - config.UserAgent = DefaultKubernetesUserAgent() + config.UserAgent = restclient.DefaultKubernetesUserAgent() } // TODO: Unconditionally set the config.Version, until we fix the config. //if config.Version == "" { diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/fake/fake.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/fake/fake.go deleted file mode 100644 index 3a314708f..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/fake/fake.go +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright 2014 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. -*/ - -// This is made a separate package and should only be imported by tests, because -// it imports testapi -package fake - -import ( - "net/http" - "net/url" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/runtime" -) - -func CreateHTTPClient(roundTripper func(*http.Request) (*http.Response, error)) *http.Client { - return &http.Client{ - Transport: roundTripperFunc(roundTripper), - } -} - -type roundTripperFunc func(*http.Request) (*http.Response, error) - -func (f roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) { - return f(req) -} - -// RESTClient provides a fake RESTClient interface. -type RESTClient struct { - Client *http.Client - Codec runtime.Codec - Req *http.Request - Resp *http.Response - Err error -} - -func (c *RESTClient) Get() *unversioned.Request { - return c.request("GET") -} - -func (c *RESTClient) Put() *unversioned.Request { - return c.request("PUT") -} - -func (c *RESTClient) Patch(_ api.PatchType) *unversioned.Request { - return c.request("PATCH") -} - -func (c *RESTClient) Post() *unversioned.Request { - return c.request("POST") -} - -func (c *RESTClient) Delete() *unversioned.Request { - return c.request("DELETE") -} - -func (c *RESTClient) request(verb string) *unversioned.Request { - return unversioned.NewRequest(c, verb, &url.URL{Host: "localhost"}, "", unversioned.ContentConfig{GroupVersion: testapi.Default.GroupVersion(), Codec: c.Codec}, nil, nil) -} - -func (c *RESTClient) Do(req *http.Request) (*http.Response, error) { - if c.Err != nil { - return nil, c.Err - } - c.Req = req - if c.Client != nil { - return c.Client.Do(req) - } - return c.Resp, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/flags_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/flags_test.go deleted file mode 100644 index ab0f94d04..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/flags_test.go +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright 2014 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 unversioned - -import ( - "testing" - "time" - - "k8s.io/kubernetes/pkg/util/sets" -) - -type fakeFlagSet struct { - t *testing.T - set sets.String -} - -func (f *fakeFlagSet) StringVar(p *string, name, value, usage string) { - if p == nil { - f.t.Errorf("unexpected nil pointer") - } - if usage == "" { - f.t.Errorf("unexpected empty usage") - } - f.set.Insert(name) -} - -func (f *fakeFlagSet) BoolVar(p *bool, name string, value bool, usage string) { - if p == nil { - f.t.Errorf("unexpected nil pointer") - } - if usage == "" { - f.t.Errorf("unexpected empty usage") - } - f.set.Insert(name) -} - -func (f *fakeFlagSet) UintVar(p *uint, name string, value uint, usage string) { - if p == nil { - f.t.Errorf("unexpected nil pointer") - } - if usage == "" { - f.t.Errorf("unexpected empty usage") - } - f.set.Insert(name) -} - -func (f *fakeFlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) { - if p == nil { - f.t.Errorf("unexpected nil pointer") - } - if usage == "" { - f.t.Errorf("unexpected empty usage") - } - f.set.Insert(name) -} - -func (f *fakeFlagSet) IntVar(p *int, name string, value int, usage string) { - if p == nil { - f.t.Errorf("unexpected nil pointer") - } - if usage == "" { - f.t.Errorf("unexpected empty usage") - } - f.set.Insert(name) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/helper.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/helper.go index 346b3fdc4..3ee6d6ec6 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/helper.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/helper.go @@ -17,26 +17,17 @@ limitations under the License. package unversioned import ( - "encoding/json" "fmt" - "io/ioutil" - "net" - "net/http" - "net/url" - "os" - "path" "reflect" - gruntime "runtime" - "strings" - "github.com/golang/glog" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/autoscaling" + "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util" + "k8s.io/kubernetes/pkg/client/restclient" + "k8s.io/kubernetes/pkg/client/typed/discovery" "k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/version" ) @@ -46,112 +37,22 @@ const ( defaultAPIPath = "/apis" ) -// Config holds the common attributes that can be passed to a Kubernetes client on -// initialization. -type Config struct { - // Host must be a host string, a host:port pair, or a URL to the base of the apiserver. - // If a URL is given then the (optional) Path of that URL represents a prefix that must - // be appended to all request URIs used to access the apiserver. This allows a frontend - // proxy to easily relocate all of the apiserver endpoints. - Host string - // APIPath is a sub-path that points to an API root. - APIPath string - // Prefix is the sub path of the server. If not specified, the client will set - // a default value. Use "/" to indicate the server root should be used - Prefix string - - // ContentConfig contains settings that affect how objects are transformed when - // sent to the server. - ContentConfig - - // Server requires Basic authentication - Username string - Password string - - // Server requires Bearer authentication. This client will not attempt to use - // refresh tokens for an OAuth2 flow. - // TODO: demonstrate an OAuth2 compatible client. - BearerToken string - - // TLSClientConfig contains settings to enable transport layer security - TLSClientConfig - - // Server should be accessed without verifying the TLS - // certificate. For testing only. - Insecure bool - - // UserAgent is an optional field that specifies the caller of this request. - UserAgent string - - // Transport may be used for custom HTTP behavior. This attribute may not - // be specified with the TLS client certificate options. Use WrapTransport - // for most client level operations. - Transport http.RoundTripper - // WrapTransport will be invoked for custom HTTP behavior after the underlying - // transport is initialized (either the transport created from TLSClientConfig, - // Transport, or http.DefaultTransport). The config may layer other RoundTrippers - // on top of the returned RoundTripper. - WrapTransport func(rt http.RoundTripper) http.RoundTripper - - // QPS indicates the maximum QPS to the master from this client. If zero, QPS is unlimited. - QPS float32 - - // Maximum burst for throttle - Burst int -} - -// TLSClientConfig contains settings to enable transport layer security -type TLSClientConfig struct { - // Server requires TLS client certificate authentication - CertFile string - // Server requires TLS client certificate authentication - KeyFile string - // Trusted root certificates for server - CAFile string - - // CertData holds PEM-encoded bytes (typically read from a client certificate file). - // CertData takes precedence over CertFile - CertData []byte - // KeyData holds PEM-encoded bytes (typically read from a client certificate key file). - // KeyData takes precedence over KeyFile - KeyData []byte - // CAData holds PEM-encoded bytes (typically read from a root certificates bundle). - // CAData takes precedence over CAFile - CAData []byte -} - -type ContentConfig struct { - // ContentType specifies the wire format used to communicate with the server. - // This value will be set as the Accept header on requests made to the server, and - // as the default content type on any object sent to the server. If not set, - // "application/json" is used. - ContentType string - // GroupVersion is the API version to talk to. Must be provided when initializing - // a RESTClient directly. When initializing a Client, will be set with the default - // code version. - GroupVersion *unversioned.GroupVersion - // Codec specifies the encoding and decoding behavior for runtime.Objects passed - // to a RESTClient or Client. Required when initializing a RESTClient, optional - // when initializing a Client. - Codec runtime.Codec -} - // New creates a Kubernetes client for the given config. This client works with pods, // replication controllers, daemons, and services. It allows operations such as list, get, update // and delete on these objects. An error is returned if the provided configuration // is not valid. -func New(c *Config) (*Client, error) { +func New(c *restclient.Config) (*Client, error) { config := *c if err := SetKubernetesDefaults(&config); err != nil { return nil, err } - client, err := RESTClientFor(&config) + client, err := restclient.RESTClientFor(&config) if err != nil { return nil, err } discoveryConfig := *c - discoveryClient, err := NewDiscoveryClientForConfig(&discoveryConfig) + discoveryClient, err := discovery.NewDiscoveryClientForConfig(&discoveryConfig) if err != nil { return nil, err } @@ -165,6 +66,15 @@ func New(c *Config) (*Client, error) { } } + var batchClient *BatchClient + if registered.IsRegistered(batch.GroupName) { + batchConfig := *c + batchClient, err = NewBatch(&batchConfig) + if err != nil { + return nil, err + } + } + var extensionsClient *ExtensionsClient if registered.IsRegistered(extensions.GroupName) { extensionsConfig := *c @@ -174,13 +84,13 @@ func New(c *Config) (*Client, error) { } } - return &Client{RESTClient: client, AutoscalingClient: autoscalingClient, ExtensionsClient: extensionsClient, DiscoveryClient: discoveryClient}, nil + return &Client{RESTClient: client, AutoscalingClient: autoscalingClient, BatchClient: batchClient, ExtensionsClient: extensionsClient, DiscoveryClient: discoveryClient}, nil } // MatchesServerVersion queries the server to compares the build version // (git hash) of the client with the server's build version. It returns an error // if it failed to contact the server or if the versions are not an exact match. -func MatchesServerVersion(client *Client, c *Config) error { +func MatchesServerVersion(client *Client, c *restclient.Config) error { var err error if client == nil { client, err = New(c) @@ -200,66 +110,6 @@ func MatchesServerVersion(client *Client, c *Config) error { return nil } -func ExtractGroupVersions(l *unversioned.APIGroupList) []string { - var groupVersions []string - for _, g := range l.Groups { - for _, gv := range g.Versions { - groupVersions = append(groupVersions, gv.GroupVersion) - } - } - return groupVersions -} - -// ServerAPIVersions returns the GroupVersions supported by the API server. -// It creates a RESTClient based on the passed in config, but it doesn't rely -// on the Version and Codec of the config, because it uses AbsPath and -// takes the raw response. -func ServerAPIVersions(c *Config) (groupVersions []string, err error) { - transport, err := TransportFor(c) - if err != nil { - return nil, err - } - client := http.Client{Transport: transport} - - configCopy := *c - configCopy.GroupVersion = nil - configCopy.APIPath = "" - baseURL, _, err := defaultServerUrlFor(&configCopy) - if err != nil { - return nil, err - } - // Get the groupVersions exposed at /api - originalPath := baseURL.Path - baseURL.Path = path.Join(originalPath, legacyAPIPath) - resp, err := client.Get(baseURL.String()) - if err != nil { - return nil, err - } - var v unversioned.APIVersions - defer resp.Body.Close() - err = json.NewDecoder(resp.Body).Decode(&v) - if err != nil { - return nil, fmt.Errorf("unexpected error: %v", err) - } - - groupVersions = append(groupVersions, v.Versions...) - // Get the groupVersions exposed at /apis - baseURL.Path = path.Join(originalPath, defaultAPIPath) - resp2, err := client.Get(baseURL.String()) - if err != nil { - return nil, err - } - var apiGroupList unversioned.APIGroupList - defer resp2.Body.Close() - err = json.NewDecoder(resp2.Body).Decode(&apiGroupList) - if err != nil { - return nil, fmt.Errorf("unexpected error: %v", err) - } - groupVersions = append(groupVersions, ExtractGroupVersions(&apiGroupList)...) - - return groupVersions, nil -} - // NegotiateVersion queries the server's supported api versions to find // a version that both client and server support. // - If no version is provided, try registered client versions in order of @@ -269,7 +119,7 @@ func ServerAPIVersions(c *Config) (groupVersions []string, err error) { // stderr and try client's registered versions in order of preference. // - If version is config default, and the server does not support it, // return an error. -func NegotiateVersion(client *Client, c *Config, requestedGV *unversioned.GroupVersion, clientRegisteredGVs []unversioned.GroupVersion) (*unversioned.GroupVersion, error) { +func NegotiateVersion(client *Client, c *restclient.Config, requestedGV *unversioned.GroupVersion, clientRegisteredGVs []unversioned.GroupVersion) (*unversioned.GroupVersion, error) { var err error if client == nil { client, err = New(c) @@ -287,7 +137,7 @@ func NegotiateVersion(client *Client, c *Config, requestedGV *unversioned.GroupV // not a negotiation specific error. return nil, err } - versions := ExtractGroupVersions(groups) + versions := unversioned.ExtractGroupVersions(groups) serverVersions := sets.String{} for _, v := range versions { serverVersions.Insert(v) @@ -339,7 +189,7 @@ func NegotiateVersion(client *Client, c *Config, requestedGV *unversioned.GroupV } // NewOrDie creates a Kubernetes client and panics if the provided API version is not recognized. -func NewOrDie(c *Config) *Client { +func NewOrDie(c *restclient.Config) *Client { client, err := New(c) if err != nil { panic(err) @@ -347,39 +197,9 @@ func NewOrDie(c *Config) *Client { return client } -// InClusterConfig returns a config object which uses the service account -// kubernetes gives to pods. It's intended for clients that expect to be -// running inside a pod running on kuberenetes. It will return an error if -// called from a process not running in a kubernetes environment. -func InClusterConfig() (*Config, error) { - host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT") - if len(host) == 0 || len(port) == 0 { - return nil, fmt.Errorf("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined") - } - - token, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/" + api.ServiceAccountTokenKey) - if err != nil { - return nil, err - } - tlsClientConfig := TLSClientConfig{} - rootCAFile := "/var/run/secrets/kubernetes.io/serviceaccount/" + api.ServiceAccountRootCAKey - if _, err := util.CertPoolFromFile(rootCAFile); err != nil { - glog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err) - } else { - tlsClientConfig.CAFile = rootCAFile - } - - return &Config{ - // TODO: switch to using cluster DNS. - Host: "https://" + net.JoinHostPort(host, port), - BearerToken: string(token), - TLSClientConfig: tlsClientConfig, - }, nil -} - // NewInCluster is a shortcut for calling InClusterConfig() and then New(). func NewInCluster() (*Client, error) { - cc, err := InClusterConfig() + cc, err := restclient.InClusterConfig() if err != nil { return nil, err } @@ -389,13 +209,10 @@ func NewInCluster() (*Client, error) { // SetKubernetesDefaults sets default values on the provided client config for accessing the // Kubernetes API or returns an error if any of the defaults are impossible or invalid. // TODO: this method needs to be split into one that sets defaults per group, expected to be fix in PR "Refactoring clientcache.go and helper.go #14592" -func SetKubernetesDefaults(config *Config) error { +func SetKubernetesDefaults(config *restclient.Config) error { if config.APIPath == "" { config.APIPath = legacyAPIPath } - if len(config.UserAgent) == 0 { - config.UserAgent = DefaultKubernetesUserAgent() - } g, err := registered.Group(api.GroupName) if err != nil { return err @@ -406,233 +223,6 @@ func SetKubernetesDefaults(config *Config) error { if config.Codec == nil { config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion) } - if config.QPS == 0.0 { - config.QPS = 5.0 - } - if config.Burst == 0 { - config.Burst = 10 - } - return nil -} - -// RESTClientFor returns a RESTClient that satisfies the requested attributes on a client Config -// object. Note that a RESTClient may require fields that are optional when initializing a Client. -// A RESTClient created by this method is generic - it expects to operate on an API that follows -// the Kubernetes conventions, but may not be the Kubernetes API. -func RESTClientFor(config *Config) (*RESTClient, error) { - if config.GroupVersion == nil { - return nil, fmt.Errorf("GroupVersion is required when initializing a RESTClient") - } - if config.Codec == nil { - return nil, fmt.Errorf("Codec is required when initializing a RESTClient") - } - - baseURL, versionedAPIPath, err := defaultServerUrlFor(config) - if err != nil { - return nil, err - } - - transport, err := TransportFor(config) - if err != nil { - return nil, err - } - - var httpClient *http.Client - if transport != http.DefaultTransport { - httpClient = &http.Client{Transport: transport} - } - - client := NewRESTClient(baseURL, versionedAPIPath, config.ContentConfig, config.QPS, config.Burst, httpClient) - - return client, nil -} - -// UnversionedRESTClientFor is the same as RESTClientFor, except that it allows -// the config.Version to be empty. -func UnversionedRESTClientFor(config *Config) (*RESTClient, error) { - if config.Codec == nil { - return nil, fmt.Errorf("Codec is required when initializing a RESTClient") - } - - baseURL, versionedAPIPath, err := defaultServerUrlFor(config) - if err != nil { - return nil, err - } - - transport, err := TransportFor(config) - if err != nil { - return nil, err - } - - var httpClient *http.Client - if transport != http.DefaultTransport { - httpClient = &http.Client{Transport: transport} - } - - versionConfig := config.ContentConfig - if versionConfig.GroupVersion == nil { - v := unversioned.SchemeGroupVersion - versionConfig.GroupVersion = &v - } - - client := NewRESTClient(baseURL, versionedAPIPath, versionConfig, config.QPS, config.Burst, httpClient) - return client, nil -} - -// DefaultServerURL converts a host, host:port, or URL string to the default base server API path -// to use with a Client at a given API version following the standard conventions for a -// Kubernetes API. -func DefaultServerURL(host, apiPath string, groupVersion unversioned.GroupVersion, defaultTLS bool) (*url.URL, string, error) { - if host == "" { - return nil, "", fmt.Errorf("host must be a URL or a host:port pair") - } - base := host - hostURL, err := url.Parse(base) - if err != nil { - return nil, "", err - } - if hostURL.Scheme == "" { - scheme := "http://" - if defaultTLS { - scheme = "https://" - } - hostURL, err = url.Parse(scheme + base) - if err != nil { - return nil, "", err - } - if hostURL.Path != "" && hostURL.Path != "/" { - return nil, "", fmt.Errorf("host must be a URL or a host:port pair: %q", base) - } - } - - // hostURL.Path is optional; a non-empty Path is treated as a prefix that is to be applied to - // all URIs used to access the host. this is useful when there's a proxy in front of the - // apiserver that has relocated the apiserver endpoints, forwarding all requests from, for - // example, /a/b/c to the apiserver. in this case the Path should be /a/b/c. - // - // if running without a frontend proxy (that changes the location of the apiserver), then - // hostURL.Path should be blank. - // - // versionedAPIPath, a path relative to baseURL.Path, points to a versioned API base - versionedAPIPath := path.Join("/", apiPath) - - // Add the version to the end of the path - if len(groupVersion.Group) > 0 { - versionedAPIPath = path.Join(versionedAPIPath, groupVersion.Group, groupVersion.Version) - - } else { - versionedAPIPath = path.Join(versionedAPIPath, groupVersion.Version) - - } - - return hostURL, versionedAPIPath, nil -} - -// IsConfigTransportTLS returns true if and only if the provided config will result in a protected -// connection to the server when it is passed to client.New() or client.RESTClientFor(). -// Use to determine when to send credentials over the wire. -// -// Note: the Insecure flag is ignored when testing for this value, so MITM attacks are -// still possible. -func IsConfigTransportTLS(config Config) bool { - // determination of TLS transport does not logically require a version to be specified - // modify the copy of the config we got to satisfy preconditions for defaultServerUrlFor - config.GroupVersion = defaultVersionFor(&config) - - baseURL, _, err := defaultServerUrlFor(&config) - if err != nil { - return false - } - return baseURL.Scheme == "https" -} - -// defaultServerUrlFor is shared between IsConfigTransportTLS and RESTClientFor. It -// requires Host and Version to be set prior to being called. -func defaultServerUrlFor(config *Config) (*url.URL, string, error) { - // TODO: move the default to secure when the apiserver supports TLS by default - // config.Insecure is taken to mean "I want HTTPS but don't bother checking the certs against a CA." - hasCA := len(config.CAFile) != 0 || len(config.CAData) != 0 - hasCert := len(config.CertFile) != 0 || len(config.CertData) != 0 - defaultTLS := hasCA || hasCert || config.Insecure - host := config.Host - if host == "" { - host = "localhost" - } - - if config.GroupVersion != nil { - return DefaultServerURL(host, config.APIPath, *config.GroupVersion, defaultTLS) - } - return DefaultServerURL(host, config.APIPath, unversioned.GroupVersion{}, defaultTLS) -} - -// defaultVersionFor is shared between IsConfigTransportTLS and RESTClientFor -func defaultVersionFor(config *Config) *unversioned.GroupVersion { - if config.GroupVersion == nil { - // Clients default to the preferred code API version - // TODO: implement version negotiation (highest version supported by server) - // TODO this drops out when groupmeta is refactored - copyGroupVersion := registered.GroupOrDie(api.GroupName).GroupVersion - return ©GroupVersion - } - - return config.GroupVersion -} - -// DefaultKubernetesUserAgent returns the default user agent that clients can use. -func DefaultKubernetesUserAgent() string { - commit := version.Get().GitCommit - if len(commit) > 7 { - commit = commit[:7] - } - if len(commit) == 0 { - commit = "unknown" - } - version := version.Get().GitVersion - seg := strings.SplitN(version, "-", 2) - version = seg[0] - return fmt.Sprintf("%s/%s (%s/%s) kubernetes/%s", path.Base(os.Args[0]), version, gruntime.GOOS, gruntime.GOARCH, commit) -} - -// LoadTLSFiles copies the data from the CertFile, KeyFile, and CAFile fields into the CertData, -// KeyData, and CAFile fields, or returns an error. If no error is returned, all three fields are -// either populated or were empty to start. -func LoadTLSFiles(c *Config) error { - var err error - c.CAData, err = dataFromSliceOrFile(c.CAData, c.CAFile) - if err != nil { - return err - } - - c.CertData, err = dataFromSliceOrFile(c.CertData, c.CertFile) - if err != nil { - return err - } - - c.KeyData, err = dataFromSliceOrFile(c.KeyData, c.KeyFile) - if err != nil { - return err - } - return nil -} - -// dataFromSliceOrFile returns data from the slice (if non-empty), or from the file, -// or an error if an error occurred reading the file -func dataFromSliceOrFile(data []byte, file string) ([]byte, error) { - if len(data) > 0 { - return data, nil - } - if len(file) > 0 { - fileData, err := ioutil.ReadFile(file) - if err != nil { - return []byte{}, err - } - return fileData, nil - } - return nil, nil -} - -func AddUserAgent(config *Config, userAgent string) *Config { - fullUserAgent := DefaultKubernetesUserAgent() + "/" + userAgent - config.UserAgent = fullUserAgent - return config + + return restclient.SetKubernetesDefaults(config) } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/helper_blackbox_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/helper_blackbox_test.go deleted file mode 100644 index 5d497d503..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/helper_blackbox_test.go +++ /dev/null @@ -1,126 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import ( - "bytes" - "encoding/json" - "errors" - "io" - "io/ioutil" - "net/http" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api/testapi" - uapi "k8s.io/kubernetes/pkg/api/unversioned" - unversionedapi "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/fake" -) - -func objBody(object interface{}) io.ReadCloser { - output, err := json.MarshalIndent(object, "", "") - if err != nil { - panic(err) - } - return ioutil.NopCloser(bytes.NewReader([]byte(output))) -} - -func TestNegotiateVersion(t *testing.T) { - tests := []struct { - name string - version *uapi.GroupVersion - expectedVersion *uapi.GroupVersion - serverVersions []string - clientVersions []uapi.GroupVersion - config *unversioned.Config - expectErr func(err error) bool - sendErr error - }{ - { - name: "server supports client default", - version: &uapi.GroupVersion{Version: "version1"}, - config: &unversioned.Config{}, - serverVersions: []string{"version1", testapi.Default.GroupVersion().String()}, - clientVersions: []uapi.GroupVersion{{Version: "version1"}, *testapi.Default.GroupVersion()}, - expectedVersion: &uapi.GroupVersion{Version: "version1"}, - }, - { - name: "server falls back to client supported", - version: testapi.Default.GroupVersion(), - config: &unversioned.Config{}, - serverVersions: []string{"version1"}, - clientVersions: []uapi.GroupVersion{{Version: "version1"}, *testapi.Default.GroupVersion()}, - expectedVersion: &uapi.GroupVersion{Version: "version1"}, - }, - { - name: "explicit version supported", - config: &unversioned.Config{ContentConfig: unversioned.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}, - serverVersions: []string{"/version1", testapi.Default.GroupVersion().String()}, - clientVersions: []uapi.GroupVersion{{Version: "version1"}, *testapi.Default.GroupVersion()}, - expectedVersion: testapi.Default.GroupVersion(), - }, - { - name: "explicit version not supported", - config: &unversioned.Config{ContentConfig: unversioned.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}, - serverVersions: []string{"version1"}, - clientVersions: []uapi.GroupVersion{{Version: "version1"}, *testapi.Default.GroupVersion()}, - expectErr: func(err error) bool { return strings.Contains(err.Error(), `server does not support API version "v1"`) }, - }, - { - name: "connection refused error", - config: &unversioned.Config{ContentConfig: unversioned.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}, - serverVersions: []string{"version1"}, - clientVersions: []uapi.GroupVersion{{Version: "version1"}, *testapi.Default.GroupVersion()}, - sendErr: errors.New("connection refused"), - expectErr: func(err error) bool { return strings.Contains(err.Error(), "connection refused") }, - }, - } - codec := testapi.Default.Codec() - - for _, test := range tests { - fakeClient := &fake.RESTClient{ - Codec: codec, - Resp: &http.Response{ - StatusCode: 200, - Body: objBody(&unversionedapi.APIVersions{Versions: test.serverVersions}), - }, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - if test.sendErr != nil { - return nil, test.sendErr - } - return &http.Response{StatusCode: 200, Body: objBody(&unversionedapi.APIVersions{Versions: test.serverVersions})}, nil - }), - } - c := unversioned.NewOrDie(test.config) - c.DiscoveryClient.Client = fakeClient.Client - response, err := unversioned.NegotiateVersion(c, test.config, test.version, test.clientVersions) - if err == nil && test.expectErr != nil { - t.Errorf("expected error, got nil for [%s].", test.name) - } - if err != nil { - if test.expectErr == nil || !test.expectErr(err) { - t.Errorf("unexpected error for [%s]: %v.", test.name, err) - } - continue - } - if *response != *test.expectedVersion { - t.Errorf("%s: expected version %s, got %s.", test.name, test.expectedVersion, response) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/helper_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/helper_test.go deleted file mode 100644 index 2d565afc2..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/helper_test.go +++ /dev/null @@ -1,280 +0,0 @@ -/* -Copyright 2014 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 unversioned - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "path" - "reflect" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" -) - -func TestIsConfigTransportTLS(t *testing.T) { - testCases := []struct { - Config *Config - TransportTLS bool - }{ - { - Config: &Config{}, - TransportTLS: false, - }, - { - Config: &Config{ - Host: "https://localhost", - }, - TransportTLS: true, - }, - { - Config: &Config{ - Host: "localhost", - TLSClientConfig: TLSClientConfig{ - CertFile: "foo", - }, - }, - TransportTLS: true, - }, - { - Config: &Config{ - Host: "///:://localhost", - TLSClientConfig: TLSClientConfig{ - CertFile: "foo", - }, - }, - TransportTLS: false, - }, - { - Config: &Config{ - Host: "1.2.3.4:567", - Insecure: true, - }, - TransportTLS: true, - }, - } - for _, testCase := range testCases { - if err := SetKubernetesDefaults(testCase.Config); err != nil { - t.Errorf("setting defaults failed for %#v: %v", testCase.Config, err) - continue - } - useTLS := IsConfigTransportTLS(*testCase.Config) - if testCase.TransportTLS != useTLS { - t.Errorf("expected %v for %#v", testCase.TransportTLS, testCase.Config) - } - } -} - -func TestSetKubernetesDefaults(t *testing.T) { - testCases := []struct { - Config Config - After Config - Err bool - }{ - { - Config{}, - Config{ - APIPath: "/api", - ContentConfig: ContentConfig{ - GroupVersion: testapi.Default.GroupVersion(), - Codec: testapi.Default.Codec(), - }, - QPS: 5, - Burst: 10, - }, - false, - }, - // Add this test back when we fixed config and SetKubernetesDefaults - // { - // Config{ - // GroupVersion: &unversioned.GroupVersion{Group: "not.a.group", Version: "not_an_api"}, - // }, - // Config{}, - // true, - // }, - } - for _, testCase := range testCases { - val := &testCase.Config - err := SetKubernetesDefaults(val) - val.UserAgent = "" - switch { - case err == nil && testCase.Err: - t.Errorf("expected error but was nil") - continue - case err != nil && !testCase.Err: - t.Errorf("unexpected error %v", err) - continue - case err != nil: - continue - } - if !reflect.DeepEqual(*val, testCase.After) { - t.Errorf("unexpected result object: %#v", val) - } - } -} - -func TestSetKubernetesDefaultsUserAgent(t *testing.T) { - config := &Config{} - if err := SetKubernetesDefaults(config); err != nil { - t.Errorf("unexpected error: %v", err) - } - if !strings.Contains(config.UserAgent, "kubernetes/") { - t.Errorf("no user agent set: %#v", config) - } -} - -func TestHelperGetServerAPIVersions(t *testing.T) { - expect := []string{"v1", "v2", "v3"} - APIVersions := unversioned.APIVersions{Versions: expect} - expect = append(expect, "group1/v1", "group1/v2", "group2/v1", "group2/v2") - APIGroupList := unversioned.APIGroupList{ - Groups: []unversioned.APIGroup{ - { - Versions: []unversioned.GroupVersionForDiscovery{ - { - GroupVersion: "group1/v1", - }, - { - GroupVersion: "group1/v2", - }, - }, - }, - { - Versions: []unversioned.GroupVersionForDiscovery{ - { - GroupVersion: "group2/v1", - }, - { - GroupVersion: "group2/v2", - }, - }, - }, - }, - } - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - var output []byte - var err error - switch req.URL.Path { - case "/api": - output, err = json.Marshal(APIVersions) - - case "/apis": - output, err = json.Marshal(APIGroupList) - } - if err != nil { - t.Errorf("unexpected encoding error: %v", err) - return - } - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write(output) - })) - // TODO: Uncomment when fix #19254 - // defer server.Close() - got, err := ServerAPIVersions(&Config{Host: server.URL, ContentConfig: ContentConfig{GroupVersion: &unversioned.GroupVersion{Group: "invalid version", Version: "one"}, Codec: testapi.Default.Codec()}}) - if err != nil { - t.Fatalf("unexpected encoding error: %v", err) - } - if e, a := expect, got; !reflect.DeepEqual(e, a) { - t.Errorf("expected %v, got %v", e, a) - } -} - -func TestSetsCodec(t *testing.T) { - testCases := map[string]struct { - Err bool - Prefix string - Codec runtime.Codec - }{ - testapi.Default.GroupVersion().Version: {false, "/api/" + testapi.Default.GroupVersion().Version, testapi.Default.Codec()}, - // Add this test back when we fixed config and SetKubernetesDefaults - // "invalidVersion": {true, "", nil}, - } - for version, expected := range testCases { - client, err := New(&Config{Host: "127.0.0.1", ContentConfig: ContentConfig{GroupVersion: &unversioned.GroupVersion{Version: version}}}) - switch { - case err == nil && expected.Err: - t.Errorf("expected error but was nil") - continue - case err != nil && !expected.Err: - t.Errorf("unexpected error %v", err) - continue - case err != nil: - continue - } - if e, a := expected.Prefix, client.RESTClient.versionedAPIPath; e != a { - t.Errorf("expected %#v, got %#v", e, a) - } - if e, a := expected.Codec, client.RESTClient.contentConfig.Codec; !reflect.DeepEqual(e, a) { - t.Errorf("expected %#v, got %#v", e, a) - } - } -} - -func TestRESTClientRequires(t *testing.T) { - if _, err := RESTClientFor(&Config{Host: "127.0.0.1", ContentConfig: ContentConfig{Codec: testapi.Default.Codec()}}); err == nil { - t.Errorf("unexpected non-error") - } - if _, err := RESTClientFor(&Config{Host: "127.0.0.1", ContentConfig: ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}); err == nil { - t.Errorf("unexpected non-error") - } - if _, err := RESTClientFor(&Config{Host: "127.0.0.1", ContentConfig: ContentConfig{GroupVersion: testapi.Default.GroupVersion(), Codec: testapi.Default.Codec()}}); err != nil { - t.Errorf("unexpected error: %v", err) - } -} - -func TestValidatesHostParameter(t *testing.T) { - testCases := []struct { - Host string - APIPath string - - URL string - Err bool - }{ - {"127.0.0.1", "", "http://127.0.0.1/" + testapi.Default.GroupVersion().Version, false}, - {"127.0.0.1:8080", "", "http://127.0.0.1:8080/" + testapi.Default.GroupVersion().Version, false}, - {"foo.bar.com", "", "http://foo.bar.com/" + testapi.Default.GroupVersion().Version, false}, - {"http://host/prefix", "", "http://host/prefix/" + testapi.Default.GroupVersion().Version, false}, - {"http://host", "", "http://host/" + testapi.Default.GroupVersion().Version, false}, - {"http://host", "/", "http://host/" + testapi.Default.GroupVersion().Version, false}, - {"http://host", "/other", "http://host/other/" + testapi.Default.GroupVersion().Version, false}, - {"host/server", "", "", true}, - } - for i, testCase := range testCases { - u, versionedAPIPath, err := DefaultServerURL(testCase.Host, testCase.APIPath, *testapi.Default.GroupVersion(), false) - switch { - case err == nil && testCase.Err: - t.Errorf("expected error but was nil") - continue - case err != nil && !testCase.Err: - t.Errorf("unexpected error %v", err) - continue - case err != nil: - continue - } - u.Path = path.Join(u.Path, versionedAPIPath) - if e, a := testCase.URL, u.String(); e != a { - t.Errorf("%d: expected host %s, got %s", i, e, a) - continue - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/horizontalpodautoscaler_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/horizontalpodautoscaler_test.go deleted file mode 100644 index b35d258af..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/horizontalpodautoscaler_test.go +++ /dev/null @@ -1,225 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "net/url" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/apis/autoscaling" - "k8s.io/kubernetes/pkg/apis/extensions" -) - -func getHorizontalPodAutoscalersResoureName() string { - return "horizontalpodautoscalers" -} - -func getClient(t *testing.T, c *simple.Client, ns, resourceGroup string) HorizontalPodAutoscalerInterface { - switch resourceGroup { - case autoscaling.GroupName: - return c.Setup(t).Autoscaling().HorizontalPodAutoscalers(ns) - case extensions.GroupName: - return c.Setup(t).Extensions().HorizontalPodAutoscalers(ns) - default: - t.Fatalf("Unknown group %v", resourceGroup) - } - return nil -} - -func testHorizontalPodAutoscalerCreate(t *testing.T, group testapi.TestGroup, resourceGroup string) { - ns := api.NamespaceDefault - horizontalPodAutoscaler := extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: ns, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, ""), - Query: simple.BuildQueryValues(nil), - Body: &horizontalPodAutoscaler, - }, - Response: simple.Response{StatusCode: 200, Body: &horizontalPodAutoscaler}, - ResourceGroup: resourceGroup, - } - - response, err := getClient(t, c, ns, resourceGroup).Create(&horizontalPodAutoscaler) - defer c.Close() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - c.Validate(t, response, err) -} - -func TestHorizontalPodAutoscalerCreate(t *testing.T) { - testHorizontalPodAutoscalerCreate(t, testapi.Extensions, extensions.GroupName) - testHorizontalPodAutoscalerCreate(t, testapi.Autoscaling, autoscaling.GroupName) -} - -func testHorizontalPodAutoscalerGet(t *testing.T, group testapi.TestGroup, resourceGroup string) { - ns := api.NamespaceDefault - horizontalPodAutoscaler := &extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: ns, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc"), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler}, - ResourceGroup: resourceGroup, - } - - response, err := getClient(t, c, ns, resourceGroup).Get("abc") - defer c.Close() - c.Validate(t, response, err) -} - -func TestHorizontalPodAutoscalerGet(t *testing.T) { - testHorizontalPodAutoscalerGet(t, testapi.Extensions, extensions.GroupName) - testHorizontalPodAutoscalerGet(t, testapi.Autoscaling, autoscaling.GroupName) -} - -func testHorizontalPodAutoscalerList(t *testing.T, group testapi.TestGroup, resourceGroup string) { - ns := api.NamespaceDefault - horizontalPodAutoscalerList := &extensions.HorizontalPodAutoscalerList{ - Items: []extensions.HorizontalPodAutoscaler{ - { - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: ns, - }, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, ""), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscalerList}, - ResourceGroup: resourceGroup, - } - response, err := getClient(t, c, ns, resourceGroup).List(api.ListOptions{}) - defer c.Close() - c.Validate(t, response, err) -} - -func TestHorizontalPodAutoscalerList(t *testing.T) { - testHorizontalPodAutoscalerList(t, testapi.Extensions, extensions.GroupName) - testHorizontalPodAutoscalerList(t, testapi.Autoscaling, autoscaling.GroupName) -} - -func testHorizontalPodAutoscalerUpdate(t *testing.T, group testapi.TestGroup, resourceGroup string) { - ns := api.NamespaceDefault - horizontalPodAutoscaler := &extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: ns, - ResourceVersion: "1", - }, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler}, - ResourceGroup: resourceGroup, - } - response, err := getClient(t, c, ns, resourceGroup).Update(horizontalPodAutoscaler) - defer c.Close() - c.Validate(t, response, err) -} - -func TestHorizontalPodAutoscalerUpdate(t *testing.T) { - testHorizontalPodAutoscalerUpdate(t, testapi.Extensions, extensions.GroupName) - testHorizontalPodAutoscalerUpdate(t, testapi.Autoscaling, autoscaling.GroupName) -} - -func testHorizontalPodAutoscalerUpdateStatus(t *testing.T, group testapi.TestGroup, resourceGroup string) { - ns := api.NamespaceDefault - horizontalPodAutoscaler := &extensions.HorizontalPodAutoscaler{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: ns, - ResourceVersion: "1", - }, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc") + "/status", Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler}, - ResourceGroup: resourceGroup, - } - response, err := getClient(t, c, ns, resourceGroup).UpdateStatus(horizontalPodAutoscaler) - defer c.Close() - c.Validate(t, response, err) -} - -func TestHorizontalPodAutoscalerUpdateStatus(t *testing.T) { - testHorizontalPodAutoscalerUpdateStatus(t, testapi.Extensions, extensions.GroupName) - testHorizontalPodAutoscalerUpdateStatus(t, testapi.Autoscaling, autoscaling.GroupName) -} - -func testHorizontalPodAutoscalerDelete(t *testing.T, group testapi.TestGroup, resourceGroup string) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: group.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200}, - ResourceGroup: resourceGroup, - } - err := getClient(t, c, ns, resourceGroup).Delete("foo", nil) - defer c.Close() - c.Validate(t, nil, err) -} - -func TestHorizontalPodAutoscalerDelete(t *testing.T) { - testHorizontalPodAutoscalerDelete(t, testapi.Extensions, extensions.GroupName) - testHorizontalPodAutoscalerDelete(t, testapi.Autoscaling, autoscaling.GroupName) -} - -func testHorizontalPodAutoscalerWatch(t *testing.T, group testapi.TestGroup, resourceGroup string) { - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: group.ResourcePathWithPrefix("watch", getHorizontalPodAutoscalersResoureName(), "", ""), - Query: url.Values{"resourceVersion": []string{}}}, - Response: simple.Response{StatusCode: 200}, - ResourceGroup: resourceGroup, - } - _, err := getClient(t, c, api.NamespaceAll, resourceGroup).Watch(api.ListOptions{}) - defer c.Close() - c.Validate(t, nil, err) -} - -func TestHorizontalPodAutoscalerWatch(t *testing.T) { - testHorizontalPodAutoscalerWatch(t, testapi.Extensions, extensions.GroupName) - testHorizontalPodAutoscalerWatch(t, testapi.Autoscaling, autoscaling.GroupName) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/import_known_versions.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/import_known_versions.go index a86afb6dd..c2d7fde2f 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/import_known_versions.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/import_known_versions.go @@ -24,6 +24,7 @@ import ( "k8s.io/kubernetes/pkg/apimachinery/registered" _ "k8s.io/kubernetes/pkg/apis/authorization/install" _ "k8s.io/kubernetes/pkg/apis/autoscaling/install" + _ "k8s.io/kubernetes/pkg/apis/batch/install" _ "k8s.io/kubernetes/pkg/apis/componentconfig/install" _ "k8s.io/kubernetes/pkg/apis/extensions/install" _ "k8s.io/kubernetes/pkg/apis/metrics/install" diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/ingress_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/ingress_test.go deleted file mode 100644 index 6bab5f5ed..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/ingress_test.go +++ /dev/null @@ -1,240 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/apis/extensions" -) - -func getIngressResourceName() string { - return "ingresses" -} - -func TestListIngress(t *testing.T) { - ns := api.NamespaceAll - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Extensions.ResourcePath(getIngressResourceName(), ns, ""), - }, - Response: simple.Response{StatusCode: 200, - Body: &extensions.IngressList{ - Items: []extensions.Ingress{ - { - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.IngressSpec{ - Rules: []extensions.IngressRule{}, - }, - }, - }, - }, - }, - } - receivedIngressList, err := c.Setup(t).Extensions().Ingress(ns).List(api.ListOptions{}) - defer c.Close() - c.Validate(t, receivedIngressList, err) -} - -func TestGetIngress(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Extensions.ResourcePath(getIngressResourceName(), ns, "foo"), - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.Ingress{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.IngressSpec{ - Rules: []extensions.IngressRule{}, - }, - }, - }, - } - receivedIngress, err := c.Setup(t).Extensions().Ingress(ns).Get("foo") - defer c.Close() - c.Validate(t, receivedIngress, err) -} - -func TestGetIngressWithNoName(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{Error: true} - receivedIngress, err := c.Setup(t).Extensions().Ingress(ns).Get("") - defer c.Close() - if (err != nil) && (err.Error() != simple.NameRequiredError) { - t.Errorf("Expected error: %v, but got %v", simple.NameRequiredError, err) - } - - c.Validate(t, receivedIngress, err) -} - -func TestUpdateIngress(t *testing.T) { - ns := api.NamespaceDefault - requestIngress := &extensions.Ingress{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: ns, - ResourceVersion: "1", - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "PUT", - Path: testapi.Extensions.ResourcePath(getIngressResourceName(), ns, "foo"), - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.Ingress{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.IngressSpec{ - Rules: []extensions.IngressRule{}, - }, - }, - }, - } - receivedIngress, err := c.Setup(t).Extensions().Ingress(ns).Update(requestIngress) - defer c.Close() - c.Validate(t, receivedIngress, err) -} - -func TestUpdateIngressStatus(t *testing.T) { - ns := api.NamespaceDefault - lbStatus := api.LoadBalancerStatus{ - Ingress: []api.LoadBalancerIngress{ - {IP: "127.0.0.1"}, - }, - } - requestIngress := &extensions.Ingress{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: ns, - ResourceVersion: "1", - }, - Status: extensions.IngressStatus{ - LoadBalancer: lbStatus, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "PUT", - Path: testapi.Extensions.ResourcePath(getIngressResourceName(), ns, "foo") + "/status", - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.Ingress{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.IngressSpec{ - Rules: []extensions.IngressRule{}, - }, - Status: extensions.IngressStatus{ - LoadBalancer: lbStatus, - }, - }, - }, - } - receivedIngress, err := c.Setup(t).Extensions().Ingress(ns).UpdateStatus(requestIngress) - defer c.Close() - c.Validate(t, receivedIngress, err) -} - -func TestDeleteIngress(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{ - Method: "DELETE", - Path: testapi.Extensions.ResourcePath(getIngressResourceName(), ns, "foo"), - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).Extensions().Ingress(ns).Delete("foo", nil) - defer c.Close() - c.Validate(t, nil, err) -} - -func TestCreateIngress(t *testing.T) { - ns := api.NamespaceDefault - requestIngress := &extensions.Ingress{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: ns, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: testapi.Extensions.ResourcePath(getIngressResourceName(), ns, ""), - Body: requestIngress, - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.Ingress{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.IngressSpec{ - Rules: []extensions.IngressRule{}, - }, - }, - }, - } - receivedIngress, err := c.Setup(t).Extensions().Ingress(ns).Create(requestIngress) - defer c.Close() - c.Validate(t, receivedIngress, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/jobs.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/jobs.go index 0d10997c7..f965a0874 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/jobs.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/jobs.go @@ -101,3 +101,67 @@ func (c *jobs) UpdateStatus(job *extensions.Job) (result *extensions.Job, err er err = c.r.Put().Namespace(c.ns).Resource("jobs").Name(job.Name).SubResource("status").Body(job).Do().Into(result) return } + +// jobsV1 implements JobsNamespacer interface using BatchClient internally +type jobsV1 struct { + r *BatchClient + ns string +} + +// newJobsV1 returns a jobsV1 +func newJobsV1(c *BatchClient, namespace string) *jobsV1 { + return &jobsV1{c, namespace} +} + +// Ensure statically that jobsV1 implements JobInterface. +var _ JobInterface = &jobsV1{} + +// List returns a list of jobs that match the label and field selectors. +func (c *jobsV1) List(opts api.ListOptions) (result *extensions.JobList, err error) { + result = &extensions.JobList{} + err = c.r.Get().Namespace(c.ns).Resource("jobs").VersionedParams(&opts, api.ParameterCodec).Do().Into(result) + return +} + +// Get returns information about a particular job. +func (c *jobsV1) Get(name string) (result *extensions.Job, err error) { + result = &extensions.Job{} + err = c.r.Get().Namespace(c.ns).Resource("jobs").Name(name).Do().Into(result) + return +} + +// Create creates a new job. +func (c *jobsV1) Create(job *extensions.Job) (result *extensions.Job, err error) { + result = &extensions.Job{} + err = c.r.Post().Namespace(c.ns).Resource("jobs").Body(job).Do().Into(result) + return +} + +// Update updates an existing job. +func (c *jobsV1) Update(job *extensions.Job) (result *extensions.Job, err error) { + result = &extensions.Job{} + err = c.r.Put().Namespace(c.ns).Resource("jobs").Name(job.Name).Body(job).Do().Into(result) + return +} + +// Delete deletes a job, returns error if one occurs. +func (c *jobsV1) Delete(name string, options *api.DeleteOptions) (err error) { + return c.r.Delete().Namespace(c.ns).Resource("jobs").Name(name).Body(options).Do().Error() +} + +// Watch returns a watch.Interface that watches the requested jobs. +func (c *jobsV1) Watch(opts api.ListOptions) (watch.Interface, error) { + return c.r.Get(). + Prefix("watch"). + Namespace(c.ns). + Resource("jobs"). + VersionedParams(&opts, api.ParameterCodec). + Watch() +} + +// UpdateStatus takes the name of the job and the new status. Returns the server's representation of the job, and an error, if it occurs. +func (c *jobsV1) UpdateStatus(job *extensions.Job) (result *extensions.Job, err error) { + result = &extensions.Job{} + err = c.r.Put().Namespace(c.ns).Resource("jobs").Name(job.Name).SubResource("status").Body(job).Do().Into(result) + return +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/jobs_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/jobs_test.go deleted file mode 100644 index 76a87eab0..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/jobs_test.go +++ /dev/null @@ -1,232 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/apis/extensions" -) - -func getJobResourceName() string { - return "jobs" -} - -func TestListJobs(t *testing.T) { - ns := api.NamespaceAll - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, ""), - }, - Response: simple.Response{StatusCode: 200, - Body: &extensions.JobList{ - Items: []extensions.Job{ - { - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.JobSpec{ - Template: api.PodTemplateSpec{}, - }, - }, - }, - }, - }, - } - receivedJobList, err := c.Setup(t).Extensions().Jobs(ns).List(api.ListOptions{}) - defer c.Close() - c.Validate(t, receivedJobList, err) -} - -func TestGetJob(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, "foo"), - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.JobSpec{ - Template: api.PodTemplateSpec{}, - }, - }, - }, - } - receivedJob, err := c.Setup(t).Extensions().Jobs(ns).Get("foo") - defer c.Close() - c.Validate(t, receivedJob, err) -} - -func TestGetJobWithNoName(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{Error: true} - receivedJob, err := c.Setup(t).Extensions().Jobs(ns).Get("") - defer c.Close() - if (err != nil) && (err.Error() != simple.NameRequiredError) { - t.Errorf("Expected error: %v, but got %v", simple.NameRequiredError, err) - } - - c.Validate(t, receivedJob, err) -} - -func TestUpdateJob(t *testing.T) { - ns := api.NamespaceDefault - requestJob := &extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: ns, - ResourceVersion: "1", - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "PUT", - Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, "foo"), - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.JobSpec{ - Template: api.PodTemplateSpec{}, - }, - }, - }, - } - receivedJob, err := c.Setup(t).Extensions().Jobs(ns).Update(requestJob) - defer c.Close() - c.Validate(t, receivedJob, err) -} - -func TestUpdateJobStatus(t *testing.T) { - ns := api.NamespaceDefault - requestJob := &extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: ns, - ResourceVersion: "1", - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "PUT", - Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, "foo") + "/status", - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.JobSpec{ - Template: api.PodTemplateSpec{}, - }, - Status: extensions.JobStatus{ - Active: 1, - }, - }, - }, - } - receivedJob, err := c.Setup(t).Extensions().Jobs(ns).UpdateStatus(requestJob) - defer c.Close() - c.Validate(t, receivedJob, err) -} - -func TestDeleteJob(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{ - Method: "DELETE", - Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, "foo"), - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).Extensions().Jobs(ns).Delete("foo", nil) - defer c.Close() - c.Validate(t, nil, err) -} - -func TestCreateJob(t *testing.T) { - ns := api.NamespaceDefault - requestJob := &extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: ns, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, ""), - Body: requestJob, - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.JobSpec{ - Template: api.PodTemplateSpec{}, - }, - }, - }, - } - receivedJob, err := c.Setup(t).Extensions().Jobs(ns).Create(requestJob) - defer c.Close() - c.Validate(t, receivedJob, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/limit_ranges_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/limit_ranges_test.go deleted file mode 100644 index f6936570c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/limit_ranges_test.go +++ /dev/null @@ -1,189 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "net/url" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/testapi" -) - -func getLimitRangesResourceName() string { - return "limitranges" -} - -func TestLimitRangeCreate(t *testing.T) { - ns := api.NamespaceDefault - limitRange := &api.LimitRange{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - }, - Spec: api.LimitRangeSpec{ - Limits: []api.LimitRangeItem{ - { - Type: api.LimitTypePod, - Max: api.ResourceList{ - api.ResourceCPU: resource.MustParse("100"), - api.ResourceMemory: resource.MustParse("10000"), - }, - Min: api.ResourceList{ - api.ResourceCPU: resource.MustParse("0"), - api.ResourceMemory: resource.MustParse("100"), - }, - }, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: testapi.Default.ResourcePath(getLimitRangesResourceName(), ns, ""), - Query: simple.BuildQueryValues(nil), - Body: limitRange, - }, - Response: simple.Response{StatusCode: 200, Body: limitRange}, - } - - response, err := c.Setup(t).LimitRanges(ns).Create(limitRange) - defer c.Close() - c.Validate(t, response, err) -} - -func TestLimitRangeGet(t *testing.T) { - ns := api.NamespaceDefault - limitRange := &api.LimitRange{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - }, - Spec: api.LimitRangeSpec{ - Limits: []api.LimitRangeItem{ - { - Type: api.LimitTypePod, - Max: api.ResourceList{ - api.ResourceCPU: resource.MustParse("100"), - api.ResourceMemory: resource.MustParse("10000"), - }, - Min: api.ResourceList{ - api.ResourceCPU: resource.MustParse("0"), - api.ResourceMemory: resource.MustParse("100"), - }, - }, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath(getLimitRangesResourceName(), ns, "abc"), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: limitRange}, - } - - response, err := c.Setup(t).LimitRanges(ns).Get("abc") - defer c.Close() - c.Validate(t, response, err) -} - -func TestLimitRangeList(t *testing.T) { - ns := api.NamespaceDefault - - limitRangeList := &api.LimitRangeList{ - Items: []api.LimitRange{ - { - ObjectMeta: api.ObjectMeta{Name: "foo"}, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath(getLimitRangesResourceName(), ns, ""), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: limitRangeList}, - } - response, err := c.Setup(t).LimitRanges(ns).List(api.ListOptions{}) - defer c.Close() - c.Validate(t, response, err) -} - -func TestLimitRangeUpdate(t *testing.T) { - ns := api.NamespaceDefault - limitRange := &api.LimitRange{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - ResourceVersion: "1", - }, - Spec: api.LimitRangeSpec{ - Limits: []api.LimitRangeItem{ - { - Type: api.LimitTypePod, - Max: api.ResourceList{ - api.ResourceCPU: resource.MustParse("100"), - api.ResourceMemory: resource.MustParse("10000"), - }, - Min: api.ResourceList{ - api.ResourceCPU: resource.MustParse("0"), - api.ResourceMemory: resource.MustParse("100"), - }, - }, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Default.ResourcePath(getLimitRangesResourceName(), ns, "abc"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: limitRange}, - } - response, err := c.Setup(t).LimitRanges(ns).Update(limitRange) - defer c.Close() - c.Validate(t, response, err) -} - -func TestLimitRangeDelete(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: testapi.Default.ResourcePath(getLimitRangesResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).LimitRanges(ns).Delete("foo") - defer c.Close() - c.Validate(t, nil, err) -} - -func TestLimitRangeWatch(t *testing.T) { - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePathWithPrefix("watch", getLimitRangesResourceName(), "", ""), - Query: url.Values{"resourceVersion": []string{}}}, - Response: simple.Response{StatusCode: 200}, - } - _, err := c.Setup(t).LimitRanges(api.NamespaceAll).Watch(api.ListOptions{}) - defer c.Close() - c.Validate(t, nil, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/namespaces_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/namespaces_test.go deleted file mode 100644 index 509980e7e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/namespaces_test.go +++ /dev/null @@ -1,189 +0,0 @@ -/* -Copyright 2014 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "net/url" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" -) - -func TestNamespaceCreate(t *testing.T) { - // we create a namespace relative to another namespace - namespace := &api.Namespace{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: testapi.Default.ResourcePath("namespaces", "", ""), - Body: namespace, - }, - Response: simple.Response{StatusCode: 200, Body: namespace}, - } - - // from the source ns, provision a new global namespace "foo" - response, err := c.Setup(t).Namespaces().Create(namespace) - defer c.Close() - - if err != nil { - t.Errorf("%#v should be nil.", err) - } - - if e, a := response.Name, namespace.Name; e != a { - t.Errorf("%#v != %#v.", e, a) - } -} - -func TestNamespaceGet(t *testing.T) { - namespace := &api.Namespace{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath("namespaces", "", "foo"), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: namespace}, - } - - response, err := c.Setup(t).Namespaces().Get("foo") - defer c.Close() - - if err != nil { - t.Errorf("%#v should be nil.", err) - } - - if e, r := response.Name, namespace.Name; e != r { - t.Errorf("%#v != %#v.", e, r) - } -} - -func TestNamespaceList(t *testing.T) { - namespaceList := &api.NamespaceList{ - Items: []api.Namespace{ - { - ObjectMeta: api.ObjectMeta{Name: "foo"}, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath("namespaces", "", ""), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: namespaceList}, - } - response, err := c.Setup(t).Namespaces().List(api.ListOptions{}) - defer c.Close() - - if err != nil { - t.Errorf("%#v should be nil.", err) - } - - if len(response.Items) != 1 { - t.Errorf("%#v response.Items should have len 1.", response.Items) - } - - responseNamespace := response.Items[0] - if e, r := responseNamespace.Name, "foo"; e != r { - t.Errorf("%#v != %#v.", e, r) - } -} - -func TestNamespaceUpdate(t *testing.T) { - requestNamespace := &api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - ResourceVersion: "1", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: api.NamespaceSpec{ - Finalizers: []api.FinalizerName{api.FinalizerKubernetes}, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "PUT", - Path: testapi.Default.ResourcePath("namespaces", "", "foo")}, - Response: simple.Response{StatusCode: 200, Body: requestNamespace}, - } - receivedNamespace, err := c.Setup(t).Namespaces().Update(requestNamespace) - defer c.Close() - c.Validate(t, receivedNamespace, err) -} - -func TestNamespaceFinalize(t *testing.T) { - requestNamespace := &api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - ResourceVersion: "1", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: api.NamespaceSpec{ - Finalizers: []api.FinalizerName{api.FinalizerKubernetes}, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "PUT", - Path: testapi.Default.ResourcePath("namespaces", "", "foo") + "/finalize", - }, - Response: simple.Response{StatusCode: 200, Body: requestNamespace}, - } - receivedNamespace, err := c.Setup(t).Namespaces().Finalize(requestNamespace) - defer c.Close() - c.Validate(t, receivedNamespace, err) -} - -func TestNamespaceDelete(t *testing.T) { - c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: testapi.Default.ResourcePath("namespaces", "", "foo")}, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).Namespaces().Delete("foo") - defer c.Close() - c.Validate(t, nil, err) -} - -func TestNamespaceWatch(t *testing.T) { - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePathWithPrefix("watch", "namespaces", "", ""), - Query: url.Values{"resourceVersion": []string{}}}, - Response: simple.Response{StatusCode: 200}, - } - _, err := c.Setup(t).Namespaces().Watch(api.ListOptions{}) - defer c.Close() - c.Validate(t, nil, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/nodes_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/nodes_test.go deleted file mode 100644 index 37735ec7d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/nodes_test.go +++ /dev/null @@ -1,177 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "net/url" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/labels" -) - -func getNodesResourceName() string { - return "nodes" -} - -func TestListNodes(t *testing.T) { - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath(getNodesResourceName(), "", ""), - }, - Response: simple.Response{StatusCode: 200, Body: &api.NodeList{ListMeta: unversioned.ListMeta{ResourceVersion: "1"}}}, - } - response, err := c.Setup(t).Nodes().List(api.ListOptions{}) - defer c.Close() - c.Validate(t, response, err) -} - -func TestListNodesLabels(t *testing.T) { - labelSelectorQueryParamName := unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String()) - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath(getNodesResourceName(), "", ""), - Query: simple.BuildQueryValues(url.Values{labelSelectorQueryParamName: []string{"foo=bar,name=baz"}})}, - Response: simple.Response{ - StatusCode: 200, - Body: &api.NodeList{ - Items: []api.Node{ - { - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - }, - }, - }, - }, - } - c.Setup(t) - defer c.Close() - c.QueryValidator[labelSelectorQueryParamName] = simple.ValidateLabels - selector := labels.Set{"foo": "bar", "name": "baz"}.AsSelector() - options := api.ListOptions{LabelSelector: selector} - receivedNodeList, err := c.Nodes().List(options) - c.Validate(t, receivedNodeList, err) -} - -func TestGetNode(t *testing.T) { - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath(getNodesResourceName(), "", "1"), - }, - Response: simple.Response{StatusCode: 200, Body: &api.Node{ObjectMeta: api.ObjectMeta{Name: "node-1"}}}, - } - response, err := c.Setup(t).Nodes().Get("1") - defer c.Close() - c.Validate(t, response, err) -} - -func TestGetNodeWithNoName(t *testing.T) { - c := &simple.Client{Error: true} - receivedNode, err := c.Setup(t).Nodes().Get("") - defer c.Close() - if (err != nil) && (err.Error() != simple.NameRequiredError) { - t.Errorf("Expected error: %v, but got %v", simple.NameRequiredError, err) - } - - c.Validate(t, receivedNode, err) -} - -func TestCreateNode(t *testing.T) { - requestNode := &api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "node-1", - }, - Status: api.NodeStatus{ - Capacity: api.ResourceList{ - api.ResourceCPU: resource.MustParse("1000m"), - api.ResourceMemory: resource.MustParse("1Mi"), - }, - }, - Spec: api.NodeSpec{ - Unschedulable: false, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: testapi.Default.ResourcePath(getNodesResourceName(), "", ""), - Body: requestNode}, - Response: simple.Response{ - StatusCode: 200, - Body: requestNode, - }, - } - receivedNode, err := c.Setup(t).Nodes().Create(requestNode) - defer c.Close() - c.Validate(t, receivedNode, err) -} - -func TestDeleteNode(t *testing.T) { - c := &simple.Client{ - Request: simple.Request{ - Method: "DELETE", - Path: testapi.Default.ResourcePath(getNodesResourceName(), "", "foo"), - }, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).Nodes().Delete("foo") - defer c.Close() - c.Validate(t, nil, err) -} - -func TestUpdateNode(t *testing.T) { - requestNode := &api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - ResourceVersion: "1", - }, - Status: api.NodeStatus{ - Capacity: api.ResourceList{ - api.ResourceCPU: resource.MustParse("1000m"), - api.ResourceMemory: resource.MustParse("1Mi"), - }, - }, - Spec: api.NodeSpec{ - Unschedulable: true, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "PUT", - Path: testapi.Default.ResourcePath(getNodesResourceName(), "", "foo"), - }, - Response: simple.Response{StatusCode: 200, Body: requestNode}, - } - response, err := c.Setup(t).Nodes().Update(requestNode) - defer c.Close() - c.Validate(t, response, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/persistentvolume_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/persistentvolume_test.go deleted file mode 100644 index 329539a83..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/persistentvolume_test.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright 2014 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "net/url" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/testapi" -) - -func getPersistentVolumesResoureName() string { - return "persistentvolumes" -} - -func TestPersistentVolumeCreate(t *testing.T) { - pv := &api.PersistentVolume{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - }, - Spec: api.PersistentVolumeSpec{ - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - PersistentVolumeSource: api.PersistentVolumeSource{ - HostPath: &api.HostPathVolumeSource{Path: "/foo"}, - }, - }, - } - - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: testapi.Default.ResourcePath(getPersistentVolumesResoureName(), "", ""), - Query: simple.BuildQueryValues(nil), - Body: pv, - }, - Response: simple.Response{StatusCode: 200, Body: pv}, - } - - response, err := c.Setup(t).PersistentVolumes().Create(pv) - defer c.Close() - c.Validate(t, response, err) -} - -func TestPersistentVolumeGet(t *testing.T) { - persistentVolume := &api.PersistentVolume{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: "foo", - }, - Spec: api.PersistentVolumeSpec{ - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - PersistentVolumeSource: api.PersistentVolumeSource{ - HostPath: &api.HostPathVolumeSource{Path: "/foo"}, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath(getPersistentVolumesResoureName(), "", "abc"), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: persistentVolume}, - } - - response, err := c.Setup(t).PersistentVolumes().Get("abc") - defer c.Close() - c.Validate(t, response, err) -} - -func TestPersistentVolumeList(t *testing.T) { - persistentVolumeList := &api.PersistentVolumeList{ - Items: []api.PersistentVolume{ - { - ObjectMeta: api.ObjectMeta{Name: "foo"}, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath(getPersistentVolumesResoureName(), "", ""), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: persistentVolumeList}, - } - response, err := c.Setup(t).PersistentVolumes().List(api.ListOptions{}) - defer c.Close() - c.Validate(t, response, err) -} - -func TestPersistentVolumeUpdate(t *testing.T) { - persistentVolume := &api.PersistentVolume{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - ResourceVersion: "1", - }, - Spec: api.PersistentVolumeSpec{ - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - PersistentVolumeSource: api.PersistentVolumeSource{ - HostPath: &api.HostPathVolumeSource{Path: "/foo"}, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Default.ResourcePath(getPersistentVolumesResoureName(), "", "abc"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: persistentVolume}, - } - response, err := c.Setup(t).PersistentVolumes().Update(persistentVolume) - defer c.Close() - c.Validate(t, response, err) -} - -func TestPersistentVolumeStatusUpdate(t *testing.T) { - persistentVolume := &api.PersistentVolume{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - ResourceVersion: "1", - }, - Spec: api.PersistentVolumeSpec{ - Capacity: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - PersistentVolumeSource: api.PersistentVolumeSource{ - HostPath: &api.HostPathVolumeSource{Path: "/foo"}, - }, - }, - Status: api.PersistentVolumeStatus{ - Phase: api.VolumeBound, - Message: "foo", - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "PUT", - Path: testapi.Default.ResourcePath(getPersistentVolumesResoureName(), "", "abc") + "/status", - Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: persistentVolume}, - } - response, err := c.Setup(t).PersistentVolumes().UpdateStatus(persistentVolume) - defer c.Close() - c.Validate(t, response, err) -} - -func TestPersistentVolumeDelete(t *testing.T) { - c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: testapi.Default.ResourcePath(getPersistentVolumesResoureName(), "", "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).PersistentVolumes().Delete("foo") - defer c.Close() - c.Validate(t, nil, err) -} - -func TestPersistentVolumeWatch(t *testing.T) { - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePathWithPrefix("watch", getPersistentVolumesResoureName(), "", ""), - Query: url.Values{"resourceVersion": []string{}}}, - Response: simple.Response{StatusCode: 200}, - } - _, err := c.Setup(t).PersistentVolumes().Watch(api.ListOptions{}) - defer c.Close() - c.Validate(t, nil, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/persistentvolumeclaim_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/persistentvolumeclaim_test.go deleted file mode 100644 index 837b72a9c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/persistentvolumeclaim_test.go +++ /dev/null @@ -1,212 +0,0 @@ -/* -Copyright 2014 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "net/url" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/testapi" -) - -func getPersistentVolumeClaimsResoureName() string { - return "persistentvolumeclaims" -} - -func TestPersistentVolumeClaimCreate(t *testing.T) { - ns := api.NamespaceDefault - pv := &api.PersistentVolumeClaim{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - }, - Spec: api.PersistentVolumeClaimSpec{ - AccessModes: []api.PersistentVolumeAccessMode{ - api.ReadWriteOnce, - api.ReadOnlyMany, - }, - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - }, - }, - } - - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: testapi.Default.ResourcePath(getPersistentVolumeClaimsResoureName(), ns, ""), - Query: simple.BuildQueryValues(nil), - Body: pv, - }, - Response: simple.Response{StatusCode: 200, Body: pv}, - } - - response, err := c.Setup(t).PersistentVolumeClaims(ns).Create(pv) - defer c.Close() - c.Validate(t, response, err) -} - -func TestPersistentVolumeClaimGet(t *testing.T) { - ns := api.NamespaceDefault - persistentVolumeClaim := &api.PersistentVolumeClaim{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: "foo", - }, - Spec: api.PersistentVolumeClaimSpec{ - AccessModes: []api.PersistentVolumeAccessMode{ - api.ReadWriteOnce, - api.ReadOnlyMany, - }, - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath(getPersistentVolumeClaimsResoureName(), ns, "abc"), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: persistentVolumeClaim}, - } - - response, err := c.Setup(t).PersistentVolumeClaims(ns).Get("abc") - defer c.Close() - c.Validate(t, response, err) -} - -func TestPersistentVolumeClaimList(t *testing.T) { - ns := api.NamespaceDefault - persistentVolumeList := &api.PersistentVolumeClaimList{ - Items: []api.PersistentVolumeClaim{ - { - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "ns"}, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath(getPersistentVolumeClaimsResoureName(), ns, ""), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: persistentVolumeList}, - } - response, err := c.Setup(t).PersistentVolumeClaims(ns).List(api.ListOptions{}) - defer c.Close() - c.Validate(t, response, err) -} - -func TestPersistentVolumeClaimUpdate(t *testing.T) { - ns := api.NamespaceDefault - persistentVolumeClaim := &api.PersistentVolumeClaim{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - ResourceVersion: "1", - }, - Spec: api.PersistentVolumeClaimSpec{ - AccessModes: []api.PersistentVolumeAccessMode{ - api.ReadWriteOnce, - api.ReadOnlyMany, - }, - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Default.ResourcePath(getPersistentVolumeClaimsResoureName(), ns, "abc"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: persistentVolumeClaim}, - } - response, err := c.Setup(t).PersistentVolumeClaims(ns).Update(persistentVolumeClaim) - defer c.Close() - c.Validate(t, response, err) -} - -func TestPersistentVolumeClaimStatusUpdate(t *testing.T) { - ns := api.NamespaceDefault - persistentVolumeClaim := &api.PersistentVolumeClaim{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - ResourceVersion: "1", - }, - Spec: api.PersistentVolumeClaimSpec{ - AccessModes: []api.PersistentVolumeAccessMode{ - api.ReadWriteOnce, - api.ReadOnlyMany, - }, - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), - }, - }, - }, - Status: api.PersistentVolumeClaimStatus{ - Phase: api.ClaimBound, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "PUT", - Path: testapi.Default.ResourcePath(getPersistentVolumeClaimsResoureName(), ns, "abc") + "/status", - Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: persistentVolumeClaim}, - } - response, err := c.Setup(t).PersistentVolumeClaims(ns).UpdateStatus(persistentVolumeClaim) - defer c.Close() - c.Validate(t, response, err) -} - -func TestPersistentVolumeClaimDelete(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: testapi.Default.ResourcePath(getPersistentVolumeClaimsResoureName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).PersistentVolumeClaims(ns).Delete("foo") - defer c.Close() - c.Validate(t, nil, err) -} - -func TestPersistentVolumeClaimWatch(t *testing.T) { - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePathWithPrefix("watch", getPersistentVolumeClaimsResoureName(), "", ""), - Query: url.Values{"resourceVersion": []string{}}}, - Response: simple.Response{StatusCode: 200}, - } - _, err := c.Setup(t).PersistentVolumeClaims(api.NamespaceAll).Watch(api.ListOptions{}) - defer c.Close() - c.Validate(t, nil, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/pod_templates_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/pod_templates_test.go deleted file mode 100644 index bd9bcb549..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/pod_templates_test.go +++ /dev/null @@ -1,151 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "net/url" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" -) - -func getPodTemplatesResoureName() string { - return "podtemplates" -} - -func TestPodTemplateCreate(t *testing.T) { - ns := api.NamespaceDefault - podTemplate := api.PodTemplate{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: ns, - }, - Template: api.PodTemplateSpec{}, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: testapi.Default.ResourcePath(getPodTemplatesResoureName(), ns, ""), - Query: simple.BuildQueryValues(nil), - Body: &podTemplate, - }, - Response: simple.Response{StatusCode: 200, Body: &podTemplate}, - } - - response, err := c.Setup(t).PodTemplates(ns).Create(&podTemplate) - defer c.Close() - c.Validate(t, response, err) -} - -func TestPodTemplateGet(t *testing.T) { - ns := api.NamespaceDefault - podTemplate := &api.PodTemplate{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: ns, - }, - Template: api.PodTemplateSpec{}, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath(getPodTemplatesResoureName(), ns, "abc"), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: podTemplate}, - } - - response, err := c.Setup(t).PodTemplates(ns).Get("abc") - defer c.Close() - c.Validate(t, response, err) -} - -func TestPodTemplateList(t *testing.T) { - ns := api.NamespaceDefault - podTemplateList := &api.PodTemplateList{ - Items: []api.PodTemplate{ - { - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: ns, - }, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath(getPodTemplatesResoureName(), ns, ""), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: podTemplateList}, - } - response, err := c.Setup(t).PodTemplates(ns).List(api.ListOptions{}) - defer c.Close() - c.Validate(t, response, err) -} - -func TestPodTemplateUpdate(t *testing.T) { - ns := api.NamespaceDefault - podTemplate := &api.PodTemplate{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: ns, - ResourceVersion: "1", - }, - Template: api.PodTemplateSpec{}, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Default.ResourcePath(getPodTemplatesResoureName(), ns, "abc"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: podTemplate}, - } - response, err := c.Setup(t).PodTemplates(ns).Update(podTemplate) - defer c.Close() - c.Validate(t, response, err) -} - -func TestPodTemplateDelete(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: testapi.Default.ResourcePath(getPodTemplatesResoureName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).PodTemplates(ns).Delete("foo", nil) - defer c.Close() - c.Validate(t, nil, err) -} - -func TestPodTemplateWatch(t *testing.T) { - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePathWithPrefix("watch", getPodTemplatesResoureName(), "", ""), - Query: url.Values{"resourceVersion": []string{}}}, - Response: simple.Response{StatusCode: 200}, - } - _, err := c.Setup(t).PodTemplates(api.NamespaceAll).Watch(api.ListOptions{}) - defer c.Close() - c.Validate(t, nil, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/pods.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/pods.go index 04bb989c2..426d3ee8e 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/pods.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/pods.go @@ -18,6 +18,7 @@ package unversioned import ( "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/watch" ) @@ -36,7 +37,7 @@ type PodInterface interface { Watch(opts api.ListOptions) (watch.Interface, error) Bind(binding *api.Binding) error UpdateStatus(pod *api.Pod) (*api.Pod, error) - GetLogs(name string, opts *api.PodLogOptions) *Request + GetLogs(name string, opts *api.PodLogOptions) *restclient.Request } // pods implements PodsNamespacer interface @@ -109,6 +110,6 @@ func (c *pods) UpdateStatus(pod *api.Pod) (result *api.Pod, err error) { } // Get constructs a request for getting the logs for a pod -func (c *pods) GetLogs(name string, opts *api.PodLogOptions) *Request { +func (c *pods) GetLogs(name string, opts *api.PodLogOptions) *restclient.Request { return c.r.Get().Namespace(c.ns).Name(name).Resource("pods").SubResource("log").VersionedParams(opts, api.ParameterCodec) } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/pods_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/pods_test.go deleted file mode 100644 index 49175facb..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/pods_test.go +++ /dev/null @@ -1,228 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import . "k8s.io/kubernetes/pkg/client/unversioned" - -import ( - "net/http" - "net/url" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" - "k8s.io/kubernetes/pkg/labels" -) - -func TestListEmptyPods(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "GET", Path: testapi.Default.ResourcePath("pods", ns, ""), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: http.StatusOK, Body: &api.PodList{}}, - } - podList, err := c.Setup(t).Pods(ns).List(api.ListOptions{}) - defer c.Close() - c.Validate(t, podList, err) -} - -func TestListPods(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "GET", Path: testapi.Default.ResourcePath("pods", ns, ""), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: http.StatusOK, - Body: &api.PodList{ - Items: []api.Pod{ - { - Status: api.PodStatus{ - Phase: api.PodRunning, - }, - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - }, - }, - }, - }, - } - receivedPodList, err := c.Setup(t).Pods(ns).List(api.ListOptions{}) - defer c.Close() - c.Validate(t, receivedPodList, err) -} - -func TestListPodsLabels(t *testing.T) { - ns := api.NamespaceDefault - labelSelectorQueryParamName := unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String()) - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath("pods", ns, ""), - Query: simple.BuildQueryValues(url.Values{labelSelectorQueryParamName: []string{"foo=bar,name=baz"}})}, - Response: simple.Response{ - StatusCode: http.StatusOK, - Body: &api.PodList{ - Items: []api.Pod{ - { - Status: api.PodStatus{ - Phase: api.PodRunning, - }, - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - }, - }, - }, - }, - } - c.Setup(t) - defer c.Close() - c.QueryValidator[labelSelectorQueryParamName] = simple.ValidateLabels - selector := labels.Set{"foo": "bar", "name": "baz"}.AsSelector() - options := api.ListOptions{LabelSelector: selector} - receivedPodList, err := c.Pods(ns).List(options) - c.Validate(t, receivedPodList, err) -} - -func TestGetPod(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "GET", Path: testapi.Default.ResourcePath("pods", ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: http.StatusOK, - Body: &api.Pod{ - Status: api.PodStatus{ - Phase: api.PodRunning, - }, - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - }, - }, - } - receivedPod, err := c.Setup(t).Pods(ns).Get("foo") - defer c.Close() - c.Validate(t, receivedPod, err) -} - -func TestGetPodWithNoName(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{Error: true} - receivedPod, err := c.Setup(t).Pods(ns).Get("") - defer c.Close() - if (err != nil) && (err.Error() != simple.NameRequiredError) { - t.Errorf("Expected error: %v, but got %v", simple.NameRequiredError, err) - } - - c.Validate(t, receivedPod, err) -} - -func TestDeletePod(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: testapi.Default.ResourcePath("pods", ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: http.StatusOK}, - } - err := c.Setup(t).Pods(ns).Delete("foo", nil) - defer c.Close() - c.Validate(t, nil, err) -} - -func TestCreatePod(t *testing.T) { - ns := api.NamespaceDefault - requestPod := &api.Pod{ - Status: api.PodStatus{ - Phase: api.PodRunning, - }, - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - } - c := &simple.Client{ - Request: simple.Request{Method: "POST", Path: testapi.Default.ResourcePath("pods", ns, ""), Query: simple.BuildQueryValues(nil), Body: requestPod}, - Response: simple.Response{ - StatusCode: http.StatusOK, - Body: requestPod, - }, - } - receivedPod, err := c.Setup(t).Pods(ns).Create(requestPod) - defer c.Close() - c.Validate(t, receivedPod, err) -} - -func TestUpdatePod(t *testing.T) { - ns := api.NamespaceDefault - requestPod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - ResourceVersion: "1", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Status: api.PodStatus{ - Phase: api.PodRunning, - }, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Default.ResourcePath("pods", ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: http.StatusOK, Body: requestPod}, - } - receivedPod, err := c.Setup(t).Pods(ns).Update(requestPod) - defer c.Close() - c.Validate(t, receivedPod, err) -} - -func TestPodGetLogs(t *testing.T) { - ns := api.NamespaceDefault - opts := &api.PodLogOptions{ - Follow: true, - Timestamps: true, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath("pods", ns, "podName") + "/log", - Query: url.Values{ - "follow": []string{"true"}, - "timestamps": []string{"true"}, - }, - }, - Response: simple.Response{StatusCode: http.StatusOK}, - } - - body, err := c.Setup(t).Pods(ns).GetLogs("podName", opts).Stream() - defer c.Close() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - defer body.Close() - c.ValidateCommon(t, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/podsecuritypolicy_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/podsecuritypolicy_test.go deleted file mode 100644 index dee0f5935..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/podsecuritypolicy_test.go +++ /dev/null @@ -1,138 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import ( - "fmt" - "net/url" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/apis/extensions" - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -func TestPodSecurityPolicyCreate(t *testing.T) { - ns := api.NamespaceNone - scc := &extensions.PodSecurityPolicy{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - }, - } - - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: testapi.Extensions.ResourcePath(getPSPResourcename(), ns, ""), - Query: simple.BuildQueryValues(nil), - Body: scc, - }, - Response: simple.Response{StatusCode: 200, Body: scc}, - } - - response, err := c.Setup(t).PodSecurityPolicies().Create(scc) - c.Validate(t, response, err) -} - -func TestPodSecurityPolicyGet(t *testing.T) { - ns := api.NamespaceNone - scc := &extensions.PodSecurityPolicy{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Extensions.ResourcePath(getPSPResourcename(), ns, "abc"), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: scc}, - } - - response, err := c.Setup(t).PodSecurityPolicies().Get("abc") - c.Validate(t, response, err) -} - -func TestPodSecurityPolicyList(t *testing.T) { - ns := api.NamespaceNone - sccList := &extensions.PodSecurityPolicyList{ - Items: []extensions.PodSecurityPolicy{ - { - ObjectMeta: api.ObjectMeta{ - Name: "abc", - }, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Extensions.ResourcePath(getPSPResourcename(), ns, ""), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: sccList}, - } - response, err := c.Setup(t).PodSecurityPolicies().List(api.ListOptions{}) - c.Validate(t, response, err) -} - -func TestPodSecurityPolicyUpdate(t *testing.T) { - ns := api.NamespaceNone - scc := &extensions.PodSecurityPolicy{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - ResourceVersion: "1", - }, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Extensions.ResourcePath(getPSPResourcename(), ns, "abc"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: scc}, - } - response, err := c.Setup(t).PodSecurityPolicies().Update(scc) - c.Validate(t, response, err) -} - -func TestPodSecurityPolicyDelete(t *testing.T) { - ns := api.NamespaceNone - c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: testapi.Extensions.ResourcePath(getPSPResourcename(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).PodSecurityPolicies().Delete("foo") - c.Validate(t, nil, err) -} - -func TestPodSecurityPolicyWatch(t *testing.T) { - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: fmt.Sprintf("%s/watch/%s", testapi.Extensions.ResourcePath("", "", ""), getPSPResourcename()), - Query: url.Values{"resourceVersion": []string{}}}, - Response: simple.Response{StatusCode: 200}, - } - _, err := c.Setup(t).PodSecurityPolicies().Watch(api.ListOptions{}) - c.Validate(t, nil, err) -} - -func getPSPResourcename() string { - return "podsecuritypolicies" -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/portforward/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/portforward/doc.go deleted file mode 100644 index 032f180f9..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/portforward/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2015 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 portforward adds support for SSH-like port forwarding from the client's -// local host to remote containers. -package portforward diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/portforward/portforward.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/portforward/portforward.go deleted file mode 100644 index 78e6695e6..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/portforward/portforward.go +++ /dev/null @@ -1,329 +0,0 @@ -/* -Copyright 2015 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 portforward - -import ( - "errors" - "fmt" - "io" - "io/ioutil" - "net" - "net/http" - "strconv" - "strings" - "sync" - - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/kubelet/server/portforward" - "k8s.io/kubernetes/pkg/util/httpstream" - "k8s.io/kubernetes/pkg/util/runtime" -) - -// PortForwarder knows how to listen for local connections and forward them to -// a remote pod via an upgraded HTTP request. -type PortForwarder struct { - ports []ForwardedPort - stopChan <-chan struct{} - - dialer httpstream.Dialer - streamConn httpstream.Connection - listeners []io.Closer - Ready chan struct{} - requestIDLock sync.Mutex - requestID int -} - -// ForwardedPort contains a Local:Remote port pairing. -type ForwardedPort struct { - Local uint16 - Remote uint16 -} - -/* - valid port specifications: - - 5000 - - forwards from localhost:5000 to pod:5000 - - 8888:5000 - - forwards from localhost:8888 to pod:5000 - - 0:5000 - :5000 - - selects a random available local port, - forwards from localhost: to pod:5000 -*/ -func parsePorts(ports []string) ([]ForwardedPort, error) { - var forwards []ForwardedPort - for _, portString := range ports { - parts := strings.Split(portString, ":") - var localString, remoteString string - if len(parts) == 1 { - localString = parts[0] - remoteString = parts[0] - } else if len(parts) == 2 { - localString = parts[0] - if localString == "" { - // support :5000 - localString = "0" - } - remoteString = parts[1] - } else { - return nil, fmt.Errorf("Invalid port format '%s'", portString) - } - - localPort, err := strconv.ParseUint(localString, 10, 16) - if err != nil { - return nil, fmt.Errorf("Error parsing local port '%s': %s", localString, err) - } - - remotePort, err := strconv.ParseUint(remoteString, 10, 16) - if err != nil { - return nil, fmt.Errorf("Error parsing remote port '%s': %s", remoteString, err) - } - if remotePort == 0 { - return nil, fmt.Errorf("Remote port must be > 0") - } - - forwards = append(forwards, ForwardedPort{uint16(localPort), uint16(remotePort)}) - } - - return forwards, nil -} - -// New creates a new PortForwarder. -func New(dialer httpstream.Dialer, ports []string, stopChan <-chan struct{}) (*PortForwarder, error) { - if len(ports) == 0 { - return nil, errors.New("You must specify at least 1 port") - } - parsedPorts, err := parsePorts(ports) - if err != nil { - return nil, err - } - return &PortForwarder{ - dialer: dialer, - ports: parsedPorts, - stopChan: stopChan, - Ready: make(chan struct{}), - }, nil -} - -// ForwardPorts formats and executes a port forwarding request. The connection will remain -// open until stopChan is closed. -func (pf *PortForwarder) ForwardPorts() error { - defer pf.Close() - - var err error - pf.streamConn, _, err = pf.dialer.Dial(portforward.PortForwardProtocolV1Name) - if err != nil { - return fmt.Errorf("error upgrading connection: %s", err) - } - defer pf.streamConn.Close() - - return pf.forward() -} - -// forward dials the remote host specific in req, upgrades the request, starts -// listeners for each port specified in ports, and forwards local connections -// to the remote host via streams. -func (pf *PortForwarder) forward() error { - var err error - - listenSuccess := false - for _, port := range pf.ports { - err = pf.listenOnPort(&port) - switch { - case err == nil: - listenSuccess = true - default: - glog.Warningf("Unable to listen on port %d: %v", port.Local, err) - } - } - - if !listenSuccess { - return fmt.Errorf("Unable to listen on any of the requested ports: %v", pf.ports) - } - - close(pf.Ready) - - // wait for interrupt or conn closure - select { - case <-pf.stopChan: - case <-pf.streamConn.CloseChan(): - runtime.HandleError(errors.New("lost connection to pod")) - } - - return nil -} - -// listenOnPort delegates tcp4 and tcp6 listener creation and waits for connections on both of these addresses. -// If both listener creation fail, an error is raised. -func (pf *PortForwarder) listenOnPort(port *ForwardedPort) error { - errTcp4 := pf.listenOnPortAndAddress(port, "tcp4", "127.0.0.1") - errTcp6 := pf.listenOnPortAndAddress(port, "tcp6", "[::1]") - if errTcp4 != nil && errTcp6 != nil { - return fmt.Errorf("All listeners failed to create with the following errors: %s, %s", errTcp4, errTcp6) - } - return nil -} - -// listenOnPortAndAddress delegates listener creation and waits for new connections -// in the background f -func (pf *PortForwarder) listenOnPortAndAddress(port *ForwardedPort, protocol string, address string) error { - listener, err := pf.getListener(protocol, address, port) - if err != nil { - return err - } - pf.listeners = append(pf.listeners, listener) - go pf.waitForConnection(listener, *port) - return nil -} - -// getListener creates a listener on the interface targeted by the given hostname on the given port with -// the given protocol. protocol is in net.Listen style which basically admits values like tcp, tcp4, tcp6 -func (pf *PortForwarder) getListener(protocol string, hostname string, port *ForwardedPort) (net.Listener, error) { - listener, err := net.Listen(protocol, fmt.Sprintf("%s:%d", hostname, port.Local)) - if err != nil { - runtime.HandleError(fmt.Errorf("Unable to create listener: Error %s", err)) - return nil, err - } - listenerAddress := listener.Addr().String() - host, localPort, _ := net.SplitHostPort(listenerAddress) - localPortUInt, err := strconv.ParseUint(localPort, 10, 16) - - if err != nil { - return nil, fmt.Errorf("Error parsing local port: %s from %s (%s)", err, listenerAddress, host) - } - port.Local = uint16(localPortUInt) - glog.Infof("Forwarding from %s:%d -> %d", hostname, localPortUInt, port.Remote) - - return listener, nil -} - -// waitForConnection waits for new connections to listener and handles them in -// the background. -func (pf *PortForwarder) waitForConnection(listener net.Listener, port ForwardedPort) { - for { - conn, err := listener.Accept() - if err != nil { - // TODO consider using something like https://github.com/hydrogen18/stoppableListener? - if !strings.Contains(strings.ToLower(err.Error()), "use of closed network connection") { - runtime.HandleError(fmt.Errorf("Error accepting connection on port %d: %v", port.Local, err)) - } - return - } - go pf.handleConnection(conn, port) - } -} - -func (pf *PortForwarder) nextRequestID() int { - pf.requestIDLock.Lock() - defer pf.requestIDLock.Unlock() - id := pf.requestID - pf.requestID++ - return id -} - -// handleConnection copies data between the local connection and the stream to -// the remote server. -func (pf *PortForwarder) handleConnection(conn net.Conn, port ForwardedPort) { - defer conn.Close() - - glog.Infof("Handling connection for %d", port.Local) - - requestID := pf.nextRequestID() - - // create error stream - headers := http.Header{} - headers.Set(api.StreamType, api.StreamTypeError) - headers.Set(api.PortHeader, fmt.Sprintf("%d", port.Remote)) - headers.Set(api.PortForwardRequestIDHeader, strconv.Itoa(requestID)) - errorStream, err := pf.streamConn.CreateStream(headers) - if err != nil { - runtime.HandleError(fmt.Errorf("error creating error stream for port %d -> %d: %v", port.Local, port.Remote, err)) - return - } - // we're not writing to this stream - errorStream.Close() - - errorChan := make(chan error) - go func() { - message, err := ioutil.ReadAll(errorStream) - switch { - case err != nil: - errorChan <- fmt.Errorf("error reading from error stream for port %d -> %d: %v", port.Local, port.Remote, err) - case len(message) > 0: - errorChan <- fmt.Errorf("an error occurred forwarding %d -> %d: %v", port.Local, port.Remote, string(message)) - } - close(errorChan) - }() - - // create data stream - headers.Set(api.StreamType, api.StreamTypeData) - dataStream, err := pf.streamConn.CreateStream(headers) - if err != nil { - runtime.HandleError(fmt.Errorf("error creating forwarding stream for port %d -> %d: %v", port.Local, port.Remote, err)) - return - } - - localError := make(chan struct{}) - remoteDone := make(chan struct{}) - - go func() { - // Copy from the remote side to the local port. - if _, err := io.Copy(conn, dataStream); err != nil && !strings.Contains(err.Error(), "use of closed network connection") { - runtime.HandleError(fmt.Errorf("error copying from remote stream to local connection: %v", err)) - } - - // inform the select below that the remote copy is done - close(remoteDone) - }() - - go func() { - // inform server we're not sending any more data after copy unblocks - defer dataStream.Close() - - // Copy from the local port to the remote side. - if _, err := io.Copy(dataStream, conn); err != nil && !strings.Contains(err.Error(), "use of closed network connection") { - runtime.HandleError(fmt.Errorf("error copying from local connection to remote stream: %v", err)) - // break out of the select below without waiting for the other copy to finish - close(localError) - } - }() - - // wait for either a local->remote error or for copying from remote->local to finish - select { - case <-remoteDone: - case <-localError: - } - - // always expect something on errorChan (it may be nil) - err = <-errorChan - if err != nil { - runtime.HandleError(err) - } -} - -func (pf *PortForwarder) Close() { - // stop all listeners - for _, l := range pf.listeners { - if err := l.Close(); err != nil { - runtime.HandleError(fmt.Errorf("error closing listener: %v", err)) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/portforward/portforward_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/portforward/portforward_test.go deleted file mode 100644 index 2d5060b29..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/portforward/portforward_test.go +++ /dev/null @@ -1,400 +0,0 @@ -/* -Copyright 2015 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 portforward - -import ( - "bytes" - "fmt" - "io" - "net" - "net/http" - "net/http/httptest" - "net/url" - "reflect" - "strings" - "sync" - "testing" - "time" - - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/remotecommand" - kubeletserver "k8s.io/kubernetes/pkg/kubelet/server" - "k8s.io/kubernetes/pkg/types" - "k8s.io/kubernetes/pkg/util/httpstream" -) - -type fakeDialer struct { - dialed bool - conn httpstream.Connection - err error - negotiatedProtocol string -} - -func (d *fakeDialer) Dial(protocols ...string) (httpstream.Connection, string, error) { - d.dialed = true - return d.conn, d.negotiatedProtocol, d.err -} - -func TestParsePortsAndNew(t *testing.T) { - tests := []struct { - input []string - expected []ForwardedPort - expectParseError bool - expectNewError bool - }{ - {input: []string{}, expectNewError: true}, - {input: []string{"a"}, expectParseError: true, expectNewError: true}, - {input: []string{":a"}, expectParseError: true, expectNewError: true}, - {input: []string{"-1"}, expectParseError: true, expectNewError: true}, - {input: []string{"65536"}, expectParseError: true, expectNewError: true}, - {input: []string{"0"}, expectParseError: true, expectNewError: true}, - {input: []string{"0:0"}, expectParseError: true, expectNewError: true}, - {input: []string{"a:5000"}, expectParseError: true, expectNewError: true}, - {input: []string{"5000:a"}, expectParseError: true, expectNewError: true}, - { - input: []string{"5000", "5000:5000", "8888:5000", "5000:8888", ":5000", "0:5000"}, - expected: []ForwardedPort{ - {5000, 5000}, - {5000, 5000}, - {8888, 5000}, - {5000, 8888}, - {0, 5000}, - {0, 5000}, - }, - }, - } - - for i, test := range tests { - parsed, err := parsePorts(test.input) - haveError := err != nil - if e, a := test.expectParseError, haveError; e != a { - t.Fatalf("%d: parsePorts: error expected=%t, got %t: %s", i, e, a, err) - } - - dialer := &fakeDialer{} - expectedStopChan := make(chan struct{}) - pf, err := New(dialer, test.input, expectedStopChan) - haveError = err != nil - if e, a := test.expectNewError, haveError; e != a { - t.Fatalf("%d: New: error expected=%t, got %t: %s", i, e, a, err) - } - - if test.expectParseError || test.expectNewError { - continue - } - - for pi, expectedPort := range test.expected { - if e, a := expectedPort.Local, parsed[pi].Local; e != a { - t.Fatalf("%d: local expected: %d, got: %d", i, e, a) - } - if e, a := expectedPort.Remote, parsed[pi].Remote; e != a { - t.Fatalf("%d: remote expected: %d, got: %d", i, e, a) - } - } - - if dialer.dialed { - t.Fatalf("%d: expected not dialed", i) - } - if e, a := test.expected, pf.ports; !reflect.DeepEqual(e, a) { - t.Fatalf("%d: ports: expected %#v, got %#v", i, e, a) - } - if e, a := expectedStopChan, pf.stopChan; e != a { - t.Fatalf("%d: stopChan: expected %#v, got %#v", i, e, a) - } - if pf.Ready == nil { - t.Fatalf("%d: Ready should be non-nil", i) - } - } -} - -type GetListenerTestCase struct { - Hostname string - Protocol string - ShouldRaiseError bool - ExpectedListenerAddress string -} - -func TestGetListener(t *testing.T) { - var pf PortForwarder - testCases := []GetListenerTestCase{ - { - Hostname: "localhost", - Protocol: "tcp4", - ShouldRaiseError: false, - ExpectedListenerAddress: "127.0.0.1", - }, - { - Hostname: "127.0.0.1", - Protocol: "tcp4", - ShouldRaiseError: false, - ExpectedListenerAddress: "127.0.0.1", - }, - { - Hostname: "[::1]", - Protocol: "tcp6", - ShouldRaiseError: false, - ExpectedListenerAddress: "::1", - }, - { - Hostname: "[::1]", - Protocol: "tcp4", - ShouldRaiseError: true, - }, - { - Hostname: "127.0.0.1", - Protocol: "tcp6", - ShouldRaiseError: true, - }, - { - // IPv6 address must be put into brackets. This test reveals this. - Hostname: "::1", - Protocol: "tcp6", - ShouldRaiseError: true, - }, - } - - for i, testCase := range testCases { - expectedListenerPort := "12345" - listener, err := pf.getListener(testCase.Protocol, testCase.Hostname, &ForwardedPort{12345, 12345}) - if err != nil && strings.Contains(err.Error(), "cannot assign requested address") { - t.Logf("Can't test #%d: %v", i, err) - continue - } - errorRaised := err != nil - - if testCase.ShouldRaiseError != errorRaised { - t.Errorf("Test case #%d failed: Data %v an error has been raised(%t) where it should not (or reciprocally): %v", i, testCase, testCase.ShouldRaiseError, err) - continue - } - if errorRaised { - continue - } - - if listener == nil { - t.Errorf("Test case #%d did not raise an error but failed in initializing listener", i) - continue - } - - host, port, _ := net.SplitHostPort(listener.Addr().String()) - t.Logf("Asked a %s forward for: %s:%v, got listener %s:%s, expected: %s", testCase.Protocol, testCase.Hostname, 12345, host, port, expectedListenerPort) - if host != testCase.ExpectedListenerAddress { - t.Errorf("Test case #%d failed: Listener does not listen on exepected address: asked %v got %v", i, testCase.ExpectedListenerAddress, host) - } - if port != expectedListenerPort { - t.Errorf("Test case #%d failed: Listener does not listen on exepected port: asked %v got %v", i, expectedListenerPort, port) - - } - listener.Close() - - } -} - -// fakePortForwarder simulates port forwarding for testing. It implements -// kubeletserver.PortForwarder. -type fakePortForwarder struct { - lock sync.Mutex - // stores data expected from the stream per port - expected map[uint16]string - // stores data received from the stream per port - received map[uint16]string - // data to be sent to the stream per port - send map[uint16]string -} - -var _ kubeletserver.PortForwarder = &fakePortForwarder{} - -func (pf *fakePortForwarder) PortForward(name string, uid types.UID, port uint16, stream io.ReadWriteCloser) error { - defer stream.Close() - - // read from the client - received := make([]byte, len(pf.expected[port])) - n, err := stream.Read(received) - if err != nil { - return fmt.Errorf("error reading from client for port %d: %v", port, err) - } - if n != len(pf.expected[port]) { - return fmt.Errorf("unexpected length read from client for port %d: got %d, expected %d. data=%q", port, n, len(pf.expected[port]), string(received)) - } - - // store the received content - pf.lock.Lock() - pf.received[port] = string(received) - pf.lock.Unlock() - - // send the hardcoded data to the client - io.Copy(stream, strings.NewReader(pf.send[port])) - - return nil -} - -// fakePortForwardServer creates an HTTP server that can handle port forwarding -// requests. -func fakePortForwardServer(t *testing.T, testName string, serverSends, expectedFromClient map[uint16]string) http.HandlerFunc { - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - pf := &fakePortForwarder{ - expected: expectedFromClient, - received: make(map[uint16]string), - send: serverSends, - } - kubeletserver.ServePortForward(w, req, pf, "pod", "uid", 0, 10*time.Second) - - for port, expected := range expectedFromClient { - actual, ok := pf.received[port] - if !ok { - t.Errorf("%s: server didn't receive any data for port %d", testName, port) - continue - } - - if expected != actual { - t.Errorf("%s: server expected to receive %q, got %q for port %d", testName, expected, actual, port) - } - } - - for port, actual := range pf.received { - if _, ok := expectedFromClient[port]; !ok { - t.Errorf("%s: server unexpectedly received %q for port %d", testName, actual, port) - } - } - }) -} - -func TestForwardPorts(t *testing.T) { - tests := map[string]struct { - ports []string - clientSends map[uint16]string - serverSends map[uint16]string - }{ - "forward 1 port with no data either direction": { - ports: []string{"5000"}, - }, - "forward 2 ports with bidirectional data": { - ports: []string{"5001", "6000"}, - clientSends: map[uint16]string{ - 5001: "abcd", - 6000: "ghij", - }, - serverSends: map[uint16]string{ - 5001: "1234", - 6000: "5678", - }, - }, - } - - for testName, test := range tests { - server := httptest.NewServer(fakePortForwardServer(t, testName, test.serverSends, test.clientSends)) - - url, _ := url.Parse(server.URL) - exec, err := remotecommand.NewExecutor(&client.Config{}, "POST", url) - if err != nil { - t.Fatal(err) - } - - stopChan := make(chan struct{}, 1) - - pf, err := New(exec, test.ports, stopChan) - if err != nil { - t.Fatalf("%s: unexpected error calling New: %v", testName, err) - } - - doneChan := make(chan error) - go func() { - doneChan <- pf.ForwardPorts() - }() - <-pf.Ready - - for port, data := range test.clientSends { - clientConn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) - if err != nil { - t.Errorf("%s: error dialing %d: %s", testName, port, err) - // TODO: Uncomment when fix #19254 - // server.Close() - continue - } - defer clientConn.Close() - - n, err := clientConn.Write([]byte(data)) - if err != nil && err != io.EOF { - t.Errorf("%s: Error sending data '%s': %s", testName, data, err) - // TODO: Uncomment when fix #19254 - // server.Close() - continue - } - if n == 0 { - t.Errorf("%s: unexpected write of 0 bytes", testName) - // TODO: Uncomment when fix #19254 - // server.Close() - continue - } - b := make([]byte, 4) - n, err = clientConn.Read(b) - if err != nil && err != io.EOF { - t.Errorf("%s: Error reading data: %s", testName, err) - // TODO: Uncomment when fix #19254 - // server.Close() - continue - } - if !bytes.Equal([]byte(test.serverSends[port]), b) { - t.Errorf("%s: expected to read '%s', got '%s'", testName, test.serverSends[port], b) - // TODO: Uncomment when fix #19254 - // server.Close() - continue - } - } - // tell r.ForwardPorts to stop - close(stopChan) - - // wait for r.ForwardPorts to actually return - err = <-doneChan - if err != nil { - t.Errorf("%s: unexpected error: %s", testName, err) - } - // TODO: Uncomment when fix #19254 - // server.Close() - } - -} - -func TestForwardPortsReturnsErrorWhenAllBindsFailed(t *testing.T) { - server := httptest.NewServer(fakePortForwardServer(t, "allBindsFailed", nil, nil)) - // TODO: Uncomment when fix #19254 - // defer server.Close() - - url, _ := url.Parse(server.URL) - exec, err := remotecommand.NewExecutor(&client.Config{}, "POST", url) - if err != nil { - t.Fatal(err) - } - - stopChan1 := make(chan struct{}, 1) - defer close(stopChan1) - - pf1, err := New(exec, []string{"5555"}, stopChan1) - if err != nil { - t.Fatalf("error creating pf1: %v", err) - } - go pf1.ForwardPorts() - <-pf1.Ready - - stopChan2 := make(chan struct{}, 1) - pf2, err := New(exec, []string{"5555"}, stopChan2) - if err != nil { - t.Fatalf("error creating pf2: %v", err) - } - if err := pf2.ForwardPorts(); err == nil { - t.Fatal("expected non-nil error for pf2.ForwardPorts") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/doc.go deleted file mode 100644 index 88197ed0d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright 2015 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 remotecommand adds support for executing commands in containers, -// with support for separate stdin, stdout, and stderr streams, as well as -// TTY. -package remotecommand diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/remotecommand.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/remotecommand.go deleted file mode 100644 index d827f1010..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/remotecommand.go +++ /dev/null @@ -1,180 +0,0 @@ -/* -Copyright 2015 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 remotecommand - -import ( - "fmt" - "io" - "net/http" - "net/url" - - "github.com/golang/glog" - - "k8s.io/kubernetes/pkg/client/transport" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/util/httpstream" - "k8s.io/kubernetes/pkg/util/httpstream/spdy" -) - -// Executor is an interface for transporting shell-style streams. -type Executor interface { - // Stream initiates the transport of the standard shell streams. It will transport any - // non-nil stream to a remote system, and return an error if a problem occurs. If tty - // is set, the stderr stream is not used (raw TTY manages stdout and stderr over the - // stdout stream). - Stream(stdin io.Reader, stdout, stderr io.Writer, tty bool) error -} - -// StreamExecutor supports the ability to dial an httpstream connection and the ability to -// run a command line stream protocol over that dialer. -type StreamExecutor interface { - Executor - httpstream.Dialer -} - -// streamExecutor handles transporting standard shell streams over an httpstream connection. -type streamExecutor struct { - upgrader httpstream.UpgradeRoundTripper - transport http.RoundTripper - - method string - url *url.URL -} - -// NewExecutor connects to the provided server and upgrades the connection to -// multiplexed bidirectional streams. The current implementation uses SPDY, -// but this could be replaced with HTTP/2 once it's available, or something else. -// TODO: the common code between this and portforward could be abstracted. -func NewExecutor(config *client.Config, method string, url *url.URL) (StreamExecutor, error) { - tlsConfig, err := client.TLSConfigFor(config) - if err != nil { - return nil, err - } - - upgradeRoundTripper := spdy.NewRoundTripper(tlsConfig) - wrapper, err := client.HTTPWrappersForConfig(config, upgradeRoundTripper) - if err != nil { - return nil, err - } - - return &streamExecutor{ - upgrader: upgradeRoundTripper, - transport: wrapper, - method: method, - url: url, - }, nil -} - -// NewStreamExecutor upgrades the request so that it supports multiplexed bidirectional -// streams. This method takes a stream upgrader and an optional function that is invoked -// to wrap the round tripper. This method may be used by clients that are lower level than -// Kubernetes clients or need to provide their own upgrade round tripper. -func NewStreamExecutor(upgrader httpstream.UpgradeRoundTripper, fn func(http.RoundTripper) http.RoundTripper, method string, url *url.URL) (StreamExecutor, error) { - var rt http.RoundTripper = upgrader - if fn != nil { - rt = fn(rt) - } - return &streamExecutor{ - upgrader: upgrader, - transport: rt, - method: method, - url: url, - }, nil -} - -// Dial opens a connection to a remote server and attempts to negotiate a SPDY -// connection. Upon success, it returns the connection and the protocol -// selected by the server. -func (e *streamExecutor) Dial(protocols ...string) (httpstream.Connection, string, error) { - rt := transport.DebugWrappers(e.transport) - - // TODO the client probably shouldn't be created here, as it doesn't allow - // flexibility to allow callers to configure it. - client := &http.Client{Transport: rt} - - req, err := http.NewRequest(e.method, e.url.String(), nil) - if err != nil { - return nil, "", fmt.Errorf("error creating request: %v", err) - } - for i := range protocols { - req.Header.Add(httpstream.HeaderProtocolVersion, protocols[i]) - } - - resp, err := client.Do(req) - if err != nil { - return nil, "", fmt.Errorf("error sending request: %v", err) - } - defer resp.Body.Close() - - conn, err := e.upgrader.NewConnection(resp) - if err != nil { - return nil, "", err - } - - return conn, resp.Header.Get(httpstream.HeaderProtocolVersion), nil -} - -const ( - // The SPDY subprotocol "channel.k8s.io" is used for remote command - // attachment/execution. This represents the initial unversioned subprotocol, - // which has the known bugs http://issues.k8s.io/13394 and - // http://issues.k8s.io/13395. - StreamProtocolV1Name = "channel.k8s.io" - // The SPDY subprotocol "v2.channel.k8s.io" is used for remote command - // attachment/execution. It is the second version of the subprotocol and - // resolves the issues present in the first version. - StreamProtocolV2Name = "v2.channel.k8s.io" -) - -type streamProtocolHandler interface { - stream(httpstream.Connection) error -} - -// Stream opens a protocol streamer to the server and streams until a client closes -// the connection or the server disconnects. -func (e *streamExecutor) Stream(stdin io.Reader, stdout, stderr io.Writer, tty bool) error { - supportedProtocols := []string{StreamProtocolV2Name, StreamProtocolV1Name} - conn, protocol, err := e.Dial(supportedProtocols...) - if err != nil { - return err - } - defer conn.Close() - - var streamer streamProtocolHandler - - switch protocol { - case StreamProtocolV2Name: - streamer = &streamProtocolV2{ - stdin: stdin, - stdout: stdout, - stderr: stderr, - tty: tty, - } - case "": - glog.V(4).Infof("The server did not negotiate a streaming protocol version. Falling back to %s", StreamProtocolV1Name) - fallthrough - case StreamProtocolV1Name: - streamer = &streamProtocolV1{ - stdin: stdin, - stdout: stdout, - stderr: stderr, - tty: tty, - } - } - - return streamer.stream(conn) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/remotecommand_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/remotecommand_test.go deleted file mode 100644 index 71acd239d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/remotecommand_test.go +++ /dev/null @@ -1,413 +0,0 @@ -/* -Copyright 2015 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 remotecommand - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/http/httptest" - "net/url" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/util/httpstream" - "k8s.io/kubernetes/pkg/util/httpstream/spdy" -) - -type streamAndReply struct { - httpstream.Stream - replySent <-chan struct{} -} - -func waitStreamReply(replySent <-chan struct{}, notify chan<- struct{}, stop <-chan struct{}) { - select { - case <-replySent: - notify <- struct{}{} - case <-stop: - } -} - -func fakeExecServer(t *testing.T, i int, stdinData, stdoutData, stderrData, errorData string, tty bool, messageCount int) http.HandlerFunc { - // error + stdin + stdout - expectedStreams := 3 - if !tty { - // stderr - expectedStreams++ - } - - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - protocol, err := httpstream.Handshake(req, w, []string{StreamProtocolV2Name}, StreamProtocolV1Name) - if err != nil { - t.Fatal(err) - } - if protocol != StreamProtocolV2Name { - t.Fatalf("unexpected protocol: %s", protocol) - } - streamCh := make(chan streamAndReply) - - upgrader := spdy.NewResponseUpgrader() - conn := upgrader.UpgradeResponse(w, req, func(stream httpstream.Stream, replySent <-chan struct{}) error { - streamCh <- streamAndReply{Stream: stream, replySent: replySent} - return nil - }) - // from this point on, we can no longer call methods on w - if conn == nil { - // The upgrader is responsible for notifying the client of any errors that - // occurred during upgrading. All we can do is return here at this point - // if we weren't successful in upgrading. - return - } - defer conn.Close() - - var errorStream, stdinStream, stdoutStream, stderrStream httpstream.Stream - receivedStreams := 0 - replyChan := make(chan struct{}) - stop := make(chan struct{}) - defer close(stop) - WaitForStreams: - for { - select { - case stream := <-streamCh: - streamType := stream.Headers().Get(api.StreamType) - switch streamType { - case api.StreamTypeError: - errorStream = stream - go waitStreamReply(stream.replySent, replyChan, stop) - case api.StreamTypeStdin: - stdinStream = stream - go waitStreamReply(stream.replySent, replyChan, stop) - case api.StreamTypeStdout: - stdoutStream = stream - go waitStreamReply(stream.replySent, replyChan, stop) - case api.StreamTypeStderr: - stderrStream = stream - go waitStreamReply(stream.replySent, replyChan, stop) - default: - t.Errorf("%d: unexpected stream type: %q", i, streamType) - } - - if receivedStreams == expectedStreams { - break WaitForStreams - } - case <-replyChan: - receivedStreams++ - if receivedStreams == expectedStreams { - break WaitForStreams - } - } - } - - if len(errorData) > 0 { - n, err := fmt.Fprint(errorStream, errorData) - if err != nil { - t.Errorf("%d: error writing to errorStream: %v", i, err) - } - if e, a := len(errorData), n; e != a { - t.Errorf("%d: expected to write %d bytes to errorStream, but only wrote %d", i, e, a) - } - errorStream.Close() - } - - if len(stdoutData) > 0 { - for j := 0; j < messageCount; j++ { - n, err := fmt.Fprint(stdoutStream, stdoutData) - if err != nil { - t.Errorf("%d: error writing to stdoutStream: %v", i, err) - } - if e, a := len(stdoutData), n; e != a { - t.Errorf("%d: expected to write %d bytes to stdoutStream, but only wrote %d", i, e, a) - } - } - stdoutStream.Close() - } - if len(stderrData) > 0 { - for j := 0; j < messageCount; j++ { - n, err := fmt.Fprint(stderrStream, stderrData) - if err != nil { - t.Errorf("%d: error writing to stderrStream: %v", i, err) - } - if e, a := len(stderrData), n; e != a { - t.Errorf("%d: expected to write %d bytes to stderrStream, but only wrote %d", i, e, a) - } - } - stderrStream.Close() - } - if len(stdinData) > 0 { - data := make([]byte, len(stdinData)) - for j := 0; j < messageCount; j++ { - n, err := io.ReadFull(stdinStream, data) - if err != nil { - t.Errorf("%d: error reading stdin stream: %v", i, err) - } - if e, a := len(stdinData), n; e != a { - t.Errorf("%d: expected to read %d bytes from stdinStream, but only read %d", i, e, a) - } - if e, a := stdinData, string(data); e != a { - t.Errorf("%d: stdin: expected %q, got %q", i, e, a) - } - } - stdinStream.Close() - } - }) -} - -func TestRequestExecuteRemoteCommand(t *testing.T) { - testCases := []struct { - Stdin string - Stdout string - Stderr string - Error string - Tty bool - MessageCount int - }{ - { - Error: "bail", - }, - { - Stdin: "a", - Stdout: "b", - Stderr: "c", - // TODO bump this to a larger number such as 100 once - // https://github.com/docker/spdystream/issues/55 is fixed and the Godep - // is bumped. Sending multiple messages over stdin/stdout/stderr results - // in more frames being spread across multiple spdystream frame workers. - // This makes it more likely that the spdystream bug will be encountered, - // where streams are closed as soon as a goaway frame is received, and - // any pending frames that haven't been processed yet may not be - // delivered (it's a race). - MessageCount: 1, - }, - { - Stdin: "a", - Stdout: "b", - Tty: true, - }, - } - - for i, testCase := range testCases { - localOut := &bytes.Buffer{} - localErr := &bytes.Buffer{} - - server := httptest.NewServer(fakeExecServer(t, i, testCase.Stdin, testCase.Stdout, testCase.Stderr, testCase.Error, testCase.Tty, testCase.MessageCount)) - - url, _ := url.ParseRequestURI(server.URL) - c := client.NewRESTClient(url, "", client.ContentConfig{GroupVersion: &unversioned.GroupVersion{Group: "x"}}, -1, -1, nil) - req := c.Post().Resource("testing") - req.SetHeader(httpstream.HeaderProtocolVersion, StreamProtocolV2Name) - req.Param("command", "ls") - req.Param("command", "/") - conf := &client.Config{ - Host: server.URL, - } - e, err := NewExecutor(conf, "POST", req.URL()) - if err != nil { - t.Errorf("%d: unexpected error: %v", i, err) - continue - } - err = e.Stream(strings.NewReader(strings.Repeat(testCase.Stdin, testCase.MessageCount)), localOut, localErr, testCase.Tty) - hasErr := err != nil - - if len(testCase.Error) > 0 { - if !hasErr { - t.Errorf("%d: expected an error", i) - } else { - if e, a := testCase.Error, err.Error(); !strings.Contains(a, e) { - t.Errorf("%d: expected error stream read '%v', got '%v'", i, e, a) - } - } - - // TODO: Uncomment when fix #19254 - // server.Close() - continue - } - - if hasErr { - t.Errorf("%d: unexpected error: %v", i, err) - // TODO: Uncomment when fix #19254 - // server.Close() - continue - } - - if len(testCase.Stdout) > 0 { - if e, a := strings.Repeat(testCase.Stdout, testCase.MessageCount), localOut; e != a.String() { - t.Errorf("%d: expected stdout data '%s', got '%s'", i, e, a) - } - } - - if testCase.Stderr != "" { - if e, a := strings.Repeat(testCase.Stderr, testCase.MessageCount), localErr; e != a.String() { - t.Errorf("%d: expected stderr data '%s', got '%s'", i, e, a) - } - } - - // TODO: Uncomment when fix #19254 - // server.Close() - } -} - -// TODO: this test is largely cut and paste, refactor to share code -func TestRequestAttachRemoteCommand(t *testing.T) { - testCases := []struct { - Stdin string - Stdout string - Stderr string - Error string - Tty bool - }{ - { - Error: "bail", - }, - { - Stdin: "a", - Stdout: "b", - Stderr: "c", - }, - { - Stdin: "a", - Stdout: "b", - Tty: true, - }, - } - - for i, testCase := range testCases { - localOut := &bytes.Buffer{} - localErr := &bytes.Buffer{} - - server := httptest.NewServer(fakeExecServer(t, i, testCase.Stdin, testCase.Stdout, testCase.Stderr, testCase.Error, testCase.Tty, 1)) - - url, _ := url.ParseRequestURI(server.URL) - c := client.NewRESTClient(url, "", client.ContentConfig{GroupVersion: &unversioned.GroupVersion{Group: "x"}}, -1, -1, nil) - req := c.Post().Resource("testing") - - conf := &client.Config{ - Host: server.URL, - } - e, err := NewExecutor(conf, "POST", req.URL()) - if err != nil { - t.Errorf("%d: unexpected error: %v", i, err) - continue - } - err = e.Stream(strings.NewReader(testCase.Stdin), localOut, localErr, testCase.Tty) - hasErr := err != nil - - if len(testCase.Error) > 0 { - if !hasErr { - t.Errorf("%d: expected an error", i) - } else { - if e, a := testCase.Error, err.Error(); !strings.Contains(a, e) { - t.Errorf("%d: expected error stream read '%v', got '%v'", i, e, a) - } - } - - // TODO: Uncomment when fix #19254 - // server.Close() - continue - } - - if hasErr { - t.Errorf("%d: unexpected error: %v", i, err) - // TODO: Uncomment when fix #19254 - // server.Close() - continue - } - - if len(testCase.Stdout) > 0 { - if e, a := testCase.Stdout, localOut; e != a.String() { - t.Errorf("%d: expected stdout data '%s', got '%s'", i, e, a) - } - } - - if testCase.Stderr != "" { - if e, a := testCase.Stderr, localErr; e != a.String() { - t.Errorf("%d: expected stderr data '%s', got '%s'", i, e, a) - } - } - - // TODO: Uncomment when fix #19254 - // server.Close() - } -} - -type fakeUpgrader struct { - req *http.Request - resp *http.Response - conn httpstream.Connection - err, connErr error - checkResponse bool - - t *testing.T -} - -func (u *fakeUpgrader) RoundTrip(req *http.Request) (*http.Response, error) { - u.req = req - return u.resp, u.err -} - -func (u *fakeUpgrader) NewConnection(resp *http.Response) (httpstream.Connection, error) { - if u.checkResponse && u.resp != resp { - u.t.Errorf("response objects passed did not match: %#v", resp) - } - return u.conn, u.connErr -} - -type fakeConnection struct { - httpstream.Connection -} - -// Dial is the common functionality between any stream based upgrader, regardless of protocol. -// This method ensures that someone can use a generic stream executor without being dependent -// on the core Kube client config behavior. -func TestDial(t *testing.T) { - upgrader := &fakeUpgrader{ - t: t, - checkResponse: true, - conn: &fakeConnection{}, - resp: &http.Response{ - StatusCode: http.StatusSwitchingProtocols, - Body: ioutil.NopCloser(&bytes.Buffer{}), - }, - } - var called bool - testFn := func(rt http.RoundTripper) http.RoundTripper { - if rt != upgrader { - t.Fatalf("unexpected round tripper: %#v", rt) - } - called = true - return rt - } - exec, err := NewStreamExecutor(upgrader, testFn, "POST", &url.URL{Host: "something.com", Scheme: "https"}) - if err != nil { - t.Fatal(err) - } - conn, protocol, err := exec.Dial("protocol1") - if err != nil { - t.Fatal(err) - } - if conn != upgrader.conn { - t.Errorf("unexpected connection: %#v", conn) - } - if !called { - t.Errorf("wrapper not called") - } - _ = protocol -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/v1.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/v1.go deleted file mode 100644 index f5428e0f9..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/v1.go +++ /dev/null @@ -1,156 +0,0 @@ -/* -Copyright 2015 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 remotecommand - -import ( - "fmt" - "io" - "io/ioutil" - "net/http" - - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/util/httpstream" -) - -// streamProtocolV1 implements the first version of the streaming exec & attach -// protocol. This version has some bugs, such as not being able to detecte when -// non-interactive stdin data has ended. See http://issues.k8s.io/13394 and -// http://issues.k8s.io/13395 for more details. -type streamProtocolV1 struct { - stdin io.Reader - stdout io.Writer - stderr io.Writer - tty bool -} - -var _ streamProtocolHandler = &streamProtocolV1{} - -func (e *streamProtocolV1) stream(conn httpstream.Connection) error { - doneChan := make(chan struct{}, 2) - errorChan := make(chan error) - - cp := func(s string, dst io.Writer, src io.Reader) { - glog.V(6).Infof("Copying %s", s) - defer glog.V(6).Infof("Done copying %s", s) - if _, err := io.Copy(dst, src); err != nil && err != io.EOF { - glog.Errorf("Error copying %s: %v", s, err) - } - if s == api.StreamTypeStdout || s == api.StreamTypeStderr { - doneChan <- struct{}{} - } - } - - var ( - err error - errorStream, remoteStdin, remoteStdout, remoteStderr httpstream.Stream - ) - - // set up all the streams first - headers := http.Header{} - headers.Set(api.StreamType, api.StreamTypeError) - errorStream, err = conn.CreateStream(headers) - if err != nil { - return err - } - defer errorStream.Reset() - - // Create all the streams first, then start the copy goroutines. The server doesn't start its copy - // goroutines until it's received all of the streams. If the client creates the stdin stream and - // immediately begins copying stdin data to the server, it's possible to overwhelm and wedge the - // spdy frame handler in the server so that it is full of unprocessed frames. The frames aren't - // getting processed because the server hasn't started its copying, and it won't do that until it - // gets all the streams. By creating all the streams first, we ensure that the server is ready to - // process data before the client starts sending any. See https://issues.k8s.io/16373 for more info. - if e.stdin != nil { - headers.Set(api.StreamType, api.StreamTypeStdin) - remoteStdin, err = conn.CreateStream(headers) - if err != nil { - return err - } - defer remoteStdin.Reset() - } - - if e.stdout != nil { - headers.Set(api.StreamType, api.StreamTypeStdout) - remoteStdout, err = conn.CreateStream(headers) - if err != nil { - return err - } - defer remoteStdout.Reset() - } - - if e.stderr != nil && !e.tty { - headers.Set(api.StreamType, api.StreamTypeStderr) - remoteStderr, err = conn.CreateStream(headers) - if err != nil { - return err - } - defer remoteStderr.Reset() - } - - // now that all the streams have been created, proceed with reading & copying - - // always read from errorStream - go func() { - message, err := ioutil.ReadAll(errorStream) - if err != nil && err != io.EOF { - errorChan <- fmt.Errorf("Error reading from error stream: %s", err) - return - } - if len(message) > 0 { - errorChan <- fmt.Errorf("Error executing remote command: %s", message) - return - } - }() - - if e.stdin != nil { - // TODO this goroutine will never exit cleanly (the io.Copy never unblocks) - // because stdin is not closed until the process exits. If we try to call - // stdin.Close(), it returns no error but doesn't unblock the copy. It will - // exit when the process exits, instead. - go cp(api.StreamTypeStdin, remoteStdin, e.stdin) - } - - waitCount := 0 - completedStreams := 0 - - if e.stdout != nil { - waitCount++ - go cp(api.StreamTypeStdout, e.stdout, remoteStdout) - } - - if e.stderr != nil && !e.tty { - waitCount++ - go cp(api.StreamTypeStderr, e.stderr, remoteStderr) - } - -Loop: - for { - select { - case <-doneChan: - completedStreams++ - if completedStreams == waitCount { - break Loop - } - case err := <-errorChan: - return err - } - } - - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/v2.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/v2.go deleted file mode 100644 index 67e8637cf..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/remotecommand/v2.go +++ /dev/null @@ -1,166 +0,0 @@ -/* -Copyright 2015 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 remotecommand - -import ( - "fmt" - "io" - "io/ioutil" - "net/http" - "sync" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/util/httpstream" - "k8s.io/kubernetes/pkg/util/runtime" -) - -// streamProtocolV2 implements version 2 of the streaming protocol for attach -// and exec. The original streaming protocol was unversioned. As a result, this -// version is referred to as version 2, even though it is the first actual -// numbered version. -type streamProtocolV2 struct { - stdin io.Reader - stdout io.Writer - stderr io.Writer - tty bool -} - -var _ streamProtocolHandler = &streamProtocolV2{} - -func (e *streamProtocolV2) stream(conn httpstream.Connection) error { - var ( - err error - errorStream, remoteStdin, remoteStdout, remoteStderr httpstream.Stream - ) - - headers := http.Header{} - - // set up all the streams first - // set up error stream - errorChan := make(chan error) - headers.Set(api.StreamType, api.StreamTypeError) - errorStream, err = conn.CreateStream(headers) - if err != nil { - return err - } - - // set up stdin stream - if e.stdin != nil { - headers.Set(api.StreamType, api.StreamTypeStdin) - remoteStdin, err = conn.CreateStream(headers) - if err != nil { - return err - } - } - - // set up stdout stream - if e.stdout != nil { - headers.Set(api.StreamType, api.StreamTypeStdout) - remoteStdout, err = conn.CreateStream(headers) - if err != nil { - return err - } - } - - // set up stderr stream - if e.stderr != nil && !e.tty { - headers.Set(api.StreamType, api.StreamTypeStderr) - remoteStderr, err = conn.CreateStream(headers) - if err != nil { - return err - } - } - - // now that all the streams have been created, proceed with reading & copying - - // always read from errorStream - go func() { - message, err := ioutil.ReadAll(errorStream) - switch { - case err != nil && err != io.EOF: - errorChan <- fmt.Errorf("error reading from error stream: %s", err) - case len(message) > 0: - errorChan <- fmt.Errorf("error executing remote command: %s", message) - default: - errorChan <- nil - } - close(errorChan) - }() - - var wg sync.WaitGroup - var once sync.Once - - if e.stdin != nil { - // copy from client's stdin to container's stdin - go func() { - // if e.stdin is noninteractive, e.g. `echo abc | kubectl exec -i -- cat`, make sure - // we close remoteStdin as soon as the copy from e.stdin to remoteStdin finishes. Otherwise - // the executed command will remain running. - defer once.Do(func() { remoteStdin.Close() }) - - if _, err := io.Copy(remoteStdin, e.stdin); err != nil { - runtime.HandleError(err) - } - }() - - // read from remoteStdin until the stream is closed. this is essential to - // be able to exit interactive sessions cleanly and not leak goroutines or - // hang the client's terminal. - // - // go-dockerclient's current hijack implementation - // (https://github.com/fsouza/go-dockerclient/blob/89f3d56d93788dfe85f864a44f85d9738fca0670/client.go#L564) - // waits for all three streams (stdin/stdout/stderr) to finish copying - // before returning. When hijack finishes copying stdout/stderr, it calls - // Close() on its side of remoteStdin, which allows this copy to complete. - // When that happens, we must Close() on our side of remoteStdin, to - // allow the copy in hijack to complete, and hijack to return. - go func() { - defer once.Do(func() { remoteStdin.Close() }) - // this "copy" doesn't actually read anything - it's just here to wait for - // the server to close remoteStdin. - if _, err := io.Copy(ioutil.Discard, remoteStdin); err != nil { - runtime.HandleError(err) - } - }() - } - - if e.stdout != nil { - wg.Add(1) - go func() { - defer wg.Done() - if _, err := io.Copy(e.stdout, remoteStdout); err != nil { - runtime.HandleError(err) - } - }() - } - - if e.stderr != nil && !e.tty { - wg.Add(1) - go func() { - defer wg.Done() - if _, err := io.Copy(e.stderr, remoteStderr); err != nil { - runtime.HandleError(err) - } - }() - } - - // we're waiting for stdout/stderr to finish copying - wg.Wait() - - // waits for errorStream to finish reading with an error or nil - return <-errorChan -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/replica_sets_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/replica_sets_test.go deleted file mode 100644 index 960baae04..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/replica_sets_test.go +++ /dev/null @@ -1,197 +0,0 @@ -/* -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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/apis/extensions" -) - -func getReplicaSetResourceName() string { - return "replicasets" -} - -func TestListReplicaSets(t *testing.T) { - ns := api.NamespaceAll - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Extensions.ResourcePath(getReplicaSetResourceName(), ns, ""), - }, - Response: simple.Response{StatusCode: 200, - Body: &extensions.ReplicaSetList{ - Items: []extensions.ReplicaSet{ - { - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.ReplicaSetSpec{ - Replicas: 2, - Template: &api.PodTemplateSpec{}, - }, - }, - }, - }, - }, - } - receivedRSList, err := c.Setup(t).Extensions().ReplicaSets(ns).List(api.ListOptions{}) - c.Validate(t, receivedRSList, err) -} - -func TestGetReplicaSet(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "GET", Path: testapi.Extensions.ResourcePath(getReplicaSetResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.ReplicaSetSpec{ - Replicas: 2, - Template: &api.PodTemplateSpec{}, - }, - }, - }, - } - receivedRS, err := c.Setup(t).Extensions().ReplicaSets(ns).Get("foo") - c.Validate(t, receivedRS, err) -} - -func TestGetReplicaSetWithNoName(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{Error: true} - receivedPod, err := c.Setup(t).Extensions().ReplicaSets(ns).Get("") - if (err != nil) && (err.Error() != simple.NameRequiredError) { - t.Errorf("Expected error: %v, but got %v", simple.NameRequiredError, err) - } - - c.Validate(t, receivedPod, err) -} - -func TestUpdateReplicaSet(t *testing.T) { - ns := api.NamespaceDefault - requestRS := &extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Extensions.ResourcePath(getReplicaSetResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.ReplicaSetSpec{ - Replicas: 2, - Template: &api.PodTemplateSpec{}, - }, - }, - }, - } - receivedRS, err := c.Setup(t).Extensions().ReplicaSets(ns).Update(requestRS) - c.Validate(t, receivedRS, err) -} - -func TestUpdateStatusReplicaSet(t *testing.T) { - ns := api.NamespaceDefault - requestRS := &extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Extensions.ResourcePath(getReplicaSetResourceName(), ns, "foo") + "/status", Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.ReplicaSetSpec{ - Replicas: 2, - Template: &api.PodTemplateSpec{}, - }, - Status: extensions.ReplicaSetStatus{ - Replicas: 2, - }, - }, - }, - } - receivedRS, err := c.Setup(t).Extensions().ReplicaSets(ns).UpdateStatus(requestRS) - c.Validate(t, receivedRS, err) -} -func TestDeleteReplicaSet(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: testapi.Extensions.ResourcePath(getReplicaSetResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).Extensions().ReplicaSets(ns).Delete("foo", nil) - c.Validate(t, nil, err) -} - -func TestCreateReplicaSet(t *testing.T) { - ns := api.NamespaceDefault - requestRS := &extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - } - c := &simple.Client{ - Request: simple.Request{Method: "POST", Path: testapi.Extensions.ResourcePath(getReplicaSetResourceName(), ns, ""), Body: requestRS, Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: extensions.ReplicaSetSpec{ - Replicas: 2, - Template: &api.PodTemplateSpec{}, - }, - }, - }, - } - receivedRS, err := c.Setup(t).Extensions().ReplicaSets(ns).Create(requestRS) - c.Validate(t, receivedRS, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/replication_controllers_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/replication_controllers_test.go deleted file mode 100644 index 151423b92..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/replication_controllers_test.go +++ /dev/null @@ -1,204 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" -) - -func getRCResourceName() string { - return "replicationcontrollers" -} - -func TestListControllers(t *testing.T) { - ns := api.NamespaceAll - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath(getRCResourceName(), ns, ""), - }, - Response: simple.Response{StatusCode: 200, - Body: &api.ReplicationControllerList{ - Items: []api.ReplicationController{ - { - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 2, - Template: &api.PodTemplateSpec{}, - }, - }, - }, - }, - }, - } - receivedControllerList, err := c.Setup(t).ReplicationControllers(ns).List(api.ListOptions{}) - defer c.Close() - c.Validate(t, receivedControllerList, err) - -} - -func TestGetController(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "GET", Path: testapi.Default.ResourcePath(getRCResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 2, - Template: &api.PodTemplateSpec{}, - }, - }, - }, - } - receivedController, err := c.Setup(t).ReplicationControllers(ns).Get("foo") - defer c.Close() - c.Validate(t, receivedController, err) -} - -func TestGetControllerWithNoName(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{Error: true} - receivedPod, err := c.Setup(t).ReplicationControllers(ns).Get("") - defer c.Close() - if (err != nil) && (err.Error() != simple.NameRequiredError) { - t.Errorf("Expected error: %v, but got %v", simple.NameRequiredError, err) - } - - c.Validate(t, receivedPod, err) -} - -func TestUpdateController(t *testing.T) { - ns := api.NamespaceDefault - requestController := &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Default.ResourcePath(getRCResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 2, - Template: &api.PodTemplateSpec{}, - }, - }, - }, - } - receivedController, err := c.Setup(t).ReplicationControllers(ns).Update(requestController) - defer c.Close() - c.Validate(t, receivedController, err) -} - -func TestUpdateStatusController(t *testing.T) { - ns := api.NamespaceDefault - requestController := &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Default.ResourcePath(getRCResourceName(), ns, "foo") + "/status", Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 2, - Template: &api.PodTemplateSpec{}, - }, - Status: api.ReplicationControllerStatus{ - Replicas: 2, - }, - }, - }, - } - receivedController, err := c.Setup(t).ReplicationControllers(ns).UpdateStatus(requestController) - defer c.Close() - c.Validate(t, receivedController, err) -} -func TestDeleteController(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: testapi.Default.ResourcePath(getRCResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).ReplicationControllers(ns).Delete("foo") - defer c.Close() - c.Validate(t, nil, err) -} - -func TestCreateController(t *testing.T) { - ns := api.NamespaceDefault - requestController := &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - } - c := &simple.Client{ - Request: simple.Request{Method: "POST", Path: testapi.Default.ResourcePath(getRCResourceName(), ns, ""), Body: requestController, Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 2, - Template: &api.PodTemplateSpec{}, - }, - }, - }, - } - receivedController, err := c.Setup(t).ReplicationControllers(ns).Create(requestController) - defer c.Close() - c.Validate(t, receivedController, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/request_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/request_test.go deleted file mode 100644 index 266be6b24..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/request_test.go +++ /dev/null @@ -1,1313 +0,0 @@ -/* -Copyright 2014 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 unversioned - -import ( - "bytes" - "errors" - "io" - "io/ioutil" - "net/http" - "net/http/httptest" - "net/url" - "os" - "reflect" - "strings" - "testing" - "time" - - "k8s.io/kubernetes/pkg/api" - apierrors "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/v1" - "k8s.io/kubernetes/pkg/labels" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util" - "k8s.io/kubernetes/pkg/util/httpstream" - "k8s.io/kubernetes/pkg/util/intstr" - utiltesting "k8s.io/kubernetes/pkg/util/testing" - "k8s.io/kubernetes/pkg/watch" - watchjson "k8s.io/kubernetes/pkg/watch/json" -) - -func TestNewRequestSetsAccept(t *testing.T) { - r := NewRequest(nil, "get", &url.URL{Path: "/path/"}, "", ContentConfig{}, nil, nil) - if r.headers.Get("Accept") != "" { - t.Errorf("unexpected headers: %#v", r.headers) - } - r = NewRequest(nil, "get", &url.URL{Path: "/path/"}, "", ContentConfig{ContentType: "application/other"}, nil, nil) - if r.headers.Get("Accept") != "application/other, */*" { - t.Errorf("unexpected headers: %#v", r.headers) - } -} - -func TestRequestWithErrorWontChange(t *testing.T) { - original := Request{ - err: errors.New("test"), - content: ContentConfig{GroupVersion: testapi.Default.GroupVersion()}, - } - r := original - changed := r.Param("foo", "bar"). - LabelsSelectorParam(labels.Set{"a": "b"}.AsSelector()). - UintParam("uint", 1). - AbsPath("/abs"). - Prefix("test"). - Suffix("testing"). - Namespace("new"). - Resource("foos"). - Name("bars"). - Body("foo"). - Timeout(time.Millisecond) - if changed != &r { - t.Errorf("returned request should point to the same object") - } - if !reflect.DeepEqual(changed, &original) { - t.Errorf("expected %#v, got %#v", &original, changed) - } -} - -func TestRequestPreservesBaseTrailingSlash(t *testing.T) { - r := &Request{baseURL: &url.URL{}, pathPrefix: "/path/"} - if s := r.URL().String(); s != "/path/" { - t.Errorf("trailing slash should be preserved: %s", s) - } -} - -func TestRequestAbsPathPreservesTrailingSlash(t *testing.T) { - r := (&Request{baseURL: &url.URL{}}).AbsPath("/foo/") - if s := r.URL().String(); s != "/foo/" { - t.Errorf("trailing slash should be preserved: %s", s) - } - - r = (&Request{baseURL: &url.URL{}}).AbsPath("/foo/") - if s := r.URL().String(); s != "/foo/" { - t.Errorf("trailing slash should be preserved: %s", s) - } -} - -func TestRequestAbsPathJoins(t *testing.T) { - r := (&Request{baseURL: &url.URL{}}).AbsPath("foo/bar", "baz") - if s := r.URL().String(); s != "foo/bar/baz" { - t.Errorf("trailing slash should be preserved: %s", s) - } -} - -func TestRequestSetsNamespace(t *testing.T) { - r := (&Request{ - baseURL: &url.URL{ - Path: "/", - }, - }).Namespace("foo") - if r.namespace == "" { - t.Errorf("namespace should be set: %#v", r) - } - - if s := r.URL().String(); s != "namespaces/foo" { - t.Errorf("namespace should be in path: %s", s) - } -} - -func TestRequestOrdersNamespaceInPath(t *testing.T) { - r := (&Request{ - baseURL: &url.URL{}, - pathPrefix: "/test/", - }).Name("bar").Resource("baz").Namespace("foo") - if s := r.URL().String(); s != "/test/namespaces/foo/baz/bar" { - t.Errorf("namespace should be in order in path: %s", s) - } -} - -func TestRequestOrdersSubResource(t *testing.T) { - r := (&Request{ - baseURL: &url.URL{}, - pathPrefix: "/test/", - }).Name("bar").Resource("baz").Namespace("foo").Suffix("test").SubResource("a", "b") - if s := r.URL().String(); s != "/test/namespaces/foo/baz/bar/a/b/test" { - t.Errorf("namespace should be in order in path: %s", s) - } -} - -func TestRequestSetTwiceError(t *testing.T) { - if (&Request{}).Name("bar").Name("baz").err == nil { - t.Errorf("setting name twice should result in error") - } - if (&Request{}).Namespace("bar").Namespace("baz").err == nil { - t.Errorf("setting namespace twice should result in error") - } - if (&Request{}).Resource("bar").Resource("baz").err == nil { - t.Errorf("setting resource twice should result in error") - } - if (&Request{}).SubResource("bar").SubResource("baz").err == nil { - t.Errorf("setting subresource twice should result in error") - } -} - -func TestInvalidSegments(t *testing.T) { - invalidSegments := []string{".", "..", "test/segment", "test%2bsegment"} - setters := map[string]func(string, *Request){ - "namespace": func(s string, r *Request) { r.Namespace(s) }, - "resource": func(s string, r *Request) { r.Resource(s) }, - "name": func(s string, r *Request) { r.Name(s) }, - "subresource": func(s string, r *Request) { r.SubResource(s) }, - } - for _, invalidSegment := range invalidSegments { - for setterName, setter := range setters { - r := &Request{} - setter(invalidSegment, r) - if r.err == nil { - t.Errorf("%s: %s: expected error, got none", setterName, invalidSegment) - } - } - } -} - -func TestRequestParam(t *testing.T) { - r := (&Request{}).Param("foo", "a") - if !reflect.DeepEqual(r.params, url.Values{"foo": []string{"a"}}) { - t.Errorf("should have set a param: %#v", r) - } - - r.Param("bar", "1") - r.Param("bar", "2") - if !reflect.DeepEqual(r.params, url.Values{"foo": []string{"a"}, "bar": []string{"1", "2"}}) { - t.Errorf("should have set a param: %#v", r) - } -} - -func TestRequestVersionedParams(t *testing.T) { - r := (&Request{content: ContentConfig{GroupVersion: &v1.SchemeGroupVersion}}).Param("foo", "a") - if !reflect.DeepEqual(r.params, url.Values{"foo": []string{"a"}}) { - t.Errorf("should have set a param: %#v", r) - } - r.VersionedParams(&api.PodLogOptions{Follow: true, Container: "bar"}, api.ParameterCodec) - - if !reflect.DeepEqual(r.params, url.Values{ - "foo": []string{"a"}, - "container": []string{"bar"}, - "follow": []string{"true"}, - }) { - t.Errorf("should have set a param: %#v", r) - } -} - -func TestRequestVersionedParamsFromListOptions(t *testing.T) { - r := &Request{content: ContentConfig{GroupVersion: &v1.SchemeGroupVersion}} - r.VersionedParams(&api.ListOptions{ResourceVersion: "1"}, api.ParameterCodec) - if !reflect.DeepEqual(r.params, url.Values{ - "resourceVersion": []string{"1"}, - }) { - t.Errorf("should have set a param: %#v", r) - } - - var timeout int64 = 10 - r.VersionedParams(&api.ListOptions{ResourceVersion: "2", TimeoutSeconds: &timeout}, api.ParameterCodec) - if !reflect.DeepEqual(r.params, url.Values{ - "resourceVersion": []string{"1", "2"}, - "timeoutSeconds": []string{"10"}, - }) { - t.Errorf("should have set a param: %#v", r) - } -} - -func TestRequestURI(t *testing.T) { - r := (&Request{}).Param("foo", "a") - r.Prefix("other") - r.RequestURI("/test?foo=b&a=b&c=1&c=2") - if r.pathPrefix != "/test" { - t.Errorf("path is wrong: %#v", r) - } - if !reflect.DeepEqual(r.params, url.Values{"a": []string{"b"}, "foo": []string{"b"}, "c": []string{"1", "2"}}) { - t.Errorf("should have set a param: %#v", r) - } -} - -type NotAnAPIObject struct{} - -func (obj NotAnAPIObject) GroupVersionKind() *unversioned.GroupVersionKind { return nil } -func (obj NotAnAPIObject) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) {} - -func TestRequestBody(t *testing.T) { - // test unknown type - r := (&Request{}).Body([]string{"test"}) - if r.err == nil || r.body != nil { - t.Errorf("should have set err and left body nil: %#v", r) - } - - // test error set when failing to read file - f, err := ioutil.TempFile("", "test") - if err != nil { - t.Fatalf("unable to create temp file") - } - defer f.Close() - os.Remove(f.Name()) - r = (&Request{}).Body(f.Name()) - if r.err == nil || r.body != nil { - t.Errorf("should have set err and left body nil: %#v", r) - } - - // test unencodable api object - r = (&Request{content: ContentConfig{Codec: testapi.Default.Codec()}}).Body(&NotAnAPIObject{}) - if r.err == nil || r.body != nil { - t.Errorf("should have set err and left body nil: %#v", r) - } -} - -func TestResultIntoWithErrReturnsErr(t *testing.T) { - res := Result{err: errors.New("test")} - if err := res.Into(&api.Pod{}); err != res.err { - t.Errorf("should have returned exact error from result") - } -} - -func TestURLTemplate(t *testing.T) { - uri, _ := url.Parse("http://localhost") - r := NewRequest(nil, "POST", uri, "", ContentConfig{GroupVersion: &unversioned.GroupVersion{Group: "test"}}, nil, nil) - r.Prefix("pre1").Resource("r1").Namespace("ns").Name("nm").Param("p0", "v0") - full := r.URL() - if full.String() != "http://localhost/pre1/namespaces/ns/r1/nm?p0=v0" { - t.Errorf("unexpected initial URL: %s", full) - } - actual := r.finalURLTemplate() - expected := "http://localhost/pre1/namespaces/%7Bnamespace%7D/r1/%7Bname%7D?p0=%7Bvalue%7D" - if actual != expected { - t.Errorf("unexpected URL template: %s %s", actual, expected) - } - if r.URL().String() != full.String() { - t.Errorf("creating URL template changed request: %s -> %s", full.String(), r.URL().String()) - } -} - -func TestTransformResponse(t *testing.T) { - invalid := []byte("aaaaa") - uri, _ := url.Parse("http://localhost") - testCases := []struct { - Response *http.Response - Data []byte - Created bool - Error bool - ErrFn func(err error) bool - }{ - {Response: &http.Response{StatusCode: 200}, Data: []byte{}}, - {Response: &http.Response{StatusCode: 201}, Data: []byte{}, Created: true}, - {Response: &http.Response{StatusCode: 199}, Error: true}, - {Response: &http.Response{StatusCode: 500}, Error: true}, - {Response: &http.Response{StatusCode: 422}, Error: true}, - {Response: &http.Response{StatusCode: 409}, Error: true}, - {Response: &http.Response{StatusCode: 404}, Error: true}, - {Response: &http.Response{StatusCode: 401}, Error: true}, - { - Response: &http.Response{ - StatusCode: 401, - Header: http.Header{"Content-Type": []string{"application/json"}}, - Body: ioutil.NopCloser(bytes.NewReader(invalid)), - }, - Error: true, - ErrFn: func(err error) bool { - return err.Error() != "aaaaa" && apierrors.IsUnauthorized(err) - }, - }, - { - Response: &http.Response{ - StatusCode: 401, - Header: http.Header{"Content-Type": []string{"text/any"}}, - Body: ioutil.NopCloser(bytes.NewReader(invalid)), - }, - Error: true, - ErrFn: func(err error) bool { - return strings.Contains(err.Error(), "server has asked for the client to provide") && apierrors.IsUnauthorized(err) - }, - }, - {Response: &http.Response{StatusCode: 403}, Error: true}, - {Response: &http.Response{StatusCode: 200, Body: ioutil.NopCloser(bytes.NewReader(invalid))}, Data: invalid}, - {Response: &http.Response{StatusCode: 200, Body: ioutil.NopCloser(bytes.NewReader(invalid))}, Data: invalid}, - } - for i, test := range testCases { - r := NewRequest(nil, "", uri, "", ContentConfig{GroupVersion: testapi.Default.GroupVersion(), Codec: testapi.Default.Codec()}, nil, nil) - if test.Response.Body == nil { - test.Response.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) - } - result := r.transformResponse(test.Response, &http.Request{}) - response, created, err := result.body, result.statusCode == http.StatusCreated, result.err - hasErr := err != nil - if hasErr != test.Error { - t.Errorf("%d: unexpected error: %t %v", i, test.Error, err) - } else if hasErr && test.Response.StatusCode > 399 { - status, ok := err.(apierrors.APIStatus) - if !ok { - t.Errorf("%d: response should have been transformable into APIStatus: %v", i, err) - continue - } - if int(status.Status().Code) != test.Response.StatusCode { - t.Errorf("%d: status code did not match response: %#v", i, status.Status()) - } - } - if test.ErrFn != nil && !test.ErrFn(err) { - t.Errorf("%d: error function did not match: %v", i, err) - } - if !(test.Data == nil && response == nil) && !api.Semantic.DeepDerivative(test.Data, response) { - t.Errorf("%d: unexpected response: %#v %#v", i, test.Data, response) - } - if test.Created != created { - t.Errorf("%d: expected created %t, got %t", i, test.Created, created) - } - } -} - -func TestTransformUnstructuredError(t *testing.T) { - testCases := []struct { - Req *http.Request - Res *http.Response - - Resource string - Name string - - ErrFn func(error) bool - }{ - { - Resource: "foo", - Name: "bar", - Req: &http.Request{ - Method: "POST", - }, - Res: &http.Response{ - StatusCode: http.StatusConflict, - Body: ioutil.NopCloser(bytes.NewReader(nil)), - }, - ErrFn: apierrors.IsAlreadyExists, - }, - { - Resource: "foo", - Name: "bar", - Req: &http.Request{ - Method: "PUT", - }, - Res: &http.Response{ - StatusCode: http.StatusConflict, - Body: ioutil.NopCloser(bytes.NewReader(nil)), - }, - ErrFn: apierrors.IsConflict, - }, - { - Resource: "foo", - Name: "bar", - Req: &http.Request{}, - Res: &http.Response{ - StatusCode: http.StatusNotFound, - Body: ioutil.NopCloser(bytes.NewReader(nil)), - }, - ErrFn: apierrors.IsNotFound, - }, - { - Req: &http.Request{}, - Res: &http.Response{ - StatusCode: http.StatusBadRequest, - Body: ioutil.NopCloser(bytes.NewReader(nil)), - }, - ErrFn: apierrors.IsBadRequest, - }, - } - - for _, testCase := range testCases { - r := &Request{ - content: ContentConfig{GroupVersion: testapi.Default.GroupVersion(), Codec: testapi.Default.Codec()}, - resourceName: testCase.Name, - resource: testCase.Resource, - } - result := r.transformResponse(testCase.Res, testCase.Req) - err := result.err - if !testCase.ErrFn(err) { - t.Errorf("unexpected error: %v", err) - continue - } - if len(testCase.Name) != 0 && !strings.Contains(err.Error(), testCase.Name) { - t.Errorf("unexpected error string: %s", err) - } - if len(testCase.Resource) != 0 && !strings.Contains(err.Error(), testCase.Resource) { - t.Errorf("unexpected error string: %s", err) - } - } -} - -type clientFunc func(req *http.Request) (*http.Response, error) - -func (f clientFunc) Do(req *http.Request) (*http.Response, error) { - return f(req) -} - -func TestRequestWatch(t *testing.T) { - testCases := []struct { - Request *Request - Err bool - ErrFn func(error) bool - Empty bool - }{ - { - Request: &Request{err: errors.New("bail")}, - Err: true, - }, - { - Request: &Request{baseURL: &url.URL{}, pathPrefix: "%"}, - Err: true, - }, - { - Request: &Request{ - client: clientFunc(func(req *http.Request) (*http.Response, error) { - return nil, errors.New("err") - }), - baseURL: &url.URL{}, - }, - Err: true, - }, - { - Request: &Request{ - content: ContentConfig{GroupVersion: testapi.Default.GroupVersion(), Codec: testapi.Default.Codec()}, - client: clientFunc(func(req *http.Request) (*http.Response, error) { - return &http.Response{ - StatusCode: http.StatusForbidden, - Body: ioutil.NopCloser(bytes.NewReader([]byte{})), - }, nil - }), - baseURL: &url.URL{}, - }, - Err: true, - ErrFn: func(err error) bool { - return apierrors.IsForbidden(err) - }, - }, - { - Request: &Request{ - content: ContentConfig{GroupVersion: testapi.Default.GroupVersion(), Codec: testapi.Default.Codec()}, - client: clientFunc(func(req *http.Request) (*http.Response, error) { - return &http.Response{ - StatusCode: http.StatusUnauthorized, - Body: ioutil.NopCloser(bytes.NewReader([]byte{})), - }, nil - }), - baseURL: &url.URL{}, - }, - Err: true, - ErrFn: func(err error) bool { - return apierrors.IsUnauthorized(err) - }, - }, - { - Request: &Request{ - content: ContentConfig{GroupVersion: testapi.Default.GroupVersion(), Codec: testapi.Default.Codec()}, - client: clientFunc(func(req *http.Request) (*http.Response, error) { - return &http.Response{ - StatusCode: http.StatusUnauthorized, - Body: ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), &unversioned.Status{ - Status: unversioned.StatusFailure, - Reason: unversioned.StatusReasonUnauthorized, - })))), - }, nil - }), - baseURL: &url.URL{}, - }, - Err: true, - ErrFn: func(err error) bool { - return apierrors.IsUnauthorized(err) - }, - }, - { - Request: &Request{ - client: clientFunc(func(req *http.Request) (*http.Response, error) { - return nil, io.EOF - }), - baseURL: &url.URL{}, - }, - Empty: true, - }, - { - Request: &Request{ - client: clientFunc(func(req *http.Request) (*http.Response, error) { - return nil, &url.Error{Err: io.EOF} - }), - baseURL: &url.URL{}, - }, - Empty: true, - }, - { - Request: &Request{ - client: clientFunc(func(req *http.Request) (*http.Response, error) { - return nil, errors.New("http: can't write HTTP request on broken connection") - }), - baseURL: &url.URL{}, - }, - Empty: true, - }, - { - Request: &Request{ - client: clientFunc(func(req *http.Request) (*http.Response, error) { - return nil, errors.New("foo: connection reset by peer") - }), - baseURL: &url.URL{}, - }, - Empty: true, - }, - } - for i, testCase := range testCases { - t.Logf("testcase %v", testCase.Request) - testCase.Request.backoffMgr = &NoBackoff{} - watch, err := testCase.Request.Watch() - hasErr := err != nil - if hasErr != testCase.Err { - t.Errorf("%d: expected %t, got %t: %v", i, testCase.Err, hasErr, err) - continue - } - if testCase.ErrFn != nil && !testCase.ErrFn(err) { - t.Errorf("%d: error not valid: %v", i, err) - } - if hasErr && watch != nil { - t.Errorf("%d: watch should be nil when error is returned", i) - continue - } - if testCase.Empty { - _, ok := <-watch.ResultChan() - if ok { - t.Errorf("%d: expected the watch to be empty: %#v", i, watch) - } - } - } -} - -func TestRequestStream(t *testing.T) { - testCases := []struct { - Request *Request - Err bool - }{ - { - Request: &Request{err: errors.New("bail")}, - Err: true, - }, - { - Request: &Request{baseURL: &url.URL{}, pathPrefix: "%"}, - Err: true, - }, - { - Request: &Request{ - client: clientFunc(func(req *http.Request) (*http.Response, error) { - return nil, errors.New("err") - }), - baseURL: &url.URL{}, - }, - Err: true, - }, - { - Request: &Request{ - client: clientFunc(func(req *http.Request) (*http.Response, error) { - return &http.Response{ - StatusCode: http.StatusUnauthorized, - Body: ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), &unversioned.Status{ - Status: unversioned.StatusFailure, - Reason: unversioned.StatusReasonUnauthorized, - })))), - }, nil - }), - content: ContentConfig{Codec: testapi.Default.Codec()}, - baseURL: &url.URL{}, - }, - Err: true, - }, - } - for i, testCase := range testCases { - testCase.Request.backoffMgr = &NoBackoff{} - body, err := testCase.Request.Stream() - hasErr := err != nil - if hasErr != testCase.Err { - t.Errorf("%d: expected %t, got %t: %v", i, testCase.Err, hasErr, err) - } - if hasErr && body != nil { - t.Errorf("%d: body should be nil when error is returned", i) - } - } -} - -type fakeUpgradeConnection struct{} - -func (c *fakeUpgradeConnection) CreateStream(headers http.Header) (httpstream.Stream, error) { - return nil, nil -} -func (c *fakeUpgradeConnection) Close() error { - return nil -} -func (c *fakeUpgradeConnection) CloseChan() <-chan bool { - return make(chan bool) -} -func (c *fakeUpgradeConnection) SetIdleTimeout(timeout time.Duration) { -} - -type fakeUpgradeRoundTripper struct { - req *http.Request - conn httpstream.Connection -} - -func (f *fakeUpgradeRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - f.req = req - b := []byte{} - body := ioutil.NopCloser(bytes.NewReader(b)) - resp := &http.Response{ - StatusCode: 101, - Body: body, - } - return resp, nil -} - -func (f *fakeUpgradeRoundTripper) NewConnection(resp *http.Response) (httpstream.Connection, error) { - return f.conn, nil -} - -func TestRequestDo(t *testing.T) { - testCases := []struct { - Request *Request - Err bool - }{ - { - Request: &Request{err: errors.New("bail")}, - Err: true, - }, - { - Request: &Request{baseURL: &url.URL{}, pathPrefix: "%"}, - Err: true, - }, - { - Request: &Request{ - client: clientFunc(func(req *http.Request) (*http.Response, error) { - return nil, errors.New("err") - }), - baseURL: &url.URL{}, - }, - Err: true, - }, - } - for i, testCase := range testCases { - testCase.Request.backoffMgr = &NoBackoff{} - body, err := testCase.Request.Do().Raw() - hasErr := err != nil - if hasErr != testCase.Err { - t.Errorf("%d: expected %t, got %t: %v", i, testCase.Err, hasErr, err) - } - if hasErr && body != nil { - t.Errorf("%d: body should be nil when error is returned", i) - } - } -} - -func TestDoRequestNewWay(t *testing.T) { - reqBody := "request body" - expectedObj := &api.Service{Spec: api.ServiceSpec{Ports: []api.ServicePort{{ - Protocol: "TCP", - Port: 12345, - TargetPort: intstr.FromInt(12345), - }}}} - expectedBody, _ := runtime.Encode(testapi.Default.Codec(), expectedObj) - fakeHandler := utiltesting.FakeHandler{ - StatusCode: 200, - ResponseBody: string(expectedBody), - T: t, - } - testServer := httptest.NewServer(&fakeHandler) - // TODO: Uncomment when fix #19254 - // defer testServer.Close() - c := testRESTClient(t, testServer) - obj, err := c.Verb("POST"). - Prefix("foo", "bar"). - Suffix("baz"). - Timeout(time.Second). - Body([]byte(reqBody)). - Do().Get() - if err != nil { - t.Errorf("Unexpected error: %v %#v", err, err) - return - } - if obj == nil { - t.Error("nil obj") - } else if !api.Semantic.DeepDerivative(expectedObj, obj) { - t.Errorf("Expected: %#v, got %#v", expectedObj, obj) - } - requestURL := testapi.Default.ResourcePathWithPrefix("foo/bar", "", "", "baz") - requestURL += "?timeout=1s" - fakeHandler.ValidateRequest(t, requestURL, "POST", &reqBody) -} - -// This test assumes that the client implementation backs off exponentially, for an individual request. -func TestBackoffLifecycle(t *testing.T) { - count := 0 - testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - count++ - t.Logf("Attempt %d", count) - if count == 5 || count == 9 { - w.WriteHeader(http.StatusOK) - return - } else { - w.WriteHeader(http.StatusGatewayTimeout) - return - } - })) - // TODO: Uncomment when fix #19254 - // defer testServer.Close() - c := testRESTClient(t, testServer) - - // Test backoff recovery and increase. This correlates to the constants - // which are used in the server implementation returning StatusOK above. - seconds := []int{0, 1, 2, 4, 8, 0, 1, 2, 4, 0} - request := c.Verb("POST").Prefix("backofftest").Suffix("abc") - request.backoffMgr = &URLBackoff{ - Backoff: util.NewBackOff( - time.Duration(1)*time.Second, - time.Duration(200)*time.Second)} - for _, sec := range seconds { - start := time.Now() - request.DoRaw() - finish := time.Since(start) - t.Logf("%v finished in %v", sec, finish) - if finish < time.Duration(sec)*time.Second || finish >= time.Duration(sec+5)*time.Second { - t.Fatalf("%v not in range %v", finish, sec) - } - } -} - -func TestCheckRetryClosesBody(t *testing.T) { - count := 0 - ch := make(chan struct{}) - testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - count++ - t.Logf("attempt %d", count) - if count >= 5 { - w.WriteHeader(http.StatusOK) - close(ch) - return - } - w.Header().Set("Retry-After", "0") - w.WriteHeader(apierrors.StatusTooManyRequests) - })) - // TODO: Uncomment when fix #19254 - // defer testServer.Close() - - c := testRESTClient(t, testServer) - _, err := c.Verb("POST"). - Prefix("foo", "bar"). - Suffix("baz"). - Timeout(time.Second). - Body([]byte(strings.Repeat("abcd", 1000))). - DoRaw() - if err != nil { - t.Fatalf("Unexpected error: %v %#v", err, err) - } - <-ch - if count != 5 { - t.Errorf("unexpected retries: %d", count) - } -} - -func TestCheckRetryHandles429And5xx(t *testing.T) { - count := 0 - ch := make(chan struct{}) - testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - t.Logf("attempt %d", count) - if count >= 4 { - w.WriteHeader(http.StatusOK) - close(ch) - return - } - w.Header().Set("Retry-After", "0") - w.WriteHeader([]int{apierrors.StatusTooManyRequests, 500, 501, 504}[count]) - count++ - })) - // TODO: Uncomment when fix #19254 - // defer testServer.Close() - - c := testRESTClient(t, testServer) - _, err := c.Verb("POST"). - Prefix("foo", "bar"). - Suffix("baz"). - Timeout(time.Second). - Body([]byte(strings.Repeat("abcd", 1000))). - DoRaw() - if err != nil { - t.Fatalf("Unexpected error: %v %#v", err, err) - } - <-ch - if count != 4 { - t.Errorf("unexpected retries: %d", count) - } -} - -func BenchmarkCheckRetryClosesBody(b *testing.B) { - count := 0 - testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - count++ - if count%3 == 0 { - w.WriteHeader(http.StatusOK) - return - } - w.Header().Set("Retry-After", "0") - w.WriteHeader(apierrors.StatusTooManyRequests) - })) - // TODO: Uncomment when fix #19254 - // defer testServer.Close() - - c := testRESTClient(b, testServer) - r := c.Verb("POST"). - Prefix("foo", "bar"). - Suffix("baz"). - Timeout(time.Second). - Body([]byte(strings.Repeat("abcd", 1000))) - - for i := 0; i < b.N; i++ { - if _, err := r.DoRaw(); err != nil { - b.Fatalf("Unexpected error: %v %#v", err, err) - } - } -} - -func TestDoRequestNewWayReader(t *testing.T) { - reqObj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - reqBodyExpected, _ := runtime.Encode(testapi.Default.Codec(), reqObj) - expectedObj := &api.Service{Spec: api.ServiceSpec{Ports: []api.ServicePort{{ - Protocol: "TCP", - Port: 12345, - TargetPort: intstr.FromInt(12345), - }}}} - expectedBody, _ := runtime.Encode(testapi.Default.Codec(), expectedObj) - fakeHandler := utiltesting.FakeHandler{ - StatusCode: 200, - ResponseBody: string(expectedBody), - T: t, - } - testServer := httptest.NewServer(&fakeHandler) - // TODO: Uncomment when fix #19254 - // defer testServer.Close() - c := testRESTClient(t, testServer) - obj, err := c.Verb("POST"). - Resource("bar"). - Name("baz"). - Prefix("foo"). - LabelsSelectorParam(labels.Set{"name": "foo"}.AsSelector()). - Timeout(time.Second). - Body(bytes.NewBuffer(reqBodyExpected)). - Do().Get() - if err != nil { - t.Errorf("Unexpected error: %v %#v", err, err) - return - } - if obj == nil { - t.Error("nil obj") - } else if !api.Semantic.DeepDerivative(expectedObj, obj) { - t.Errorf("Expected: %#v, got %#v", expectedObj, obj) - } - tmpStr := string(reqBodyExpected) - requestURL := testapi.Default.ResourcePathWithPrefix("foo", "bar", "", "baz") - requestURL += "?" + unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String()) + "=name%3Dfoo&timeout=1s" - fakeHandler.ValidateRequest(t, requestURL, "POST", &tmpStr) -} - -func TestDoRequestNewWayObj(t *testing.T) { - reqObj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - reqBodyExpected, _ := runtime.Encode(testapi.Default.Codec(), reqObj) - expectedObj := &api.Service{Spec: api.ServiceSpec{Ports: []api.ServicePort{{ - Protocol: "TCP", - Port: 12345, - TargetPort: intstr.FromInt(12345), - }}}} - expectedBody, _ := runtime.Encode(testapi.Default.Codec(), expectedObj) - fakeHandler := utiltesting.FakeHandler{ - StatusCode: 200, - ResponseBody: string(expectedBody), - T: t, - } - testServer := httptest.NewServer(&fakeHandler) - // TODO: Uncomment when fix #19254 - // defer testServer.Close() - c := testRESTClient(t, testServer) - obj, err := c.Verb("POST"). - Suffix("baz"). - Name("bar"). - Resource("foo"). - LabelsSelectorParam(labels.Set{"name": "foo"}.AsSelector()). - Timeout(time.Second). - Body(reqObj). - Do().Get() - if err != nil { - t.Errorf("Unexpected error: %v %#v", err, err) - return - } - if obj == nil { - t.Error("nil obj") - } else if !api.Semantic.DeepDerivative(expectedObj, obj) { - t.Errorf("Expected: %#v, got %#v", expectedObj, obj) - } - tmpStr := string(reqBodyExpected) - requestURL := testapi.Default.ResourcePath("foo", "", "bar/baz") - requestURL += "?" + unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String()) + "=name%3Dfoo&timeout=1s" - fakeHandler.ValidateRequest(t, requestURL, "POST", &tmpStr) -} - -func TestDoRequestNewWayFile(t *testing.T) { - reqObj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - reqBodyExpected, err := runtime.Encode(testapi.Default.Codec(), reqObj) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - file, err := ioutil.TempFile("", "foo") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - defer file.Close() - - _, err = file.Write(reqBodyExpected) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - expectedObj := &api.Service{Spec: api.ServiceSpec{Ports: []api.ServicePort{{ - Protocol: "TCP", - Port: 12345, - TargetPort: intstr.FromInt(12345), - }}}} - expectedBody, _ := runtime.Encode(testapi.Default.Codec(), expectedObj) - fakeHandler := utiltesting.FakeHandler{ - StatusCode: 200, - ResponseBody: string(expectedBody), - T: t, - } - testServer := httptest.NewServer(&fakeHandler) - // TODO: Uncomment when fix #19254 - // defer testServer.Close() - c := testRESTClient(t, testServer) - wasCreated := true - obj, err := c.Verb("POST"). - Prefix("foo/bar", "baz"). - Timeout(time.Second). - Body(file.Name()). - Do().WasCreated(&wasCreated).Get() - if err != nil { - t.Errorf("Unexpected error: %v %#v", err, err) - return - } - if obj == nil { - t.Error("nil obj") - } else if !api.Semantic.DeepDerivative(expectedObj, obj) { - t.Errorf("Expected: %#v, got %#v", expectedObj, obj) - } - if wasCreated { - t.Errorf("expected object was not created") - } - tmpStr := string(reqBodyExpected) - requestURL := testapi.Default.ResourcePathWithPrefix("foo/bar/baz", "", "", "") - requestURL += "?timeout=1s" - fakeHandler.ValidateRequest(t, requestURL, "POST", &tmpStr) -} - -func TestWasCreated(t *testing.T) { - reqObj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - reqBodyExpected, err := runtime.Encode(testapi.Default.Codec(), reqObj) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - expectedObj := &api.Service{Spec: api.ServiceSpec{Ports: []api.ServicePort{{ - Protocol: "TCP", - Port: 12345, - TargetPort: intstr.FromInt(12345), - }}}} - expectedBody, _ := runtime.Encode(testapi.Default.Codec(), expectedObj) - fakeHandler := utiltesting.FakeHandler{ - StatusCode: 201, - ResponseBody: string(expectedBody), - T: t, - } - testServer := httptest.NewServer(&fakeHandler) - // TODO: Uncomment when fix #19254 - // defer testServer.Close() - c := testRESTClient(t, testServer) - wasCreated := false - obj, err := c.Verb("PUT"). - Prefix("foo/bar", "baz"). - Timeout(time.Second). - Body(reqBodyExpected). - Do().WasCreated(&wasCreated).Get() - if err != nil { - t.Errorf("Unexpected error: %v %#v", err, err) - return - } - if obj == nil { - t.Error("nil obj") - } else if !api.Semantic.DeepDerivative(expectedObj, obj) { - t.Errorf("Expected: %#v, got %#v", expectedObj, obj) - } - if !wasCreated { - t.Errorf("Expected object was created") - } - - tmpStr := string(reqBodyExpected) - requestURL := testapi.Default.ResourcePathWithPrefix("foo/bar/baz", "", "", "") - requestURL += "?timeout=1s" - fakeHandler.ValidateRequest(t, requestURL, "PUT", &tmpStr) -} - -func TestVerbs(t *testing.T) { - c := testRESTClient(t, nil) - if r := c.Post(); r.verb != "POST" { - t.Errorf("Post verb is wrong") - } - if r := c.Put(); r.verb != "PUT" { - t.Errorf("Put verb is wrong") - } - if r := c.Get(); r.verb != "GET" { - t.Errorf("Get verb is wrong") - } - if r := c.Delete(); r.verb != "DELETE" { - t.Errorf("Delete verb is wrong") - } -} - -func TestAbsPath(t *testing.T) { - for i, tc := range []struct { - configPrefix string - resourcePrefix string - absPath string - wantsAbsPath string - }{ - {"", "", "", "/"}, - {"", "", "/", "/"}, - {"", "", "/api", "/api"}, - {"", "", "/api/", "/api/"}, - {"", "", "/apis", "/apis"}, - {"", "/foo", "/bar/foo", "/bar/foo"}, - {"", "/api/foo/123", "/bar/foo", "/bar/foo"}, - {"/p1", "", "", "/p1"}, - {"/p1", "", "/", "/p1/"}, - {"/p1", "", "/api", "/p1/api"}, - {"/p1", "", "/apis", "/p1/apis"}, - {"/p1", "/r1", "/apis", "/p1/apis"}, - {"/p1", "/api/r1", "/apis", "/p1/apis"}, - {"/p1/api/p2", "", "", "/p1/api/p2"}, - {"/p1/api/p2", "", "/", "/p1/api/p2/"}, - {"/p1/api/p2", "", "/api", "/p1/api/p2/api"}, - {"/p1/api/p2", "", "/api/", "/p1/api/p2/api/"}, - {"/p1/api/p2", "/r1", "/api/", "/p1/api/p2/api/"}, - {"/p1/api/p2", "/api/r1", "/api/", "/p1/api/p2/api/"}, - } { - c := NewOrDie(&Config{Host: "http://localhost:123" + tc.configPrefix}) - r := c.Post().Prefix(tc.resourcePrefix).AbsPath(tc.absPath) - if r.pathPrefix != tc.wantsAbsPath { - t.Errorf("test case %d failed, unexpected path: %q, expected %q", i, r.pathPrefix, tc.wantsAbsPath) - } - } -} - -func TestUintParam(t *testing.T) { - table := []struct { - name string - testVal uint64 - expectStr string - }{ - {"foo", 31415, "http://localhost?foo=31415"}, - {"bar", 42, "http://localhost?bar=42"}, - {"baz", 0, "http://localhost?baz=0"}, - } - - for _, item := range table { - u, _ := url.Parse("http://localhost") - r := NewRequest(nil, "GET", u, "", ContentConfig{GroupVersion: &unversioned.GroupVersion{Group: "test"}}, nil, nil).AbsPath("").UintParam(item.name, item.testVal) - if e, a := item.expectStr, r.URL().String(); e != a { - t.Errorf("expected %v, got %v", e, a) - } - } -} - -func TestUnacceptableParamNames(t *testing.T) { - table := []struct { - name string - testVal string - expectSuccess bool - }{ - {"timeout", "42", false}, - } - - for _, item := range table { - c := testRESTClient(t, nil) - r := c.Get().setParam(item.name, item.testVal) - if e, a := item.expectSuccess, r.err == nil; e != a { - t.Errorf("expected %v, got %v (%v)", e, a, r.err) - } - } -} - -func TestBody(t *testing.T) { - const data = "test payload" - - obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - bodyExpected, _ := runtime.Encode(testapi.Default.Codec(), obj) - - f, err := ioutil.TempFile("", "test_body") - if err != nil { - t.Fatalf("TempFile error: %v", err) - } - if _, err := f.WriteString(data); err != nil { - t.Fatalf("TempFile.WriteString error: %v", err) - } - f.Close() - - var nilObject *api.DeleteOptions - typedObject := interface{}(nilObject) - c := testRESTClient(t, nil) - tests := []struct { - input interface{} - expected string - headers map[string]string - }{ - {[]byte(data), data, nil}, - {f.Name(), data, nil}, - {strings.NewReader(data), data, nil}, - {obj, string(bodyExpected), map[string]string{"Content-Type": "application/json"}}, - {typedObject, "", nil}, - } - for i, tt := range tests { - r := c.Post().Body(tt.input) - if r.err != nil { - t.Errorf("%d: r.Body(%#v) error: %v", i, tt, r.err) - continue - } - if tt.headers != nil { - for k, v := range tt.headers { - if r.headers.Get(k) != v { - t.Errorf("%d: r.headers[%q] = %q; want %q", i, k, v, v) - } - } - } - - if r.body == nil { - if len(tt.expected) != 0 { - t.Errorf("%d: r.body = %q; want %q", i, r.body, tt.expected) - } - continue - } - buf := make([]byte, len(tt.expected)) - if _, err := r.body.Read(buf); err != nil { - t.Errorf("%d: r.body.Read error: %v", i, err) - continue - } - body := string(buf) - if body != tt.expected { - t.Errorf("%d: r.body = %q; want %q", i, body, tt.expected) - } - } -} - -func TestWatch(t *testing.T) { - var table = []struct { - t watch.EventType - obj runtime.Object - }{ - {watch.Added, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "first"}}}, - {watch.Modified, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "second"}}}, - {watch.Deleted, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "last"}}}, - } - - testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - flusher, ok := w.(http.Flusher) - if !ok { - panic("need flusher!") - } - - w.Header().Set("Transfer-Encoding", "chunked") - w.WriteHeader(http.StatusOK) - flusher.Flush() - - encoder := watchjson.NewEncoder(w, testapi.Default.Codec()) - for _, item := range table { - if err := encoder.Encode(&watch.Event{Type: item.t, Object: item.obj}); err != nil { - panic(err) - } - flusher.Flush() - } - })) - // TODO: Uncomment when fix #19254 - // defer testServer.Close() - - s := testRESTClient(t, testServer) - watching, err := s.Get().Prefix("path/to/watch/thing").Watch() - if err != nil { - t.Fatalf("Unexpected error") - } - - for _, item := range table { - got, ok := <-watching.ResultChan() - if !ok { - t.Fatalf("Unexpected early close") - } - if e, a := item.t, got.Type; e != a { - t.Errorf("Expected %v, got %v", e, a) - } - if e, a := item.obj, got.Object; !api.Semantic.DeepDerivative(e, a) { - t.Errorf("Expected %v, got %v", e, a) - } - } - - _, ok := <-watching.ResultChan() - if ok { - t.Fatal("Unexpected non-close") - } -} - -func TestStream(t *testing.T) { - expectedBody := "expected body" - - testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - flusher, ok := w.(http.Flusher) - if !ok { - panic("need flusher!") - } - w.Header().Set("Transfer-Encoding", "chunked") - w.WriteHeader(http.StatusOK) - w.Write([]byte(expectedBody)) - flusher.Flush() - })) - // TODO: Uncomment when fix #19254 - // defer testServer.Close() - - s := testRESTClient(t, testServer) - readCloser, err := s.Get().Prefix("path/to/stream/thing").Stream() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - defer readCloser.Close() - buf := new(bytes.Buffer) - buf.ReadFrom(readCloser) - resultBody := buf.String() - - if expectedBody != resultBody { - t.Errorf("Expected %s, got %s", expectedBody, resultBody) - } -} - -func testRESTClient(t testing.TB, srv *httptest.Server) *RESTClient { - baseURL, _ := url.Parse("http://localhost") - if srv != nil { - var err error - baseURL, err = url.Parse(srv.URL) - if err != nil { - t.Fatalf("failed to parse test URL: %v", err) - } - } - versionedAPIPath := testapi.Default.ResourcePath("", "", "") - return NewRESTClient(baseURL, versionedAPIPath, ContentConfig{GroupVersion: testapi.Default.GroupVersion(), Codec: testapi.Default.Codec()}, 0, 0, nil) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/resource_quotas_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/resource_quotas_test.go deleted file mode 100644 index ff8abddce..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/resource_quotas_test.go +++ /dev/null @@ -1,208 +0,0 @@ -/* -Copyright 2014 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "net/url" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/testapi" -) - -func getResourceQuotasResoureName() string { - return "resourcequotas" -} - -func TestResourceQuotaCreate(t *testing.T) { - ns := api.NamespaceDefault - resourceQuota := &api.ResourceQuota{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: "foo", - }, - Spec: api.ResourceQuotaSpec{ - Hard: api.ResourceList{ - api.ResourceCPU: resource.MustParse("100"), - api.ResourceMemory: resource.MustParse("10000"), - api.ResourcePods: resource.MustParse("10"), - api.ResourceServices: resource.MustParse("10"), - api.ResourceReplicationControllers: resource.MustParse("10"), - api.ResourceQuotas: resource.MustParse("10"), - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: testapi.Default.ResourcePath(getResourceQuotasResoureName(), ns, ""), - Query: simple.BuildQueryValues(nil), - Body: resourceQuota, - }, - Response: simple.Response{StatusCode: 200, Body: resourceQuota}, - } - - response, err := c.Setup(t).ResourceQuotas(ns).Create(resourceQuota) - defer c.Close() - c.Validate(t, response, err) -} - -func TestResourceQuotaGet(t *testing.T) { - ns := api.NamespaceDefault - resourceQuota := &api.ResourceQuota{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: "foo", - }, - Spec: api.ResourceQuotaSpec{ - Hard: api.ResourceList{ - api.ResourceCPU: resource.MustParse("100"), - api.ResourceMemory: resource.MustParse("10000"), - api.ResourcePods: resource.MustParse("10"), - api.ResourceServices: resource.MustParse("10"), - api.ResourceReplicationControllers: resource.MustParse("10"), - api.ResourceQuotas: resource.MustParse("10"), - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath(getResourceQuotasResoureName(), ns, "abc"), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: resourceQuota}, - } - - response, err := c.Setup(t).ResourceQuotas(ns).Get("abc") - defer c.Close() - c.Validate(t, response, err) -} - -func TestResourceQuotaList(t *testing.T) { - ns := api.NamespaceDefault - - resourceQuotaList := &api.ResourceQuotaList{ - Items: []api.ResourceQuota{ - { - ObjectMeta: api.ObjectMeta{Name: "foo"}, - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath(getResourceQuotasResoureName(), ns, ""), - Query: simple.BuildQueryValues(nil), - Body: nil, - }, - Response: simple.Response{StatusCode: 200, Body: resourceQuotaList}, - } - response, err := c.Setup(t).ResourceQuotas(ns).List(api.ListOptions{}) - defer c.Close() - c.Validate(t, response, err) -} - -func TestResourceQuotaUpdate(t *testing.T) { - ns := api.NamespaceDefault - resourceQuota := &api.ResourceQuota{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: "foo", - ResourceVersion: "1", - }, - Spec: api.ResourceQuotaSpec{ - Hard: api.ResourceList{ - api.ResourceCPU: resource.MustParse("100"), - api.ResourceMemory: resource.MustParse("10000"), - api.ResourcePods: resource.MustParse("10"), - api.ResourceServices: resource.MustParse("10"), - api.ResourceReplicationControllers: resource.MustParse("10"), - api.ResourceQuotas: resource.MustParse("10"), - }, - }, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Default.ResourcePath(getResourceQuotasResoureName(), ns, "abc"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: resourceQuota}, - } - response, err := c.Setup(t).ResourceQuotas(ns).Update(resourceQuota) - defer c.Close() - c.Validate(t, response, err) -} - -func TestResourceQuotaStatusUpdate(t *testing.T) { - ns := api.NamespaceDefault - resourceQuota := &api.ResourceQuota{ - ObjectMeta: api.ObjectMeta{ - Name: "abc", - Namespace: "foo", - ResourceVersion: "1", - }, - Status: api.ResourceQuotaStatus{ - Hard: api.ResourceList{ - api.ResourceCPU: resource.MustParse("100"), - api.ResourceMemory: resource.MustParse("10000"), - api.ResourcePods: resource.MustParse("10"), - api.ResourceServices: resource.MustParse("10"), - api.ResourceReplicationControllers: resource.MustParse("10"), - api.ResourceQuotas: resource.MustParse("10"), - }, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "PUT", - Path: testapi.Default.ResourcePath(getResourceQuotasResoureName(), ns, "abc") + "/status", - Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: resourceQuota}, - } - response, err := c.Setup(t).ResourceQuotas(ns).UpdateStatus(resourceQuota) - defer c.Close() - c.Validate(t, response, err) -} - -func TestResourceQuotaDelete(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: testapi.Default.ResourcePath(getResourceQuotasResoureName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).ResourceQuotas(ns).Delete("foo") - defer c.Close() - c.Validate(t, nil, err) -} - -func TestResourceQuotaWatch(t *testing.T) { - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePathWithPrefix("watch", getResourceQuotasResoureName(), "", ""), - Query: url.Values{"resourceVersion": []string{}}}, - Response: simple.Response{StatusCode: 200}, - } - _, err := c.Setup(t).ResourceQuotas(api.NamespaceAll).Watch(api.ListOptions{}) - defer c.Close() - c.Validate(t, nil, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/restclient_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/restclient_test.go deleted file mode 100644 index 912611f4a..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/restclient_test.go +++ /dev/null @@ -1,196 +0,0 @@ -/* -Copyright 2014 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 unversioned - -import ( - "net/http" - "net/http/httptest" - "net/url" - "os" - "reflect" - "testing" - "time" - - "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util" - utiltesting "k8s.io/kubernetes/pkg/util/testing" -) - -func TestDoRequestSuccess(t *testing.T) { - status := &unversioned.Status{Status: unversioned.StatusSuccess} - expectedBody, _ := runtime.Encode(testapi.Default.Codec(), status) - fakeHandler := utiltesting.FakeHandler{ - StatusCode: 200, - ResponseBody: string(expectedBody), - T: t, - } - testServer := httptest.NewServer(&fakeHandler) - // TODO: Uncomment when fix #19254 - // defer testServer.Close() - c, err := RESTClientFor(&Config{ - Host: testServer.URL, - ContentConfig: ContentConfig{ - GroupVersion: testapi.Default.GroupVersion(), - Codec: testapi.Default.Codec(), - }, - Username: "user", - Password: "pass", - }) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - body, err := c.Get().Prefix("test").Do().Raw() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if fakeHandler.RequestReceived.Header["Authorization"] == nil { - t.Errorf("Request is missing authorization header: %#v", fakeHandler.RequestReceived) - } - statusOut, err := runtime.Decode(testapi.Default.Codec(), body) - if err != nil { - t.Errorf("Unexpected error %#v", err) - } - if !reflect.DeepEqual(status, statusOut) { - t.Errorf("Unexpected mis-match. Expected %#v. Saw %#v", status, statusOut) - } - fakeHandler.ValidateRequest(t, "/"+testapi.Default.GroupVersion().String()+"/test", "GET", nil) -} - -func TestDoRequestFailed(t *testing.T) { - status := &unversioned.Status{ - Code: http.StatusNotFound, - Status: unversioned.StatusFailure, - Reason: unversioned.StatusReasonNotFound, - Message: " \"\" not found", - Details: &unversioned.StatusDetails{}, - } - expectedBody, _ := runtime.Encode(testapi.Default.Codec(), status) - fakeHandler := utiltesting.FakeHandler{ - StatusCode: 404, - ResponseBody: string(expectedBody), - T: t, - } - testServer := httptest.NewServer(&fakeHandler) - // TODO: Uncomment when fix #19254 - // defer testServer.Close() - c, err := RESTClientFor(&Config{ - Host: testServer.URL, - ContentConfig: ContentConfig{ - GroupVersion: testapi.Default.GroupVersion(), - Codec: testapi.Default.Codec(), - }, - }) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - body, err := c.Get().Do().Raw() - if err == nil || body != nil { - t.Errorf("unexpected non-error: %#v", body) - } - ss, ok := err.(errors.APIStatus) - if !ok { - t.Errorf("unexpected error type %v", err) - } - actual := ss.Status() - expected := *status - // The decoder will apply the default Version and Kind to the Status. - expected.APIVersion = "v1" - expected.Kind = "Status" - if !reflect.DeepEqual(&expected, &actual) { - t.Errorf("Unexpected mis-match: %s", util.ObjectDiff(status, &actual)) - } -} - -func TestDoRequestCreated(t *testing.T) { - status := &unversioned.Status{Status: unversioned.StatusSuccess} - expectedBody, _ := runtime.Encode(testapi.Default.Codec(), status) - fakeHandler := utiltesting.FakeHandler{ - StatusCode: 201, - ResponseBody: string(expectedBody), - T: t, - } - testServer := httptest.NewServer(&fakeHandler) - // TODO: Uncomment when fix #19254 - // defer testServer.Close() - c, err := RESTClientFor(&Config{ - Host: testServer.URL, - ContentConfig: ContentConfig{ - GroupVersion: testapi.Default.GroupVersion(), - Codec: testapi.Default.Codec(), - }, - Username: "user", - Password: "pass", - }) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - created := false - body, err := c.Get().Prefix("test").Do().WasCreated(&created).Raw() - if err != nil { - t.Errorf("Unexpected error %#v", err) - } - if !created { - t.Errorf("Expected object to be created") - } - statusOut, err := runtime.Decode(testapi.Default.Codec(), body) - if err != nil { - t.Errorf("Unexpected error %#v", err) - } - if !reflect.DeepEqual(status, statusOut) { - t.Errorf("Unexpected mis-match. Expected %#v. Saw %#v", status, statusOut) - } - fakeHandler.ValidateRequest(t, "/"+testapi.Default.GroupVersion().String()+"/test", "GET", nil) -} - -func TestCreateBackoffManager(t *testing.T) { - - theUrl, _ := url.Parse("http://localhost") - - // 1 second base backoff + duration of 2 seconds -> exponential backoff for requests. - os.Setenv(envBackoffBase, "1") - os.Setenv(envBackoffDuration, "2") - backoff := readExpBackoffConfig() - backoff.UpdateBackoff(theUrl, nil, 500) - backoff.UpdateBackoff(theUrl, nil, 500) - if backoff.CalculateBackoff(theUrl)/time.Second != 2 { - t.Errorf("Backoff env not working.") - } - - // 0 duration -> no backoff. - os.Setenv(envBackoffBase, "1") - os.Setenv(envBackoffDuration, "0") - backoff.UpdateBackoff(theUrl, nil, 500) - backoff.UpdateBackoff(theUrl, nil, 500) - backoff = readExpBackoffConfig() - if backoff.CalculateBackoff(theUrl)/time.Second != 0 { - t.Errorf("Zero backoff duration, but backoff still occuring.") - } - - // No env -> No backoff. - os.Setenv(envBackoffBase, "") - os.Setenv(envBackoffDuration, "") - backoff = readExpBackoffConfig() - backoff.UpdateBackoff(theUrl, nil, 500) - backoff.UpdateBackoff(theUrl, nil, 500) - if backoff.CalculateBackoff(theUrl)/time.Second != 0 { - t.Errorf("Backoff should have been 0.") - } - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/services.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/services.go index 159187dbd..8b40a5d04 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/services.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/services.go @@ -18,6 +18,7 @@ package unversioned import ( "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/util/net" "k8s.io/kubernetes/pkg/watch" ) @@ -36,7 +37,7 @@ type ServiceInterface interface { UpdateStatus(srv *api.Service) (*api.Service, error) Delete(name string) error Watch(opts api.ListOptions) (watch.Interface, error) - ProxyGet(scheme, name, port, path string, params map[string]string) ResponseWrapper + ProxyGet(scheme, name, port, path string, params map[string]string) restclient.ResponseWrapper } // services implements ServicesNamespacer interface @@ -106,7 +107,7 @@ func (c *services) Watch(opts api.ListOptions) (watch.Interface, error) { } // ProxyGet returns a response of the service by calling it through the proxy. -func (c *services) ProxyGet(scheme, name, port, path string, params map[string]string) ResponseWrapper { +func (c *services) ProxyGet(scheme, name, port, path string, params map[string]string) restclient.ResponseWrapper { request := c.r.Get(). Namespace(c.ns). Resource("services"). diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/services_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/services_test.go deleted file mode 100644 index 343aef6f6..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/services_test.go +++ /dev/null @@ -1,242 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import ( - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -import ( - "net/url" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/labels" -) - -func TestListServices(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath("services", ns, ""), - Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, - Body: &api.ServiceList{ - Items: []api.Service{ - { - ObjectMeta: api.ObjectMeta{ - Name: "name", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "one": "two", - }, - }, - }, - }, - }, - }, - } - receivedServiceList, err := c.Setup(t).Services(ns).List(api.ListOptions{}) - defer c.Close() - t.Logf("received services: %v %#v", err, receivedServiceList) - c.Validate(t, receivedServiceList, err) -} - -func TestListServicesLabels(t *testing.T) { - ns := api.NamespaceDefault - labelSelectorQueryParamName := unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String()) - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath("services", ns, ""), - Query: simple.BuildQueryValues(url.Values{labelSelectorQueryParamName: []string{"foo=bar,name=baz"}})}, - Response: simple.Response{StatusCode: 200, - Body: &api.ServiceList{ - Items: []api.Service{ - { - ObjectMeta: api.ObjectMeta{ - Name: "name", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "one": "two", - }, - }, - }, - }, - }, - }, - } - c.Setup(t) - defer c.Close() - c.QueryValidator[labelSelectorQueryParamName] = simple.ValidateLabels - selector := labels.Set{"foo": "bar", "name": "baz"}.AsSelector() - options := api.ListOptions{LabelSelector: selector} - receivedServiceList, err := c.Services(ns).List(options) - c.Validate(t, receivedServiceList, err) -} - -func TestGetService(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath("services", ns, "1"), - Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: &api.Service{ObjectMeta: api.ObjectMeta{Name: "service-1"}}}, - } - response, err := c.Setup(t).Services(ns).Get("1") - defer c.Close() - c.Validate(t, response, err) -} - -func TestGetServiceWithNoName(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{Error: true} - receivedPod, err := c.Setup(t).Services(ns).Get("") - defer c.Close() - if (err != nil) && (err.Error() != simple.NameRequiredError) { - t.Errorf("Expected error: %v, but got %v", simple.NameRequiredError, err) - } - - c.Validate(t, receivedPod, err) -} - -func TestCreateService(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{ - Method: "POST", - Path: testapi.Default.ResourcePath("services", ns, ""), - Body: &api.Service{ObjectMeta: api.ObjectMeta{Name: "service-1"}}, - Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: &api.Service{ObjectMeta: api.ObjectMeta{Name: "service-1"}}}, - } - response, err := c.Setup(t).Services(ns).Create(&api.Service{ObjectMeta: api.ObjectMeta{Name: "service-1"}}) - defer c.Close() - c.Validate(t, response, err) -} - -func TestUpdateService(t *testing.T) { - ns := api.NamespaceDefault - svc := &api.Service{ObjectMeta: api.ObjectMeta{Name: "service-1", ResourceVersion: "1"}} - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Default.ResourcePath("services", ns, "service-1"), Body: svc, Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200, Body: svc}, - } - response, err := c.Setup(t).Services(ns).Update(svc) - defer c.Close() - c.Validate(t, response, err) -} - -func TestDeleteService(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: testapi.Default.ResourcePath("services", ns, "1"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).Services(ns).Delete("1") - defer c.Close() - c.Validate(t, nil, err) -} - -func TestUpdateServiceStatus(t *testing.T) { - ns := api.NamespaceDefault - lbStatus := api.LoadBalancerStatus{ - Ingress: []api.LoadBalancerIngress{ - {IP: "127.0.0.1"}, - }, - } - requestService := &api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: ns, - ResourceVersion: "1", - }, - Status: api.ServiceStatus{ - LoadBalancer: lbStatus, - }, - } - c := &simple.Client{ - Request: simple.Request{ - Method: "PUT", - Path: testapi.Default.ResourcePath("services", ns, "foo") + "/status", - Query: simple.BuildQueryValues(nil), - }, - Response: simple.Response{ - StatusCode: 200, - Body: &api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Spec: api.ServiceSpec{}, - Status: api.ServiceStatus{ - LoadBalancer: lbStatus, - }, - }, - }, - } - receivedService, err := c.Setup(t).Services(ns).UpdateStatus(requestService) - defer c.Close() - c.Validate(t, receivedService, err) -} - -func TestServiceProxyGet(t *testing.T) { - body := "OK" - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath("services", ns, "service-1") + "/proxy/foo", - Query: simple.BuildQueryValues(url.Values{"param-name": []string{"param-value"}}), - }, - Response: simple.Response{StatusCode: 200, RawBody: &body}, - } - response, err := c.Setup(t).Services(ns).ProxyGet("", "service-1", "", "foo", map[string]string{"param-name": "param-value"}).DoRaw() - defer c.Close() - c.ValidateRaw(t, response, err) - - // With scheme and port specified - c = &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath("services", ns, "https:service-1:my-port") + "/proxy/foo", - Query: simple.BuildQueryValues(url.Values{"param-name": []string{"param-value"}}), - }, - Response: simple.Response{StatusCode: 200, RawBody: &body}, - } - response, err = c.Setup(t).Services(ns).ProxyGet("https", "service-1", "my-port", "foo", map[string]string{"param-name": "param-value"}).DoRaw() - defer c.Close() - c.ValidateRaw(t, response, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/actions.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/actions.go deleted file mode 100644 index 1e5a5e470..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/actions.go +++ /dev/null @@ -1,446 +0,0 @@ -/* -Copyright 2015 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 testclient - -import ( - "strings" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/fields" - "k8s.io/kubernetes/pkg/labels" - "k8s.io/kubernetes/pkg/runtime" -) - -func NewRootGetAction(resource, name string) GetActionImpl { - action := GetActionImpl{} - action.Verb = "get" - action.Resource = resource - action.Name = name - - return action -} - -func NewGetAction(resource, namespace, name string) GetActionImpl { - action := GetActionImpl{} - action.Verb = "get" - action.Resource = resource - action.Namespace = namespace - action.Name = name - - return action -} - -func NewRootListAction(resource string, opts api.ListOptions) ListActionImpl { - action := ListActionImpl{} - action.Verb = "list" - action.Resource = resource - labelSelector := opts.LabelSelector - if labelSelector == nil { - labelSelector = labels.Everything() - } - fieldSelector := opts.FieldSelector - if fieldSelector == nil { - fieldSelector = fields.Everything() - } - action.ListRestrictions = ListRestrictions{labelSelector, fieldSelector} - - return action -} - -func NewListAction(resource, namespace string, opts api.ListOptions) ListActionImpl { - action := ListActionImpl{} - action.Verb = "list" - action.Resource = resource - action.Namespace = namespace - labelSelector := opts.LabelSelector - if labelSelector == nil { - labelSelector = labels.Everything() - } - fieldSelector := opts.FieldSelector - if fieldSelector == nil { - fieldSelector = fields.Everything() - } - action.ListRestrictions = ListRestrictions{labelSelector, fieldSelector} - - return action -} - -func NewRootCreateAction(resource string, object runtime.Object) CreateActionImpl { - action := CreateActionImpl{} - action.Verb = "create" - action.Resource = resource - action.Object = object - - return action -} - -func NewCreateAction(resource, namespace string, object runtime.Object) CreateActionImpl { - action := CreateActionImpl{} - action.Verb = "create" - action.Resource = resource - action.Namespace = namespace - action.Object = object - - return action -} - -func NewRootUpdateAction(resource string, object runtime.Object) UpdateActionImpl { - action := UpdateActionImpl{} - action.Verb = "update" - action.Resource = resource - action.Object = object - - return action -} - -func NewUpdateAction(resource, namespace string, object runtime.Object) UpdateActionImpl { - action := UpdateActionImpl{} - action.Verb = "update" - action.Resource = resource - action.Namespace = namespace - action.Object = object - - return action -} - -func NewRootPatchAction(resource string, object runtime.Object) PatchActionImpl { - action := PatchActionImpl{} - action.Verb = "patch" - action.Resource = resource - action.Object = object - - return action -} - -func NewPatchAction(resource, namespace string, object runtime.Object) PatchActionImpl { - action := PatchActionImpl{} - action.Verb = "patch" - action.Resource = resource - action.Namespace = namespace - action.Object = object - - return action -} - -func NewUpdateSubresourceAction(resource, subresource, namespace string, object runtime.Object) UpdateActionImpl { - action := UpdateActionImpl{} - action.Verb = "update" - action.Resource = resource - action.Subresource = subresource - action.Namespace = namespace - action.Object = object - - return action -} - -func NewRootDeleteAction(resource, name string) DeleteActionImpl { - action := DeleteActionImpl{} - action.Verb = "delete" - action.Resource = resource - action.Name = name - - return action -} - -func NewDeleteAction(resource, namespace, name string) DeleteActionImpl { - action := DeleteActionImpl{} - action.Verb = "delete" - action.Resource = resource - action.Namespace = namespace - action.Name = name - - return action -} - -func NewRootDeleteCollectionAction(resource string, opts api.ListOptions) DeleteCollectionActionImpl { - action := DeleteCollectionActionImpl{} - action.Verb = "delete-collection" - action.Resource = resource - labelSelector := opts.LabelSelector - if labelSelector == nil { - labelSelector = labels.Everything() - } - fieldSelector := opts.FieldSelector - if fieldSelector == nil { - fieldSelector = fields.Everything() - } - action.ListRestrictions = ListRestrictions{labelSelector, fieldSelector} - - return action -} - -func NewDeleteCollectionAction(resource, namespace string, opts api.ListOptions) DeleteCollectionActionImpl { - action := DeleteCollectionActionImpl{} - action.Verb = "delete-collection" - action.Resource = resource - action.Namespace = namespace - labelSelector := opts.LabelSelector - if labelSelector == nil { - labelSelector = labels.Everything() - } - fieldSelector := opts.FieldSelector - if fieldSelector == nil { - fieldSelector = fields.Everything() - } - action.ListRestrictions = ListRestrictions{labelSelector, fieldSelector} - - return action -} - -func NewRootWatchAction(resource string, opts api.ListOptions) WatchActionImpl { - action := WatchActionImpl{} - action.Verb = "watch" - action.Resource = resource - labelSelector := opts.LabelSelector - if labelSelector == nil { - labelSelector = labels.Everything() - } - fieldSelector := opts.FieldSelector - if fieldSelector == nil { - fieldSelector = fields.Everything() - } - action.WatchRestrictions = WatchRestrictions{labelSelector, fieldSelector, opts.ResourceVersion} - - return action -} - -func NewWatchAction(resource, namespace string, opts api.ListOptions) WatchActionImpl { - action := WatchActionImpl{} - action.Verb = "watch" - action.Resource = resource - action.Namespace = namespace - labelSelector := opts.LabelSelector - if labelSelector == nil { - labelSelector = labels.Everything() - } - fieldSelector := opts.FieldSelector - if fieldSelector == nil { - fieldSelector = fields.Everything() - } - action.WatchRestrictions = WatchRestrictions{labelSelector, fieldSelector, opts.ResourceVersion} - - return action -} - -func NewProxyGetAction(resource, namespace, scheme, name, port, path string, params map[string]string) ProxyGetActionImpl { - action := ProxyGetActionImpl{} - action.Verb = "get" - action.Resource = resource - action.Namespace = namespace - action.Scheme = scheme - action.Name = name - action.Port = port - action.Path = path - action.Params = params - return action -} - -type ListRestrictions struct { - Labels labels.Selector - Fields fields.Selector -} -type WatchRestrictions struct { - Labels labels.Selector - Fields fields.Selector - ResourceVersion string -} - -type Action interface { - GetNamespace() string - GetVerb() string - GetResource() string - GetSubresource() string - Matches(verb, resource string) bool -} - -type GenericAction interface { - Action - GetValue() interface{} -} - -type GetAction interface { - Action - GetName() string -} - -type ListAction interface { - Action - GetListRestrictions() ListRestrictions -} - -type CreateAction interface { - Action - GetObject() runtime.Object -} - -type UpdateAction interface { - Action - GetObject() runtime.Object -} - -type DeleteAction interface { - Action - GetName() string -} - -type WatchAction interface { - Action - GetWatchRestrictions() WatchRestrictions -} - -type ProxyGetAction interface { - Action - GetScheme() string - GetName() string - GetPort() string - GetPath() string - GetParams() map[string]string -} - -type ActionImpl struct { - Namespace string - Verb string - Resource string - Subresource string -} - -func (a ActionImpl) GetNamespace() string { - return a.Namespace -} -func (a ActionImpl) GetVerb() string { - return a.Verb -} -func (a ActionImpl) GetResource() string { - return a.Resource -} -func (a ActionImpl) GetSubresource() string { - return a.Subresource -} -func (a ActionImpl) Matches(verb, resource string) bool { - return strings.ToLower(verb) == strings.ToLower(a.Verb) && - strings.ToLower(resource) == strings.ToLower(a.Resource) -} - -type GenericActionImpl struct { - ActionImpl - Value interface{} -} - -func (a GenericActionImpl) GetValue() interface{} { - return a.Value -} - -type GetActionImpl struct { - ActionImpl - Name string -} - -func (a GetActionImpl) GetName() string { - return a.Name -} - -type ListActionImpl struct { - ActionImpl - ListRestrictions ListRestrictions -} - -func (a ListActionImpl) GetListRestrictions() ListRestrictions { - return a.ListRestrictions -} - -type CreateActionImpl struct { - ActionImpl - Object runtime.Object -} - -func (a CreateActionImpl) GetObject() runtime.Object { - return a.Object -} - -type UpdateActionImpl struct { - ActionImpl - Object runtime.Object -} - -func (a UpdateActionImpl) GetObject() runtime.Object { - return a.Object -} - -type PatchActionImpl struct { - ActionImpl - Object runtime.Object -} - -func (a PatchActionImpl) GetObject() runtime.Object { - return a.Object -} - -type DeleteActionImpl struct { - ActionImpl - Name string -} - -func (a DeleteActionImpl) GetName() string { - return a.Name -} - -type DeleteCollectionActionImpl struct { - ActionImpl - ListRestrictions ListRestrictions -} - -func (a DeleteCollectionActionImpl) GetListRestrictions() ListRestrictions { - return a.ListRestrictions -} - -type WatchActionImpl struct { - ActionImpl - WatchRestrictions WatchRestrictions -} - -func (a WatchActionImpl) GetWatchRestrictions() WatchRestrictions { - return a.WatchRestrictions -} - -type ProxyGetActionImpl struct { - ActionImpl - Scheme string - Name string - Port string - Path string - Params map[string]string -} - -func (a ProxyGetActionImpl) GetScheme() string { - return a.Scheme -} - -func (a ProxyGetActionImpl) GetName() string { - return a.Name -} - -func (a ProxyGetActionImpl) GetPort() string { - return a.Port -} - -func (a ProxyGetActionImpl) GetPath() string { - return a.Path -} - -func (a ProxyGetActionImpl) GetParams() map[string]string { - return a.Params -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_componentstatuses.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_componentstatuses.go deleted file mode 100644 index 34bf210d4..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_componentstatuses.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright 2015 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" -) - -// Fake implements ComponentStatusInterface. -type FakeComponentStatuses struct { - Fake *Fake -} - -func (c *FakeComponentStatuses) Get(name string) (*api.ComponentStatus, error) { - obj, err := c.Fake.Invokes(NewRootGetAction("componentstatuses", name), &api.ComponentStatus{}) - if obj == nil { - return nil, err - } - - return obj.(*api.ComponentStatus), err -} - -func (c *FakeComponentStatuses) List(opts api.ListOptions) (result *api.ComponentStatusList, err error) { - obj, err := c.Fake.Invokes(NewRootListAction("componentstatuses", opts), &api.ComponentStatusList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.ComponentStatusList), err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_configmaps.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_configmaps.go deleted file mode 100644 index 17a5bfeb8..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_configmaps.go +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright 2015 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/watch" -) - -const ( - configMapResourceName string = "configMaps" -) - -// FakeConfigMaps implements ConfigMapInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the method you want to test easier. -type FakeConfigMaps struct { - Fake *Fake - Namespace string -} - -func (c *FakeConfigMaps) Get(name string) (*api.ConfigMap, error) { - obj, err := c.Fake.Invokes(NewGetAction(configMapResourceName, c.Namespace, name), &api.ConfigMap{}) - if obj == nil { - return nil, err - } - - return obj.(*api.ConfigMap), err -} - -func (c *FakeConfigMaps) List(opts api.ListOptions) (*api.ConfigMapList, error) { - obj, err := c.Fake.Invokes(NewListAction(configMapResourceName, c.Namespace, opts), &api.ConfigMapList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.ConfigMapList), err -} - -func (c *FakeConfigMaps) Create(cfg *api.ConfigMap) (*api.ConfigMap, error) { - obj, err := c.Fake.Invokes(NewCreateAction(configMapResourceName, c.Namespace, cfg), cfg) - if obj == nil { - return nil, err - } - - return obj.(*api.ConfigMap), err -} - -func (c *FakeConfigMaps) Update(cfg *api.ConfigMap) (*api.ConfigMap, error) { - obj, err := c.Fake.Invokes(NewUpdateAction(configMapResourceName, c.Namespace, cfg), cfg) - if obj == nil { - return nil, err - } - - return obj.(*api.ConfigMap), err -} - -func (c *FakeConfigMaps) Delete(name string) error { - _, err := c.Fake.Invokes(NewDeleteAction(configMapResourceName, c.Namespace, name), &api.ConfigMap{}) - return err -} - -func (c *FakeConfigMaps) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction(configMapResourceName, c.Namespace, opts)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_daemon_sets.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_daemon_sets.go deleted file mode 100644 index d0e1e73e1..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_daemon_sets.go +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright 2015 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" - kclientlib "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/watch" -) - -// FakeDaemonSet implements DaemonInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the method you want to test easier. -type FakeDaemonSets struct { - Fake *FakeExperimental - Namespace string -} - -// Ensure statically that FakeDaemonSets implements DaemonInterface. -var _ kclientlib.DaemonSetInterface = &FakeDaemonSets{} - -func (c *FakeDaemonSets) Get(name string) (*extensions.DaemonSet, error) { - obj, err := c.Fake.Invokes(NewGetAction("daemonsets", c.Namespace, name), &extensions.DaemonSet{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.DaemonSet), err -} - -func (c *FakeDaemonSets) List(opts api.ListOptions) (*extensions.DaemonSetList, error) { - obj, err := c.Fake.Invokes(NewListAction("daemonsets", c.Namespace, opts), &extensions.DaemonSetList{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.DaemonSetList), err -} - -func (c *FakeDaemonSets) Create(daemon *extensions.DaemonSet) (*extensions.DaemonSet, error) { - obj, err := c.Fake.Invokes(NewCreateAction("daemonsets", c.Namespace, daemon), &extensions.DaemonSet{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.DaemonSet), err -} - -func (c *FakeDaemonSets) Update(daemon *extensions.DaemonSet) (*extensions.DaemonSet, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("daemonsets", c.Namespace, daemon), &extensions.DaemonSet{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.DaemonSet), err -} - -func (c *FakeDaemonSets) UpdateStatus(daemon *extensions.DaemonSet) (*extensions.DaemonSet, error) { - obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("daemonsets", "status", c.Namespace, daemon), &extensions.DaemonSet{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.DaemonSet), err -} - -func (c *FakeDaemonSets) Delete(name string) error { - _, err := c.Fake.Invokes(NewDeleteAction("daemonsets", c.Namespace, name), &extensions.DaemonSet{}) - return err -} - -func (c *FakeDaemonSets) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("daemonsets", c.Namespace, opts)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_deployments.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_deployments.go deleted file mode 100644 index f53f27198..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_deployments.go +++ /dev/null @@ -1,105 +0,0 @@ -/* -Copyright 2015 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/labels" - "k8s.io/kubernetes/pkg/watch" -) - -// FakeDeployments implements DeploymentsInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the methods you want to test easier. -type FakeDeployments struct { - Fake *FakeExperimental - Namespace string -} - -func (c *FakeDeployments) Get(name string) (*extensions.Deployment, error) { - obj, err := c.Fake.Invokes(NewGetAction("deployments", c.Namespace, name), &extensions.Deployment{}) - if obj == nil { - return nil, err - } - - return obj.(*extensions.Deployment), err -} - -func (c *FakeDeployments) List(opts api.ListOptions) (*extensions.DeploymentList, error) { - obj, err := c.Fake.Invokes(NewListAction("deployments", c.Namespace, opts), &extensions.DeploymentList{}) - if obj == nil { - return nil, err - } - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &extensions.DeploymentList{} - for _, deployment := range obj.(*extensions.DeploymentList).Items { - if label.Matches(labels.Set(deployment.Labels)) { - list.Items = append(list.Items, deployment) - } - } - return list, err -} - -func (c *FakeDeployments) Create(deployment *extensions.Deployment) (*extensions.Deployment, error) { - obj, err := c.Fake.Invokes(NewCreateAction("deployments", c.Namespace, deployment), deployment) - if obj == nil { - return nil, err - } - - return obj.(*extensions.Deployment), err -} - -func (c *FakeDeployments) Update(deployment *extensions.Deployment) (*extensions.Deployment, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("deployments", c.Namespace, deployment), deployment) - if obj == nil { - return nil, err - } - - return obj.(*extensions.Deployment), err -} - -func (c *FakeDeployments) UpdateStatus(deployment *extensions.Deployment) (*extensions.Deployment, error) { - obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("deployments", "status", c.Namespace, deployment), deployment) - if obj == nil { - return nil, err - } - - return obj.(*extensions.Deployment), err -} - -func (c *FakeDeployments) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake.Invokes(NewDeleteAction("deployments", c.Namespace, name), &extensions.Deployment{}) - return err -} - -func (c *FakeDeployments) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("deployments", c.Namespace, opts)) -} - -func (c *FakeDeployments) Rollback(deploymentRollback *extensions.DeploymentRollback) error { - action := CreateActionImpl{} - action.Verb = "create" - action.Resource = "deployments" - action.Subresource = "rollback" - action.Object = deploymentRollback - - _, err := c.Fake.Invokes(action, deploymentRollback) - return err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_endpoints.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_endpoints.go deleted file mode 100644 index 68f6178f7..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_endpoints.go +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2014 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/watch" -) - -// FakeEndpoints implements EndpointInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the method you want to test easier. -type FakeEndpoints struct { - Fake *Fake - Namespace string -} - -func (c *FakeEndpoints) Get(name string) (*api.Endpoints, error) { - obj, err := c.Fake.Invokes(NewGetAction("endpoints", c.Namespace, name), &api.Endpoints{}) - if obj == nil { - return nil, err - } - - return obj.(*api.Endpoints), err -} - -func (c *FakeEndpoints) List(opts api.ListOptions) (*api.EndpointsList, error) { - obj, err := c.Fake.Invokes(NewListAction("endpoints", c.Namespace, opts), &api.EndpointsList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.EndpointsList), err -} - -func (c *FakeEndpoints) Create(endpoints *api.Endpoints) (*api.Endpoints, error) { - obj, err := c.Fake.Invokes(NewCreateAction("endpoints", c.Namespace, endpoints), endpoints) - if obj == nil { - return nil, err - } - - return obj.(*api.Endpoints), err -} - -func (c *FakeEndpoints) Update(endpoints *api.Endpoints) (*api.Endpoints, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("endpoints", c.Namespace, endpoints), endpoints) - if obj == nil { - return nil, err - } - - return obj.(*api.Endpoints), err -} - -func (c *FakeEndpoints) Delete(name string) error { - _, err := c.Fake.Invokes(NewDeleteAction("endpoints", c.Namespace, name), &api.Endpoints{}) - return err -} - -func (c *FakeEndpoints) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("endpoints", c.Namespace, opts)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_events.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_events.go deleted file mode 100644 index 3da2143fc..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_events.go +++ /dev/null @@ -1,151 +0,0 @@ -/* -Copyright 2014 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/fields" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/watch" -) - -// FakeEvents implements EventInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the method you want to test easier. -type FakeEvents struct { - Fake *Fake - Namespace string -} - -// Get returns the given event, or an error. -func (c *FakeEvents) Get(name string) (*api.Event, error) { - action := NewRootGetAction("events", name) - if c.Namespace != "" { - action = NewGetAction("events", c.Namespace, name) - } - obj, err := c.Fake.Invokes(action, &api.Event{}) - if obj == nil { - return nil, err - } - - return obj.(*api.Event), err -} - -// List returns a list of events matching the selectors. -func (c *FakeEvents) List(opts api.ListOptions) (*api.EventList, error) { - action := NewRootListAction("events", opts) - if c.Namespace != "" { - action = NewListAction("events", c.Namespace, opts) - } - obj, err := c.Fake.Invokes(action, &api.EventList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.EventList), err -} - -// Create makes a new event. Returns the copy of the event the server returns, or an error. -func (c *FakeEvents) Create(event *api.Event) (*api.Event, error) { - action := NewRootCreateAction("events", event) - if c.Namespace != "" { - action = NewCreateAction("events", c.Namespace, event) - } - obj, err := c.Fake.Invokes(action, event) - if obj == nil { - return nil, err - } - - return obj.(*api.Event), err -} - -// Update replaces an existing event. Returns the copy of the event the server returns, or an error. -func (c *FakeEvents) Update(event *api.Event) (*api.Event, error) { - action := NewRootUpdateAction("events", event) - if c.Namespace != "" { - action = NewUpdateAction("events", c.Namespace, event) - } - obj, err := c.Fake.Invokes(action, event) - if obj == nil { - return nil, err - } - - return obj.(*api.Event), err -} - -// Patch patches an existing event. Returns the copy of the event the server returns, or an error. -func (c *FakeEvents) Patch(event *api.Event, data []byte) (*api.Event, error) { - action := NewRootPatchAction("events", event) - if c.Namespace != "" { - action = NewPatchAction("events", c.Namespace, event) - } - obj, err := c.Fake.Invokes(action, event) - if obj == nil { - return nil, err - } - - return obj.(*api.Event), err -} - -func (c *FakeEvents) Delete(name string) error { - action := NewRootDeleteAction("events", name) - if c.Namespace != "" { - action = NewDeleteAction("events", c.Namespace, name) - } - _, err := c.Fake.Invokes(action, &api.Event{}) - return err -} - -func (c *FakeEvents) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { - action := NewRootDeleteCollectionAction("events", listOptions) - if c.Namespace != "" { - action = NewDeleteCollectionAction("events", c.Namespace, listOptions) - } - _, err := c.Fake.Invokes(action, &api.EventList{}) - return err -} - -// Watch starts watching for events matching the given selectors. -func (c *FakeEvents) Watch(opts api.ListOptions) (watch.Interface, error) { - action := NewRootWatchAction("events", opts) - if c.Namespace != "" { - action = NewWatchAction("events", c.Namespace, opts) - } - return c.Fake.InvokesWatch(action) -} - -// Search returns a list of events matching the specified object. -func (c *FakeEvents) Search(objOrRef runtime.Object) (*api.EventList, error) { - action := NewRootListAction("events", api.ListOptions{}) - if c.Namespace != "" { - action = NewListAction("events", c.Namespace, api.ListOptions{}) - } - obj, err := c.Fake.Invokes(action, &api.EventList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.EventList), err -} - -func (c *FakeEvents) GetFieldSelector(involvedObjectName, involvedObjectNamespace, involvedObjectKind, involvedObjectUID *string) fields.Selector { - action := GenericActionImpl{} - action.Verb = "get-field-selector" - action.Resource = "events" - - c.Fake.Invokes(action, nil) - return fields.Everything() -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_horizontal_pod_autoscalers.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_horizontal_pod_autoscalers.go deleted file mode 100644 index e50b326d9..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_horizontal_pod_autoscalers.go +++ /dev/null @@ -1,164 +0,0 @@ -/* -Copyright 2015 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/labels" - "k8s.io/kubernetes/pkg/watch" -) - -// FakeHorizontalPodAutoscalers implements HorizontalPodAutoscalerInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the methods you want to test easier. -type FakeHorizontalPodAutoscalers struct { - Fake *FakeExperimental - Namespace string -} - -func (c *FakeHorizontalPodAutoscalers) Get(name string) (*extensions.HorizontalPodAutoscaler, error) { - obj, err := c.Fake.Invokes(NewGetAction("horizontalpodautoscalers", c.Namespace, name), &extensions.HorizontalPodAutoscaler{}) - if obj == nil { - return nil, err - } - - return obj.(*extensions.HorizontalPodAutoscaler), err -} - -func (c *FakeHorizontalPodAutoscalers) List(opts api.ListOptions) (*extensions.HorizontalPodAutoscalerList, error) { - obj, err := c.Fake.Invokes(NewListAction("horizontalpodautoscalers", c.Namespace, opts), &extensions.HorizontalPodAutoscalerList{}) - if obj == nil { - return nil, err - } - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &extensions.HorizontalPodAutoscalerList{} - for _, a := range obj.(*extensions.HorizontalPodAutoscalerList).Items { - if label.Matches(labels.Set(a.Labels)) { - list.Items = append(list.Items, a) - } - } - return list, err -} - -func (c *FakeHorizontalPodAutoscalers) Create(a *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error) { - obj, err := c.Fake.Invokes(NewCreateAction("horizontalpodautoscalers", c.Namespace, a), a) - if obj == nil { - return nil, err - } - - return obj.(*extensions.HorizontalPodAutoscaler), err -} - -func (c *FakeHorizontalPodAutoscalers) Update(a *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("horizontalpodautoscalers", c.Namespace, a), a) - if obj == nil { - return nil, err - } - - return obj.(*extensions.HorizontalPodAutoscaler), err -} - -func (c *FakeHorizontalPodAutoscalers) UpdateStatus(a *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error) { - obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("horizontalpodautoscalers", "status", c.Namespace, a), &extensions.HorizontalPodAutoscaler{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.HorizontalPodAutoscaler), err -} - -func (c *FakeHorizontalPodAutoscalers) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake.Invokes(NewDeleteAction("horizontalpodautoscalers", c.Namespace, name), &extensions.HorizontalPodAutoscaler{}) - return err -} - -func (c *FakeHorizontalPodAutoscalers) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("horizontalpodautoscalers", c.Namespace, opts)) -} - -// FakeHorizontalPodAutoscalers implements HorizontalPodAutoscalerInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the methods you want to test easier. -// This is a test implementation of HorizontalPodAutoscalersV1 -// TODO(piosz): get back to one client implementation once HPA will be graduated to GA completely -type FakeHorizontalPodAutoscalersV1 struct { - Fake *FakeAutoscaling - Namespace string -} - -func (c *FakeHorizontalPodAutoscalersV1) Get(name string) (*extensions.HorizontalPodAutoscaler, error) { - obj, err := c.Fake.Invokes(NewGetAction("horizontalpodautoscalers", c.Namespace, name), &extensions.HorizontalPodAutoscaler{}) - if obj == nil { - return nil, err - } - - return obj.(*extensions.HorizontalPodAutoscaler), err -} - -func (c *FakeHorizontalPodAutoscalersV1) List(opts api.ListOptions) (*extensions.HorizontalPodAutoscalerList, error) { - obj, err := c.Fake.Invokes(NewListAction("horizontalpodautoscalers", c.Namespace, opts), &extensions.HorizontalPodAutoscalerList{}) - if obj == nil { - return nil, err - } - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &extensions.HorizontalPodAutoscalerList{} - for _, a := range obj.(*extensions.HorizontalPodAutoscalerList).Items { - if label.Matches(labels.Set(a.Labels)) { - list.Items = append(list.Items, a) - } - } - return list, err -} - -func (c *FakeHorizontalPodAutoscalersV1) Create(a *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error) { - obj, err := c.Fake.Invokes(NewCreateAction("horizontalpodautoscalers", c.Namespace, a), a) - if obj == nil { - return nil, err - } - - return obj.(*extensions.HorizontalPodAutoscaler), err -} - -func (c *FakeHorizontalPodAutoscalersV1) Update(a *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("horizontalpodautoscalers", c.Namespace, a), a) - if obj == nil { - return nil, err - } - - return obj.(*extensions.HorizontalPodAutoscaler), err -} - -func (c *FakeHorizontalPodAutoscalersV1) UpdateStatus(a *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error) { - obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("horizontalpodautoscalers", "status", c.Namespace, a), &extensions.HorizontalPodAutoscaler{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.HorizontalPodAutoscaler), err -} - -func (c *FakeHorizontalPodAutoscalersV1) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake.Invokes(NewDeleteAction("horizontalpodautoscalers", c.Namespace, name), &extensions.HorizontalPodAutoscaler{}) - return err -} - -func (c *FakeHorizontalPodAutoscalersV1) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("horizontalpodautoscalers", c.Namespace, opts)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_ingress.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_ingress.go deleted file mode 100644 index b15458997..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_ingress.go +++ /dev/null @@ -1,84 +0,0 @@ -/* -Copyright 2015 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/watch" -) - -// FakeIngress implements IngressInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the method you want to test easier. -type FakeIngress struct { - Fake *FakeExperimental - Namespace string -} - -func (c *FakeIngress) Get(name string) (*extensions.Ingress, error) { - obj, err := c.Fake.Invokes(NewGetAction("ingresses", c.Namespace, name), &extensions.Ingress{}) - if obj == nil { - return nil, err - } - - return obj.(*extensions.Ingress), err -} - -func (c *FakeIngress) List(opts api.ListOptions) (*extensions.IngressList, error) { - obj, err := c.Fake.Invokes(NewListAction("ingresses", c.Namespace, opts), &extensions.IngressList{}) - if obj == nil { - return nil, err - } - - return obj.(*extensions.IngressList), err -} - -func (c *FakeIngress) Create(ingress *extensions.Ingress) (*extensions.Ingress, error) { - obj, err := c.Fake.Invokes(NewCreateAction("ingresses", c.Namespace, ingress), ingress) - if obj == nil { - return nil, err - } - - return obj.(*extensions.Ingress), err -} - -func (c *FakeIngress) Update(ingress *extensions.Ingress) (*extensions.Ingress, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("ingresses", c.Namespace, ingress), ingress) - if obj == nil { - return nil, err - } - - return obj.(*extensions.Ingress), err -} - -func (c *FakeIngress) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake.Invokes(NewDeleteAction("ingresses", c.Namespace, name), &extensions.Ingress{}) - return err -} - -func (c *FakeIngress) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("ingresses", c.Namespace, opts)) -} - -func (c *FakeIngress) UpdateStatus(ingress *extensions.Ingress) (result *extensions.Ingress, err error) { - obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("ingresses", "status", c.Namespace, ingress), ingress) - if obj == nil { - return nil, err - } - - return obj.(*extensions.Ingress), err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_jobs.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_jobs.go deleted file mode 100644 index d6fb79fa1..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_jobs.go +++ /dev/null @@ -1,84 +0,0 @@ -/* -Copyright 2015 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/watch" -) - -// FakeJobs implements JobInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the method you want to test easier. -type FakeJobs struct { - Fake *FakeExperimental - Namespace string -} - -func (c *FakeJobs) Get(name string) (*extensions.Job, error) { - obj, err := c.Fake.Invokes(NewGetAction("jobs", c.Namespace, name), &extensions.Job{}) - if obj == nil { - return nil, err - } - - return obj.(*extensions.Job), err -} - -func (c *FakeJobs) List(opts api.ListOptions) (*extensions.JobList, error) { - obj, err := c.Fake.Invokes(NewListAction("jobs", c.Namespace, opts), &extensions.JobList{}) - if obj == nil { - return nil, err - } - - return obj.(*extensions.JobList), err -} - -func (c *FakeJobs) Create(job *extensions.Job) (*extensions.Job, error) { - obj, err := c.Fake.Invokes(NewCreateAction("jobs", c.Namespace, job), job) - if obj == nil { - return nil, err - } - - return obj.(*extensions.Job), err -} - -func (c *FakeJobs) Update(job *extensions.Job) (*extensions.Job, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("jobs", c.Namespace, job), job) - if obj == nil { - return nil, err - } - - return obj.(*extensions.Job), err -} - -func (c *FakeJobs) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake.Invokes(NewDeleteAction("jobs", c.Namespace, name), &extensions.Job{}) - return err -} - -func (c *FakeJobs) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("jobs", c.Namespace, opts)) -} - -func (c *FakeJobs) UpdateStatus(job *extensions.Job) (result *extensions.Job, err error) { - obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("jobs", "status", c.Namespace, job), job) - if obj == nil { - return nil, err - } - - return obj.(*extensions.Job), err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_limit_ranges.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_limit_ranges.go deleted file mode 100644 index 3669f5ff8..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_limit_ranges.go +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2014 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/watch" -) - -// FakeLimitRanges implements PodsInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the methods you want to test easier. -type FakeLimitRanges struct { - Fake *Fake - Namespace string -} - -func (c *FakeLimitRanges) Get(name string) (*api.LimitRange, error) { - obj, err := c.Fake.Invokes(NewGetAction("limitranges", c.Namespace, name), &api.LimitRange{}) - if obj == nil { - return nil, err - } - - return obj.(*api.LimitRange), err -} - -func (c *FakeLimitRanges) List(opts api.ListOptions) (*api.LimitRangeList, error) { - obj, err := c.Fake.Invokes(NewListAction("limitranges", c.Namespace, opts), &api.LimitRangeList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.LimitRangeList), err -} - -func (c *FakeLimitRanges) Create(limitRange *api.LimitRange) (*api.LimitRange, error) { - obj, err := c.Fake.Invokes(NewCreateAction("limitranges", c.Namespace, limitRange), limitRange) - if obj == nil { - return nil, err - } - - return obj.(*api.LimitRange), err -} - -func (c *FakeLimitRanges) Update(limitRange *api.LimitRange) (*api.LimitRange, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("limitranges", c.Namespace, limitRange), limitRange) - if obj == nil { - return nil, err - } - - return obj.(*api.LimitRange), err -} - -func (c *FakeLimitRanges) Delete(name string) error { - _, err := c.Fake.Invokes(NewDeleteAction("limitranges", c.Namespace, name), &api.LimitRange{}) - return err -} - -func (c *FakeLimitRanges) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("limitranges", c.Namespace, opts)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_namespaces.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_namespaces.go deleted file mode 100644 index 8c4ac1ac2..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_namespaces.go +++ /dev/null @@ -1,103 +0,0 @@ -/* -Copyright 2014 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/watch" -) - -// FakeNamespaces implements NamespacesInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the methods you want to test easier. -type FakeNamespaces struct { - Fake *Fake -} - -func (c *FakeNamespaces) Get(name string) (*api.Namespace, error) { - obj, err := c.Fake.Invokes(NewRootGetAction("namespaces", name), &api.Namespace{}) - if obj == nil { - return nil, err - } - - return obj.(*api.Namespace), err -} - -func (c *FakeNamespaces) List(opts api.ListOptions) (*api.NamespaceList, error) { - obj, err := c.Fake.Invokes(NewRootListAction("namespaces", opts), &api.NamespaceList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.NamespaceList), err -} - -func (c *FakeNamespaces) Create(namespace *api.Namespace) (*api.Namespace, error) { - obj, err := c.Fake.Invokes(NewRootCreateAction("namespaces", namespace), namespace) - if obj == nil { - return nil, err - } - - return obj.(*api.Namespace), err -} - -func (c *FakeNamespaces) Update(namespace *api.Namespace) (*api.Namespace, error) { - obj, err := c.Fake.Invokes(NewRootUpdateAction("namespaces", namespace), namespace) - if obj == nil { - return nil, err - } - - return obj.(*api.Namespace), err -} - -func (c *FakeNamespaces) Delete(name string) error { - _, err := c.Fake.Invokes(NewRootDeleteAction("namespaces", name), &api.Namespace{}) - return err -} - -func (c *FakeNamespaces) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewRootWatchAction("namespaces", opts)) -} - -func (c *FakeNamespaces) Finalize(namespace *api.Namespace) (*api.Namespace, error) { - action := CreateActionImpl{} - action.Verb = "create" - action.Resource = "namespaces" - action.Subresource = "finalize" - action.Object = namespace - - obj, err := c.Fake.Invokes(action, namespace) - if obj == nil { - return nil, err - } - - return obj.(*api.Namespace), err -} - -func (c *FakeNamespaces) Status(namespace *api.Namespace) (*api.Namespace, error) { - action := CreateActionImpl{} - action.Verb = "create" - action.Resource = "namespaces" - action.Subresource = "status" - action.Object = namespace - - obj, err := c.Fake.Invokes(action, namespace) - if obj == nil { - return nil, err - } - - return obj.(*api.Namespace), err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_nodes.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_nodes.go deleted file mode 100644 index b1943366e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_nodes.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2014 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/watch" -) - -// FakeNodes implements NodeInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the method you want to test easier. -type FakeNodes struct { - Fake *Fake -} - -func (c *FakeNodes) Get(name string) (*api.Node, error) { - obj, err := c.Fake.Invokes(NewRootGetAction("nodes", name), &api.Node{}) - if obj == nil { - return nil, err - } - - return obj.(*api.Node), err -} - -func (c *FakeNodes) List(opts api.ListOptions) (*api.NodeList, error) { - obj, err := c.Fake.Invokes(NewRootListAction("nodes", opts), &api.NodeList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.NodeList), err -} - -func (c *FakeNodes) Create(node *api.Node) (*api.Node, error) { - obj, err := c.Fake.Invokes(NewRootCreateAction("nodes", node), node) - if obj == nil { - return nil, err - } - - return obj.(*api.Node), err -} - -func (c *FakeNodes) Update(node *api.Node) (*api.Node, error) { - obj, err := c.Fake.Invokes(NewRootUpdateAction("nodes", node), node) - if obj == nil { - return nil, err - } - - return obj.(*api.Node), err -} - -func (c *FakeNodes) Delete(name string) error { - _, err := c.Fake.Invokes(NewRootDeleteAction("nodes", name), &api.Node{}) - return err -} - -func (c *FakeNodes) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewRootWatchAction("nodes", opts)) -} - -func (c *FakeNodes) UpdateStatus(node *api.Node) (*api.Node, error) { - action := CreateActionImpl{} - action.Verb = "update" - action.Resource = "nodes" - action.Subresource = "status" - action.Object = node - - obj, err := c.Fake.Invokes(action, node) - if obj == nil { - return nil, err - } - - return obj.(*api.Node), err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_persistent_volume_claims.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_persistent_volume_claims.go deleted file mode 100644 index cadfb084c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_persistent_volume_claims.go +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright 2014 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/watch" -) - -type FakePersistentVolumeClaims struct { - Fake *Fake - Namespace string -} - -func (c *FakePersistentVolumeClaims) Get(name string) (*api.PersistentVolumeClaim, error) { - obj, err := c.Fake.Invokes(NewGetAction("persistentvolumeclaims", c.Namespace, name), &api.PersistentVolumeClaim{}) - if obj == nil { - return nil, err - } - - return obj.(*api.PersistentVolumeClaim), err -} - -func (c *FakePersistentVolumeClaims) List(opts api.ListOptions) (*api.PersistentVolumeClaimList, error) { - obj, err := c.Fake.Invokes(NewListAction("persistentvolumeclaims", c.Namespace, opts), &api.PersistentVolumeClaimList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.PersistentVolumeClaimList), err -} - -func (c *FakePersistentVolumeClaims) Create(claim *api.PersistentVolumeClaim) (*api.PersistentVolumeClaim, error) { - obj, err := c.Fake.Invokes(NewCreateAction("persistentvolumeclaims", c.Namespace, claim), claim) - if obj == nil { - return nil, err - } - - return obj.(*api.PersistentVolumeClaim), err -} - -func (c *FakePersistentVolumeClaims) Update(claim *api.PersistentVolumeClaim) (*api.PersistentVolumeClaim, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("persistentvolumeclaims", c.Namespace, claim), claim) - if obj == nil { - return nil, err - } - - return obj.(*api.PersistentVolumeClaim), err -} - -func (c *FakePersistentVolumeClaims) Delete(name string) error { - _, err := c.Fake.Invokes(NewDeleteAction("persistentvolumeclaims", c.Namespace, name), &api.PersistentVolumeClaim{}) - return err -} - -func (c *FakePersistentVolumeClaims) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("persistentvolumeclaims", c.Namespace, opts)) -} - -func (c *FakePersistentVolumeClaims) UpdateStatus(claim *api.PersistentVolumeClaim) (*api.PersistentVolumeClaim, error) { - obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("persistentvolumeclaims", "status", c.Namespace, claim), claim) - if obj == nil { - return nil, err - } - - return obj.(*api.PersistentVolumeClaim), err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_persistent_volumes.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_persistent_volumes.go deleted file mode 100644 index cb184bc44..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_persistent_volumes.go +++ /dev/null @@ -1,86 +0,0 @@ -/* -Copyright 2014 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/watch" -) - -type FakePersistentVolumes struct { - Fake *Fake -} - -func (c *FakePersistentVolumes) Get(name string) (*api.PersistentVolume, error) { - obj, err := c.Fake.Invokes(NewRootGetAction("persistentvolumes", name), &api.PersistentVolume{}) - if obj == nil { - return nil, err - } - - return obj.(*api.PersistentVolume), err -} - -func (c *FakePersistentVolumes) List(opts api.ListOptions) (*api.PersistentVolumeList, error) { - obj, err := c.Fake.Invokes(NewRootListAction("persistentvolumes", opts), &api.PersistentVolumeList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.PersistentVolumeList), err -} - -func (c *FakePersistentVolumes) Create(pv *api.PersistentVolume) (*api.PersistentVolume, error) { - obj, err := c.Fake.Invokes(NewRootCreateAction("persistentvolumes", pv), pv) - if obj == nil { - return nil, err - } - - return obj.(*api.PersistentVolume), err -} - -func (c *FakePersistentVolumes) Update(pv *api.PersistentVolume) (*api.PersistentVolume, error) { - obj, err := c.Fake.Invokes(NewRootUpdateAction("persistentvolumes", pv), pv) - if obj == nil { - return nil, err - } - - return obj.(*api.PersistentVolume), err -} - -func (c *FakePersistentVolumes) Delete(name string) error { - _, err := c.Fake.Invokes(NewRootDeleteAction("persistentvolumes", name), &api.PersistentVolume{}) - return err -} - -func (c *FakePersistentVolumes) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewRootWatchAction("persistentvolumes", opts)) -} - -func (c *FakePersistentVolumes) UpdateStatus(pv *api.PersistentVolume) (*api.PersistentVolume, error) { - action := UpdateActionImpl{} - action.Verb = "update" - action.Resource = "persistentvolumes" - action.Subresource = "status" - action.Object = pv - - obj, err := c.Fake.Invokes(action, pv) - if obj == nil { - return nil, err - } - - return obj.(*api.PersistentVolume), err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_pod_templates.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_pod_templates.go deleted file mode 100644 index 47ff3d8ac..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_pod_templates.go +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2015 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/watch" -) - -// FakePodTemplates implements PodTemplatesInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the methods you want to test easier. -type FakePodTemplates struct { - Fake *Fake - Namespace string -} - -func (c *FakePodTemplates) Get(name string) (*api.PodTemplate, error) { - obj, err := c.Fake.Invokes(NewGetAction("podtemplates", c.Namespace, name), &api.PodTemplate{}) - if obj == nil { - return nil, err - } - - return obj.(*api.PodTemplate), err -} - -func (c *FakePodTemplates) List(opts api.ListOptions) (*api.PodTemplateList, error) { - obj, err := c.Fake.Invokes(NewListAction("podtemplates", c.Namespace, opts), &api.PodTemplateList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.PodTemplateList), err -} - -func (c *FakePodTemplates) Create(pod *api.PodTemplate) (*api.PodTemplate, error) { - obj, err := c.Fake.Invokes(NewCreateAction("podtemplates", c.Namespace, pod), pod) - if obj == nil { - return nil, err - } - - return obj.(*api.PodTemplate), err -} - -func (c *FakePodTemplates) Update(pod *api.PodTemplate) (*api.PodTemplate, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("podtemplates", c.Namespace, pod), pod) - if obj == nil { - return nil, err - } - - return obj.(*api.PodTemplate), err -} - -func (c *FakePodTemplates) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake.Invokes(NewDeleteAction("podtemplates", c.Namespace, name), &api.PodTemplate{}) - return err -} - -func (c *FakePodTemplates) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("podtemplates", c.Namespace, opts)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_pods.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_pods.go deleted file mode 100644 index 824356aac..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_pods.go +++ /dev/null @@ -1,117 +0,0 @@ -/* -Copyright 2014 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/labels" - "k8s.io/kubernetes/pkg/watch" -) - -// FakePods implements PodsInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the methods you want to test easier. -type FakePods struct { - Fake *Fake - Namespace string -} - -func (c *FakePods) Get(name string) (*api.Pod, error) { - obj, err := c.Fake.Invokes(NewGetAction("pods", c.Namespace, name), &api.Pod{}) - if obj == nil { - return nil, err - } - - return obj.(*api.Pod), err -} - -func (c *FakePods) List(opts api.ListOptions) (*api.PodList, error) { - obj, err := c.Fake.Invokes(NewListAction("pods", c.Namespace, opts), &api.PodList{}) - if obj == nil { - return nil, err - } - label := opts.LabelSelector - if label == nil { - label = labels.Everything() - } - list := &api.PodList{} - for _, pod := range obj.(*api.PodList).Items { - if label.Matches(labels.Set(pod.Labels)) { - list.Items = append(list.Items, pod) - } - } - return list, err -} - -func (c *FakePods) Create(pod *api.Pod) (*api.Pod, error) { - obj, err := c.Fake.Invokes(NewCreateAction("pods", c.Namespace, pod), pod) - if obj == nil { - return nil, err - } - - return obj.(*api.Pod), err -} - -func (c *FakePods) Update(pod *api.Pod) (*api.Pod, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("pods", c.Namespace, pod), pod) - if obj == nil { - return nil, err - } - - return obj.(*api.Pod), err -} - -func (c *FakePods) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake.Invokes(NewDeleteAction("pods", c.Namespace, name), &api.Pod{}) - return err -} - -func (c *FakePods) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("pods", c.Namespace, opts)) -} - -func (c *FakePods) Bind(binding *api.Binding) error { - action := CreateActionImpl{} - action.Verb = "create" - action.Resource = "pods" - action.Subresource = "bindings" - action.Object = binding - - _, err := c.Fake.Invokes(action, binding) - return err -} - -func (c *FakePods) UpdateStatus(pod *api.Pod) (*api.Pod, error) { - obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("pods", "status", c.Namespace, pod), pod) - if obj == nil { - return nil, err - } - - return obj.(*api.Pod), err -} - -func (c *FakePods) GetLogs(name string, opts *api.PodLogOptions) *client.Request { - action := GenericActionImpl{} - action.Verb = "get" - action.Namespace = c.Namespace - action.Resource = "pod" - action.Subresource = "logs" - action.Value = opts - - _, _ = c.Fake.Invokes(action, &api.Pod{}) - return &client.Request{} -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_podsecuritypolicy.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_podsecuritypolicy.go deleted file mode 100644 index bb611d322..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_podsecuritypolicy.go +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright 2014 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/watch" -) - -// FakePodSecurityPolicy implements PodSecurityPolicyInterface. Meant to be -// embedded into a struct to get a default implementation. This makes faking out just -// the method you want to test easier. -type FakePodSecurityPolicy struct { - Fake *Fake - Namespace string -} - -func (c *FakePodSecurityPolicy) List(opts api.ListOptions) (*extensions.PodSecurityPolicyList, error) { - obj, err := c.Fake.Invokes(NewListAction("podsecuritypolicies", c.Namespace, opts), &extensions.PodSecurityPolicyList{}) - if obj == nil { - return nil, err - } - - return obj.(*extensions.PodSecurityPolicyList), err -} - -func (c *FakePodSecurityPolicy) Get(name string) (*extensions.PodSecurityPolicy, error) { - obj, err := c.Fake.Invokes(NewGetAction("podsecuritypolicies", c.Namespace, name), &extensions.PodSecurityPolicy{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.PodSecurityPolicy), err -} - -func (c *FakePodSecurityPolicy) Create(scc *extensions.PodSecurityPolicy) (*extensions.PodSecurityPolicy, error) { - obj, err := c.Fake.Invokes(NewCreateAction("podsecuritypolicies", c.Namespace, scc), &extensions.PodSecurityPolicy{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.PodSecurityPolicy), err -} - -func (c *FakePodSecurityPolicy) Update(scc *extensions.PodSecurityPolicy) (*extensions.PodSecurityPolicy, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("podsecuritypolicies", c.Namespace, scc), &extensions.PodSecurityPolicy{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.PodSecurityPolicy), err -} - -func (c *FakePodSecurityPolicy) Delete(name string) error { - _, err := c.Fake.Invokes(NewDeleteAction("podsecuritypolicies", c.Namespace, name), &extensions.PodSecurityPolicy{}) - return err -} - -func (c *FakePodSecurityPolicy) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("podsecuritypolicies", c.Namespace, opts)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_replica_sets.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_replica_sets.go deleted file mode 100644 index 29c985c8c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_replica_sets.go +++ /dev/null @@ -1,83 +0,0 @@ -/* -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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/watch" -) - -// FakeReplicaSets implements ReplicaSetsInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the method you want to test easier. -type FakeReplicaSets struct { - Fake *FakeExperimental - Namespace string -} - -func (c *FakeReplicaSets) Get(name string) (*extensions.ReplicaSet, error) { - obj, err := c.Fake.Invokes(NewGetAction("replicasets", c.Namespace, name), &extensions.ReplicaSet{}) - if obj == nil { - return nil, err - } - - return obj.(*extensions.ReplicaSet), err -} - -func (c *FakeReplicaSets) List(opts api.ListOptions) (*extensions.ReplicaSetList, error) { - obj, err := c.Fake.Invokes(NewListAction("replicasets", c.Namespace, opts), &extensions.ReplicaSetList{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.ReplicaSetList), err -} - -func (c *FakeReplicaSets) Create(rs *extensions.ReplicaSet) (*extensions.ReplicaSet, error) { - obj, err := c.Fake.Invokes(NewCreateAction("replicasets", c.Namespace, rs), rs) - if obj == nil { - return nil, err - } - - return obj.(*extensions.ReplicaSet), err -} - -func (c *FakeReplicaSets) Update(rs *extensions.ReplicaSet) (*extensions.ReplicaSet, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("replicasets", c.Namespace, rs), rs) - if obj == nil { - return nil, err - } - - return obj.(*extensions.ReplicaSet), err -} - -func (c *FakeReplicaSets) Delete(name string, options *api.DeleteOptions) error { - _, err := c.Fake.Invokes(NewDeleteAction("replicasets", c.Namespace, name), &extensions.ReplicaSet{}) - return err -} - -func (c *FakeReplicaSets) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("replicasets", c.Namespace, opts)) -} - -func (c *FakeReplicaSets) UpdateStatus(rs *extensions.ReplicaSet) (result *extensions.ReplicaSet, err error) { - obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("replicasets", "status", c.Namespace, rs), rs) - if obj == nil { - return nil, err - } - - return obj.(*extensions.ReplicaSet), err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_replication_controllers.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_replication_controllers.go deleted file mode 100644 index e44b82615..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_replication_controllers.go +++ /dev/null @@ -1,82 +0,0 @@ -/* -Copyright 2014 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/watch" -) - -// FakeReplicationControllers implements ReplicationControllerInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the method you want to test easier. -type FakeReplicationControllers struct { - Fake *Fake - Namespace string -} - -func (c *FakeReplicationControllers) Get(name string) (*api.ReplicationController, error) { - obj, err := c.Fake.Invokes(NewGetAction("replicationcontrollers", c.Namespace, name), &api.ReplicationController{}) - if obj == nil { - return nil, err - } - - return obj.(*api.ReplicationController), err -} - -func (c *FakeReplicationControllers) List(opts api.ListOptions) (*api.ReplicationControllerList, error) { - obj, err := c.Fake.Invokes(NewListAction("replicationcontrollers", c.Namespace, opts), &api.ReplicationControllerList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.ReplicationControllerList), err -} - -func (c *FakeReplicationControllers) Create(controller *api.ReplicationController) (*api.ReplicationController, error) { - obj, err := c.Fake.Invokes(NewCreateAction("replicationcontrollers", c.Namespace, controller), controller) - if obj == nil { - return nil, err - } - - return obj.(*api.ReplicationController), err -} - -func (c *FakeReplicationControllers) Update(controller *api.ReplicationController) (*api.ReplicationController, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("replicationcontrollers", c.Namespace, controller), controller) - if obj == nil { - return nil, err - } - - return obj.(*api.ReplicationController), err -} - -func (c *FakeReplicationControllers) UpdateStatus(controller *api.ReplicationController) (*api.ReplicationController, error) { - obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("replicationcontrollers", "status", c.Namespace, controller), &api.ReplicationController{}) - if obj == nil { - return nil, err - } - return obj.(*api.ReplicationController), err -} - -func (c *FakeReplicationControllers) Delete(name string) error { - _, err := c.Fake.Invokes(NewDeleteAction("replicationcontrollers", c.Namespace, name), &api.ReplicationController{}) - return err -} - -func (c *FakeReplicationControllers) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("replicationcontrollers", c.Namespace, opts)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_resource_quotas.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_resource_quotas.go deleted file mode 100644 index d5090f0d0..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_resource_quotas.go +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright 2014 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/watch" -) - -// FakeResourceQuotas implements ResourceQuotaInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the methods you want to test easier. -type FakeResourceQuotas struct { - Fake *Fake - Namespace string -} - -func (c *FakeResourceQuotas) Get(name string) (*api.ResourceQuota, error) { - obj, err := c.Fake.Invokes(NewGetAction("resourcequotas", c.Namespace, name), &api.ResourceQuota{}) - if obj == nil { - return nil, err - } - - return obj.(*api.ResourceQuota), err -} - -func (c *FakeResourceQuotas) List(opts api.ListOptions) (*api.ResourceQuotaList, error) { - obj, err := c.Fake.Invokes(NewListAction("resourcequotas", c.Namespace, opts), &api.ResourceQuotaList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.ResourceQuotaList), err -} - -func (c *FakeResourceQuotas) Create(resourceQuota *api.ResourceQuota) (*api.ResourceQuota, error) { - obj, err := c.Fake.Invokes(NewCreateAction("resourcequotas", c.Namespace, resourceQuota), resourceQuota) - if obj == nil { - return nil, err - } - - return obj.(*api.ResourceQuota), err -} - -func (c *FakeResourceQuotas) Update(resourceQuota *api.ResourceQuota) (*api.ResourceQuota, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("resourcequotas", c.Namespace, resourceQuota), resourceQuota) - if obj == nil { - return nil, err - } - - return obj.(*api.ResourceQuota), err -} - -func (c *FakeResourceQuotas) Delete(name string) error { - _, err := c.Fake.Invokes(NewDeleteAction("resourcequotas", c.Namespace, name), &api.ResourceQuota{}) - return err -} - -func (c *FakeResourceQuotas) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("resourcequotas", c.Namespace, opts)) -} - -func (c *FakeResourceQuotas) UpdateStatus(resourceQuota *api.ResourceQuota) (*api.ResourceQuota, error) { - obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("resourcequotas", "status", c.Namespace, resourceQuota), resourceQuota) - if obj == nil { - return nil, err - } - - return obj.(*api.ResourceQuota), err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_scales.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_scales.go deleted file mode 100644 index 53ff5f87d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_scales.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2015 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 testclient - -import ( - "k8s.io/kubernetes/pkg/apis/extensions" -) - -// FakeScales implements ScaleInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the methods you want to test easier. -type FakeScales struct { - Fake *FakeExperimental - Namespace string -} - -func (c *FakeScales) Get(kind string, name string) (result *extensions.Scale, err error) { - action := GetActionImpl{} - action.Verb = "get" - action.Namespace = c.Namespace - action.Resource = kind - action.Subresource = "scale" - action.Name = name - obj, err := c.Fake.Invokes(action, &extensions.Scale{}) - result = obj.(*extensions.Scale) - return -} - -func (c *FakeScales) Update(kind string, scale *extensions.Scale) (result *extensions.Scale, err error) { - action := UpdateActionImpl{} - action.Verb = "update" - action.Namespace = c.Namespace - action.Resource = kind - action.Subresource = "scale" - action.Object = scale - obj, err := c.Fake.Invokes(action, scale) - result = obj.(*extensions.Scale) - return -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_secrets.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_secrets.go deleted file mode 100644 index f54cffb87..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_secrets.go +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2014 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/watch" -) - -// Fake implements SecretInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the method you want to test easier. -type FakeSecrets struct { - Fake *Fake - Namespace string -} - -func (c *FakeSecrets) Get(name string) (*api.Secret, error) { - obj, err := c.Fake.Invokes(NewGetAction("secrets", c.Namespace, name), &api.Secret{}) - if obj == nil { - return nil, err - } - - return obj.(*api.Secret), err -} - -func (c *FakeSecrets) List(opts api.ListOptions) (*api.SecretList, error) { - obj, err := c.Fake.Invokes(NewListAction("secrets", c.Namespace, opts), &api.SecretList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.SecretList), err -} - -func (c *FakeSecrets) Create(secret *api.Secret) (*api.Secret, error) { - obj, err := c.Fake.Invokes(NewCreateAction("secrets", c.Namespace, secret), secret) - if obj == nil { - return nil, err - } - - return obj.(*api.Secret), err -} - -func (c *FakeSecrets) Update(secret *api.Secret) (*api.Secret, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("secrets", c.Namespace, secret), secret) - if obj == nil { - return nil, err - } - - return obj.(*api.Secret), err -} - -func (c *FakeSecrets) Delete(name string) error { - _, err := c.Fake.Invokes(NewDeleteAction("secrets", c.Namespace, name), &api.Secret{}) - return err -} - -func (c *FakeSecrets) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("secrets", c.Namespace, opts)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_service_accounts.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_service_accounts.go deleted file mode 100644 index e40641668..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_service_accounts.go +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2014 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/watch" -) - -// FakeServiceAccounts implements ServiceAccountsInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the method you want to test easier. -type FakeServiceAccounts struct { - Fake *Fake - Namespace string -} - -func (c *FakeServiceAccounts) Get(name string) (*api.ServiceAccount, error) { - obj, err := c.Fake.Invokes(NewGetAction("serviceaccounts", c.Namespace, name), &api.ServiceAccount{}) - if obj == nil { - return nil, err - } - - return obj.(*api.ServiceAccount), err -} - -func (c *FakeServiceAccounts) List(opts api.ListOptions) (*api.ServiceAccountList, error) { - obj, err := c.Fake.Invokes(NewListAction("serviceaccounts", c.Namespace, opts), &api.ServiceAccountList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.ServiceAccountList), err -} - -func (c *FakeServiceAccounts) Create(serviceAccount *api.ServiceAccount) (*api.ServiceAccount, error) { - obj, err := c.Fake.Invokes(NewCreateAction("serviceaccounts", c.Namespace, serviceAccount), serviceAccount) - if obj == nil { - return nil, err - } - - return obj.(*api.ServiceAccount), err -} - -func (c *FakeServiceAccounts) Update(serviceAccount *api.ServiceAccount) (*api.ServiceAccount, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("serviceaccounts", c.Namespace, serviceAccount), serviceAccount) - if obj == nil { - return nil, err - } - - return obj.(*api.ServiceAccount), err -} - -func (c *FakeServiceAccounts) Delete(name string) error { - _, err := c.Fake.Invokes(NewDeleteAction("serviceaccounts", c.Namespace, name), &api.ServiceAccount{}) - return err -} - -func (c *FakeServiceAccounts) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("serviceaccounts", c.Namespace, opts)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_services.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_services.go deleted file mode 100644 index 2f17c7578..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_services.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2014 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/watch" -) - -// Fake implements ServiceInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the method you want to test easier. -type FakeServices struct { - Fake *Fake - Namespace string -} - -func (c *FakeServices) Get(name string) (*api.Service, error) { - obj, err := c.Fake.Invokes(NewGetAction("services", c.Namespace, name), &api.Service{}) - if obj == nil { - return nil, err - } - - return obj.(*api.Service), err -} - -func (c *FakeServices) List(opts api.ListOptions) (*api.ServiceList, error) { - obj, err := c.Fake.Invokes(NewListAction("services", c.Namespace, opts), &api.ServiceList{}) - if obj == nil { - return nil, err - } - - return obj.(*api.ServiceList), err -} - -func (c *FakeServices) Create(service *api.Service) (*api.Service, error) { - obj, err := c.Fake.Invokes(NewCreateAction("services", c.Namespace, service), service) - if obj == nil { - return nil, err - } - - return obj.(*api.Service), err -} - -func (c *FakeServices) Update(service *api.Service) (*api.Service, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("services", c.Namespace, service), service) - if obj == nil { - return nil, err - } - - return obj.(*api.Service), err -} - -func (c *FakeServices) UpdateStatus(service *api.Service) (result *api.Service, err error) { - obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("services", "status", c.Namespace, service), service) - if obj == nil { - return nil, err - } - - return obj.(*api.Service), err -} - -func (c *FakeServices) Delete(name string) error { - _, err := c.Fake.Invokes(NewDeleteAction("services", c.Namespace, name), &api.Service{}) - return err -} - -func (c *FakeServices) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("services", c.Namespace, opts)) -} - -func (c *FakeServices) ProxyGet(scheme, name, port, path string, params map[string]string) client.ResponseWrapper { - return c.Fake.InvokesProxy(NewProxyGetAction("services", c.Namespace, scheme, name, port, path, params)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_test.go deleted file mode 100644 index 303d6d786..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_test.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2014 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 testclient - -import ( - "testing" - - client "k8s.io/kubernetes/pkg/client/unversioned" -) - -// This test file just ensures that Fake and structs it is embedded in -// implement Interface. - -func TestFakeImplementsInterface(t *testing.T) { - _ = client.Interface(&Fake{}) -} - -type MyFake struct { - *Fake -} - -func TestEmbeddedFakeImplementsInterface(t *testing.T) { - _ = client.Interface(MyFake{&Fake{}}) - _ = client.Interface(&MyFake{&Fake{}}) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_thirdpartyresources.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_thirdpartyresources.go deleted file mode 100644 index 8aa198d70..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fake_thirdpartyresources.go +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright 2015 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 testclient - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apis/extensions" - kclientlib "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/watch" -) - -// FakeThirdPartyResources implements ThirdPartyResourceInterface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the method you want to test easier. -type FakeThirdPartyResources struct { - Fake *FakeExperimental - Namespace string -} - -// Ensure statically that FakeThirdPartyResources implements DaemonInterface. -var _ kclientlib.ThirdPartyResourceInterface = &FakeThirdPartyResources{} - -func (c *FakeThirdPartyResources) Get(name string) (*extensions.ThirdPartyResource, error) { - obj, err := c.Fake.Invokes(NewGetAction("thirdpartyresources", c.Namespace, name), &extensions.ThirdPartyResource{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.ThirdPartyResource), err -} - -func (c *FakeThirdPartyResources) List(opts api.ListOptions) (*extensions.ThirdPartyResourceList, error) { - obj, err := c.Fake.Invokes(NewListAction("thirdpartyresources", c.Namespace, opts), &extensions.ThirdPartyResourceList{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.ThirdPartyResourceList), err -} - -func (c *FakeThirdPartyResources) Create(daemon *extensions.ThirdPartyResource) (*extensions.ThirdPartyResource, error) { - obj, err := c.Fake.Invokes(NewCreateAction("thirdpartyresources", c.Namespace, daemon), &extensions.ThirdPartyResource{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.ThirdPartyResource), err -} - -func (c *FakeThirdPartyResources) Update(daemon *extensions.ThirdPartyResource) (*extensions.ThirdPartyResource, error) { - obj, err := c.Fake.Invokes(NewUpdateAction("thirdpartyresources", c.Namespace, daemon), &extensions.ThirdPartyResource{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.ThirdPartyResource), err -} - -func (c *FakeThirdPartyResources) UpdateStatus(daemon *extensions.ThirdPartyResource) (*extensions.ThirdPartyResource, error) { - obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("thirdpartyresources", "status", c.Namespace, daemon), &extensions.ThirdPartyResource{}) - if obj == nil { - return nil, err - } - return obj.(*extensions.ThirdPartyResource), err -} - -func (c *FakeThirdPartyResources) Delete(name string) error { - _, err := c.Fake.Invokes(NewDeleteAction("thirdpartyresources", c.Namespace, name), &extensions.ThirdPartyResource{}) - return err -} - -func (c *FakeThirdPartyResources) Watch(opts api.ListOptions) (watch.Interface, error) { - return c.Fake.InvokesWatch(NewWatchAction("thirdpartyresources", c.Namespace, opts)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fixture.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fixture.go deleted file mode 100644 index 64388fb63..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/fixture.go +++ /dev/null @@ -1,316 +0,0 @@ -/* -Copyright 2015 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 testclient - -import ( - "fmt" - "io/ioutil" - "reflect" - "strings" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/unversioned" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/yaml" - "k8s.io/kubernetes/pkg/watch" -) - -// ObjectRetriever abstracts the implementation for retrieving or setting generic -// objects. It is intended to be used to fake calls to a server by returning -// objects based on their kind and name. -type ObjectRetriever interface { - // Kind should return a resource or a list of resources (depending on the provided kind and - // name). It should return an error if the caller should communicate an error to the server. - Kind(gvk unversioned.GroupVersionKind, name string) (runtime.Object, error) - // Add adds a runtime object for test purposes into this object. - Add(runtime.Object) error -} - -// ObjectScheme abstracts the implementation of common operations on objects. -type ObjectScheme interface { - runtime.ObjectCreater - runtime.ObjectCopier - runtime.ObjectTyper -} - -// ObjectReaction returns a ReactionFunc that takes a generic action string of the form -// - or -- and attempts to return a runtime -// Object or error that matches the requested action. For instance, list-replicationControllers -// should attempt to return a list of replication controllers. This method delegates to the -// ObjectRetriever interface to satisfy retrieval of lists or retrieval of single items. -// TODO: add support for sub resources -func ObjectReaction(o ObjectRetriever, mapper meta.RESTMapper) ReactionFunc { - - return func(action Action) (bool, runtime.Object, error) { - kind, err := mapper.KindFor(unversioned.GroupVersionResource{Resource: action.GetResource()}) - if err != nil { - return false, nil, fmt.Errorf("unrecognized action %s: %v", action.GetResource(), err) - } - - // TODO: have mapper return a Kind for a subresource? - switch castAction := action.(type) { - case ListAction: - kind.Kind += "List" - resource, err := o.Kind(kind, "") - return true, resource, err - - case GetAction: - resource, err := o.Kind(kind, castAction.GetName()) - return true, resource, err - - case DeleteAction: - resource, err := o.Kind(kind, castAction.GetName()) - return true, resource, err - - case CreateAction: - meta, err := api.ObjectMetaFor(castAction.GetObject()) - if err != nil { - return true, nil, err - } - resource, err := o.Kind(kind, meta.Name) - return true, resource, err - - case UpdateAction: - meta, err := api.ObjectMetaFor(castAction.GetObject()) - if err != nil { - return true, nil, err - } - resource, err := o.Kind(kind, meta.Name) - return true, resource, err - - default: - return false, nil, fmt.Errorf("no reaction implemented for %s", action) - } - - return true, nil, nil - } -} - -// AddObjectsFromPath loads the JSON or YAML file containing Kubernetes API resources -// and adds them to the provided ObjectRetriever. -func AddObjectsFromPath(path string, o ObjectRetriever, decoder runtime.Decoder) error { - data, err := ioutil.ReadFile(path) - if err != nil { - return err - } - data, err = yaml.ToJSON(data) - if err != nil { - return err - } - obj, err := runtime.Decode(decoder, data) - if err != nil { - return err - } - if err := o.Add(obj); err != nil { - return err - } - return nil -} - -type objects struct { - types map[string][]runtime.Object - last map[string]int - scheme ObjectScheme - decoder runtime.Decoder -} - -var _ ObjectRetriever = &objects{} - -// NewObjects implements the ObjectRetriever interface by introspecting the -// objects provided to Add() and returning them when the Kind method is invoked. -// If an api.List object is provided to Add(), each child item is added. If an -// object is added that is itself a list (PodList, ServiceList) then that is added -// to the "PodList" kind. If no PodList is added, the retriever will take any loaded -// Pods and return them in a list. If an api.Status is added, and the Details.Kind field -// is set, that status will be returned instead (as an error if Status != Success, or -// as a runtime.Object if Status == Success). If multiple PodLists are provided, they -// will be returned in order by the Kind call, and the last PodList will be reused for -// subsequent calls. -func NewObjects(scheme ObjectScheme, decoder runtime.Decoder) ObjectRetriever { - return objects{ - types: make(map[string][]runtime.Object), - last: make(map[string]int), - scheme: scheme, - decoder: decoder, - } -} - -func (o objects) Kind(kind unversioned.GroupVersionKind, name string) (runtime.Object, error) { - kind.Version = runtime.APIVersionInternal - - empty, _ := o.scheme.New(kind) - nilValue := reflect.Zero(reflect.TypeOf(empty)).Interface().(runtime.Object) - - arr, ok := o.types[kind.Kind] - if !ok { - if strings.HasSuffix(kind.Kind, "List") { - itemKind := kind.Kind[:len(kind.Kind)-4] - arr, ok := o.types[itemKind] - if !ok { - return empty, nil - } - out, err := o.scheme.New(kind) - if err != nil { - return nilValue, err - } - if err := meta.SetList(out, arr); err != nil { - return nilValue, err - } - if out, err = o.scheme.Copy(out); err != nil { - return nilValue, err - } - return out, nil - } - return nilValue, errors.NewNotFound(unversioned.GroupResource{Group: kind.Group, Resource: kind.Kind}, name) - } - - index := o.last[kind.Kind] - if index >= len(arr) { - index = len(arr) - 1 - } - if index < 0 { - return nilValue, errors.NewNotFound(unversioned.GroupResource{Group: kind.Group, Resource: kind.Kind}, name) - } - out, err := o.scheme.Copy(arr[index]) - if err != nil { - return nilValue, err - } - o.last[kind.Kind] = index + 1 - - if status, ok := out.(*unversioned.Status); ok { - if status.Details != nil { - status.Details.Kind = kind.Kind - } - if status.Status != unversioned.StatusSuccess { - return nilValue, &errors.StatusError{ErrStatus: *status} - } - } - - return out, nil -} - -func (o objects) Add(obj runtime.Object) error { - gvk, err := o.scheme.ObjectKind(obj) - if err != nil { - return err - } - kind := gvk.Kind - - switch { - case meta.IsListType(obj): - if kind != "List" { - o.types[kind] = append(o.types[kind], obj) - } - - list, err := meta.ExtractList(obj) - if err != nil { - return err - } - if errs := runtime.DecodeList(list, o.decoder); len(errs) > 0 { - return errs[0] - } - for _, obj := range list { - if err := o.Add(obj); err != nil { - return err - } - } - default: - if status, ok := obj.(*unversioned.Status); ok && status.Details != nil { - kind = status.Details.Kind - } - o.types[kind] = append(o.types[kind], obj) - } - - return nil -} - -func DefaultWatchReactor(watchInterface watch.Interface, err error) WatchReactionFunc { - return func(action Action) (bool, watch.Interface, error) { - return true, watchInterface, err - } -} - -// SimpleReactor is a Reactor. Each reaction function is attached to a given verb,resource tuple. "*" in either field matches everything for that value. -// For instance, *,pods matches all verbs on pods. This allows for easier composition of reaction functions -type SimpleReactor struct { - Verb string - Resource string - - Reaction ReactionFunc -} - -func (r *SimpleReactor) Handles(action Action) bool { - verbCovers := r.Verb == "*" || r.Verb == action.GetVerb() - if !verbCovers { - return false - } - resourceCovers := r.Resource == "*" || r.Resource == action.GetResource() - if !resourceCovers { - return false - } - - return true -} - -func (r *SimpleReactor) React(action Action) (bool, runtime.Object, error) { - return r.Reaction(action) -} - -// SimpleWatchReactor is a WatchReactor. Each reaction function is attached to a given resource. "*" matches everything for that value. -// For instance, *,pods matches all verbs on pods. This allows for easier composition of reaction functions -type SimpleWatchReactor struct { - Resource string - - Reaction WatchReactionFunc -} - -func (r *SimpleWatchReactor) Handles(action Action) bool { - resourceCovers := r.Resource == "*" || r.Resource == action.GetResource() - if !resourceCovers { - return false - } - - return true -} - -func (r *SimpleWatchReactor) React(action Action) (bool, watch.Interface, error) { - return r.Reaction(action) -} - -// SimpleProxyReactor is a ProxyReactor. Each reaction function is attached to a given resource. "*" matches everything for that value. -// For instance, *,pods matches all verbs on pods. This allows for easier composition of reaction functions. -type SimpleProxyReactor struct { - Resource string - - Reaction ProxyReactionFunc -} - -func (r *SimpleProxyReactor) Handles(action Action) bool { - resourceCovers := r.Resource == "*" || r.Resource == action.GetResource() - if !resourceCovers { - return false - } - - return true -} - -func (r *SimpleProxyReactor) React(action Action) (bool, client.ResponseWrapper, error) { - return r.Reaction(action) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/simple/simple_testclient.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/simple/simple_testclient.go deleted file mode 100644 index ab32f5295..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/simple/simple_testclient.go +++ /dev/null @@ -1,240 +0,0 @@ -/* -Copyright 2015 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 simple - -import ( - "net/http/httptest" - "net/url" - "path" - "reflect" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/fields" - "k8s.io/kubernetes/pkg/labels" - "k8s.io/kubernetes/pkg/runtime" - utiltesting "k8s.io/kubernetes/pkg/util/testing" -) - -const NameRequiredError = "resource name may not be empty" - -type Request struct { - Method string - Path string - Header string - Query url.Values - Body runtime.Object - RawBody *string -} - -type Response struct { - StatusCode int - Body runtime.Object - RawBody *string -} - -type Client struct { - *client.Client - Clientset *clientset.Clientset - Request Request - Response Response - Error bool - Created bool - server *httptest.Server - handler *utiltesting.FakeHandler - // For query args, an optional function to validate the contents - // useful when the contents can change but still be correct. - // Maps from query arg key to validator. - // If no validator is present, string equality is used. - QueryValidator map[string]func(string, string) bool - - // If your object could exist in multiple groups, set this to - // correspond to the URL you're testing it with. - ResourceGroup string -} - -func (c *Client) Setup(t *testing.T) *Client { - c.handler = &utiltesting.FakeHandler{ - StatusCode: c.Response.StatusCode, - } - if responseBody := c.body(t, c.Response.Body, c.Response.RawBody); responseBody != nil { - c.handler.ResponseBody = *responseBody - } - c.server = httptest.NewServer(c.handler) - if c.Client == nil { - c.Client = client.NewOrDie(&client.Config{ - Host: c.server.URL, - ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}, - }) - - // TODO: caesarxuchao: hacky way to specify version of Experimental client. - // We will fix this by supporting multiple group versions in Config - c.AutoscalingClient = client.NewAutoscalingOrDie(&client.Config{ - Host: c.server.URL, - ContentConfig: client.ContentConfig{GroupVersion: testapi.Autoscaling.GroupVersion()}, - }) - c.ExtensionsClient = client.NewExtensionsOrDie(&client.Config{ - Host: c.server.URL, - ContentConfig: client.ContentConfig{GroupVersion: testapi.Extensions.GroupVersion()}, - }) - - c.Clientset = clientset.NewForConfigOrDie(&client.Config{Host: c.server.URL}) - } - c.QueryValidator = map[string]func(string, string) bool{} - return c -} - -func (c *Client) Close() { - if c.server != nil { - // TODO: Uncomment when fix #19254 - // c.server.Close() - } -} - -func (c *Client) ServerURL() string { - return c.server.URL -} - -func (c *Client) Validate(t *testing.T, received runtime.Object, err error) { - c.ValidateCommon(t, err) - - if c.Response.Body != nil && !api.Semantic.DeepDerivative(c.Response.Body, received) { - t.Errorf("bad response for request %#v: \nexpected %#v\ngot %#v\n", c.Request, c.Response.Body, received) - } -} - -func (c *Client) ValidateRaw(t *testing.T, received []byte, err error) { - c.ValidateCommon(t, err) - - if c.Response.Body != nil && !reflect.DeepEqual(c.Response.Body, received) { - t.Errorf("bad response for request %#v: expected %#v, got %#v", c.Request, c.Response.Body, received) - } -} - -func (c *Client) ValidateCommon(t *testing.T, err error) { - if c.Error { - if err == nil { - t.Errorf("error expected for %#v, got none", c.Request) - } - return - } - if err != nil { - t.Errorf("no error expected for %#v, got: %v", c.Request, err) - } - - if c.handler.RequestReceived == nil { - t.Errorf("handler had an empty request, %#v", c) - return - } - - requestBody := c.body(t, c.Request.Body, c.Request.RawBody) - actualQuery := c.handler.RequestReceived.URL.Query() - t.Logf("got query: %v", actualQuery) - t.Logf("path: %v", c.Request.Path) - // We check the query manually, so blank it out so that FakeHandler.ValidateRequest - // won't check it. - c.handler.RequestReceived.URL.RawQuery = "" - c.handler.ValidateRequest(t, path.Join(c.Request.Path), c.Request.Method, requestBody) - for key, values := range c.Request.Query { - validator, ok := c.QueryValidator[key] - if !ok { - switch key { - case unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String()): - validator = ValidateLabels - case unversioned.FieldSelectorQueryParam(testapi.Default.GroupVersion().String()): - validator = validateFields - default: - validator = func(a, b string) bool { return a == b } - } - } - observed := actualQuery.Get(key) - wanted := strings.Join(values, "") - if !validator(wanted, observed) { - t.Errorf("Unexpected query arg for key: %s. Expected %s, Received %s", key, wanted, observed) - } - } - if c.Request.Header != "" { - if c.handler.RequestReceived.Header.Get(c.Request.Header) == "" { - t.Errorf("header %q not found in request %#v", c.Request.Header, c.handler.RequestReceived) - } - } - - if expected, received := requestBody, c.handler.RequestBody; expected != nil && *expected != received { - t.Errorf("bad body for request %#v: expected %s, got %s", c.Request, *expected, received) - } -} - -// buildQueryValues is a convenience function for knowing if a namespace should be in a query param or not -func BuildQueryValues(query url.Values) url.Values { - v := url.Values{} - if query != nil { - for key, values := range query { - for _, value := range values { - v.Add(key, value) - } - } - } - return v -} - -func ValidateLabels(a, b string) bool { - sA, eA := labels.Parse(a) - if eA != nil { - return false - } - sB, eB := labels.Parse(b) - if eB != nil { - return false - } - return sA.String() == sB.String() -} - -func validateFields(a, b string) bool { - sA, _ := fields.ParseSelector(a) - sB, _ := fields.ParseSelector(b) - return sA.String() == sB.String() -} - -func (c *Client) body(t *testing.T, obj runtime.Object, raw *string) *string { - if obj != nil { - fqKind, err := api.Scheme.ObjectKind(obj) - if err != nil { - t.Errorf("unexpected encoding error: %v", err) - } - groupName := fqKind.GroupVersion().Group - if c.ResourceGroup != "" { - groupName = c.ResourceGroup - } - var bs []byte - g, found := testapi.Groups[groupName] - if !found { - t.Errorf("Group %s is not registered in testapi", groupName) - } - bs, err = runtime.Encode(g.Codec(), obj) - if err != nil { - t.Errorf("unexpected encoding error: %v", err) - } - body := string(bs) - return &body - } - return raw -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/testclient.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/testclient.go deleted file mode 100644 index ef962e417..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/testclient.go +++ /dev/null @@ -1,399 +0,0 @@ -/* -Copyright 2015 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 testclient - -import ( - "fmt" - "sync" - - "github.com/emicklei/go-restful/swagger" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/v1" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/version" - "k8s.io/kubernetes/pkg/watch" -) - -// NewSimpleFake returns a client that will respond with the provided objects -func NewSimpleFake(objects ...runtime.Object) *Fake { - o := NewObjects(api.Scheme, api.Codecs.UniversalDecoder()) - for _, obj := range objects { - if err := o.Add(obj); err != nil { - panic(err) - } - } - - fakeClient := &Fake{} - fakeClient.AddReactor("*", "*", ObjectReaction(o, api.RESTMapper)) - - fakeClient.AddWatchReactor("*", DefaultWatchReactor(watch.NewFake(), nil)) - - return fakeClient -} - -// Fake implements client.Interface. Meant to be embedded into a struct to get a default -// implementation. This makes faking out just the method you want to test easier. -type Fake struct { - sync.RWMutex - actions []Action // these may be castable to other types, but "Action" is the minimum - - // ReactionChain is the list of reactors that will be attempted for every request in the order they are tried - ReactionChain []Reactor - // WatchReactionChain is the list of watch reactors that will be attempted for every request in the order they are tried - WatchReactionChain []WatchReactor - // ProxyReactionChain is the list of proxy reactors that will be attempted for every request in the order they are tried - ProxyReactionChain []ProxyReactor - - Resources map[string]*unversioned.APIResourceList -} - -// Reactor is an interface to allow the composition of reaction functions. -type Reactor interface { - // Handles indicates whether or not this Reactor deals with a given action - Handles(action Action) bool - // React handles the action and returns results. It may choose to delegate by indicated handled=false - React(action Action) (handled bool, ret runtime.Object, err error) -} - -// WatchReactor is an interface to allow the composition of watch functions. -type WatchReactor interface { - // Handles indicates whether or not this Reactor deals with a given action - Handles(action Action) bool - // React handles a watch action and returns results. It may choose to delegate by indicated handled=false - React(action Action) (handled bool, ret watch.Interface, err error) -} - -// ProxyReactor is an interface to allow the composition of proxy get functions. -type ProxyReactor interface { - // Handles indicates whether or not this Reactor deals with a given action - Handles(action Action) bool - // React handles a watch action and returns results. It may choose to delegate by indicated handled=false - React(action Action) (handled bool, ret client.ResponseWrapper, err error) -} - -// ReactionFunc is a function that returns an object or error for a given Action. If "handled" is false, -// then the test client will continue ignore the results and continue to the next ReactionFunc -type ReactionFunc func(action Action) (handled bool, ret runtime.Object, err error) - -// WatchReactionFunc is a function that returns a watch interface. If "handled" is false, -// then the test client will continue ignore the results and continue to the next ReactionFunc -type WatchReactionFunc func(action Action) (handled bool, ret watch.Interface, err error) - -// ProxyReactionFunc is a function that returns a ResponseWrapper interface for a given Action. If "handled" is false, -// then the test client will continue ignore the results and continue to the next ProxyReactionFunc -type ProxyReactionFunc func(action Action) (handled bool, ret client.ResponseWrapper, err error) - -// AddReactor appends a reactor to the end of the chain -func (c *Fake) AddReactor(verb, resource string, reaction ReactionFunc) { - c.ReactionChain = append(c.ReactionChain, &SimpleReactor{verb, resource, reaction}) -} - -// PrependReactor adds a reactor to the beginning of the chain -func (c *Fake) PrependReactor(verb, resource string, reaction ReactionFunc) { - c.ReactionChain = append([]Reactor{&SimpleReactor{verb, resource, reaction}}, c.ReactionChain...) -} - -// AddWatchReactor appends a reactor to the end of the chain -func (c *Fake) AddWatchReactor(resource string, reaction WatchReactionFunc) { - c.WatchReactionChain = append(c.WatchReactionChain, &SimpleWatchReactor{resource, reaction}) -} - -// PrependWatchReactor adds a reactor to the beginning of the chain -func (c *Fake) PrependWatchReactor(resource string, reaction WatchReactionFunc) { - c.WatchReactionChain = append([]WatchReactor{&SimpleWatchReactor{resource, reaction}}, c.WatchReactionChain...) -} - -// AddProxyReactor appends a reactor to the end of the chain -func (c *Fake) AddProxyReactor(resource string, reaction ProxyReactionFunc) { - c.ProxyReactionChain = append(c.ProxyReactionChain, &SimpleProxyReactor{resource, reaction}) -} - -// PrependProxyReactor adds a reactor to the beginning of the chain -func (c *Fake) PrependProxyReactor(resource string, reaction ProxyReactionFunc) { - c.ProxyReactionChain = append([]ProxyReactor{&SimpleProxyReactor{resource, reaction}}, c.ProxyReactionChain...) -} - -// Invokes records the provided Action and then invokes the ReactFn (if provided). -// defaultReturnObj is expected to be of the same type a normal call would return. -func (c *Fake) Invokes(action Action, defaultReturnObj runtime.Object) (runtime.Object, error) { - c.Lock() - defer c.Unlock() - - c.actions = append(c.actions, action) - for _, reactor := range c.ReactionChain { - if !reactor.Handles(action) { - continue - } - - handled, ret, err := reactor.React(action) - if !handled { - continue - } - - return ret, err - } - - return defaultReturnObj, nil -} - -// InvokesWatch records the provided Action and then invokes the ReactFn (if provided). -func (c *Fake) InvokesWatch(action Action) (watch.Interface, error) { - c.Lock() - defer c.Unlock() - - c.actions = append(c.actions, action) - for _, reactor := range c.WatchReactionChain { - if !reactor.Handles(action) { - continue - } - - handled, ret, err := reactor.React(action) - if !handled { - continue - } - - return ret, err - } - - return nil, fmt.Errorf("unhandled watch: %#v", action) -} - -// InvokesProxy records the provided Action and then invokes the ReactFn (if provided). -func (c *Fake) InvokesProxy(action Action) client.ResponseWrapper { - c.Lock() - defer c.Unlock() - - c.actions = append(c.actions, action) - for _, reactor := range c.ProxyReactionChain { - if !reactor.Handles(action) { - continue - } - - handled, ret, err := reactor.React(action) - if !handled || err != nil { - continue - } - - return ret - } - - return nil -} - -// ClearActions clears the history of actions called on the fake client -func (c *Fake) ClearActions() { - c.Lock() - c.Unlock() - - c.actions = make([]Action, 0) -} - -// Actions returns a chronologically ordered slice fake actions called on the fake client -func (c *Fake) Actions() []Action { - c.RLock() - defer c.RUnlock() - fa := make([]Action, len(c.actions)) - copy(fa, c.actions) - return fa -} - -func (c *Fake) LimitRanges(namespace string) client.LimitRangeInterface { - return &FakeLimitRanges{Fake: c, Namespace: namespace} -} - -func (c *Fake) ResourceQuotas(namespace string) client.ResourceQuotaInterface { - return &FakeResourceQuotas{Fake: c, Namespace: namespace} -} - -func (c *Fake) ReplicationControllers(namespace string) client.ReplicationControllerInterface { - return &FakeReplicationControllers{Fake: c, Namespace: namespace} -} - -func (c *Fake) Nodes() client.NodeInterface { - return &FakeNodes{Fake: c} -} - -func (c *Fake) PodSecurityPolicies() client.PodSecurityPolicyInterface { - return &FakePodSecurityPolicy{Fake: c} -} - -func (c *Fake) Events(namespace string) client.EventInterface { - return &FakeEvents{Fake: c, Namespace: namespace} -} - -func (c *Fake) Endpoints(namespace string) client.EndpointsInterface { - return &FakeEndpoints{Fake: c, Namespace: namespace} -} - -func (c *Fake) PersistentVolumes() client.PersistentVolumeInterface { - return &FakePersistentVolumes{Fake: c} -} - -func (c *Fake) PersistentVolumeClaims(namespace string) client.PersistentVolumeClaimInterface { - return &FakePersistentVolumeClaims{Fake: c, Namespace: namespace} -} - -func (c *Fake) Pods(namespace string) client.PodInterface { - return &FakePods{Fake: c, Namespace: namespace} -} - -func (c *Fake) PodTemplates(namespace string) client.PodTemplateInterface { - return &FakePodTemplates{Fake: c, Namespace: namespace} -} - -func (c *Fake) Services(namespace string) client.ServiceInterface { - return &FakeServices{Fake: c, Namespace: namespace} -} - -func (c *Fake) ServiceAccounts(namespace string) client.ServiceAccountsInterface { - return &FakeServiceAccounts{Fake: c, Namespace: namespace} -} - -func (c *Fake) Secrets(namespace string) client.SecretsInterface { - return &FakeSecrets{Fake: c, Namespace: namespace} -} - -func (c *Fake) Namespaces() client.NamespaceInterface { - return &FakeNamespaces{Fake: c} -} - -func (c *Fake) Autoscaling() client.AutoscalingInterface { - return &FakeAutoscaling{c} -} - -func (c *Fake) Extensions() client.ExtensionsInterface { - return &FakeExperimental{c} -} - -func (c *Fake) Discovery() client.DiscoveryInterface { - return &FakeDiscovery{c} -} - -func (c *Fake) ComponentStatuses() client.ComponentStatusInterface { - return &FakeComponentStatuses{Fake: c} -} - -func (c *Fake) ConfigMaps(namespace string) client.ConfigMapsInterface { - return &FakeConfigMaps{Fake: c, Namespace: namespace} -} - -// SwaggerSchema returns an empty swagger.ApiDeclaration for testing -func (c *Fake) SwaggerSchema(version unversioned.GroupVersion) (*swagger.ApiDeclaration, error) { - action := ActionImpl{} - action.Verb = "get" - if version == v1.SchemeGroupVersion { - action.Resource = "/swaggerapi/api/" + version.Version - } else { - action.Resource = "/swaggerapi/apis/" + version.Group + "/" + version.Version - } - - c.Invokes(action, nil) - return &swagger.ApiDeclaration{}, nil -} - -// NewSimpleFakeAutoscaling returns a client that will respond with the provided objects -func NewSimpleFakeAutoscaling(objects ...runtime.Object) *FakeAutoscaling { - return &FakeAutoscaling{Fake: NewSimpleFake(objects...)} -} - -type FakeAutoscaling struct { - *Fake -} - -func (c *FakeAutoscaling) HorizontalPodAutoscalers(namespace string) client.HorizontalPodAutoscalerInterface { - return &FakeHorizontalPodAutoscalersV1{Fake: c, Namespace: namespace} -} - -// NewSimpleFakeExp returns a client that will respond with the provided objects -func NewSimpleFakeExp(objects ...runtime.Object) *FakeExperimental { - return &FakeExperimental{Fake: NewSimpleFake(objects...)} -} - -type FakeExperimental struct { - *Fake -} - -func (c *FakeExperimental) DaemonSets(namespace string) client.DaemonSetInterface { - return &FakeDaemonSets{Fake: c, Namespace: namespace} -} - -func (c *FakeExperimental) HorizontalPodAutoscalers(namespace string) client.HorizontalPodAutoscalerInterface { - return &FakeHorizontalPodAutoscalers{Fake: c, Namespace: namespace} -} - -func (c *FakeExperimental) Deployments(namespace string) client.DeploymentInterface { - return &FakeDeployments{Fake: c, Namespace: namespace} -} - -func (c *FakeExperimental) Scales(namespace string) client.ScaleInterface { - return &FakeScales{Fake: c, Namespace: namespace} -} - -func (c *FakeExperimental) Jobs(namespace string) client.JobInterface { - return &FakeJobs{Fake: c, Namespace: namespace} -} - -func (c *FakeExperimental) Ingress(namespace string) client.IngressInterface { - return &FakeIngress{Fake: c, Namespace: namespace} -} - -func (c *FakeExperimental) ThirdPartyResources(namespace string) client.ThirdPartyResourceInterface { - return &FakeThirdPartyResources{Fake: c, Namespace: namespace} -} - -func (c *FakeExperimental) ReplicaSets(namespace string) client.ReplicaSetInterface { - return &FakeReplicaSets{Fake: c, Namespace: namespace} -} - -type FakeDiscovery struct { - *Fake -} - -func (c *FakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*unversioned.APIResourceList, error) { - action := ActionImpl{ - Verb: "get", - Resource: "resource", - } - c.Invokes(action, nil) - return c.Resources[groupVersion], nil -} - -func (c *FakeDiscovery) ServerResources() (map[string]*unversioned.APIResourceList, error) { - action := ActionImpl{ - Verb: "get", - Resource: "resource", - } - c.Invokes(action, nil) - return c.Resources, nil -} - -func (c *FakeDiscovery) ServerGroups() (*unversioned.APIGroupList, error) { - return nil, nil -} - -func (c *FakeDiscovery) ServerVersion() (*version.Info, error) { - action := ActionImpl{} - action.Verb = "get" - action.Resource = "version" - - c.Invokes(action, nil) - versionInfo := version.Get() - return &versionInfo, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/testclient_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/testclient_test.go deleted file mode 100644 index b31bdc213..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/testclient/testclient_test.go +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2015 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 testclient - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/runtime" -) - -func TestNewClient(t *testing.T) { - o := NewObjects(api.Scheme, api.Codecs.UniversalDecoder()) - if err := AddObjectsFromPath("../../../../examples/guestbook/frontend-service.yaml", o, api.Codecs.UniversalDecoder()); err != nil { - t.Fatal(err) - } - client := &Fake{} - client.AddReactor("*", "*", ObjectReaction(o, testapi.Default.RESTMapper())) - list, err := client.Services("test").List(api.ListOptions{}) - if err != nil { - t.Fatal(err) - } - if len(list.Items) != 1 { - t.Fatalf("unexpected list %#v", list) - } - - // When list is invoked a second time, the same results are returned. - list, err = client.Services("test").List(api.ListOptions{}) - if err != nil { - t.Fatal(err) - } - if len(list.Items) != 1 { - t.Fatalf("unexpected list %#v", list) - } - t.Logf("list: %#v", list) -} - -func TestErrors(t *testing.T) { - o := NewObjects(api.Scheme, api.Codecs.UniversalDecoder()) - o.Add(&api.List{ - Items: []runtime.Object{ - // This first call to List will return this error - &(errors.NewNotFound(api.Resource("ServiceList"), "").(*errors.StatusError).ErrStatus), - // The second call to List will return this error - &(errors.NewForbidden(api.Resource("ServiceList"), "", nil).(*errors.StatusError).ErrStatus), - }, - }) - client := &Fake{} - client.AddReactor("*", "*", ObjectReaction(o, testapi.Default.RESTMapper())) - _, err := client.Services("test").List(api.ListOptions{}) - if !errors.IsNotFound(err) { - t.Fatalf("unexpected error: %v", err) - } - t.Logf("error: %#v", err.(*errors.StatusError).Status()) - _, err = client.Services("test").List(api.ListOptions{}) - if !errors.IsForbidden(err) { - t.Fatalf("unexpected error: %v", err) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/thirdpartyresources_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/thirdpartyresources_test.go deleted file mode 100644 index bf2e9e15c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/thirdpartyresources_test.go +++ /dev/null @@ -1,185 +0,0 @@ -/* -Copyright 2015 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 unversioned_test - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/apis/extensions" - . "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" -) - -func getThirdPartyResourceName() string { - return "thirdpartyresources" -} - -func TestListThirdPartyResources(t *testing.T) { - ns := api.NamespaceAll - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Extensions.ResourcePath(getThirdPartyResourceName(), ns, ""), - }, - Response: simple.Response{StatusCode: 200, - Body: &extensions.ThirdPartyResourceList{ - Items: []extensions.ThirdPartyResource{ - { - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Description: "test third party resource", - }, - }, - }, - }, - } - receivedDSs, err := c.Setup(t).Extensions().ThirdPartyResources(ns).List(api.ListOptions{}) - defer c.Close() - c.Validate(t, receivedDSs, err) - -} - -func TestGetThirdPartyResource(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "GET", Path: testapi.Extensions.ResourcePath(getThirdPartyResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.ThirdPartyResource{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Description: "test third party resource", - }, - }, - } - receivedThirdPartyResource, err := c.Setup(t).Extensions().ThirdPartyResources(ns).Get("foo") - defer c.Close() - c.Validate(t, receivedThirdPartyResource, err) -} - -func TestGetThirdPartyResourceWithNoName(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{Error: true} - receivedPod, err := c.Setup(t).Extensions().ThirdPartyResources(ns).Get("") - defer c.Close() - if (err != nil) && (err.Error() != simple.NameRequiredError) { - t.Errorf("Expected error: %v, but got %v", simple.NameRequiredError, err) - } - - c.Validate(t, receivedPod, err) -} - -func TestUpdateThirdPartyResource(t *testing.T) { - ns := api.NamespaceDefault - requestThirdPartyResource := &extensions.ThirdPartyResource{ - ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Extensions.ResourcePath(getThirdPartyResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.ThirdPartyResource{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Description: "test third party resource", - }, - }, - } - receivedThirdPartyResource, err := c.Setup(t).Extensions().ThirdPartyResources(ns).Update(requestThirdPartyResource) - defer c.Close() - c.Validate(t, receivedThirdPartyResource, err) -} - -func TestUpdateThirdPartyResourceUpdateStatus(t *testing.T) { - ns := api.NamespaceDefault - requestThirdPartyResource := &extensions.ThirdPartyResource{ - ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, - } - c := &simple.Client{ - Request: simple.Request{Method: "PUT", Path: testapi.Extensions.ResourcePath(getThirdPartyResourceName(), ns, "foo") + "/status", Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.ThirdPartyResource{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Description: "test third party resource", - }, - }, - } - receivedThirdPartyResource, err := c.Setup(t).Extensions().ThirdPartyResources(ns).UpdateStatus(requestThirdPartyResource) - defer c.Close() - c.Validate(t, receivedThirdPartyResource, err) -} - -func TestDeleteThirdPartyResource(t *testing.T) { - ns := api.NamespaceDefault - c := &simple.Client{ - Request: simple.Request{Method: "DELETE", Path: testapi.Extensions.ResourcePath(getThirdPartyResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{StatusCode: 200}, - } - err := c.Setup(t).Extensions().ThirdPartyResources(ns).Delete("foo") - defer c.Close() - c.Validate(t, nil, err) -} - -func TestCreateThirdPartyResource(t *testing.T) { - ns := api.NamespaceDefault - requestThirdPartyResource := &extensions.ThirdPartyResource{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - } - c := &simple.Client{ - Request: simple.Request{Method: "POST", Path: testapi.Extensions.ResourcePath(getThirdPartyResourceName(), ns, ""), Body: requestThirdPartyResource, Query: simple.BuildQueryValues(nil)}, - Response: simple.Response{ - StatusCode: 200, - Body: &extensions.ThirdPartyResource{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{ - "foo": "bar", - "name": "baz", - }, - }, - Description: "test third party resource", - }, - }, - } - receivedThirdPartyResource, err := c.Setup(t).Extensions().ThirdPartyResources(ns).Create(requestThirdPartyResource) - defer c.Close() - c.Validate(t, receivedThirdPartyResource, err) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/urlbackoff_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/urlbackoff_test.go deleted file mode 100644 index 8457c977b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/unversioned/urlbackoff_test.go +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright 2014 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 unversioned - -import ( - "k8s.io/kubernetes/pkg/util" - "net/url" - "testing" - "time" -) - -func parse(raw string) *url.URL { - theUrl, _ := url.Parse(raw) - return theUrl -} - -func TestURLBackoffFunctionalityCollisions(t *testing.T) { - myBackoff := &URLBackoff{ - Backoff: util.NewBackOff(1*time.Second, 60*time.Second), - } - - // Add some noise and make sure backoff for a clean URL is zero. - myBackoff.UpdateBackoff(parse("http://100.200.300.400:8080"), nil, 500) - - myBackoff.UpdateBackoff(parse("http://1.2.3.4:8080"), nil, 500) - - if myBackoff.CalculateBackoff(parse("http://1.2.3.4:100")) > 0 { - t.Errorf("URLs are colliding in the backoff map!") - } -} - -// TestURLBackoffFunctionality generally tests the URLBackoff wrapper. We avoid duplicating tests from backoff and request. -func TestURLBackoffFunctionality(t *testing.T) { - myBackoff := &URLBackoff{ - Backoff: util.NewBackOff(1*time.Second, 60*time.Second), - } - - // Now test that backoff increases, then recovers. - // 200 and 300 should both result in clearing the backoff. - // all others like 429 should result in increased backoff. - seconds := []int{0, - 1, 2, 4, 8, 0, - 1, 2} - returnCodes := []int{ - 429, 500, 501, 502, 300, - 500, 501, 502, - } - - if len(seconds) != len(returnCodes) { - t.Fatalf("responseCode to backoff arrays should be the same length... sanity check failed.") - } - - for i, sec := range seconds { - backoffSec := myBackoff.CalculateBackoff(parse("http://1.2.3.4:100")) - if backoffSec < time.Duration(sec)*time.Second || backoffSec > time.Duration(sec+5)*time.Second { - t.Errorf("Backoff out of range %v: %v %v", i, sec, backoffSec) - } - myBackoff.UpdateBackoff(parse("http://1.2.3.4:100/responseCodeForFuncTest"), nil, returnCodes[i]) - } - - if myBackoff.CalculateBackoff(parse("http://1.2.3.4:100")) == 0 { - t.Errorf("The final return code %v should have resulted in a backoff ! ", returnCodes[7]) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/OWNERS b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/OWNERS new file mode 100644 index 000000000..31a963ed8 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/OWNERS @@ -0,0 +1,3 @@ +assignees: + - davidopp + - mikedanese diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/cloud.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/cloud.go index e145b26de..dd120735e 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/cloud.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/cloud.go @@ -84,7 +84,7 @@ type LoadBalancer interface { // if so, what its status is. GetLoadBalancer(name, region string) (status *api.LoadBalancerStatus, exists bool, err error) // EnsureLoadBalancer creates a new load balancer 'name', or updates the existing one. Returns the status of the balancer - EnsureLoadBalancer(name, region string, loadBalancerIP net.IP, ports []*api.ServicePort, hosts []string, serviceName types.NamespacedName, affinityType api.ServiceAffinity) (*api.LoadBalancerStatus, error) + EnsureLoadBalancer(name, region string, loadBalancerIP net.IP, ports []*api.ServicePort, hosts []string, serviceName types.NamespacedName, affinityType api.ServiceAffinity, annotations map[string]string) (*api.LoadBalancerStatus, error) // UpdateLoadBalancer updates hosts under the specified load balancer. UpdateLoadBalancer(name, region string, hosts []string) error // EnsureLoadBalancerDeleted deletes the specified load balancer if it diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws.go deleted file mode 100644 index 9df66a2b7..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws.go +++ /dev/null @@ -1,2380 +0,0 @@ -/* -Copyright 2014 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 aws - -import ( - "errors" - "fmt" - "io" - "net" - "net/url" - "os" - "regexp" - "strings" - "sync" - "time" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" - "github.com/aws/aws-sdk-go/aws/ec2metadata" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/autoscaling" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/elb" - "github.com/scalingdata/gcfg" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/cloudprovider" - "k8s.io/kubernetes/pkg/credentialprovider/aws" - "k8s.io/kubernetes/pkg/types" - "k8s.io/kubernetes/pkg/util/sets" - - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/api/unversioned" -) - -const ProviderName = "aws" - -// The tag name we use to differentiate multiple logically independent clusters running in the same AZ -const TagNameKubernetesCluster = "KubernetesCluster" - -// The tag name we use to differentiate multiple services. Used currently for ELBs only. -const TagNameKubernetesService = "kubernetes.io/service-name" - -// We sometimes read to see if something exists; then try to create it if we didn't find it -// This can fail once in a consistent system if done in parallel -// In an eventually consistent system, it could fail unboundedly -// MaxReadThenCreateRetries sets the maximum number of attempts we will make -const MaxReadThenCreateRetries = 30 - -// Default volume type for newly created Volumes -// TODO: Remove when user/admin can configure volume types and thus we don't -// need hardcoded defaults. -const DefaultVolumeType = "gp2" - -// Used to call aws_credentials.Init() just once -var once sync.Once - -// Abstraction over AWS, to allow mocking/other implementations -type AWSServices interface { - Compute(region string) (EC2, error) - LoadBalancing(region string) (ELB, error) - Autoscaling(region string) (ASG, error) - Metadata() (EC2Metadata, error) -} - -// TODO: Should we rename this to AWS (EBS & ELB are not technically part of EC2) -// Abstraction over EC2, to allow mocking/other implementations -// Note that the DescribeX functions return a list, so callers don't need to deal with paging -type EC2 interface { - // Query EC2 for instances matching the filter - DescribeInstances(request *ec2.DescribeInstancesInput) ([]*ec2.Instance, error) - - // Attach a volume to an instance - AttachVolume(*ec2.AttachVolumeInput) (*ec2.VolumeAttachment, error) - // Detach a volume from an instance it is attached to - DetachVolume(request *ec2.DetachVolumeInput) (resp *ec2.VolumeAttachment, err error) - // Lists volumes - DescribeVolumes(request *ec2.DescribeVolumesInput) ([]*ec2.Volume, error) - // Create an EBS volume - CreateVolume(request *ec2.CreateVolumeInput) (resp *ec2.Volume, err error) - // Delete an EBS volume - DeleteVolume(*ec2.DeleteVolumeInput) (*ec2.DeleteVolumeOutput, error) - - DescribeSecurityGroups(request *ec2.DescribeSecurityGroupsInput) ([]*ec2.SecurityGroup, error) - - CreateSecurityGroup(*ec2.CreateSecurityGroupInput) (*ec2.CreateSecurityGroupOutput, error) - DeleteSecurityGroup(request *ec2.DeleteSecurityGroupInput) (*ec2.DeleteSecurityGroupOutput, error) - - AuthorizeSecurityGroupIngress(*ec2.AuthorizeSecurityGroupIngressInput) (*ec2.AuthorizeSecurityGroupIngressOutput, error) - RevokeSecurityGroupIngress(*ec2.RevokeSecurityGroupIngressInput) (*ec2.RevokeSecurityGroupIngressOutput, error) - - DescribeSubnets(*ec2.DescribeSubnetsInput) ([]*ec2.Subnet, error) - - CreateTags(*ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) - - DescribeRouteTables(request *ec2.DescribeRouteTablesInput) ([]*ec2.RouteTable, error) - CreateRoute(request *ec2.CreateRouteInput) (*ec2.CreateRouteOutput, error) - DeleteRoute(request *ec2.DeleteRouteInput) (*ec2.DeleteRouteOutput, error) - - ModifyInstanceAttribute(request *ec2.ModifyInstanceAttributeInput) (*ec2.ModifyInstanceAttributeOutput, error) -} - -// This is a simple pass-through of the ELB client interface, which allows for testing -type ELB interface { - CreateLoadBalancer(*elb.CreateLoadBalancerInput) (*elb.CreateLoadBalancerOutput, error) - DeleteLoadBalancer(*elb.DeleteLoadBalancerInput) (*elb.DeleteLoadBalancerOutput, error) - DescribeLoadBalancers(*elb.DescribeLoadBalancersInput) (*elb.DescribeLoadBalancersOutput, error) - RegisterInstancesWithLoadBalancer(*elb.RegisterInstancesWithLoadBalancerInput) (*elb.RegisterInstancesWithLoadBalancerOutput, error) - DeregisterInstancesFromLoadBalancer(*elb.DeregisterInstancesFromLoadBalancerInput) (*elb.DeregisterInstancesFromLoadBalancerOutput, error) - - DetachLoadBalancerFromSubnets(*elb.DetachLoadBalancerFromSubnetsInput) (*elb.DetachLoadBalancerFromSubnetsOutput, error) - AttachLoadBalancerToSubnets(*elb.AttachLoadBalancerToSubnetsInput) (*elb.AttachLoadBalancerToSubnetsOutput, error) - - CreateLoadBalancerListeners(*elb.CreateLoadBalancerListenersInput) (*elb.CreateLoadBalancerListenersOutput, error) - DeleteLoadBalancerListeners(*elb.DeleteLoadBalancerListenersInput) (*elb.DeleteLoadBalancerListenersOutput, error) - - ApplySecurityGroupsToLoadBalancer(*elb.ApplySecurityGroupsToLoadBalancerInput) (*elb.ApplySecurityGroupsToLoadBalancerOutput, error) - - ConfigureHealthCheck(*elb.ConfigureHealthCheckInput) (*elb.ConfigureHealthCheckOutput, error) -} - -// This is a simple pass-through of the Autoscaling client interface, which allows for testing -type ASG interface { - UpdateAutoScalingGroup(*autoscaling.UpdateAutoScalingGroupInput) (*autoscaling.UpdateAutoScalingGroupOutput, error) - DescribeAutoScalingGroups(*autoscaling.DescribeAutoScalingGroupsInput) (*autoscaling.DescribeAutoScalingGroupsOutput, error) -} - -// Abstraction over the AWS metadata service -type EC2Metadata interface { - // Query the EC2 metadata service (used to discover instance-id etc) - GetMetadata(path string) (string, error) -} - -type VolumeOptions struct { - CapacityGB int - Tags *map[string]string -} - -// Volumes is an interface for managing cloud-provisioned volumes -// TODO: Allow other clouds to implement this -type Volumes interface { - // Attach the disk to the specified instance - // instanceName can be empty to mean "the instance on which we are running" - // Returns the device (e.g. /dev/xvdf) where we attached the volume - AttachDisk(diskName string, instanceName string, readOnly bool) (string, error) - // Detach the disk from the specified instance - // instanceName can be empty to mean "the instance on which we are running" - // Returns the device where the volume was attached - DetachDisk(diskName string, instanceName string) (string, error) - - // Create a volume with the specified options - CreateDisk(volumeOptions *VolumeOptions) (volumeName string, err error) - // Delete the specified volume - // Returns true iff the volume was deleted - // If the was not found, returns (false, nil) - DeleteDisk(volumeName string) (bool, error) - - // Get labels to apply to volume on creation - GetVolumeLabels(volumeName string) (map[string]string, error) -} - -// InstanceGroups is an interface for managing cloud-managed instance groups / autoscaling instance groups -// TODO: Allow other clouds to implement this -type InstanceGroups interface { - // Set the size to the fixed size - ResizeInstanceGroup(instanceGroupName string, size int) error - // Queries the cloud provider for information about the specified instance group - DescribeInstanceGroup(instanceGroupName string) (InstanceGroupInfo, error) -} - -// InstanceGroupInfo is returned by InstanceGroups.Describe, and exposes information about the group. -type InstanceGroupInfo interface { - // The number of instances currently running under control of this group - CurrentSize() (int, error) -} - -// AWSCloud is an implementation of Interface, LoadBalancer and Instances for Amazon Web Services. -type AWSCloud struct { - ec2 EC2 - elb ELB - asg ASG - metadata EC2Metadata - cfg *AWSCloudConfig - availabilityZone string - region string - - filterTags map[string]string - - // The AWS instance that we are running on - selfAWSInstance *awsInstance - - mutex sync.Mutex -} - -var _ Volumes = &AWSCloud{} - -type AWSCloudConfig struct { - Global struct { - // TODO: Is there any use for this? We can get it from the instance metadata service - // Maybe if we're not running on AWS, e.g. bootstrap; for now it is not very useful - Zone string - - KubernetesClusterTag string - } -} - -// awsSdkEC2 is an implementation of the EC2 interface, backed by aws-sdk-go -type awsSdkEC2 struct { - ec2 *ec2.EC2 -} - -type awsSDKProvider struct { - creds *credentials.Credentials -} - -func addHandlers(h *request.Handlers) { - h.Sign.PushFrontNamed(request.NamedHandler{ - Name: "k8s/logger", - Fn: awsHandlerLogger, - }) -} - -func (p *awsSDKProvider) Compute(regionName string) (EC2, error) { - service := ec2.New(session.New(&aws.Config{ - Region: ®ionName, - Credentials: p.creds, - })) - - addHandlers(&service.Handlers) - - ec2 := &awsSdkEC2{ - ec2: service, - } - return ec2, nil -} - -func (p *awsSDKProvider) LoadBalancing(regionName string) (ELB, error) { - elbClient := elb.New(session.New(&aws.Config{ - Region: ®ionName, - Credentials: p.creds, - })) - - addHandlers(&elbClient.Handlers) - - return elbClient, nil -} - -func (p *awsSDKProvider) Autoscaling(regionName string) (ASG, error) { - client := autoscaling.New(session.New(&aws.Config{ - Region: ®ionName, - Credentials: p.creds, - })) - - addHandlers(&client.Handlers) - - return client, nil -} - -func (p *awsSDKProvider) Metadata() (EC2Metadata, error) { - client := ec2metadata.New(session.New(&aws.Config{})) - return client, nil -} - -func stringPointerArray(orig []string) []*string { - if orig == nil { - return nil - } - n := make([]*string, len(orig)) - for i := range orig { - n[i] = &orig[i] - } - return n -} - -func isNilOrEmpty(s *string) bool { - return s == nil || *s == "" -} - -func orEmpty(s *string) string { - if s == nil { - return "" - } - return *s -} - -func newEc2Filter(name string, value string) *ec2.Filter { - filter := &ec2.Filter{ - Name: aws.String(name), - Values: []*string{ - aws.String(value), - }, - } - return filter -} - -func (self *AWSCloud) AddSSHKeyToAllInstances(user string, keyData []byte) error { - return errors.New("unimplemented") -} - -func (a *AWSCloud) CurrentNodeName(hostname string) (string, error) { - selfInstance, err := a.getSelfAWSInstance() - if err != nil { - return "", err - } - return selfInstance.nodeName, nil -} - -// Implementation of EC2.Instances -func (self *awsSdkEC2) DescribeInstances(request *ec2.DescribeInstancesInput) ([]*ec2.Instance, error) { - // Instances are paged - results := []*ec2.Instance{} - var nextToken *string - - for { - response, err := self.ec2.DescribeInstances(request) - if err != nil { - return nil, fmt.Errorf("error listing AWS instances: %v", err) - } - - for _, reservation := range response.Reservations { - results = append(results, reservation.Instances...) - } - - nextToken = response.NextToken - if isNilOrEmpty(nextToken) { - break - } - request.NextToken = nextToken - } - - return results, nil -} - -// Implements EC2.DescribeSecurityGroups -func (s *awsSdkEC2) DescribeSecurityGroups(request *ec2.DescribeSecurityGroupsInput) ([]*ec2.SecurityGroup, error) { - // Security groups are not paged - response, err := s.ec2.DescribeSecurityGroups(request) - if err != nil { - return nil, fmt.Errorf("error listing AWS security groups: %v", err) - } - return response.SecurityGroups, nil -} - -func (s *awsSdkEC2) AttachVolume(request *ec2.AttachVolumeInput) (*ec2.VolumeAttachment, error) { - return s.ec2.AttachVolume(request) -} - -func (s *awsSdkEC2) DetachVolume(request *ec2.DetachVolumeInput) (*ec2.VolumeAttachment, error) { - return s.ec2.DetachVolume(request) -} - -func (s *awsSdkEC2) DescribeVolumes(request *ec2.DescribeVolumesInput) ([]*ec2.Volume, error) { - // Volumes are paged - results := []*ec2.Volume{} - var nextToken *string - - for { - response, err := s.ec2.DescribeVolumes(request) - - if err != nil { - return nil, fmt.Errorf("error listing AWS volumes: %v", err) - } - - results = append(results, response.Volumes...) - - nextToken = response.NextToken - if isNilOrEmpty(nextToken) { - break - } - request.NextToken = nextToken - } - - return results, nil -} - -func (s *awsSdkEC2) CreateVolume(request *ec2.CreateVolumeInput) (resp *ec2.Volume, err error) { - return s.ec2.CreateVolume(request) -} - -func (s *awsSdkEC2) DeleteVolume(request *ec2.DeleteVolumeInput) (*ec2.DeleteVolumeOutput, error) { - return s.ec2.DeleteVolume(request) -} - -func (s *awsSdkEC2) DescribeSubnets(request *ec2.DescribeSubnetsInput) ([]*ec2.Subnet, error) { - // Subnets are not paged - response, err := s.ec2.DescribeSubnets(request) - if err != nil { - return nil, fmt.Errorf("error listing AWS subnets: %v", err) - } - return response.Subnets, nil -} - -func (s *awsSdkEC2) CreateSecurityGroup(request *ec2.CreateSecurityGroupInput) (*ec2.CreateSecurityGroupOutput, error) { - return s.ec2.CreateSecurityGroup(request) -} - -func (s *awsSdkEC2) DeleteSecurityGroup(request *ec2.DeleteSecurityGroupInput) (*ec2.DeleteSecurityGroupOutput, error) { - return s.ec2.DeleteSecurityGroup(request) -} - -func (s *awsSdkEC2) AuthorizeSecurityGroupIngress(request *ec2.AuthorizeSecurityGroupIngressInput) (*ec2.AuthorizeSecurityGroupIngressOutput, error) { - return s.ec2.AuthorizeSecurityGroupIngress(request) -} - -func (s *awsSdkEC2) RevokeSecurityGroupIngress(request *ec2.RevokeSecurityGroupIngressInput) (*ec2.RevokeSecurityGroupIngressOutput, error) { - return s.ec2.RevokeSecurityGroupIngress(request) -} - -func (s *awsSdkEC2) CreateTags(request *ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) { - return s.ec2.CreateTags(request) -} - -func (s *awsSdkEC2) DescribeRouteTables(request *ec2.DescribeRouteTablesInput) ([]*ec2.RouteTable, error) { - // Not paged - response, err := s.ec2.DescribeRouteTables(request) - if err != nil { - return nil, fmt.Errorf("error listing AWS route tables: %v", err) - } - return response.RouteTables, nil -} - -func (s *awsSdkEC2) CreateRoute(request *ec2.CreateRouteInput) (*ec2.CreateRouteOutput, error) { - return s.ec2.CreateRoute(request) -} - -func (s *awsSdkEC2) DeleteRoute(request *ec2.DeleteRouteInput) (*ec2.DeleteRouteOutput, error) { - return s.ec2.DeleteRoute(request) -} - -func (s *awsSdkEC2) ModifyInstanceAttribute(request *ec2.ModifyInstanceAttributeInput) (*ec2.ModifyInstanceAttributeOutput, error) { - return s.ec2.ModifyInstanceAttribute(request) -} - -func init() { - cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) { - creds := credentials.NewChainCredentials( - []credentials.Provider{ - &credentials.EnvProvider{}, - &ec2rolecreds.EC2RoleProvider{ - Client: ec2metadata.New(session.New(&aws.Config{})), - }, - &credentials.SharedCredentialsProvider{}, - }) - aws := &awsSDKProvider{creds: creds} - return newAWSCloud(config, aws) - }) -} - -// readAWSCloudConfig reads an instance of AWSCloudConfig from config reader. -func readAWSCloudConfig(config io.Reader, metadata EC2Metadata) (*AWSCloudConfig, error) { - var cfg AWSCloudConfig - var err error - - if config != nil { - err = gcfg.ReadInto(&cfg, config) - if err != nil { - return nil, err - } - } - - if cfg.Global.Zone == "" { - if metadata != nil { - glog.Info("Zone not specified in configuration file; querying AWS metadata service") - cfg.Global.Zone, err = getAvailabilityZone(metadata) - if err != nil { - return nil, err - } - } - if cfg.Global.Zone == "" { - return nil, fmt.Errorf("no zone specified in configuration file") - } - } - - return &cfg, nil -} - -func getInstanceType(metadata EC2Metadata) (string, error) { - return metadata.GetMetadata("instance-type") -} - -func getAvailabilityZone(metadata EC2Metadata) (string, error) { - return metadata.GetMetadata("placement/availability-zone") -} - -func isRegionValid(region string) bool { - regions := [...]string{ - "us-east-1", - "us-west-1", - "us-west-2", - "eu-west-1", - "eu-central-1", - "ap-southeast-1", - "ap-southeast-2", - "ap-northeast-1", - "cn-north-1", - "us-gov-west-1", - "sa-east-1", - } - for _, r := range regions { - if r == region { - return true - } - } - return false -} - -// Derives the region from a valid az name. -// Returns an error if the az is known invalid (empty) -func azToRegion(az string) (string, error) { - if len(az) < 1 { - return "", fmt.Errorf("invalid (empty) AZ") - } - region := az[:len(az)-1] - return region, nil -} - -// newAWSCloud creates a new instance of AWSCloud. -// AWSProvider and instanceId are primarily for tests -func newAWSCloud(config io.Reader, awsServices AWSServices) (*AWSCloud, error) { - metadata, err := awsServices.Metadata() - if err != nil { - return nil, fmt.Errorf("error creating AWS metadata client: %v", err) - } - - cfg, err := readAWSCloudConfig(config, metadata) - if err != nil { - return nil, fmt.Errorf("unable to read AWS cloud provider config file: %v", err) - } - - zone := cfg.Global.Zone - if len(zone) <= 1 { - return nil, fmt.Errorf("invalid AWS zone in config file: %s", zone) - } - regionName, err := azToRegion(zone) - if err != nil { - return nil, err - } - - valid := isRegionValid(regionName) - if !valid { - return nil, fmt.Errorf("not a valid AWS zone (unknown region): %s", zone) - } - - ec2, err := awsServices.Compute(regionName) - if err != nil { - return nil, fmt.Errorf("error creating AWS EC2 client: %v", err) - } - - elb, err := awsServices.LoadBalancing(regionName) - if err != nil { - return nil, fmt.Errorf("error creating AWS ELB client: %v", err) - } - - asg, err := awsServices.Autoscaling(regionName) - if err != nil { - return nil, fmt.Errorf("error creating AWS autoscaling client: %v", err) - } - - awsCloud := &AWSCloud{ - ec2: ec2, - elb: elb, - asg: asg, - metadata: metadata, - cfg: cfg, - region: regionName, - availabilityZone: zone, - } - - filterTags := map[string]string{} - if cfg.Global.KubernetesClusterTag != "" { - filterTags[TagNameKubernetesCluster] = cfg.Global.KubernetesClusterTag - } else { - selfInstance, err := awsCloud.getSelfAWSInstance() - if err != nil { - return nil, err - } - selfInstanceInfo, err := selfInstance.getInfo() - if err != nil { - return nil, err - } - for _, tag := range selfInstanceInfo.Tags { - if orEmpty(tag.Key) == TagNameKubernetesCluster { - filterTags[TagNameKubernetesCluster] = orEmpty(tag.Value) - } - } - } - - awsCloud.filterTags = filterTags - if len(filterTags) > 0 { - glog.Infof("AWS cloud filtering on tags: %v", filterTags) - } else { - glog.Infof("AWS cloud - no tag filtering") - } - - // Register handler for ECR credentials - once.Do(func() { - aws_credentials.Init() - }) - - return awsCloud, nil -} - -func (aws *AWSCloud) Clusters() (cloudprovider.Clusters, bool) { - return nil, false -} - -// ProviderName returns the cloud provider ID. -func (aws *AWSCloud) ProviderName() string { - return ProviderName -} - -// ScrubDNS filters DNS settings for pods. -func (aws *AWSCloud) ScrubDNS(nameservers, searches []string) (nsOut, srchOut []string) { - return nameservers, searches -} - -// LoadBalancer returns an implementation of LoadBalancer for Amazon Web Services. -func (s *AWSCloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) { - return s, true -} - -// Instances returns an implementation of Instances for Amazon Web Services. -func (aws *AWSCloud) Instances() (cloudprovider.Instances, bool) { - return aws, true -} - -// Zones returns an implementation of Zones for Amazon Web Services. -func (aws *AWSCloud) Zones() (cloudprovider.Zones, bool) { - return aws, true -} - -// Routes returns an implementation of Routes for Amazon Web Services. -func (aws *AWSCloud) Routes() (cloudprovider.Routes, bool) { - return aws, true -} - -// NodeAddresses is an implementation of Instances.NodeAddresses. -func (aws *AWSCloud) NodeAddresses(name string) ([]api.NodeAddress, error) { - self, err := aws.getSelfAWSInstance() - if err != nil { - return nil, err - } - if self.nodeName == name || len(name) == 0 { - addresses := []api.NodeAddress{} - - internalIP, err := aws.metadata.GetMetadata("local-ipv4") - if err != nil { - return nil, err - } - addresses = append(addresses, api.NodeAddress{Type: api.NodeInternalIP, Address: internalIP}) - // Legacy compatibility: the private ip was the legacy host ip - addresses = append(addresses, api.NodeAddress{Type: api.NodeLegacyHostIP, Address: internalIP}) - - externalIP, err := aws.metadata.GetMetadata("public-ipv4") - if err != nil { - //TODO: It would be nice to be able to determine the reason for the failure, - // but the AWS client masks all failures with the same error description. - glog.V(2).Info("Could not determine public IP from AWS metadata.") - } else { - addresses = append(addresses, api.NodeAddress{Type: api.NodeExternalIP, Address: externalIP}) - } - - return addresses, nil - } - instance, err := aws.getInstanceByNodeName(name) - if err != nil { - return nil, err - } - - addresses := []api.NodeAddress{} - - if !isNilOrEmpty(instance.PrivateIpAddress) { - ipAddress := *instance.PrivateIpAddress - ip := net.ParseIP(ipAddress) - if ip == nil { - return nil, fmt.Errorf("EC2 instance had invalid private address: %s (%s)", orEmpty(instance.InstanceId), ipAddress) - } - addresses = append(addresses, api.NodeAddress{Type: api.NodeInternalIP, Address: ip.String()}) - - // Legacy compatibility: the private ip was the legacy host ip - addresses = append(addresses, api.NodeAddress{Type: api.NodeLegacyHostIP, Address: ip.String()}) - } - - // TODO: Other IP addresses (multiple ips)? - if !isNilOrEmpty(instance.PublicIpAddress) { - ipAddress := *instance.PublicIpAddress - ip := net.ParseIP(ipAddress) - if ip == nil { - return nil, fmt.Errorf("EC2 instance had invalid public address: %s (%s)", orEmpty(instance.InstanceId), ipAddress) - } - addresses = append(addresses, api.NodeAddress{Type: api.NodeExternalIP, Address: ip.String()}) - } - - return addresses, nil -} - -// ExternalID returns the cloud provider ID of the specified instance (deprecated). -func (aws *AWSCloud) ExternalID(name string) (string, error) { - awsInstance, err := aws.getSelfAWSInstance() - if err != nil { - return "", err - } - - if awsInstance.nodeName == name { - // We assume that if this is run on the instance itself, the instance exists and is alive - return awsInstance.awsID, nil - } else { - // We must verify that the instance still exists - // Note that if the instance does not exist or is no longer running, we must return ("", cloudprovider.InstanceNotFound) - instance, err := aws.findInstanceByNodeName(name) - if err != nil { - return "", err - } - if instance == nil || !isAlive(instance) { - return "", cloudprovider.InstanceNotFound - } - return orEmpty(instance.InstanceId), nil - } -} - -// InstanceID returns the cloud provider ID of the specified instance. -func (aws *AWSCloud) InstanceID(name string) (string, error) { - awsInstance, err := aws.getSelfAWSInstance() - if err != nil { - return "", err - } - - // In the future it is possible to also return an endpoint as: - // // - if awsInstance.nodeName == name { - return "/" + awsInstance.availabilityZone + "/" + awsInstance.awsID, nil - } else { - inst, err := aws.getInstanceByNodeName(name) - if err != nil { - return "", err - } - return "/" + orEmpty(inst.Placement.AvailabilityZone) + "/" + orEmpty(inst.InstanceId), nil - } -} - -// InstanceType returns the type of the specified instance. -func (aws *AWSCloud) InstanceType(name string) (string, error) { - awsInstance, err := aws.getSelfAWSInstance() - if err != nil { - return "", err - } - - if awsInstance.nodeName == name { - return awsInstance.instanceType, nil - } else { - inst, err := aws.getInstanceByNodeName(name) - if err != nil { - return "", err - } - return orEmpty(inst.InstanceType), nil - } -} - -// Check if the instance is alive (running or pending) -// We typically ignore instances that are not alive -func isAlive(instance *ec2.Instance) bool { - if instance.State == nil { - glog.Warning("Instance state was unexpectedly nil: ", instance) - return false - } - stateName := orEmpty(instance.State.Name) - switch stateName { - case "shutting-down", "terminated", "stopping", "stopped": - return false - case "pending", "running": - return true - default: - glog.Errorf("Unknown EC2 instance state: %s", stateName) - return false - } -} - -// Return a list of instances matching regex string. -func (s *AWSCloud) getInstancesByRegex(regex string) ([]string, error) { - filters := []*ec2.Filter{} - filters = s.addFilters(filters) - request := &ec2.DescribeInstancesInput{ - Filters: filters, - } - - instances, err := s.ec2.DescribeInstances(request) - if err != nil { - return []string{}, err - } - if len(instances) == 0 { - return []string{}, fmt.Errorf("no instances returned") - } - - if strings.HasPrefix(regex, "'") && strings.HasSuffix(regex, "'") { - glog.Infof("Stripping quotes around regex (%s)", regex) - regex = regex[1 : len(regex)-1] - } - - re, err := regexp.Compile(regex) - if err != nil { - return []string{}, err - } - - matchingInstances := []string{} - for _, instance := range instances { - // TODO: Push filtering down into EC2 API filter? - if !isAlive(instance) { - continue - } - - // Only return fully-ready instances when listing instances - // (vs a query by name, where we will return it if we find it) - if orEmpty(instance.State.Name) == "pending" { - glog.V(2).Infof("Skipping EC2 instance (pending): %s", *instance.InstanceId) - continue - } - - privateDNSName := orEmpty(instance.PrivateDnsName) - if privateDNSName == "" { - glog.V(2).Infof("Skipping EC2 instance (no PrivateDNSName): %s", - orEmpty(instance.InstanceId)) - continue - } - - for _, tag := range instance.Tags { - if orEmpty(tag.Key) == "Name" && re.MatchString(orEmpty(tag.Value)) { - matchingInstances = append(matchingInstances, privateDNSName) - break - } - } - } - glog.V(2).Infof("Matched EC2 instances: %s", matchingInstances) - return matchingInstances, nil -} - -// List is an implementation of Instances.List. -func (aws *AWSCloud) List(filter string) ([]string, error) { - // TODO: Should really use tag query. No need to go regexp. - return aws.getInstancesByRegex(filter) -} - -// GetZone implements Zones.GetZone -func (self *AWSCloud) GetZone() (cloudprovider.Zone, error) { - return cloudprovider.Zone{ - FailureDomain: self.availabilityZone, - Region: self.region, - }, nil -} - -// Abstraction around AWS Instance Types -// There isn't an API to get information for a particular instance type (that I know of) -type awsInstanceType struct { -} - -// Used to represent a mount device for attaching an EBS volume -// This should be stored as a single letter (i.e. c, not sdc or /dev/sdc) -type mountDevice string - -// TODO: Also return number of mounts allowed? -func (self *awsInstanceType) getEBSMountDevices() []mountDevice { - // See: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/block-device-mapping-concepts.html - devices := []mountDevice{} - for c := 'f'; c <= 'p'; c++ { - devices = append(devices, mountDevice(fmt.Sprintf("%c", c))) - } - return devices -} - -type awsInstance struct { - ec2 EC2 - - // id in AWS - awsID string - - // node name in k8s - nodeName string - - // availability zone the instance resides in - availabilityZone string - - // instance type - instanceType string - - mutex sync.Mutex - - // We must cache because otherwise there is a race condition, - // where we assign a device mapping and then get a second request before we attach the volume - deviceMappings map[mountDevice]string -} - -func newAWSInstance(ec2 EC2, awsID, nodeName, availabilityZone, instanceType string) *awsInstance { - self := &awsInstance{ec2: ec2, awsID: awsID, nodeName: nodeName, availabilityZone: availabilityZone, instanceType: instanceType} - - // We lazy-init deviceMappings - self.deviceMappings = nil - - return self -} - -// Gets the awsInstanceType that models the instance type of this instance -func (self *awsInstance) getInstanceType() *awsInstanceType { - // TODO: Make this real - awsInstanceType := &awsInstanceType{} - return awsInstanceType -} - -// Gets the full information about this instance from the EC2 API -func (self *awsInstance) getInfo() (*ec2.Instance, error) { - instanceID := self.awsID - request := &ec2.DescribeInstancesInput{ - InstanceIds: []*string{&instanceID}, - } - - instances, err := self.ec2.DescribeInstances(request) - if err != nil { - return nil, err - } - if len(instances) == 0 { - return nil, fmt.Errorf("no instances found for instance: %s", self.awsID) - } - if len(instances) > 1 { - return nil, fmt.Errorf("multiple instances found for instance: %s", self.awsID) - } - return instances[0], nil -} - -// Gets the mountDevice already assigned to the volume, or assigns an unused mountDevice. -// If the volume is already assigned, this will return the existing mountDevice with alreadyAttached=true. -// Otherwise the mountDevice is assigned by finding the first available mountDevice, and it is returned with alreadyAttached=false. -func (self *awsInstance) getMountDevice(volumeID string, assign bool) (assigned mountDevice, alreadyAttached bool, err error) { - instanceType := self.getInstanceType() - if instanceType == nil { - return "", false, fmt.Errorf("could not get instance type for instance: %s", self.awsID) - } - - // We lock to prevent concurrent mounts from conflicting - // We may still conflict if someone calls the API concurrently, - // but the AWS API will then fail one of the two attach operations - self.mutex.Lock() - defer self.mutex.Unlock() - - // We cache both for efficiency and correctness - if self.deviceMappings == nil { - info, err := self.getInfo() - if err != nil { - return "", false, err - } - deviceMappings := map[mountDevice]string{} - for _, blockDevice := range info.BlockDeviceMappings { - name := aws.StringValue(blockDevice.DeviceName) - if strings.HasPrefix(name, "/dev/sd") { - name = name[7:] - } - if strings.HasPrefix(name, "/dev/xvd") { - name = name[8:] - } - if len(name) != 1 { - glog.Warningf("Unexpected EBS DeviceName: %q", aws.StringValue(blockDevice.DeviceName)) - } - deviceMappings[mountDevice(name)] = aws.StringValue(blockDevice.Ebs.VolumeId) - } - self.deviceMappings = deviceMappings - } - - // Check to see if this volume is already assigned a device on this machine - for mountDevice, mappingVolumeID := range self.deviceMappings { - if volumeID == mappingVolumeID { - if assign { - glog.Warningf("Got assignment call for already-assigned volume: %s@%s", mountDevice, mappingVolumeID) - } - return mountDevice, true, nil - } - } - - if !assign { - return mountDevice(""), false, nil - } - - // Check all the valid mountpoints to see if any of them are free - valid := instanceType.getEBSMountDevices() - chosen := mountDevice("") - for _, mountDevice := range valid { - _, found := self.deviceMappings[mountDevice] - if !found { - chosen = mountDevice - break - } - } - - if chosen == "" { - glog.Warningf("Could not assign a mount device (all in use?). mappings=%v, valid=%v", self.deviceMappings, valid) - return "", false, nil - } - - self.deviceMappings[chosen] = volumeID - glog.V(2).Infof("Assigned mount device %s -> volume %s", chosen, volumeID) - - return chosen, false, nil -} - -func (self *awsInstance) releaseMountDevice(volumeID string, mountDevice mountDevice) { - self.mutex.Lock() - defer self.mutex.Unlock() - - existingVolumeID, found := self.deviceMappings[mountDevice] - if !found { - glog.Errorf("releaseMountDevice on non-allocated device") - return - } - if volumeID != existingVolumeID { - glog.Errorf("releaseMountDevice on device assigned to different volume") - return - } - glog.V(2).Infof("Releasing mount device mapping: %s -> volume %s", mountDevice, volumeID) - delete(self.deviceMappings, mountDevice) -} - -type awsDisk struct { - ec2 EC2 - - // Name in k8s - name string - // id in AWS - awsID string - // az which holds the volume - az string -} - -func newAWSDisk(aws *AWSCloud, name string) (*awsDisk, error) { - if !strings.HasPrefix(name, "aws://") { - name = "aws://" + aws.availabilityZone + "/" + name - } - // name looks like aws://availability-zone/id - url, err := url.Parse(name) - if err != nil { - // TODO: Maybe we should pass a URL into the Volume functions - return nil, fmt.Errorf("Invalid disk name (%s): %v", name, err) - } - if url.Scheme != "aws" { - return nil, fmt.Errorf("Invalid scheme for AWS volume (%s)", name) - } - - awsID := url.Path - if len(awsID) > 1 && awsID[0] == '/' { - awsID = awsID[1:] - } - - // TODO: Regex match? - if strings.Contains(awsID, "/") || !strings.HasPrefix(awsID, "vol-") { - return nil, fmt.Errorf("Invalid format for AWS volume (%s)", name) - } - az := url.Host - // TODO: Better validation? - // TODO: Default to our AZ? Look it up? - // TODO: Should this be a region or an AZ? - if az == "" { - return nil, fmt.Errorf("Invalid format for AWS volume (%s)", name) - } - disk := &awsDisk{ec2: aws.ec2, name: name, awsID: awsID, az: az} - return disk, nil -} - -// Gets the full information about this volume from the EC2 API -func (self *awsDisk) getInfo() (*ec2.Volume, error) { - volumeID := self.awsID - - request := &ec2.DescribeVolumesInput{ - VolumeIds: []*string{&volumeID}, - } - - volumes, err := self.ec2.DescribeVolumes(request) - if err != nil { - return nil, fmt.Errorf("error querying ec2 for volume info: %v", err) - } - if len(volumes) == 0 { - return nil, fmt.Errorf("no volumes found for volume: %s", self.awsID) - } - if len(volumes) > 1 { - return nil, fmt.Errorf("multiple volumes found for volume: %s", self.awsID) - } - return volumes[0], nil -} - -func (self *awsDisk) waitForAttachmentStatus(status string) error { - // TODO: There may be a faster way to get this when we're attaching locally - attempt := 0 - maxAttempts := 60 - - for { - info, err := self.getInfo() - if err != nil { - return err - } - if len(info.Attachments) > 1 { - glog.Warningf("Found multiple attachments for volume: %v", info) - } - attachmentStatus := "" - for _, attachment := range info.Attachments { - if attachmentStatus != "" { - glog.Warning("Found multiple attachments: ", info) - } - if attachment.State != nil { - attachmentStatus = *attachment.State - } else { - // Shouldn't happen, but don't panic... - glog.Warning("Ignoring nil attachment state: ", attachment) - } - } - if attachmentStatus == "" { - attachmentStatus = "detached" - } - if attachmentStatus == status { - return nil - } - - glog.V(2).Infof("Waiting for volume state: actual=%s, desired=%s", attachmentStatus, status) - - attempt++ - if attempt > maxAttempts { - glog.Warningf("Timeout waiting for volume state: actual=%s, desired=%s", attachmentStatus, status) - return errors.New("Timeout waiting for volume state") - } - - time.Sleep(1 * time.Second) - } -} - -// Deletes the EBS disk -func (self *awsDisk) deleteVolume() (bool, error) { - request := &ec2.DeleteVolumeInput{VolumeId: aws.String(self.awsID)} - _, err := self.ec2.DeleteVolume(request) - if err != nil { - if awsError, ok := err.(awserr.Error); ok { - if awsError.Code() == "InvalidVolume.NotFound" { - return false, nil - } - } - return false, fmt.Errorf("error deleting EBS volumes: %v", err) - } - return true, nil -} - -// Gets the awsInstance for the EC2 instance on which we are running -// may return nil in case of error -func (s *AWSCloud) getSelfAWSInstance() (*awsInstance, error) { - // Note that we cache some state in awsInstance (mountpoints), so we must preserve the instance - - s.mutex.Lock() - defer s.mutex.Unlock() - - i := s.selfAWSInstance - if i == nil { - instanceId, err := s.metadata.GetMetadata("instance-id") - if err != nil { - return nil, fmt.Errorf("error fetching instance-id from ec2 metadata service: %v", err) - } - // privateDnsName, err := s.metadata.GetMetadata("local-hostname") - // See #11543 - need to use ec2 API to get the privateDnsName in case of private dns zone e.g. mydomain.io - instance, err := s.getInstanceByID(instanceId) - if err != nil { - return nil, fmt.Errorf("error finding instance %s: %v", instanceId, err) - } - privateDnsName := aws.StringValue(instance.PrivateDnsName) - availabilityZone, err := getAvailabilityZone(s.metadata) - if err != nil { - return nil, fmt.Errorf("error fetching availability zone from ec2 metadata service: %v", err) - } - instanceType, err := getInstanceType(s.metadata) - if err != nil { - return nil, fmt.Errorf("error fetching instance type from ec2 metadata service: %v", err) - } - - i = newAWSInstance(s.ec2, instanceId, privateDnsName, availabilityZone, instanceType) - s.selfAWSInstance = i - } - - return i, nil -} - -// Gets the awsInstance with node-name nodeName, or the 'self' instance if nodeName == "" -func (aws *AWSCloud) getAwsInstance(nodeName string) (*awsInstance, error) { - var awsInstance *awsInstance - var err error - if nodeName == "" { - awsInstance, err = aws.getSelfAWSInstance() - if err != nil { - return nil, fmt.Errorf("error getting self-instance: %v", err) - } - } else { - instance, err := aws.getInstanceByNodeName(nodeName) - if err != nil { - return nil, fmt.Errorf("error finding instance %s: %v", nodeName, err) - } - - awsInstance = newAWSInstance(aws.ec2, orEmpty(instance.InstanceId), orEmpty(instance.PrivateDnsName), orEmpty(instance.Placement.AvailabilityZone), orEmpty(instance.InstanceType)) - } - - return awsInstance, nil -} - -// Implements Volumes.AttachDisk -func (c *AWSCloud) AttachDisk(diskName string, instanceName string, readOnly bool) (string, error) { - disk, err := newAWSDisk(c, diskName) - if err != nil { - return "", err - } - - awsInstance, err := c.getAwsInstance(instanceName) - if err != nil { - return "", err - } - - if readOnly { - // TODO: We could enforce this when we mount the volume (?) - // TODO: We could also snapshot the volume and attach copies of it - return "", errors.New("AWS volumes cannot be mounted read-only") - } - - mountDevice, alreadyAttached, err := awsInstance.getMountDevice(disk.awsID, true) - if err != nil { - return "", err - } - - // Inside the instance, the mountpoint always looks like /dev/xvdX (?) - hostDevice := "/dev/xvd" + string(mountDevice) - // In the EC2 API, it is sometimes is /dev/sdX and sometimes /dev/xvdX - // We are running on the node here, so we check if /dev/xvda exists to determine this - ec2Device := "/dev/xvd" + string(mountDevice) - if _, err := os.Stat("/dev/xvda"); os.IsNotExist(err) { - ec2Device = "/dev/sd" + string(mountDevice) - } - - attached := false - defer func() { - if !attached { - awsInstance.releaseMountDevice(disk.awsID, mountDevice) - } - }() - - if !alreadyAttached { - request := &ec2.AttachVolumeInput{ - Device: aws.String(ec2Device), - InstanceId: aws.String(awsInstance.awsID), - VolumeId: aws.String(disk.awsID), - } - - attachResponse, err := c.ec2.AttachVolume(request) - if err != nil { - // TODO: Check if the volume was concurrently attached? - return "", fmt.Errorf("Error attaching EBS volume: %v", err) - } - - glog.V(2).Infof("AttachVolume request returned %v", attachResponse) - } - - err = disk.waitForAttachmentStatus("attached") - if err != nil { - return "", err - } - - attached = true - - return hostDevice, nil -} - -// Implements Volumes.DetachDisk -func (aws *AWSCloud) DetachDisk(diskName string, instanceName string) (string, error) { - disk, err := newAWSDisk(aws, diskName) - if err != nil { - return "", err - } - - awsInstance, err := aws.getAwsInstance(instanceName) - if err != nil { - return "", err - } - - mountDevice, alreadyAttached, err := awsInstance.getMountDevice(disk.awsID, false) - if err != nil { - return "", err - } - - if !alreadyAttached { - glog.Warning("DetachDisk called on non-attached disk: ", diskName) - // TODO: Continue? Tolerate non-attached error in DetachVolume? - } - - request := ec2.DetachVolumeInput{ - InstanceId: &awsInstance.awsID, - VolumeId: &disk.awsID, - } - - response, err := aws.ec2.DetachVolume(&request) - if err != nil { - return "", fmt.Errorf("error detaching EBS volume: %v", err) - } - if response == nil { - return "", errors.New("no response from DetachVolume") - } - - // TODO: Fix this - just remove the cache? - // If we don't have a cache; we don't have to wait any more (the driver does it for us) - // Also, maybe we could get the locally connected drivers from the AWS metadata service? - - // At this point we are waiting for the volume being detached. This - // releases the volume and invalidates the cache even when there is a timeout. - // - // TODO: A timeout leaves the cache in an inconsistent state. The volume is still - // detaching though the cache shows it as ready to be attached again. Subsequent - // attach operations will fail. The attach is being retried and eventually - // works though. An option would be to completely flush the cache upon timeouts. - // - defer func() { - // TODO: Not thread safe? - for mountDevice, existingVolumeID := range awsInstance.deviceMappings { - if existingVolumeID == disk.awsID { - awsInstance.releaseMountDevice(disk.awsID, mountDevice) - return - } - } - }() - - err = disk.waitForAttachmentStatus("detached") - if err != nil { - return "", err - } - - hostDevicePath := "/dev/xvd" + string(mountDevice) - return hostDevicePath, err -} - -// Implements Volumes.CreateVolume -func (s *AWSCloud) CreateDisk(volumeOptions *VolumeOptions) (string, error) { - // TODO: Should we tag this with the cluster id (so it gets deleted when the cluster does?) - - request := &ec2.CreateVolumeInput{} - request.AvailabilityZone = &s.availabilityZone - volSize := int64(volumeOptions.CapacityGB) - request.Size = &volSize - request.VolumeType = aws.String(DefaultVolumeType) - response, err := s.ec2.CreateVolume(request) - if err != nil { - return "", err - } - - az := orEmpty(response.AvailabilityZone) - awsID := orEmpty(response.VolumeId) - - volumeName := "aws://" + az + "/" + awsID - - // apply tags - if volumeOptions.Tags != nil { - tags := []*ec2.Tag{} - for k, v := range *volumeOptions.Tags { - tag := &ec2.Tag{} - tag.Key = aws.String(k) - tag.Value = aws.String(v) - tags = append(tags, tag) - } - tagRequest := &ec2.CreateTagsInput{} - tagRequest.Resources = []*string{&awsID} - tagRequest.Tags = tags - if _, err := s.createTags(tagRequest); err != nil { - // delete the volume and hope it succeeds - _, delerr := s.DeleteDisk(volumeName) - if delerr != nil { - // delete did not succeed, we have a stray volume! - return "", fmt.Errorf("error tagging volume %s, could not delete the volume: %v", volumeName, delerr) - } - return "", fmt.Errorf("error tagging volume %s: %v", volumeName, err) - } - } - return volumeName, nil -} - -// Implements Volumes.DeleteDisk -func (aws *AWSCloud) DeleteDisk(volumeName string) (bool, error) { - awsDisk, err := newAWSDisk(aws, volumeName) - if err != nil { - return false, err - } - return awsDisk.deleteVolume() -} - -// Implements Volumes.GetVolumeLabels -func (c *AWSCloud) GetVolumeLabels(volumeName string) (map[string]string, error) { - awsDisk, err := newAWSDisk(c, volumeName) - if err != nil { - return nil, err - } - info, err := awsDisk.getInfo() - if err != nil { - return nil, err - } - labels := make(map[string]string) - az := aws.StringValue(info.AvailabilityZone) - if az == "" { - return nil, fmt.Errorf("volume did not have AZ information: %q", info.VolumeId) - } - - labels[unversioned.LabelZoneFailureDomain] = az - region, err := azToRegion(az) - if err != nil { - return nil, err - } - labels[unversioned.LabelZoneRegion] = region - - return labels, nil -} - -// Gets the current load balancer state -func (s *AWSCloud) describeLoadBalancer(name string) (*elb.LoadBalancerDescription, error) { - request := &elb.DescribeLoadBalancersInput{} - request.LoadBalancerNames = []*string{&name} - - response, err := s.elb.DescribeLoadBalancers(request) - if err != nil { - if awsError, ok := err.(awserr.Error); ok { - if awsError.Code() == "LoadBalancerNotFound" { - return nil, nil - } - } - return nil, err - } - - var ret *elb.LoadBalancerDescription - for _, loadBalancer := range response.LoadBalancerDescriptions { - if ret != nil { - glog.Errorf("Found multiple load balancers with name: %s", name) - } - ret = loadBalancer - } - return ret, nil -} - -// Retrieves instance's vpc id from metadata -func (self *AWSCloud) findVPCID() (string, error) { - macs, err := self.metadata.GetMetadata("network/interfaces/macs/") - if err != nil { - return "", fmt.Errorf("Could not list interfaces of the instance", err) - } - - // loop over interfaces, first vpc id returned wins - for _, macPath := range strings.Split(macs, "\n") { - if len(macPath) == 0 { - continue - } - url := fmt.Sprintf("network/interfaces/macs/%svpc-id", macPath) - vpcID, err := self.metadata.GetMetadata(url) - if err != nil { - continue - } - return vpcID, nil - } - return "", fmt.Errorf("Could not find VPC ID in instance metadata") -} - -// Retrieves the specified security group from the AWS API, or returns nil if not found -func (s *AWSCloud) findSecurityGroup(securityGroupId string) (*ec2.SecurityGroup, error) { - describeSecurityGroupsRequest := &ec2.DescribeSecurityGroupsInput{ - GroupIds: []*string{&securityGroupId}, - } - - groups, err := s.ec2.DescribeSecurityGroups(describeSecurityGroupsRequest) - if err != nil { - glog.Warning("Error retrieving security group", err) - return nil, err - } - - if len(groups) == 0 { - return nil, nil - } - if len(groups) != 1 { - // This should not be possible - ids should be unique - return nil, fmt.Errorf("multiple security groups found with same id") - } - group := groups[0] - return group, nil -} - -func isEqualIntPointer(l, r *int64) bool { - if l == nil { - return r == nil - } - if r == nil { - return l == nil - } - return *l == *r -} - -func isEqualStringPointer(l, r *string) bool { - if l == nil { - return r == nil - } - if r == nil { - return l == nil - } - return *l == *r -} - -func ipPermissionExists(newPermission, existing *ec2.IpPermission, compareGroupUserIDs bool) bool { - if !isEqualIntPointer(newPermission.FromPort, existing.FromPort) { - return false - } - if !isEqualIntPointer(newPermission.ToPort, existing.ToPort) { - return false - } - if !isEqualStringPointer(newPermission.IpProtocol, existing.IpProtocol) { - return false - } - // Check only if newPermission is a subset of existing. Usually it has zero or one elements. - // Not doing actual CIDR math yet; not clear it's needed, either. - glog.V(4).Infof("Comparing %v to %v", newPermission, existing) - if len(newPermission.IpRanges) > len(existing.IpRanges) { - return false - } - - for j := range newPermission.IpRanges { - found := false - for k := range existing.IpRanges { - if isEqualStringPointer(newPermission.IpRanges[j].CidrIp, existing.IpRanges[k].CidrIp) { - found = true - break - } - } - if found == false { - return false - } - } - for _, leftPair := range newPermission.UserIdGroupPairs { - for _, rightPair := range existing.UserIdGroupPairs { - if isEqualUserGroupPair(leftPair, rightPair, compareGroupUserIDs) { - return true - } - } - return false - } - - return true -} - -func isEqualUserGroupPair(l, r *ec2.UserIdGroupPair, compareGroupUserIDs bool) bool { - glog.V(2).Infof("Comparing %v to %v", *l.GroupId, *r.GroupId) - if isEqualStringPointer(l.GroupId, r.GroupId) { - if compareGroupUserIDs { - if isEqualStringPointer(l.UserId, r.UserId) { - return true - } - } else { - return true - } - } - - return false -} - -// Makes sure the security group includes the specified permissions -// Returns true if and only if changes were made -// The security group must already exist -func (s *AWSCloud) ensureSecurityGroupIngress(securityGroupId string, addPermissions []*ec2.IpPermission) (bool, error) { - group, err := s.findSecurityGroup(securityGroupId) - if err != nil { - glog.Warning("Error retrieving security group", err) - return false, err - } - - if group == nil { - return false, fmt.Errorf("security group not found: %s", securityGroupId) - } - - glog.V(2).Infof("Existing security group ingress: %s %v", securityGroupId, group.IpPermissions) - - changes := []*ec2.IpPermission{} - for _, addPermission := range addPermissions { - hasUserID := false - for i := range addPermission.UserIdGroupPairs { - if addPermission.UserIdGroupPairs[i].UserId != nil { - hasUserID = true - } - } - - found := false - for _, groupPermission := range group.IpPermissions { - if ipPermissionExists(addPermission, groupPermission, hasUserID) { - found = true - break - } - } - - if !found { - changes = append(changes, addPermission) - } - } - - if len(changes) == 0 { - return false, nil - } - - glog.V(2).Infof("Adding security group ingress: %s %v", securityGroupId, changes) - - request := &ec2.AuthorizeSecurityGroupIngressInput{} - request.GroupId = &securityGroupId - request.IpPermissions = changes - _, err = s.ec2.AuthorizeSecurityGroupIngress(request) - if err != nil { - glog.Warning("Error authorizing security group ingress", err) - return false, err - } - - return true, nil -} - -// Makes sure the security group no longer includes the specified permissions -// Returns true if and only if changes were made -// If the security group no longer exists, will return (false, nil) -func (s *AWSCloud) removeSecurityGroupIngress(securityGroupId string, removePermissions []*ec2.IpPermission) (bool, error) { - group, err := s.findSecurityGroup(securityGroupId) - if err != nil { - glog.Warning("Error retrieving security group", err) - return false, err - } - - if group == nil { - glog.Warning("Security group not found: ", securityGroupId) - return false, nil - } - - changes := []*ec2.IpPermission{} - for _, removePermission := range removePermissions { - hasUserID := false - for i := range removePermission.UserIdGroupPairs { - if removePermission.UserIdGroupPairs[i].UserId != nil { - hasUserID = true - } - } - - var found *ec2.IpPermission - for _, groupPermission := range group.IpPermissions { - if ipPermissionExists(removePermission, groupPermission, hasUserID) { - found = removePermission - break - } - } - - if found != nil { - changes = append(changes, found) - } - } - - if len(changes) == 0 { - return false, nil - } - - glog.V(2).Infof("Removing security group ingress: %s %v", securityGroupId, changes) - - request := &ec2.RevokeSecurityGroupIngressInput{} - request.GroupId = &securityGroupId - request.IpPermissions = changes - _, err = s.ec2.RevokeSecurityGroupIngress(request) - if err != nil { - glog.Warning("Error revoking security group ingress", err) - return false, err - } - - return true, nil -} - -// Makes sure the security group exists -// Returns the security group id or error -func (s *AWSCloud) ensureSecurityGroup(name string, description string, vpcID string) (string, error) { - groupID := "" - attempt := 0 - for { - attempt++ - - request := &ec2.DescribeSecurityGroupsInput{} - filters := []*ec2.Filter{ - newEc2Filter("group-name", name), - newEc2Filter("vpc-id", vpcID), - } - request.Filters = s.addFilters(filters) - - securityGroups, err := s.ec2.DescribeSecurityGroups(request) - if err != nil { - return "", err - } - - if len(securityGroups) >= 1 { - if len(securityGroups) > 1 { - glog.Warning("Found multiple security groups with name:", name) - } - return orEmpty(securityGroups[0].GroupId), nil - } - - createRequest := &ec2.CreateSecurityGroupInput{} - createRequest.VpcId = &vpcID - createRequest.GroupName = &name - createRequest.Description = &description - - createResponse, err := s.ec2.CreateSecurityGroup(createRequest) - if err != nil { - ignore := false - switch err := err.(type) { - case awserr.Error: - if err.Code() == "InvalidGroup.Duplicate" && attempt < MaxReadThenCreateRetries { - glog.V(2).Infof("Got InvalidGroup.Duplicate while creating security group (race?); will retry") - ignore = true - } - } - if !ignore { - glog.Error("Error creating security group: ", err) - return "", err - } - time.Sleep(1 * time.Second) - } else { - groupID = orEmpty(createResponse.GroupId) - break - } - } - if groupID == "" { - return "", fmt.Errorf("created security group, but id was not returned: %s", name) - } - - tags := []*ec2.Tag{} - for k, v := range s.filterTags { - tag := &ec2.Tag{} - tag.Key = aws.String(k) - tag.Value = aws.String(v) - tags = append(tags, tag) - } - - if len(tags) > 0 { - tagRequest := &ec2.CreateTagsInput{} - tagRequest.Resources = []*string{&groupID} - tagRequest.Tags = tags - if _, err := s.createTags(tagRequest); err != nil { - // Not clear how to recover fully from this; we're OK because we don't match on tags, but that is a little odd - return "", fmt.Errorf("error tagging security group: %v", err) - } - } - return groupID, nil -} - -// createTags calls EC2 CreateTags, but adds retry-on-failure logic -// We retry mainly because if we create an object, we cannot tag it until it is "fully created" (eventual consistency) -// The error code varies though (depending on what we are tagging), so we simply retry on all errors -func (s *AWSCloud) createTags(request *ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) { - // TODO: We really should do exponential backoff here - attempt := 0 - maxAttempts := 60 - - for { - response, err := s.ec2.CreateTags(request) - if err == nil { - return response, err - } - - // We could check that the error is retryable, but the error code changes based on what we are tagging - // SecurityGroup: InvalidGroup.NotFound - attempt++ - if attempt > maxAttempts { - glog.Warningf("Failed to create tags (too many attempts): %v", err) - return response, err - } - glog.V(2).Infof("Failed to create tags; will retry. Error was %v", err) - time.Sleep(1 * time.Second) - } -} - -func (s *AWSCloud) listSubnetIDsinVPC(vpcId string) ([]string, error) { - - subnetIds := []string{} - - request := &ec2.DescribeSubnetsInput{} - filters := []*ec2.Filter{} - filters = append(filters, newEc2Filter("vpc-id", vpcId)) - // Note, this will only return subnets tagged with the cluster identifier for this Kubernetes cluster. - // In the case where an AZ has public & private subnets per AWS best practices, the deployment should ensure - // only the public subnet (where the ELB will go) is so tagged. - filters = s.addFilters(filters) - request.Filters = filters - - subnets, err := s.ec2.DescribeSubnets(request) - if err != nil { - glog.Error("Error describing subnets: ", err) - return nil, err - } - - availabilityZones := sets.NewString() - for _, subnet := range subnets { - az := orEmpty(subnet.AvailabilityZone) - id := orEmpty(subnet.SubnetId) - if availabilityZones.Has(az) { - glog.Warning("Found multiple subnets per AZ '", az, "', ignoring subnet '", id, "'") - continue - } - subnetIds = append(subnetIds, id) - availabilityZones.Insert(az) - } - - return subnetIds, nil -} - -// EnsureLoadBalancer implements LoadBalancer.EnsureLoadBalancer -// TODO(justinsb) It is weird that these take a region. I suspect it won't work cross-region anyway. -func (s *AWSCloud) EnsureLoadBalancer(name, region string, publicIP net.IP, ports []*api.ServicePort, hosts []string, serviceName types.NamespacedName, affinity api.ServiceAffinity) (*api.LoadBalancerStatus, error) { - glog.V(2).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v, %v)", name, region, publicIP, ports, hosts, serviceName) - - if region != s.region { - return nil, fmt.Errorf("requested load balancer region '%s' does not match cluster region '%s'", region, s.region) - } - - if affinity != api.ServiceAffinityNone { - // ELB supports sticky sessions, but only when configured for HTTP/HTTPS - return nil, fmt.Errorf("unsupported load balancer affinity: %v", affinity) - } - - if len(ports) == 0 { - return nil, fmt.Errorf("requested load balancer with no ports") - } - - for _, port := range ports { - if port.Protocol != api.ProtocolTCP { - return nil, fmt.Errorf("Only TCP LoadBalancer is supported for AWS ELB") - } - } - - if publicIP != nil { - return nil, fmt.Errorf("publicIP cannot be specified for AWS ELB") - } - - instances, err := s.getInstancesByNodeNames(hosts) - if err != nil { - return nil, err - } - - vpcId, err := s.findVPCID() - if err != nil { - glog.Error("Error finding VPC", err) - return nil, err - } - - // Construct list of configured subnets - subnetIDs, err := s.listSubnetIDsinVPC(vpcId) - if err != nil { - glog.Error("Error listing subnets in VPC", err) - return nil, err - } - - // Create a security group for the load balancer - var securityGroupID string - { - sgName := "k8s-elb-" + name - sgDescription := fmt.Sprintf("Security group for Kubernetes ELB %s (%v)", name, serviceName) - securityGroupID, err = s.ensureSecurityGroup(sgName, sgDescription, vpcId) - if err != nil { - glog.Error("Error creating load balancer security group: ", err) - return nil, err - } - - permissions := []*ec2.IpPermission{} - for _, port := range ports { - portInt64 := int64(port.Port) - protocol := strings.ToLower(string(port.Protocol)) - sourceIp := "0.0.0.0/0" - - permission := &ec2.IpPermission{} - permission.FromPort = &portInt64 - permission.ToPort = &portInt64 - permission.IpRanges = []*ec2.IpRange{{CidrIp: &sourceIp}} - permission.IpProtocol = &protocol - - permissions = append(permissions, permission) - } - _, err = s.ensureSecurityGroupIngress(securityGroupID, permissions) - if err != nil { - return nil, err - } - } - securityGroupIDs := []string{securityGroupID} - - // Figure out what mappings we want on the load balancer - listeners := []*elb.Listener{} - for _, port := range ports { - if port.NodePort == 0 { - glog.Errorf("Ignoring port without NodePort defined: %v", port) - continue - } - instancePort := int64(port.NodePort) - loadBalancerPort := int64(port.Port) - protocol := strings.ToLower(string(port.Protocol)) - - listener := &elb.Listener{} - listener.InstancePort = &instancePort - listener.LoadBalancerPort = &loadBalancerPort - listener.Protocol = &protocol - listener.InstanceProtocol = &protocol - - listeners = append(listeners, listener) - } - - // Build the load balancer itself - loadBalancer, err := s.ensureLoadBalancer(serviceName, name, listeners, subnetIDs, securityGroupIDs) - if err != nil { - return nil, err - } - - err = s.ensureLoadBalancerHealthCheck(loadBalancer, listeners) - if err != nil { - return nil, err - } - - err = s.updateInstanceSecurityGroupsForLoadBalancer(loadBalancer, instances) - if err != nil { - glog.Warning("Error opening ingress rules for the load balancer to the instances: ", err) - return nil, err - } - - err = s.ensureLoadBalancerInstances(orEmpty(loadBalancer.LoadBalancerName), loadBalancer.Instances, instances) - if err != nil { - glog.Warningf("Error registering instances with the load balancer: %v", err) - return nil, err - } - - glog.V(1).Infof("Loadbalancer %s (%v) has DNS name %s", name, serviceName, orEmpty(loadBalancer.DNSName)) - - // TODO: Wait for creation? - - status := toStatus(loadBalancer) - return status, nil -} - -// GetLoadBalancer is an implementation of LoadBalancer.GetLoadBalancer -func (s *AWSCloud) GetLoadBalancer(name, region string) (*api.LoadBalancerStatus, bool, error) { - if region != s.region { - return nil, false, fmt.Errorf("requested load balancer region '%s' does not match cluster region '%s'", region, s.region) - } - - lb, err := s.describeLoadBalancer(name) - if err != nil { - return nil, false, err - } - - if lb == nil { - return nil, false, nil - } - - status := toStatus(lb) - return status, true, nil -} - -func toStatus(lb *elb.LoadBalancerDescription) *api.LoadBalancerStatus { - status := &api.LoadBalancerStatus{} - - if !isNilOrEmpty(lb.DNSName) { - var ingress api.LoadBalancerIngress - ingress.Hostname = orEmpty(lb.DNSName) - status.Ingress = []api.LoadBalancerIngress{ingress} - } - - return status -} - -// Returns the first security group for an instance, or nil -// We only create instances with one security group, so we warn if there are multiple or none -func findSecurityGroupForInstance(instance *ec2.Instance) *string { - var securityGroupId *string - for _, securityGroup := range instance.SecurityGroups { - if securityGroup == nil || securityGroup.GroupId == nil { - // Not expected, but avoid panic - glog.Warning("Unexpected empty security group for instance: ", orEmpty(instance.InstanceId)) - continue - } - - if securityGroupId != nil { - // We create instances with one SG - glog.Warningf("Multiple security groups found for instance (%s); will use first group (%s)", orEmpty(instance.InstanceId), *securityGroupId) - continue - } else { - securityGroupId = securityGroup.GroupId - } - } - - if securityGroupId == nil { - glog.Warning("No security group found for instance ", orEmpty(instance.InstanceId)) - } - - return securityGroupId -} - -// Open security group ingress rules on the instances so that the load balancer can talk to them -// Will also remove any security groups ingress rules for the load balancer that are _not_ needed for allInstances -func (s *AWSCloud) updateInstanceSecurityGroupsForLoadBalancer(lb *elb.LoadBalancerDescription, allInstances []*ec2.Instance) error { - // Determine the load balancer security group id - loadBalancerSecurityGroupId := "" - for _, securityGroup := range lb.SecurityGroups { - if isNilOrEmpty(securityGroup) { - continue - } - if loadBalancerSecurityGroupId != "" { - // We create LBs with one SG - glog.Warning("Multiple security groups for load balancer: ", orEmpty(lb.LoadBalancerName)) - } - loadBalancerSecurityGroupId = *securityGroup - } - if loadBalancerSecurityGroupId == "" { - return fmt.Errorf("Could not determine security group for load balancer: %s", orEmpty(lb.LoadBalancerName)) - } - - // Get the actual list of groups that allow ingress from the load-balancer - describeRequest := &ec2.DescribeSecurityGroupsInput{} - filters := []*ec2.Filter{} - filters = append(filters, newEc2Filter("ip-permission.group-id", loadBalancerSecurityGroupId)) - describeRequest.Filters = s.addFilters(filters) - actualGroups, err := s.ec2.DescribeSecurityGroups(describeRequest) - if err != nil { - return fmt.Errorf("error querying security groups: %v", err) - } - - // Open the firewall from the load balancer to the instance - // We don't actually have a trivial way to know in advance which security group the instance is in - // (it is probably the minion security group, but we don't easily have that). - // However, we _do_ have the list of security groups on the instance records. - - // Map containing the changes we want to make; true to add, false to remove - instanceSecurityGroupIds := map[string]bool{} - - // Scan instances for groups we want open - for _, instance := range allInstances { - securityGroupId := findSecurityGroupForInstance(instance) - if isNilOrEmpty(securityGroupId) { - glog.Warning("Ignoring instance without security group: ", orEmpty(instance.InstanceId)) - continue - } - - instanceSecurityGroupIds[*securityGroupId] = true - } - - // Compare to actual groups - for _, actualGroup := range actualGroups { - if isNilOrEmpty(actualGroup.GroupId) { - glog.Warning("Ignoring group without ID: ", actualGroup) - continue - } - - actualGroupID := *actualGroup.GroupId - - adding, found := instanceSecurityGroupIds[actualGroupID] - if found && adding { - // We don't need to make a change; the permission is already in place - delete(instanceSecurityGroupIds, actualGroupID) - } else { - // This group is not needed by allInstances; delete it - instanceSecurityGroupIds[actualGroupID] = false - } - } - - for instanceSecurityGroupId, add := range instanceSecurityGroupIds { - if add { - glog.V(2).Infof("Adding rule for traffic from the load balancer (%s) to instances (%s)", loadBalancerSecurityGroupId, instanceSecurityGroupId) - } else { - glog.V(2).Infof("Removing rule for traffic from the load balancer (%s) to instance (%s)", loadBalancerSecurityGroupId, instanceSecurityGroupId) - } - sourceGroupId := &ec2.UserIdGroupPair{} - sourceGroupId.GroupId = &loadBalancerSecurityGroupId - - allProtocols := "-1" - - permission := &ec2.IpPermission{} - permission.IpProtocol = &allProtocols - permission.UserIdGroupPairs = []*ec2.UserIdGroupPair{sourceGroupId} - - permissions := []*ec2.IpPermission{permission} - - if add { - changed, err := s.ensureSecurityGroupIngress(instanceSecurityGroupId, permissions) - if err != nil { - return err - } - if !changed { - glog.Warning("Allowing ingress was not needed; concurrent change? groupId=", instanceSecurityGroupId) - } - } else { - changed, err := s.removeSecurityGroupIngress(instanceSecurityGroupId, permissions) - if err != nil { - return err - } - if !changed { - glog.Warning("Revoking ingress was not needed; concurrent change? groupId=", instanceSecurityGroupId) - } - } - } - - return nil -} - -// EnsureLoadBalancerDeleted implements LoadBalancer.EnsureLoadBalancerDeleted. -func (s *AWSCloud) EnsureLoadBalancerDeleted(name, region string) error { - if region != s.region { - return fmt.Errorf("requested load balancer region '%s' does not match cluster region '%s'", region, s.region) - } - - lb, err := s.describeLoadBalancer(name) - if err != nil { - return err - } - - if lb == nil { - glog.Info("Load balancer already deleted: ", name) - return nil - } - - { - // De-authorize the load balancer security group from the instances security group - err = s.updateInstanceSecurityGroupsForLoadBalancer(lb, nil) - if err != nil { - glog.Error("Error deregistering load balancer from instance security groups: ", err) - return err - } - } - - { - // Delete the load balancer itself - request := &elb.DeleteLoadBalancerInput{} - request.LoadBalancerName = lb.LoadBalancerName - - _, err = s.elb.DeleteLoadBalancer(request) - if err != nil { - // TODO: Check if error was because load balancer was concurrently deleted - glog.Error("Error deleting load balancer: ", err) - return err - } - } - - { - // Delete the security group(s) for the load balancer - // Note that this is annoying: the load balancer disappears from the API immediately, but it is still - // deleting in the background. We get a DependencyViolation until the load balancer has deleted itself - - // Collect the security groups to delete - securityGroupIDs := map[string]struct{}{} - for _, securityGroupID := range lb.SecurityGroups { - if isNilOrEmpty(securityGroupID) { - glog.Warning("Ignoring empty security group in ", name) - continue - } - securityGroupIDs[*securityGroupID] = struct{}{} - } - - // Loop through and try to delete them - timeoutAt := time.Now().Add(time.Second * 300) - for { - for securityGroupID := range securityGroupIDs { - request := &ec2.DeleteSecurityGroupInput{} - request.GroupId = &securityGroupID - _, err := s.ec2.DeleteSecurityGroup(request) - if err == nil { - delete(securityGroupIDs, securityGroupID) - } else { - ignore := false - if awsError, ok := err.(awserr.Error); ok { - if awsError.Code() == "DependencyViolation" { - glog.V(2).Infof("Ignoring DependencyViolation while deleting load-balancer security group (%s), assuming because LB is in process of deleting", securityGroupID) - ignore = true - } - } - if !ignore { - return fmt.Errorf("error while deleting load balancer security group (%s): %v", securityGroupID, err) - } - } - } - - if len(securityGroupIDs) == 0 { - glog.V(2).Info("Deleted all security groups for load balancer: ", name) - break - } - - if time.Now().After(timeoutAt) { - return fmt.Errorf("timed out waiting for load-balancer deletion: %s", name) - } - - glog.V(2).Info("Waiting for load-balancer to delete so we can delete security groups: ", name) - - time.Sleep(5 * time.Second) - } - } - - return nil -} - -// UpdateLoadBalancer implements LoadBalancer.UpdateLoadBalancer -func (s *AWSCloud) UpdateLoadBalancer(name, region string, hosts []string) error { - if region != s.region { - return fmt.Errorf("requested load balancer region '%s' does not match cluster region '%s'", region, s.region) - } - - instances, err := s.getInstancesByNodeNames(hosts) - if err != nil { - return err - } - - lb, err := s.describeLoadBalancer(name) - if err != nil { - return err - } - - if lb == nil { - return fmt.Errorf("Load balancer not found") - } - - err = s.ensureLoadBalancerInstances(orEmpty(lb.LoadBalancerName), lb.Instances, instances) - if err != nil { - return nil - } - - err = s.updateInstanceSecurityGroupsForLoadBalancer(lb, instances) - if err != nil { - return err - } - - return nil -} - -// Returns the instance with the specified ID -func (a *AWSCloud) getInstanceByID(instanceID string) (*ec2.Instance, error) { - instances, err := a.getInstancesByIDs([]*string{&instanceID}) - if err != nil { - return nil, err - } - - if len(instances) == 0 { - return nil, fmt.Errorf("no instances found for instance: %s", instanceID) - } - if len(instances) > 1 { - return nil, fmt.Errorf("multiple instances found for instance: %s", instanceID) - } - - return instances[instanceID], nil -} - -func (a *AWSCloud) getInstancesByIDs(instanceIDs []*string) (map[string]*ec2.Instance, error) { - instancesByID := make(map[string]*ec2.Instance) - if len(instanceIDs) == 0 { - return instancesByID, nil - } - - request := &ec2.DescribeInstancesInput{ - InstanceIds: instanceIDs, - } - - instances, err := a.ec2.DescribeInstances(request) - if err != nil { - return nil, err - } - - for _, instance := range instances { - instanceID := orEmpty(instance.InstanceId) - if instanceID == "" { - continue - } - - instancesByID[instanceID] = instance - } - - return instancesByID, nil -} - -// Fetches instances by node names; returns an error if any cannot be found. -// This is currently implemented by fetching all the instances, because this is currently called for all nodes (i.e. the majority) -// In practice, the breakeven vs looping through and calling getInstanceByNodeName is probably around N=2. -func (a *AWSCloud) getInstancesByNodeNames(nodeNames []string) ([]*ec2.Instance, error) { - allInstances, err := a.getAllInstances() - if err != nil { - return nil, err - } - - nodeNamesMap := make(map[string]int, len(nodeNames)) - for i, nodeName := range nodeNames { - nodeNamesMap[nodeName] = i - } - - instances := make([]*ec2.Instance, len(nodeNames)) - for _, instance := range allInstances { - nodeName := aws.StringValue(instance.PrivateDnsName) - if nodeName == "" { - glog.V(2).Infof("ignoring ec2 instance with no PrivateDnsName: %q", aws.StringValue(instance.InstanceId)) - continue - } - i, found := nodeNamesMap[nodeName] - if !found { - continue - } - instances[i] = instance - } - - for i, instance := range instances { - if instance == nil { - nodeName := nodeNames[i] - return nil, fmt.Errorf("unable to find instance %q", nodeName) - } - } - - return instances, nil -} - -// Returns all instances that are tagged as being in this cluster. -func (a *AWSCloud) getAllInstances() ([]*ec2.Instance, error) { - filters := []*ec2.Filter{} - filters = a.addFilters(filters) - request := &ec2.DescribeInstancesInput{ - Filters: filters, - } - - return a.ec2.DescribeInstances(request) -} - -// Returns the instance with the specified node name -// Returns nil if it does not exist -func (a *AWSCloud) findInstanceByNodeName(nodeName string) (*ec2.Instance, error) { - filters := []*ec2.Filter{ - newEc2Filter("private-dns-name", nodeName), - } - filters = a.addFilters(filters) - request := &ec2.DescribeInstancesInput{ - Filters: filters, - } - - instances, err := a.ec2.DescribeInstances(request) - if err != nil { - return nil, err - } - if len(instances) == 0 { - return nil, nil - } - if len(instances) > 1 { - return nil, fmt.Errorf("multiple instances found for name: %s", nodeName) - } - return instances[0], nil -} - -// Returns the instance with the specified node name -// Like findInstanceByNodeName, but returns error if node not found -func (a *AWSCloud) getInstanceByNodeName(nodeName string) (*ec2.Instance, error) { - instance, err := a.findInstanceByNodeName(nodeName) - if err == nil && instance == nil { - return nil, fmt.Errorf("no instances found for name: %s", nodeName) - } - return instance, err -} - -// Add additional filters, to match on our tags -// This lets us run multiple k8s clusters in a single EC2 AZ -func (s *AWSCloud) addFilters(filters []*ec2.Filter) []*ec2.Filter { - for k, v := range s.filterTags { - filters = append(filters, newEc2Filter("tag:"+k, v)) - } - return filters -} - -// Returns the cluster name or an empty string -func (s *AWSCloud) getClusterName() string { - return s.filterTags[TagNameKubernetesCluster] -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_instancegroups.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_instancegroups.go deleted file mode 100644 index d870b04db..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_instancegroups.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2014 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 aws - -import ( - "fmt" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/autoscaling" - "github.com/golang/glog" -) - -// AWSCloud implements InstanceGroups -var _ InstanceGroups = &AWSCloud{} - -// Implement InstanceGroups.ResizeInstanceGroup -// Set the size to the fixed size -func (a *AWSCloud) ResizeInstanceGroup(instanceGroupName string, size int) error { - request := &autoscaling.UpdateAutoScalingGroupInput{ - AutoScalingGroupName: aws.String(instanceGroupName), - MinSize: aws.Int64(int64(size)), - MaxSize: aws.Int64(int64(size)), - } - if _, err := a.asg.UpdateAutoScalingGroup(request); err != nil { - return fmt.Errorf("error resizing AWS autoscaling group: %v", err) - } - return nil -} - -// Implement InstanceGroups.DescribeInstanceGroup -// Queries the cloud provider for information about the specified instance group -func (a *AWSCloud) DescribeInstanceGroup(instanceGroupName string) (InstanceGroupInfo, error) { - request := &autoscaling.DescribeAutoScalingGroupsInput{ - AutoScalingGroupNames: []*string{aws.String(instanceGroupName)}, - } - response, err := a.asg.DescribeAutoScalingGroups(request) - if err != nil { - return nil, fmt.Errorf("error listing AWS autoscaling group (%s): %v", instanceGroupName, err) - } - - if len(response.AutoScalingGroups) == 0 { - return nil, nil - } - if len(response.AutoScalingGroups) > 1 { - glog.Warning("AWS returned multiple autoscaling groups with name ", instanceGroupName) - } - group := response.AutoScalingGroups[0] - return &awsInstanceGroup{group: group}, nil -} - -// awsInstanceGroup implements InstanceGroupInfo -var _ InstanceGroupInfo = &awsInstanceGroup{} - -type awsInstanceGroup struct { - group *autoscaling.Group -} - -// Implement InstanceGroupInfo.CurrentSize -// The number of instances currently running under control of this group -func (g *awsInstanceGroup) CurrentSize() (int, error) { - return len(g.group.Instances), nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer.go deleted file mode 100644 index 83b299df8..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer.go +++ /dev/null @@ -1,304 +0,0 @@ -/* -Copyright 2014 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 aws - -import ( - "fmt" - "strconv" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/elb" - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/types" - "k8s.io/kubernetes/pkg/util/sets" -) - -func (s *AWSCloud) ensureLoadBalancer(namespacedName types.NamespacedName, name string, listeners []*elb.Listener, subnetIDs []string, securityGroupIDs []string) (*elb.LoadBalancerDescription, error) { - loadBalancer, err := s.describeLoadBalancer(name) - if err != nil { - return nil, err - } - - dirty := false - - if loadBalancer == nil { - createRequest := &elb.CreateLoadBalancerInput{} - createRequest.LoadBalancerName = aws.String(name) - - createRequest.Listeners = listeners - - // We are supposed to specify one subnet per AZ. - // TODO: What happens if we have more than one subnet per AZ? - createRequest.Subnets = stringPointerArray(subnetIDs) - - createRequest.SecurityGroups = stringPointerArray(securityGroupIDs) - - createRequest.Tags = []*elb.Tag{ - {Key: aws.String(TagNameKubernetesCluster), Value: aws.String(s.getClusterName())}, - {Key: aws.String(TagNameKubernetesService), Value: aws.String(namespacedName.String())}, - } - - glog.Infof("Creating load balancer for %v with name: ", namespacedName, name) - _, err := s.elb.CreateLoadBalancer(createRequest) - if err != nil { - return nil, err - } - dirty = true - } else { - { - // Sync subnets - expected := sets.NewString(subnetIDs...) - actual := stringSetFromPointers(loadBalancer.Subnets) - - additions := expected.Difference(actual) - removals := actual.Difference(expected) - - if removals.Len() != 0 { - request := &elb.DetachLoadBalancerFromSubnetsInput{} - request.LoadBalancerName = aws.String(name) - request.Subnets = stringSetToPointers(removals) - glog.V(2).Info("Detaching load balancer from removed subnets") - _, err := s.elb.DetachLoadBalancerFromSubnets(request) - if err != nil { - return nil, fmt.Errorf("error detaching AWS loadbalancer from subnets: %v", err) - } - dirty = true - } - - if additions.Len() != 0 { - request := &elb.AttachLoadBalancerToSubnetsInput{} - request.LoadBalancerName = aws.String(name) - request.Subnets = stringSetToPointers(additions) - glog.V(2).Info("Attaching load balancer to added subnets") - _, err := s.elb.AttachLoadBalancerToSubnets(request) - if err != nil { - return nil, fmt.Errorf("error attaching AWS loadbalancer to subnets: %v", err) - } - dirty = true - } - } - - { - // Sync security groups - expected := sets.NewString(securityGroupIDs...) - actual := stringSetFromPointers(loadBalancer.SecurityGroups) - - if !expected.Equal(actual) { - // This call just replaces the security groups, unlike e.g. subnets (!) - request := &elb.ApplySecurityGroupsToLoadBalancerInput{} - request.LoadBalancerName = aws.String(name) - request.SecurityGroups = stringPointerArray(securityGroupIDs) - glog.V(2).Info("Applying updated security groups to load balancer") - _, err := s.elb.ApplySecurityGroupsToLoadBalancer(request) - if err != nil { - return nil, fmt.Errorf("error applying AWS loadbalancer security groups: %v", err) - } - dirty = true - } - } - - { - // Sync listeners - listenerDescriptions := loadBalancer.ListenerDescriptions - - foundSet := make(map[int]bool) - removals := []*int64{} - for _, listenerDescription := range listenerDescriptions { - actual := listenerDescription.Listener - if actual == nil { - glog.Warning("Ignoring empty listener in AWS loadbalancer: ", name) - continue - } - - found := -1 - for i, expected := range listeners { - if orEmpty(actual.Protocol) != orEmpty(expected.Protocol) { - continue - } - if orEmpty(actual.InstanceProtocol) != orEmpty(expected.InstanceProtocol) { - continue - } - if orZero(actual.InstancePort) != orZero(expected.InstancePort) { - continue - } - if orZero(actual.LoadBalancerPort) != orZero(expected.LoadBalancerPort) { - continue - } - if orEmpty(actual.SSLCertificateId) != orEmpty(expected.SSLCertificateId) { - continue - } - found = i - } - if found != -1 { - foundSet[found] = true - } else { - removals = append(removals, actual.LoadBalancerPort) - } - } - - additions := []*elb.Listener{} - for i := range listeners { - if foundSet[i] { - continue - } - additions = append(additions, listeners[i]) - } - - if len(removals) != 0 { - request := &elb.DeleteLoadBalancerListenersInput{} - request.LoadBalancerName = aws.String(name) - request.LoadBalancerPorts = removals - glog.V(2).Info("Deleting removed load balancer listeners") - _, err := s.elb.DeleteLoadBalancerListeners(request) - if err != nil { - return nil, fmt.Errorf("error deleting AWS loadbalancer listeners: %v", err) - } - dirty = true - } - - if len(additions) != 0 { - request := &elb.CreateLoadBalancerListenersInput{} - request.LoadBalancerName = aws.String(name) - request.Listeners = additions - glog.V(2).Info("Creating added load balancer listeners") - _, err := s.elb.CreateLoadBalancerListeners(request) - if err != nil { - return nil, fmt.Errorf("error creating AWS loadbalancer listeners: %v", err) - } - dirty = true - } - } - } - - if dirty { - loadBalancer, err = s.describeLoadBalancer(name) - if err != nil { - glog.Warning("Unable to retrieve load balancer after creation/update") - return nil, err - } - } - - return loadBalancer, nil -} - -// Makes sure that the health check for an ELB matches the configured listeners -func (s *AWSCloud) ensureLoadBalancerHealthCheck(loadBalancer *elb.LoadBalancerDescription, listeners []*elb.Listener) error { - actual := loadBalancer.HealthCheck - - // Default AWS settings - expectedHealthyThreshold := int64(2) - expectedUnhealthyThreshold := int64(6) - expectedTimeout := int64(5) - expectedInterval := int64(10) - - // We only configure a TCP health-check on the first port - expectedTarget := "" - for _, listener := range listeners { - if listener.InstancePort == nil { - continue - } - expectedTarget = "TCP:" + strconv.FormatInt(*listener.InstancePort, 10) - break - } - - if expectedTarget == "" { - return fmt.Errorf("unable to determine health check port (no valid listeners)") - } - - if expectedTarget == orEmpty(actual.Target) && - expectedHealthyThreshold == orZero(actual.HealthyThreshold) && - expectedUnhealthyThreshold == orZero(actual.UnhealthyThreshold) && - expectedTimeout == orZero(actual.Timeout) && - expectedInterval == orZero(actual.Interval) { - return nil - } - - glog.V(2).Info("Updating load-balancer health-check") - - healthCheck := &elb.HealthCheck{} - healthCheck.HealthyThreshold = &expectedHealthyThreshold - healthCheck.UnhealthyThreshold = &expectedUnhealthyThreshold - healthCheck.Timeout = &expectedTimeout - healthCheck.Interval = &expectedInterval - healthCheck.Target = &expectedTarget - - request := &elb.ConfigureHealthCheckInput{} - request.HealthCheck = healthCheck - request.LoadBalancerName = loadBalancer.LoadBalancerName - - _, err := s.elb.ConfigureHealthCheck(request) - if err != nil { - return fmt.Errorf("error configuring load-balancer health-check: %v", err) - } - - return nil -} - -// Makes sure that exactly the specified hosts are registered as instances with the load balancer -func (s *AWSCloud) ensureLoadBalancerInstances(loadBalancerName string, lbInstances []*elb.Instance, instances []*ec2.Instance) error { - expected := sets.NewString() - for _, instance := range instances { - expected.Insert(orEmpty(instance.InstanceId)) - } - - actual := sets.NewString() - for _, lbInstance := range lbInstances { - actual.Insert(orEmpty(lbInstance.InstanceId)) - } - - additions := expected.Difference(actual) - removals := actual.Difference(expected) - - addInstances := []*elb.Instance{} - for _, instanceId := range additions.List() { - addInstance := &elb.Instance{} - addInstance.InstanceId = aws.String(instanceId) - addInstances = append(addInstances, addInstance) - } - - removeInstances := []*elb.Instance{} - for _, instanceId := range removals.List() { - removeInstance := &elb.Instance{} - removeInstance.InstanceId = aws.String(instanceId) - removeInstances = append(removeInstances, removeInstance) - } - - if len(addInstances) > 0 { - registerRequest := &elb.RegisterInstancesWithLoadBalancerInput{} - registerRequest.Instances = addInstances - registerRequest.LoadBalancerName = aws.String(loadBalancerName) - _, err := s.elb.RegisterInstancesWithLoadBalancer(registerRequest) - if err != nil { - return err - } - glog.V(1).Infof("Instances added to load-balancer %s", loadBalancerName) - } - - if len(removeInstances) > 0 { - deregisterRequest := &elb.DeregisterInstancesFromLoadBalancerInput{} - deregisterRequest.Instances = removeInstances - deregisterRequest.LoadBalancerName = aws.String(loadBalancerName) - _, err := s.elb.DeregisterInstancesFromLoadBalancer(deregisterRequest) - if err != nil { - return err - } - glog.V(1).Infof("Instances removed from load-balancer %s", loadBalancerName) - } - - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_routes.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_routes.go deleted file mode 100644 index c1ffddc3a..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_routes.go +++ /dev/null @@ -1,188 +0,0 @@ -/* -Copyright 2014 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 aws - -import ( - "fmt" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/cloudprovider" -) - -func (s *AWSCloud) findRouteTable(clusterName string) (*ec2.RouteTable, error) { - // This should be unnecessary (we already filter on TagNameKubernetesCluster, - // and something is broken if cluster name doesn't match, but anyway... - // TODO: All clouds should be cluster-aware by default - filters := []*ec2.Filter{newEc2Filter("tag:"+TagNameKubernetesCluster, clusterName)} - request := &ec2.DescribeRouteTablesInput{Filters: s.addFilters(filters)} - - tables, err := s.ec2.DescribeRouteTables(request) - if err != nil { - return nil, err - } - - if len(tables) == 0 { - return nil, fmt.Errorf("unable to find route table for AWS cluster: %s", clusterName) - } - - if len(tables) != 1 { - return nil, fmt.Errorf("found multiple matching AWS route tables for AWS cluster: %s", clusterName) - } - return tables[0], nil -} - -// ListRoutes implements Routes.ListRoutes -// List all routes that match the filter -func (s *AWSCloud) ListRoutes(clusterName string) ([]*cloudprovider.Route, error) { - table, err := s.findRouteTable(clusterName) - if err != nil { - return nil, err - } - - var routes []*cloudprovider.Route - var instanceIDs []*string - - for _, r := range table.Routes { - instanceID := orEmpty(r.InstanceId) - - if instanceID == "" { - continue - } - - instanceIDs = append(instanceIDs, &instanceID) - } - - instances, err := s.getInstancesByIDs(instanceIDs) - if err != nil { - return nil, err - } - - for _, r := range table.Routes { - instanceID := orEmpty(r.InstanceId) - destinationCIDR := orEmpty(r.DestinationCidrBlock) - - if instanceID == "" || destinationCIDR == "" { - continue - } - - instance, found := instances[instanceID] - if !found { - glog.Warningf("unable to find instance ID %s in the list of instances being routed to", instanceID) - continue - } - instanceName := orEmpty(instance.PrivateDnsName) - routeName := clusterName + "-" + destinationCIDR - routes = append(routes, &cloudprovider.Route{Name: routeName, TargetInstance: instanceName, DestinationCIDR: destinationCIDR}) - } - - return routes, nil -} - -// Sets the instance attribute "source-dest-check" to the specified value -func (s *AWSCloud) configureInstanceSourceDestCheck(instanceID string, sourceDestCheck bool) error { - request := &ec2.ModifyInstanceAttributeInput{} - request.InstanceId = aws.String(instanceID) - request.SourceDestCheck = &ec2.AttributeBooleanValue{Value: aws.Bool(sourceDestCheck)} - - _, err := s.ec2.ModifyInstanceAttribute(request) - if err != nil { - return fmt.Errorf("error configuring source-dest-check on instance %s: %v", instanceID, err) - } - return nil -} - -// CreateRoute implements Routes.CreateRoute -// Create the described route -func (s *AWSCloud) CreateRoute(clusterName string, nameHint string, route *cloudprovider.Route) error { - instance, err := s.getInstanceByNodeName(route.TargetInstance) - if err != nil { - return err - } - - // In addition to configuring the route itself, we also need to configure the instance to accept that traffic - // On AWS, this requires turning source-dest checks off - err = s.configureInstanceSourceDestCheck(orEmpty(instance.InstanceId), false) - if err != nil { - return err - } - - table, err := s.findRouteTable(clusterName) - if err != nil { - return err - } - - var deleteRoute *ec2.Route - for _, r := range table.Routes { - destinationCIDR := aws.StringValue(r.DestinationCidrBlock) - - if destinationCIDR != route.DestinationCIDR { - continue - } - - if aws.StringValue(r.State) == ec2.RouteStateBlackhole { - deleteRoute = r - } - } - - if deleteRoute != nil { - glog.Infof("deleting blackholed route: %s", aws.StringValue(deleteRoute.DestinationCidrBlock)) - - request := &ec2.DeleteRouteInput{} - request.DestinationCidrBlock = deleteRoute.DestinationCidrBlock - request.RouteTableId = table.RouteTableId - - _, err = s.ec2.DeleteRoute(request) - if err != nil { - return fmt.Errorf("error deleting blackholed AWS route (%s): %v", deleteRoute.DestinationCidrBlock, err) - } - } - - request := &ec2.CreateRouteInput{} - // TODO: use ClientToken for idempotency? - request.DestinationCidrBlock = aws.String(route.DestinationCIDR) - request.InstanceId = instance.InstanceId - request.RouteTableId = table.RouteTableId - - _, err = s.ec2.CreateRoute(request) - if err != nil { - return fmt.Errorf("error creating AWS route (%s): %v", route.DestinationCIDR, err) - } - - return nil -} - -// DeleteRoute implements Routes.DeleteRoute -// Delete the specified route -func (s *AWSCloud) DeleteRoute(clusterName string, route *cloudprovider.Route) error { - table, err := s.findRouteTable(clusterName) - if err != nil { - return err - } - - request := &ec2.DeleteRouteInput{} - request.DestinationCidrBlock = aws.String(route.DestinationCIDR) - request.RouteTableId = table.RouteTableId - - _, err = s.ec2.DeleteRoute(request) - if err != nil { - return fmt.Errorf("error deleting AWS route (%s): %v", route.DestinationCIDR, err) - } - - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_test.go deleted file mode 100644 index 3151c1246..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_test.go +++ /dev/null @@ -1,939 +0,0 @@ -/* -Copyright 2014 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 aws - -import ( - "fmt" - "io" - "reflect" - "strings" - "testing" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/elb" - - "github.com/aws/aws-sdk-go/service/autoscaling" - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/types" -) - -const TestClusterId = "clusterid.test" - -func TestReadAWSCloudConfig(t *testing.T) { - tests := []struct { - name string - - reader io.Reader - aws AWSServices - - expectError bool - zone string - }{ - { - "No config reader", - nil, nil, - true, "", - }, - { - "Empty config, no metadata", - strings.NewReader(""), nil, - true, "", - }, - { - "No zone in config, no metadata", - strings.NewReader("[global]\n"), nil, - true, "", - }, - { - "Zone in config, no metadata", - strings.NewReader("[global]\nzone = eu-west-1a"), nil, - false, "eu-west-1a", - }, - { - "No zone in config, metadata does not have zone", - strings.NewReader("[global]\n"), NewFakeAWSServices().withAz(""), - true, "", - }, - { - "No zone in config, metadata has zone", - strings.NewReader("[global]\n"), NewFakeAWSServices(), - false, "us-east-1a", - }, - { - "Zone in config should take precedence over metadata", - strings.NewReader("[global]\nzone = eu-west-1a"), NewFakeAWSServices(), - false, "eu-west-1a", - }, - } - - for _, test := range tests { - t.Logf("Running test case %s", test.name) - var metadata EC2Metadata - if test.aws != nil { - metadata, _ = test.aws.Metadata() - } - cfg, err := readAWSCloudConfig(test.reader, metadata) - if test.expectError { - if err == nil { - t.Errorf("Should error for case %s (cfg=%v)", test.name, cfg) - } - } else { - if err != nil { - t.Errorf("Should succeed for case: %s", test.name) - } - if cfg.Global.Zone != test.zone { - t.Errorf("Incorrect zone value (%s vs %s) for case: %s", - cfg.Global.Zone, test.zone, test.name) - } - } - } -} - -type FakeAWSServices struct { - availabilityZone string - instances []*ec2.Instance - instanceId string - privateDnsName string - networkInterfacesMacs []string - networkInterfacesVpcIDs []string - internalIP string - externalIP string - - ec2 *FakeEC2 - elb *FakeELB - asg *FakeASG - metadata *FakeMetadata -} - -func NewFakeAWSServices() *FakeAWSServices { - s := &FakeAWSServices{} - s.availabilityZone = "us-east-1a" - s.ec2 = &FakeEC2{aws: s} - s.elb = &FakeELB{aws: s} - s.asg = &FakeASG{aws: s} - s.metadata = &FakeMetadata{aws: s} - - s.networkInterfacesMacs = []string{"aa:bb:cc:dd:ee:00", "aa:bb:cc:dd:ee:01"} - s.networkInterfacesVpcIDs = []string{"vpc-mac0", "vpc-mac1"} - - s.instanceId = "i-self" - s.privateDnsName = "ip-172-20-0-100.ec2.internal" - s.internalIP = "192.168.0.1" - s.externalIP = "1.2.3.4" - var selfInstance ec2.Instance - selfInstance.InstanceId = &s.instanceId - selfInstance.PrivateDnsName = &s.privateDnsName - s.instances = []*ec2.Instance{&selfInstance} - - var tag ec2.Tag - tag.Key = aws.String(TagNameKubernetesCluster) - tag.Value = aws.String(TestClusterId) - selfInstance.Tags = []*ec2.Tag{&tag} - - return s -} - -func (s *FakeAWSServices) withAz(az string) *FakeAWSServices { - s.availabilityZone = az - return s -} - -func (s *FakeAWSServices) withInstances(instances []*ec2.Instance) *FakeAWSServices { - s.instances = instances - return s -} - -func (s *FakeAWSServices) Compute(region string) (EC2, error) { - return s.ec2, nil -} - -func (s *FakeAWSServices) LoadBalancing(region string) (ELB, error) { - return s.elb, nil -} - -func (s *FakeAWSServices) Autoscaling(region string) (ASG, error) { - return s.asg, nil -} - -func (s *FakeAWSServices) Metadata() (EC2Metadata, error) { - return s.metadata, nil -} - -func TestFilterTags(t *testing.T) { - awsServices := NewFakeAWSServices() - c, err := newAWSCloud(strings.NewReader("[global]"), awsServices) - if err != nil { - t.Errorf("Error building aws cloud: %v", err) - return - } - - if len(c.filterTags) != 1 { - t.Errorf("unexpected filter tags: %v", c.filterTags) - return - } - - if c.filterTags[TagNameKubernetesCluster] != TestClusterId { - t.Errorf("unexpected filter tags: %v", c.filterTags) - } -} - -func TestNewAWSCloud(t *testing.T) { - tests := []struct { - name string - - reader io.Reader - awsServices AWSServices - - expectError bool - zone string - }{ - { - "No config reader", - nil, NewFakeAWSServices().withAz(""), - true, "", - }, - { - "Config specified invalid zone", - strings.NewReader("[global]\nzone = blahonga"), NewFakeAWSServices(), - true, "", - }, - { - "Config specifies valid zone", - strings.NewReader("[global]\nzone = eu-west-1a"), NewFakeAWSServices(), - false, "eu-west-1a", - }, - { - "Gets zone from metadata when not in config", - - strings.NewReader("[global]\n"), - NewFakeAWSServices(), - false, "us-east-1a", - }, - { - "No zone in config or metadata", - strings.NewReader("[global]\n"), - NewFakeAWSServices().withAz(""), - true, "", - }, - } - - for _, test := range tests { - t.Logf("Running test case %s", test.name) - c, err := newAWSCloud(test.reader, test.awsServices) - if test.expectError { - if err == nil { - t.Errorf("Should error for case %s", test.name) - } - } else { - if err != nil { - t.Errorf("Should succeed for case: %s, got %v", test.name, err) - } else if c.availabilityZone != test.zone { - t.Errorf("Incorrect zone value (%s vs %s) for case: %s", - c.availabilityZone, test.zone, test.name) - } - } - } -} - -type FakeEC2 struct { - aws *FakeAWSServices - Subnets []*ec2.Subnet - DescribeSubnetsInput *ec2.DescribeSubnetsInput -} - -func contains(haystack []*string, needle string) bool { - for _, s := range haystack { - // (deliberately panic if s == nil) - if needle == *s { - return true - } - } - return false -} - -func instanceMatchesFilter(instance *ec2.Instance, filter *ec2.Filter) bool { - name := *filter.Name - if name == "private-dns-name" { - if instance.PrivateDnsName == nil { - return false - } - return contains(filter.Values, *instance.PrivateDnsName) - } - panic("Unknown filter name: " + name) -} - -func (self *FakeEC2) DescribeInstances(request *ec2.DescribeInstancesInput) ([]*ec2.Instance, error) { - matches := []*ec2.Instance{} - for _, instance := range self.aws.instances { - if request.InstanceIds != nil { - if instance.InstanceId == nil { - glog.Warning("Instance with no instance id: ", instance) - continue - } - - found := false - for _, instanceId := range request.InstanceIds { - if *instanceId == *instance.InstanceId { - found = true - break - } - } - if !found { - continue - } - } - if request.Filters != nil { - allMatch := true - for _, filter := range request.Filters { - if !instanceMatchesFilter(instance, filter) { - allMatch = false - break - } - } - if !allMatch { - continue - } - } - matches = append(matches, instance) - } - - return matches, nil -} - -type FakeMetadata struct { - aws *FakeAWSServices -} - -func (self *FakeMetadata) GetMetadata(key string) (string, error) { - networkInterfacesPrefix := "network/interfaces/macs/" - if key == "placement/availability-zone" { - return self.aws.availabilityZone, nil - } else if key == "instance-id" { - return self.aws.instanceId, nil - } else if key == "local-hostname" { - return self.aws.privateDnsName, nil - } else if key == "local-ipv4" { - return self.aws.internalIP, nil - } else if key == "public-ipv4" { - return self.aws.externalIP, nil - } else if strings.HasPrefix(key, networkInterfacesPrefix) { - if key == networkInterfacesPrefix { - return strings.Join(self.aws.networkInterfacesMacs, "/\n") + "/\n", nil - } else { - keySplit := strings.Split(key, "/") - macParam := keySplit[3] - if len(keySplit) == 5 && keySplit[4] == "vpc-id" { - for i, macElem := range self.aws.networkInterfacesMacs { - if macParam == macElem { - return self.aws.networkInterfacesVpcIDs[i], nil - } - } - } - return "", nil - } - } else { - return "", nil - } -} - -func (ec2 *FakeEC2) AttachVolume(request *ec2.AttachVolumeInput) (resp *ec2.VolumeAttachment, err error) { - panic("Not implemented") -} - -func (ec2 *FakeEC2) DetachVolume(request *ec2.DetachVolumeInput) (resp *ec2.VolumeAttachment, err error) { - panic("Not implemented") -} - -func (ec2 *FakeEC2) DescribeVolumes(request *ec2.DescribeVolumesInput) ([]*ec2.Volume, error) { - panic("Not implemented") -} - -func (ec2 *FakeEC2) CreateVolume(request *ec2.CreateVolumeInput) (resp *ec2.Volume, err error) { - panic("Not implemented") -} - -func (ec2 *FakeEC2) DeleteVolume(request *ec2.DeleteVolumeInput) (resp *ec2.DeleteVolumeOutput, err error) { - panic("Not implemented") -} - -func (ec2 *FakeEC2) DescribeSecurityGroups(request *ec2.DescribeSecurityGroupsInput) ([]*ec2.SecurityGroup, error) { - panic("Not implemented") -} - -func (ec2 *FakeEC2) CreateSecurityGroup(*ec2.CreateSecurityGroupInput) (*ec2.CreateSecurityGroupOutput, error) { - panic("Not implemented") -} - -func (ec2 *FakeEC2) DeleteSecurityGroup(*ec2.DeleteSecurityGroupInput) (*ec2.DeleteSecurityGroupOutput, error) { - panic("Not implemented") -} - -func (ec2 *FakeEC2) AuthorizeSecurityGroupIngress(*ec2.AuthorizeSecurityGroupIngressInput) (*ec2.AuthorizeSecurityGroupIngressOutput, error) { - panic("Not implemented") -} - -func (ec2 *FakeEC2) RevokeSecurityGroupIngress(*ec2.RevokeSecurityGroupIngressInput) (*ec2.RevokeSecurityGroupIngressOutput, error) { - panic("Not implemented") -} - -func (ec2 *FakeEC2) DescribeSubnets(request *ec2.DescribeSubnetsInput) ([]*ec2.Subnet, error) { - ec2.DescribeSubnetsInput = request - return ec2.Subnets, nil -} - -func (ec2 *FakeEC2) CreateTags(*ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) { - panic("Not implemented") -} - -func (s *FakeEC2) DescribeRouteTables(request *ec2.DescribeRouteTablesInput) ([]*ec2.RouteTable, error) { - panic("Not implemented") -} - -func (s *FakeEC2) CreateRoute(request *ec2.CreateRouteInput) (*ec2.CreateRouteOutput, error) { - panic("Not implemented") -} - -func (s *FakeEC2) DeleteRoute(request *ec2.DeleteRouteInput) (*ec2.DeleteRouteOutput, error) { - panic("Not implemented") -} - -func (s *FakeEC2) ModifyInstanceAttribute(request *ec2.ModifyInstanceAttributeInput) (*ec2.ModifyInstanceAttributeOutput, error) { - panic("Not implemented") -} - -type FakeELB struct { - aws *FakeAWSServices -} - -func (ec2 *FakeELB) CreateLoadBalancer(*elb.CreateLoadBalancerInput) (*elb.CreateLoadBalancerOutput, error) { - panic("Not implemented") -} - -func (ec2 *FakeELB) DeleteLoadBalancer(*elb.DeleteLoadBalancerInput) (*elb.DeleteLoadBalancerOutput, error) { - panic("Not implemented") -} - -func (ec2 *FakeELB) DescribeLoadBalancers(*elb.DescribeLoadBalancersInput) (*elb.DescribeLoadBalancersOutput, error) { - panic("Not implemented") -} -func (ec2 *FakeELB) RegisterInstancesWithLoadBalancer(*elb.RegisterInstancesWithLoadBalancerInput) (*elb.RegisterInstancesWithLoadBalancerOutput, error) { - panic("Not implemented") -} - -func (ec2 *FakeELB) DeregisterInstancesFromLoadBalancer(*elb.DeregisterInstancesFromLoadBalancerInput) (*elb.DeregisterInstancesFromLoadBalancerOutput, error) { - panic("Not implemented") -} - -func (ec2 *FakeELB) DetachLoadBalancerFromSubnets(*elb.DetachLoadBalancerFromSubnetsInput) (*elb.DetachLoadBalancerFromSubnetsOutput, error) { - panic("Not implemented") -} - -func (ec2 *FakeELB) AttachLoadBalancerToSubnets(*elb.AttachLoadBalancerToSubnetsInput) (*elb.AttachLoadBalancerToSubnetsOutput, error) { - panic("Not implemented") -} - -func (ec2 *FakeELB) CreateLoadBalancerListeners(*elb.CreateLoadBalancerListenersInput) (*elb.CreateLoadBalancerListenersOutput, error) { - panic("Not implemented") -} - -func (ec2 *FakeELB) DeleteLoadBalancerListeners(*elb.DeleteLoadBalancerListenersInput) (*elb.DeleteLoadBalancerListenersOutput, error) { - panic("Not implemented") -} - -func (ec2 *FakeELB) ApplySecurityGroupsToLoadBalancer(*elb.ApplySecurityGroupsToLoadBalancerInput) (*elb.ApplySecurityGroupsToLoadBalancerOutput, error) { - panic("Not implemented") -} - -func (elb *FakeELB) ConfigureHealthCheck(*elb.ConfigureHealthCheckInput) (*elb.ConfigureHealthCheckOutput, error) { - panic("Not implemented") -} - -type FakeASG struct { - aws *FakeAWSServices -} - -func (a *FakeASG) UpdateAutoScalingGroup(*autoscaling.UpdateAutoScalingGroupInput) (*autoscaling.UpdateAutoScalingGroupOutput, error) { - panic("Not implemented") -} - -func (a *FakeASG) DescribeAutoScalingGroups(*autoscaling.DescribeAutoScalingGroupsInput) (*autoscaling.DescribeAutoScalingGroupsOutput, error) { - panic("Not implemented") -} - -func mockInstancesResp(instances []*ec2.Instance) (*AWSCloud, *FakeAWSServices) { - awsServices := NewFakeAWSServices().withInstances(instances) - return &AWSCloud{ - ec2: awsServices.ec2, - availabilityZone: awsServices.availabilityZone, - metadata: &FakeMetadata{aws: awsServices}, - }, awsServices -} - -func mockAvailabilityZone(region string, availabilityZone string) *AWSCloud { - awsServices := NewFakeAWSServices().withAz(availabilityZone) - return &AWSCloud{ - ec2: awsServices.ec2, - availabilityZone: awsServices.availabilityZone, - region: region, - } -} - -func TestList(t *testing.T) { - // TODO this setup is not very clean and could probably be improved - var instance0 ec2.Instance - var instance1 ec2.Instance - var instance2 ec2.Instance - var instance3 ec2.Instance - - //0 - tag0 := ec2.Tag{ - Key: aws.String("Name"), - Value: aws.String("foo"), - } - instance0.Tags = []*ec2.Tag{&tag0} - instance0.InstanceId = aws.String("instance0") - instance0.PrivateDnsName = aws.String("instance0.ec2.internal") - state0 := ec2.InstanceState{ - Name: aws.String("running"), - } - instance0.State = &state0 - - //1 - tag1 := ec2.Tag{ - Key: aws.String("Name"), - Value: aws.String("bar"), - } - instance1.Tags = []*ec2.Tag{&tag1} - instance1.InstanceId = aws.String("instance1") - instance1.PrivateDnsName = aws.String("instance1.ec2.internal") - state1 := ec2.InstanceState{ - Name: aws.String("running"), - } - instance1.State = &state1 - - //2 - tag2 := ec2.Tag{ - Key: aws.String("Name"), - Value: aws.String("baz"), - } - instance2.Tags = []*ec2.Tag{&tag2} - instance2.InstanceId = aws.String("instance2") - instance2.PrivateDnsName = aws.String("instance2.ec2.internal") - state2 := ec2.InstanceState{ - Name: aws.String("running"), - } - instance2.State = &state2 - - //3 - tag3 := ec2.Tag{ - Key: aws.String("Name"), - Value: aws.String("quux"), - } - instance3.Tags = []*ec2.Tag{&tag3} - instance3.InstanceId = aws.String("instance3") - instance3.PrivateDnsName = aws.String("instance3.ec2.internal") - state3 := ec2.InstanceState{ - Name: aws.String("running"), - } - instance3.State = &state3 - - instances := []*ec2.Instance{&instance0, &instance1, &instance2, &instance3} - aws, _ := mockInstancesResp(instances) - - table := []struct { - input string - expect []string - }{ - {"blahonga", []string{}}, - {"quux", []string{"instance3.ec2.internal"}}, - {"a", []string{"instance1.ec2.internal", "instance2.ec2.internal"}}, - } - - for _, item := range table { - result, err := aws.List(item.input) - if err != nil { - t.Errorf("Expected call with %v to succeed, failed with %s", item.input, err) - } - if e, a := item.expect, result; !reflect.DeepEqual(e, a) { - t.Errorf("Expected %v, got %v", e, a) - } - } -} - -func testHasNodeAddress(t *testing.T, addrs []api.NodeAddress, addressType api.NodeAddressType, address string) { - for _, addr := range addrs { - if addr.Type == addressType && addr.Address == address { - return - } - } - t.Errorf("Did not find expected address: %s:%s in %v", addressType, address, addrs) -} - -func TestNodeAddresses(t *testing.T) { - // Note these instances have the same name - // (we test that this produces an error) - var instance0 ec2.Instance - var instance1 ec2.Instance - var instance2 ec2.Instance - - //0 - instance0.InstanceId = aws.String("i-self") - instance0.PrivateDnsName = aws.String("instance-same.ec2.internal") - instance0.PrivateIpAddress = aws.String("192.168.0.1") - instance0.PublicIpAddress = aws.String("1.2.3.4") - instance0.InstanceType = aws.String("c3.large") - state0 := ec2.InstanceState{ - Name: aws.String("running"), - } - instance0.State = &state0 - - //1 - instance1.InstanceId = aws.String("i-self") - instance1.PrivateDnsName = aws.String("instance-same.ec2.internal") - instance1.PrivateIpAddress = aws.String("192.168.0.2") - instance1.InstanceType = aws.String("c3.large") - state1 := ec2.InstanceState{ - Name: aws.String("running"), - } - instance1.State = &state1 - - //2 - instance2.InstanceId = aws.String("i-self") - instance2.PrivateDnsName = aws.String("instance-other.ec2.internal") - instance2.PrivateIpAddress = aws.String("192.168.0.1") - instance2.PublicIpAddress = aws.String("1.2.3.4") - instance2.InstanceType = aws.String("c3.large") - state2 := ec2.InstanceState{ - Name: aws.String("running"), - } - instance2.State = &state2 - - instances := []*ec2.Instance{&instance0, &instance1, &instance2} - - aws1, _ := mockInstancesResp([]*ec2.Instance{}) - _, err1 := aws1.NodeAddresses("instance-mismatch.ec2.internal") - if err1 == nil { - t.Errorf("Should error when no instance found") - } - - aws2, _ := mockInstancesResp(instances) - _, err2 := aws2.NodeAddresses("instance-same.ec2.internal") - if err2 == nil { - t.Errorf("Should error when multiple instances found") - } - - aws3, _ := mockInstancesResp(instances[0:1]) - addrs3, err3 := aws3.NodeAddresses("instance-same.ec2.internal") - if err3 != nil { - t.Errorf("Should not error when instance found") - } - if len(addrs3) != 3 { - t.Errorf("Should return exactly 3 NodeAddresses") - } - testHasNodeAddress(t, addrs3, api.NodeInternalIP, "192.168.0.1") - testHasNodeAddress(t, addrs3, api.NodeLegacyHostIP, "192.168.0.1") - testHasNodeAddress(t, addrs3, api.NodeExternalIP, "1.2.3.4") - - aws4, fakeServices := mockInstancesResp([]*ec2.Instance{}) - fakeServices.externalIP = "2.3.4.5" - fakeServices.internalIP = "192.168.0.2" - aws4.selfAWSInstance = &awsInstance{nodeName: fakeServices.instanceId} - - addrs4, err4 := aws4.NodeAddresses(fakeServices.instanceId) - if err4 != nil { - t.Errorf("unexpected error: %v", err4) - } - testHasNodeAddress(t, addrs4, api.NodeInternalIP, "192.168.0.2") - testHasNodeAddress(t, addrs4, api.NodeExternalIP, "2.3.4.5") -} - -func TestGetRegion(t *testing.T) { - aws := mockAvailabilityZone("us-west-2", "us-west-2e") - zones, ok := aws.Zones() - if !ok { - t.Fatalf("Unexpected missing zones impl") - } - zone, err := zones.GetZone() - if err != nil { - t.Fatalf("unexpected error %v", err) - } - if zone.Region != "us-west-2" { - t.Errorf("Unexpected region: %s", zone.Region) - } - if zone.FailureDomain != "us-west-2e" { - t.Errorf("Unexpected FailureDomain: %s", zone.FailureDomain) - } -} - -func TestFindVPCID(t *testing.T) { - awsServices := NewFakeAWSServices() - c, err := newAWSCloud(strings.NewReader("[global]"), awsServices) - if err != nil { - t.Errorf("Error building aws cloud: %v", err) - return - } - vpcID, err := c.findVPCID() - if err != nil { - t.Errorf("Unexpected error:", err) - } - if vpcID != "vpc-mac0" { - t.Errorf("Unexpected vpcID: %s", vpcID) - } -} - -func TestLoadBalancerMatchesClusterRegion(t *testing.T) { - awsServices := NewFakeAWSServices() - c, err := newAWSCloud(strings.NewReader("[global]"), awsServices) - if err != nil { - t.Errorf("Error building aws cloud: %v", err) - return - } - - badELBRegion := "bad-elb-region" - errorMessage := fmt.Sprintf("requested load balancer region '%s' does not match cluster region '%s'", badELBRegion, c.region) - - _, _, err = c.GetLoadBalancer("elb-name", badELBRegion) - if err == nil || err.Error() != errorMessage { - t.Errorf("Expected GetLoadBalancer region mismatch error.") - } - - serviceName := types.NamespacedName{Namespace: "foo", Name: "bar"} - - _, err = c.EnsureLoadBalancer("elb-name", badELBRegion, nil, nil, nil, serviceName, api.ServiceAffinityNone) - if err == nil || err.Error() != errorMessage { - t.Errorf("Expected EnsureLoadBalancer region mismatch error.") - } - - err = c.EnsureLoadBalancerDeleted("elb-name", badELBRegion) - if err == nil || err.Error() != errorMessage { - t.Errorf("Expected EnsureLoadBalancerDeleted region mismatch error.") - } - - err = c.UpdateLoadBalancer("elb-name", badELBRegion, nil) - if err == nil || err.Error() != errorMessage { - t.Errorf("Expected UpdateLoadBalancer region mismatch error.") - } -} - -func constructSubnets(subnetsIn map[int]map[string]string) (subnetsOut []*ec2.Subnet) { - for i := range subnetsIn { - subnetsOut = append( - subnetsOut, - constructSubnet( - subnetsIn[i]["id"], - subnetsIn[i]["az"], - ), - ) - } - return -} - -func constructSubnet(id string, az string) *ec2.Subnet { - return &ec2.Subnet{ - SubnetId: &id, - AvailabilityZone: &az, - } -} - -func TestSubnetIDsinVPC(t *testing.T) { - awsServices := NewFakeAWSServices() - c, err := newAWSCloud(strings.NewReader("[global]"), awsServices) - if err != nil { - t.Errorf("Error building aws cloud: %v", err) - return - } - - vpcID := "vpc-deadbeef" - - // test with 3 subnets from 3 different AZs - subnets := make(map[int]map[string]string) - subnets[0] = make(map[string]string) - subnets[0]["id"] = "subnet-a0000001" - subnets[0]["az"] = "af-south-1a" - subnets[1] = make(map[string]string) - subnets[1]["id"] = "subnet-b0000001" - subnets[1]["az"] = "af-south-1b" - subnets[2] = make(map[string]string) - subnets[2]["id"] = "subnet-c0000001" - subnets[2]["az"] = "af-south-1c" - awsServices.ec2.Subnets = constructSubnets(subnets) - - result, err := c.listSubnetIDsinVPC(vpcID) - if err != nil { - t.Errorf("Error listing subnets: %v", err) - return - } - - if len(result) != 3 { - t.Errorf("Expected 3 subnets but got %d", len(result)) - return - } - - result_set := make(map[string]bool) - for _, v := range result { - result_set[v] = true - } - - for i := range subnets { - if !result_set[subnets[i]["id"]] { - t.Errorf("Expected subnet%d '%s' in result: %v", i, subnets[i]["id"], result) - return - } - } - - // test with 4 subnets from 3 different AZs - // add duplicate az subnet - subnets[3] = make(map[string]string) - subnets[3]["id"] = "subnet-c0000002" - subnets[3]["az"] = "af-south-1c" - awsServices.ec2.Subnets = constructSubnets(subnets) - - result, err = c.listSubnetIDsinVPC(vpcID) - if err != nil { - t.Errorf("Error listing subnets: %v", err) - return - } - - if len(result) != 3 { - t.Errorf("Expected 3 subnets but got %d", len(result)) - return - } - -} - -func TestIpPermissionExistsHandlesMultipleGroupIds(t *testing.T) { - oldIpPermission := ec2.IpPermission{ - UserIdGroupPairs: []*ec2.UserIdGroupPair{ - {GroupId: aws.String("firstGroupId")}, - {GroupId: aws.String("secondGroupId")}, - {GroupId: aws.String("thirdGroupId")}, - }, - } - - existingIpPermission := ec2.IpPermission{ - UserIdGroupPairs: []*ec2.UserIdGroupPair{ - {GroupId: aws.String("secondGroupId")}, - }, - } - - newIpPermission := ec2.IpPermission{ - UserIdGroupPairs: []*ec2.UserIdGroupPair{ - {GroupId: aws.String("fourthGroupId")}, - }, - } - - equals := ipPermissionExists(&existingIpPermission, &oldIpPermission, false) - if !equals { - t.Errorf("Should have been considered equal since first is in the second array of groups") - } - - equals = ipPermissionExists(&newIpPermission, &oldIpPermission, false) - if equals { - t.Errorf("Should have not been considered equal since first is not in the second array of groups") - } -} - -func TestIpPermissionExistsHandlesRangeSubsets(t *testing.T) { - // Two existing scenarios we'll test against - emptyIpPermission := ec2.IpPermission{} - - oldIpPermission := ec2.IpPermission{ - IpRanges: []*ec2.IpRange{ - {CidrIp: aws.String("10.0.0.0/8")}, - {CidrIp: aws.String("192.168.1.0/24")}, - }, - } - - // Two already existing ranges and a new one - existingIpPermission := ec2.IpPermission{ - IpRanges: []*ec2.IpRange{ - {CidrIp: aws.String("10.0.0.0/8")}, - }, - } - existingIpPermission2 := ec2.IpPermission{ - IpRanges: []*ec2.IpRange{ - {CidrIp: aws.String("192.168.1.0/24")}, - }, - } - - newIpPermission := ec2.IpPermission{ - IpRanges: []*ec2.IpRange{ - {CidrIp: aws.String("172.16.0.0/16")}, - }, - } - - exists := ipPermissionExists(&emptyIpPermission, &emptyIpPermission, false) - if !exists { - t.Errorf("Should have been considered existing since we're comparing a range array against itself") - } - exists = ipPermissionExists(&oldIpPermission, &oldIpPermission, false) - if !exists { - t.Errorf("Should have been considered existing since we're comparing a range array against itself") - } - - exists = ipPermissionExists(&existingIpPermission, &oldIpPermission, false) - if !exists { - t.Errorf("Should have been considered existing since 10.* is in oldIpPermission's array of ranges") - } - exists = ipPermissionExists(&existingIpPermission2, &oldIpPermission, false) - if !exists { - t.Errorf("Should have been considered existing since 192.* is in oldIpPermission2's array of ranges") - } - - exists = ipPermissionExists(&newIpPermission, &emptyIpPermission, false) - if exists { - t.Errorf("Should have not been considered existing since we compared against a missing array of ranges") - } - exists = ipPermissionExists(&newIpPermission, &oldIpPermission, false) - if exists { - t.Errorf("Should have not been considered existing since 172.* is not in oldIpPermission's array of ranges") - } -} - -func TestIpPermissionExistsHandlesMultipleGroupIdsWithUserIds(t *testing.T) { - oldIpPermission := ec2.IpPermission{ - UserIdGroupPairs: []*ec2.UserIdGroupPair{ - {GroupId: aws.String("firstGroupId"), UserId: aws.String("firstUserId")}, - {GroupId: aws.String("secondGroupId"), UserId: aws.String("secondUserId")}, - {GroupId: aws.String("thirdGroupId"), UserId: aws.String("thirdUserId")}, - }, - } - - existingIpPermission := ec2.IpPermission{ - UserIdGroupPairs: []*ec2.UserIdGroupPair{ - {GroupId: aws.String("secondGroupId"), UserId: aws.String("secondUserId")}, - }, - } - - newIpPermission := ec2.IpPermission{ - UserIdGroupPairs: []*ec2.UserIdGroupPair{ - {GroupId: aws.String("secondGroupId"), UserId: aws.String("anotherUserId")}, - }, - } - - equals := ipPermissionExists(&existingIpPermission, &oldIpPermission, true) - if !equals { - t.Errorf("Should have been considered equal since first is in the second array of groups") - } - - equals = ipPermissionExists(&newIpPermission, &oldIpPermission, true) - if equals { - t.Errorf("Should have not been considered equal since first is not in the second array of groups") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_utils.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_utils.go deleted file mode 100644 index 310b4898e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/aws_utils.go +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2014 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 aws - -import ( - "github.com/aws/aws-sdk-go/aws" - "k8s.io/kubernetes/pkg/util/sets" -) - -func stringSetToPointers(in sets.String) []*string { - if in == nil { - return nil - } - out := make([]*string, len(in)) - for k := range in { - out = append(out, aws.String(k)) - } - return out -} - -func stringSetFromPointers(in []*string) sets.String { - if in == nil { - return nil - } - out := sets.NewString() - for i := range in { - out.Insert(orEmpty(in[i])) - } - return out -} - -func orZero(v *int64) int64 { - if v == nil { - return 0 - } - return *v -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/log_handler.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/log_handler.go deleted file mode 100644 index 177c7074a..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/aws/log_handler.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2015 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 aws - -import ( - "github.com/aws/aws-sdk-go/aws/request" - "github.com/golang/glog" -) - -// Handler for aws-sdk-go that logs all requests -func awsHandlerLogger(req *request.Request) { - service := req.ClientInfo.ServiceName - - name := "?" - if req.Operation != nil { - name = req.Operation.Name - } - - glog.V(4).Infof("AWS request: %s %s", service, name) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/fake/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/fake/doc.go deleted file mode 100644 index ff22d568f..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/fake/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2014 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 fake is a test-double implementation of cloudprovider -// Interface, LoadBalancer and Instances. It is useful for testing. -package fake diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/fake/fake.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/fake/fake.go deleted file mode 100644 index 498dce53c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/fake/fake.go +++ /dev/null @@ -1,259 +0,0 @@ -/* -Copyright 2014 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 fake - -import ( - "errors" - "fmt" - "net" - "regexp" - "sync" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/cloudprovider" - "k8s.io/kubernetes/pkg/types" -) - -const ProviderName = "fake" - -// FakeBalancer is a fake storage of balancer information -type FakeBalancer struct { - Name string - Region string - ExternalIP net.IP - Ports []*api.ServicePort - Hosts []string -} - -type FakeUpdateBalancerCall struct { - Name string - Region string - Hosts []string -} - -// FakeCloud is a test-double implementation of Interface, LoadBalancer, Instances, and Routes. It is useful for testing. -type FakeCloud struct { - Exists bool - Err error - Calls []string - Addresses []api.NodeAddress - ExtID map[string]string - InstanceTypes map[string]string - Machines []string - NodeResources *api.NodeResources - ClusterList []string - MasterName string - ExternalIP net.IP - Balancers map[string]FakeBalancer - UpdateCalls []FakeUpdateBalancerCall - RouteMap map[string]*FakeRoute - Lock sync.Mutex - cloudprovider.Zone -} - -type FakeRoute struct { - ClusterName string - Route cloudprovider.Route -} - -func (f *FakeCloud) addCall(desc string) { - f.Calls = append(f.Calls, desc) -} - -// ClearCalls clears internal record of method calls to this FakeCloud. -func (f *FakeCloud) ClearCalls() { - f.Calls = []string{} -} - -func (f *FakeCloud) ListClusters() ([]string, error) { - return f.ClusterList, f.Err -} - -func (f *FakeCloud) Master(name string) (string, error) { - return f.MasterName, f.Err -} - -func (f *FakeCloud) Clusters() (cloudprovider.Clusters, bool) { - return f, true -} - -// ProviderName returns the cloud provider ID. -func (f *FakeCloud) ProviderName() string { - return ProviderName -} - -// ScrubDNS filters DNS settings for pods. -func (f *FakeCloud) ScrubDNS(nameservers, searches []string) (nsOut, srchOut []string) { - return nameservers, searches -} - -// LoadBalancer returns a fake implementation of LoadBalancer. -// Actually it just returns f itself. -func (f *FakeCloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) { - return f, true -} - -// Instances returns a fake implementation of Instances. -// -// Actually it just returns f itself. -func (f *FakeCloud) Instances() (cloudprovider.Instances, bool) { - return f, true -} - -func (f *FakeCloud) Zones() (cloudprovider.Zones, bool) { - return f, true -} - -func (f *FakeCloud) Routes() (cloudprovider.Routes, bool) { - return f, true -} - -// GetLoadBalancer is a stub implementation of LoadBalancer.GetLoadBalancer. -func (f *FakeCloud) GetLoadBalancer(name, region string) (*api.LoadBalancerStatus, bool, error) { - status := &api.LoadBalancerStatus{} - status.Ingress = []api.LoadBalancerIngress{{IP: f.ExternalIP.String()}} - - return status, f.Exists, f.Err -} - -// EnsureLoadBalancer is a test-spy implementation of LoadBalancer.EnsureLoadBalancer. -// It adds an entry "create" into the internal method call record. -func (f *FakeCloud) EnsureLoadBalancer(name, region string, externalIP net.IP, ports []*api.ServicePort, hosts []string, serviceName types.NamespacedName, affinityType api.ServiceAffinity) (*api.LoadBalancerStatus, error) { - f.addCall("create") - if f.Balancers == nil { - f.Balancers = make(map[string]FakeBalancer) - } - f.Balancers[name] = FakeBalancer{name, region, externalIP, ports, hosts} - - status := &api.LoadBalancerStatus{} - status.Ingress = []api.LoadBalancerIngress{{IP: f.ExternalIP.String()}} - - return status, f.Err -} - -// UpdateLoadBalancer is a test-spy implementation of LoadBalancer.UpdateLoadBalancer. -// It adds an entry "update" into the internal method call record. -func (f *FakeCloud) UpdateLoadBalancer(name, region string, hosts []string) error { - f.addCall("update") - f.UpdateCalls = append(f.UpdateCalls, FakeUpdateBalancerCall{name, region, hosts}) - return f.Err -} - -// EnsureLoadBalancerDeleted is a test-spy implementation of LoadBalancer.EnsureLoadBalancerDeleted. -// It adds an entry "delete" into the internal method call record. -func (f *FakeCloud) EnsureLoadBalancerDeleted(name, region string) error { - f.addCall("delete") - return f.Err -} - -func (f *FakeCloud) AddSSHKeyToAllInstances(user string, keyData []byte) error { - return errors.New("unimplemented") -} - -// Implementation of Instances.CurrentNodeName -func (f *FakeCloud) CurrentNodeName(hostname string) (string, error) { - return hostname, nil -} - -// NodeAddresses is a test-spy implementation of Instances.NodeAddresses. -// It adds an entry "node-addresses" into the internal method call record. -func (f *FakeCloud) NodeAddresses(instance string) ([]api.NodeAddress, error) { - f.addCall("node-addresses") - return f.Addresses, f.Err -} - -// ExternalID is a test-spy implementation of Instances.ExternalID. -// It adds an entry "external-id" into the internal method call record. -// It returns an external id to the mapped instance name, if not found, it will return "ext-{instance}" -func (f *FakeCloud) ExternalID(instance string) (string, error) { - f.addCall("external-id") - return f.ExtID[instance], f.Err -} - -// InstanceID returns the cloud provider ID of the specified instance. -func (f *FakeCloud) InstanceID(instance string) (string, error) { - f.addCall("instance-id") - return f.ExtID[instance], nil -} - -// InstanceType returns the type of the specified instance. -func (f *FakeCloud) InstanceType(instance string) (string, error) { - f.addCall("instance-type") - return f.InstanceTypes[instance], nil -} - -// List is a test-spy implementation of Instances.List. -// It adds an entry "list" into the internal method call record. -func (f *FakeCloud) List(filter string) ([]string, error) { - f.addCall("list") - result := []string{} - for _, machine := range f.Machines { - if match, _ := regexp.MatchString(filter, machine); match { - result = append(result, machine) - } - } - return result, f.Err -} - -func (f *FakeCloud) GetZone() (cloudprovider.Zone, error) { - f.addCall("get-zone") - return f.Zone, f.Err -} - -func (f *FakeCloud) ListRoutes(clusterName string) ([]*cloudprovider.Route, error) { - f.Lock.Lock() - defer f.Lock.Unlock() - f.addCall("list-routes") - var routes []*cloudprovider.Route - for _, fakeRoute := range f.RouteMap { - if clusterName == fakeRoute.ClusterName { - routeCopy := fakeRoute.Route - routes = append(routes, &routeCopy) - } - } - return routes, f.Err -} - -func (f *FakeCloud) CreateRoute(clusterName string, nameHint string, route *cloudprovider.Route) error { - f.Lock.Lock() - defer f.Lock.Unlock() - f.addCall("create-route") - name := clusterName + "-" + nameHint - if _, exists := f.RouteMap[name]; exists { - f.Err = fmt.Errorf("route %q already exists", name) - return f.Err - } - fakeRoute := FakeRoute{} - fakeRoute.Route = *route - fakeRoute.Route.Name = name - fakeRoute.ClusterName = clusterName - f.RouteMap[name] = &fakeRoute - return nil -} - -func (f *FakeCloud) DeleteRoute(clusterName string, route *cloudprovider.Route) error { - f.Lock.Lock() - defer f.Lock.Unlock() - f.addCall("delete-route") - name := route.Name - if _, exists := f.RouteMap[name]; !exists { - f.Err = fmt.Errorf("no route found with name %q", name) - return f.Err - } - delete(f.RouteMap, name) - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce.go index f7bd672fb..bf458ce8e 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce.go @@ -30,10 +30,12 @@ import ( "time" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/service" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/types" utilerrors "k8s.io/kubernetes/pkg/util/errors" + netsets "k8s.io/kubernetes/pkg/util/net/sets" "k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/util/wait" @@ -61,6 +63,11 @@ const ( operationPollInterval = 3 * time.Second operationPollTimeoutDuration = 30 * time.Minute + + // Each page can have 500 results, but we cap how many pages + // are iterated through to prevent infinite loops if the API + // were to continuously return a nextPageToken. + maxPages = 25 ) // GCECloud is an implementation of Interface, LoadBalancer and Instances for Google Compute Engine. @@ -69,7 +76,7 @@ type GCECloud struct { containerService *container.Service projectID string region string - localZone string // The zone in which we are runniing + localZone string // The zone in which we are running managedZones []string // List of zones we are spanning (for Ubernetes-Lite, primarily when running on master) networkURL string useMetadataServer bool @@ -131,6 +138,19 @@ func getCurrentExternalIDViaMetadata() (string, error) { return externalID, nil } +func getCurrentMachineTypeViaMetadata() (string, error) { + mType, err := metadata.Get("instance/machine-type") + if err != nil { + return "", fmt.Errorf("couldn't get machine type: %v", err) + } + parts := strings.Split(mType, "/") + if len(parts) != 4 { + return "", fmt.Errorf("unexpected response for machine type: %s", mType) + } + + return parts[3], nil +} + func getNetworkNameViaMetadata() (string, error) { result, err := metadata.Get("instance/network-interfaces/0/network") if err != nil { @@ -144,6 +164,7 @@ func getNetworkNameViaMetadata() (string, error) { } func getNetworkNameViaAPICall(svc *compute.Service, projectID string) (string, error) { + // TODO: use PageToken to list all not just the first 500 networkList, err := svc.Networks.List(projectID).Do() if err != nil { return "", err @@ -157,6 +178,7 @@ func getNetworkNameViaAPICall(svc *compute.Service, projectID string) (string, e } func getZonesForRegion(svc *compute.Service, projectID, region string) ([]string, error) { + // TODO: use PageToken to list all not just the first 500 listCall := svc.Zones.List(projectID) // Filtering by region doesn't seem to work @@ -432,12 +454,13 @@ func isHTTPErrorCode(err error, code int) bool { // Due to an interesting series of design decisions, this handles both creating // new load balancers and updating existing load balancers, recognizing when // each is needed. -func (gce *GCECloud) EnsureLoadBalancer(name, region string, requestedIP net.IP, ports []*api.ServicePort, hostNames []string, serviceName types.NamespacedName, affinityType api.ServiceAffinity) (*api.LoadBalancerStatus, error) { +func (gce *GCECloud) EnsureLoadBalancer(name, region string, requestedIP net.IP, ports []*api.ServicePort, hostNames []string, svc types.NamespacedName, affinityType api.ServiceAffinity, annotations map[string]string) (*api.LoadBalancerStatus, error) { portStr := []string{} for _, p := range ports { portStr = append(portStr, fmt.Sprintf("%s/%d", p.Protocol, p.Port)) } - glog.V(2).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v, %v)", name, region, requestedIP, portStr, hostNames, serviceName) + serviceName := svc.String() + glog.V(2).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v, %v, %v)", name, region, requestedIP, portStr, hostNames, serviceName, annotations) if len(hostNames) == 0 { return nil, fmt.Errorf("Cannot EnsureLoadBalancer() with no hosts") @@ -511,7 +534,7 @@ func (gce *GCECloud) EnsureLoadBalancer(name, region string, requestedIP net.IP, // to this forwarding rule, so we can keep it. isUserOwnedIP = false isSafeToReleaseIP = true - ipAddress, _, err = gce.ensureStaticIP(name, region, fwdRuleIP) + ipAddress, _, err = gce.ensureStaticIP(name, serviceName, region, fwdRuleIP) if err != nil { return nil, fmt.Errorf("failed to ensure static IP %s: %v", fwdRuleIP, err) } @@ -532,7 +555,7 @@ func (gce *GCECloud) EnsureLoadBalancer(name, region string, requestedIP net.IP, // IP from ephemeral to static, or it will just get the IP if it is // already static. existed := false - ipAddress, existed, err = gce.ensureStaticIP(name, region, fwdRuleIP) + ipAddress, existed, err = gce.ensureStaticIP(name, serviceName, region, fwdRuleIP) if err != nil { return nil, fmt.Errorf("failed to ensure static IP %s: %v", fwdRuleIP, err) } @@ -555,22 +578,28 @@ func (gce *GCECloud) EnsureLoadBalancer(name, region string, requestedIP net.IP, // Deal with the firewall next. The reason we do this here rather than last // is because the forwarding rule is used as the indicator that the load // balancer is fully created - it's what getLoadBalancer checks for. - firewallExists, firewallNeedsUpdate, err := gce.firewallNeedsUpdate(name, region, ipAddress, ports) + // Check if user specified the allow source range + sourceRanges, err := service.GetLoadBalancerSourceRanges(annotations) + if err != nil { + return nil, err + } + + firewallExists, firewallNeedsUpdate, err := gce.firewallNeedsUpdate(name, serviceName, region, ipAddress, ports, sourceRanges) if err != nil { return nil, err } if firewallNeedsUpdate { - desc := makeFirewallDescription(ipAddress) + desc := makeFirewallDescription(serviceName, ipAddress) // Unlike forwarding rules and target pools, firewalls can be updated // without needing to be deleted and recreated. if firewallExists { - if err := gce.updateFirewall(name, region, desc, "0.0.0.0/0", ports, hosts); err != nil { + if err := gce.updateFirewall(name, region, desc, sourceRanges, ports, hosts); err != nil { return nil, err } glog.V(4).Infof("EnsureLoadBalancer(%v(%v)): updated firewall", name, serviceName) } else { - if err := gce.createFirewall(name, region, desc, "0.0.0.0/0", ports, hosts); err != nil { + if err := gce.createFirewall(name, region, desc, sourceRanges, ports, hosts); err != nil { return nil, err } glog.V(4).Infof("EnsureLoadBalancer(%v(%v)): created firewall", name, serviceName) @@ -609,13 +638,13 @@ func (gce *GCECloud) EnsureLoadBalancer(name, region string, requestedIP net.IP, // Once we've deleted the resources (if necessary), build them back up (or for // the first time if they're new). if tpNeedsUpdate { - if err := gce.createTargetPool(name, region, hosts, affinityType); err != nil { + if err := gce.createTargetPool(name, serviceName, region, hosts, affinityType); err != nil { return nil, fmt.Errorf("failed to create target pool %s: %v", name, err) } glog.V(4).Infof("EnsureLoadBalancer(%v(%v)): created target pool", name, serviceName) } if tpNeedsUpdate || fwdRuleNeedsUpdate { - if err := gce.createForwardingRule(name, region, ipAddress, ports); err != nil { + if err := gce.createForwardingRule(name, serviceName, region, ipAddress, ports); err != nil { return nil, fmt.Errorf("failed to create forwarding rule %s: %v", name, err) } // End critical section. It is safe to release the static IP (which @@ -713,7 +742,7 @@ func translateAffinityType(affinityType api.ServiceAffinity) string { } } -func (gce *GCECloud) firewallNeedsUpdate(name, region, ipAddress string, ports []*api.ServicePort) (exists bool, needsUpdate bool, err error) { +func (gce *GCECloud) firewallNeedsUpdate(name, serviceName, region, ipAddress string, ports []*api.ServicePort, sourceRanges netsets.IPNet) (exists bool, needsUpdate bool, err error) { fw, err := gce.service.Firewalls.Get(gce.projectID, makeFirewallName(name)).Do() if err != nil { if isHTTPErrorCode(err, http.StatusNotFound) { @@ -721,7 +750,7 @@ func (gce *GCECloud) firewallNeedsUpdate(name, region, ipAddress string, ports [ } return false, false, fmt.Errorf("error getting load balancer's target pool: %v", err) } - if fw.Description != makeFirewallDescription(ipAddress) { + if fw.Description != makeFirewallDescription(serviceName, ipAddress) { return true, true, nil } if len(fw.Allowed) != 1 || (fw.Allowed[0].IPProtocol != "tcp" && fw.Allowed[0].IPProtocol != "udp") { @@ -737,6 +766,17 @@ func (gce *GCECloud) firewallNeedsUpdate(name, region, ipAddress string, ports [ } // The service controller already verified that the protocol matches on all ports, no need to check. + actualSourceRanges, err := netsets.ParseIPNets(fw.SourceRanges...) + if err != nil { + // This really shouldn't happen... GCE has returned something unexpected + glog.Warningf("Error parsing firewall SourceRanges: %v", fw.SourceRanges) + // We don't return the error, because we can hopefully recover from this by reconfiguring the firewall + return true, true, nil + } + + if !sourceRanges.Equal(actualSourceRanges) { + return true, true, nil + } return true, false, nil } @@ -744,8 +784,9 @@ func makeFirewallName(name string) string { return fmt.Sprintf("k8s-fw-%s", name) } -func makeFirewallDescription(ipAddress string) string { - return fmt.Sprintf("KubernetesAutoGenerated_OnlyAllowTrafficForDestinationIP_%s", ipAddress) +func makeFirewallDescription(serviceName, ipAddress string) string { + return fmt.Sprintf(`{"kubernetes.io/service-ip":"%s", "kubernetes.io/service-name":"%s"}`, + ipAddress, serviceName) } func slicesEqual(x, y []string) bool { @@ -762,16 +803,18 @@ func slicesEqual(x, y []string) bool { return true } -func (gce *GCECloud) createForwardingRule(name, region, ipAddress string, ports []*api.ServicePort) error { +func (gce *GCECloud) createForwardingRule(name, serviceName, region, ipAddress string, ports []*api.ServicePort) error { portRange, err := loadBalancerPortRange(ports) if err != nil { return err } req := &compute.ForwardingRule{ - Name: name, - IPAddress: ipAddress, IPProtocol: string(ports[0].Protocol), - PortRange: portRange, - Target: gce.targetPoolURL(name, region), + Name: name, + Description: fmt.Sprintf(`{"kubernetes.io/service-name":"%s"}`, serviceName), + IPAddress: ipAddress, + IPProtocol: string(ports[0].Protocol), + PortRange: portRange, + Target: gce.targetPoolURL(name, region), } op, err := gce.service.ForwardingRules.Insert(gce.projectID, region, req).Do() @@ -787,13 +830,14 @@ func (gce *GCECloud) createForwardingRule(name, region, ipAddress string, ports return nil } -func (gce *GCECloud) createTargetPool(name, region string, hosts []*gceInstance, affinityType api.ServiceAffinity) error { +func (gce *GCECloud) createTargetPool(name, serviceName, region string, hosts []*gceInstance, affinityType api.ServiceAffinity) error { var instances []string for _, host := range hosts { instances = append(instances, makeHostURL(gce.projectID, host.Zone, host.Name)) } pool := &compute.TargetPool{ Name: name, + Description: fmt.Sprintf(`{"kubernetes.io/service-name":"%s"}`, serviceName), Instances: instances, SessionAffinity: translateAffinityType(affinityType), } @@ -810,8 +854,8 @@ func (gce *GCECloud) createTargetPool(name, region string, hosts []*gceInstance, return nil } -func (gce *GCECloud) createFirewall(name, region, desc, srcRange string, ports []*api.ServicePort, hosts []*gceInstance) error { - firewall, err := gce.firewallObject(name, region, desc, srcRange, ports, hosts) +func (gce *GCECloud) createFirewall(name, region, desc string, sourceRanges netsets.IPNet, ports []*api.ServicePort, hosts []*gceInstance) error { + firewall, err := gce.firewallObject(name, region, desc, sourceRanges, ports, hosts) if err != nil { return err } @@ -828,8 +872,8 @@ func (gce *GCECloud) createFirewall(name, region, desc, srcRange string, ports [ return nil } -func (gce *GCECloud) updateFirewall(name, region, desc, srcRange string, ports []*api.ServicePort, hosts []*gceInstance) error { - firewall, err := gce.firewallObject(name, region, desc, srcRange, ports, hosts) +func (gce *GCECloud) updateFirewall(name, region, desc string, sourceRanges netsets.IPNet, ports []*api.ServicePort, hosts []*gceInstance) error { + firewall, err := gce.firewallObject(name, region, desc, sourceRanges, ports, hosts) if err != nil { return err } @@ -846,7 +890,7 @@ func (gce *GCECloud) updateFirewall(name, region, desc, srcRange string, ports [ return nil } -func (gce *GCECloud) firewallObject(name, region, desc, srcRange string, ports []*api.ServicePort, hosts []*gceInstance) (*compute.Firewall, error) { +func (gce *GCECloud) firewallObject(name, region, desc string, sourceRanges netsets.IPNet, ports []*api.ServicePort, hosts []*gceInstance) (*compute.Firewall, error) { allowedPorts := make([]string, len(ports)) for ix := range ports { allowedPorts[ix] = strconv.Itoa(ports[ix].Port) @@ -859,7 +903,7 @@ func (gce *GCECloud) firewallObject(name, region, desc, srcRange string, ports [ Name: makeFirewallName(name), Description: desc, Network: gce.networkURL, - SourceRanges: []string{srcRange}, + SourceRanges: sourceRanges.StringSlice(), TargetTags: hostTags, Allowed: []*compute.FirewallAllowed{ { @@ -885,31 +929,42 @@ func (gce *GCECloud) computeHostTags(hosts []*gceInstance) ([]string, error) { tags := sets.NewString() for zone, hostNames := range hostNamesByZone { - listCall := gce.service.Instances.List(gce.projectID, zone) + pageToken := "" + page := 0 + for ; page == 0 || (pageToken != "" && page < maxPages); page++ { + listCall := gce.service.Instances.List(gce.projectID, zone) - // Add the filter for hosts - listCall = listCall.Filter("name eq (" + strings.Join(hostNames, "|") + ")") + // Add the filter for hosts + listCall = listCall.Filter("name eq (" + strings.Join(hostNames, "|") + ")") - // Add the fields we want - listCall = listCall.Fields("items(name,tags)") + // Add the fields we want + listCall = listCall.Fields("items(name,tags)") - res, err := listCall.Do() - if err != nil { - return nil, err - } + if pageToken != "" { + listCall = listCall.PageToken(pageToken) + } - for _, instance := range res.Items { - longest_tag := "" - for _, tag := range instance.Tags.Items { - if strings.HasPrefix(instance.Name, tag) && len(tag) > len(longest_tag) { - longest_tag = tag + res, err := listCall.Do() + if err != nil { + return nil, err + } + pageToken = res.NextPageToken + for _, instance := range res.Items { + longest_tag := "" + for _, tag := range instance.Tags.Items { + if strings.HasPrefix(instance.Name, tag) && len(tag) > len(longest_tag) { + longest_tag = tag + } + } + if len(longest_tag) > 0 { + tags.Insert(longest_tag) + } else if len(tags) > 0 { + return nil, fmt.Errorf("Some, but not all, instances have prefix tags (%s is missing)", instance.Name) } } - if len(longest_tag) > 0 { - tags.Insert(longest_tag) - } else if len(tags) > 0 { - return nil, fmt.Errorf("Some, but not all, instances have prefix tags (%s is missing)", instance.Name) - } + } + if page >= maxPages { + glog.Errorf("computeHostTags exceeded maxPages=%d for Instances.List: truncating.", maxPages) } } @@ -921,26 +976,41 @@ func (gce *GCECloud) computeHostTags(hosts []*gceInstance) ([]string, error) { } func (gce *GCECloud) projectOwnsStaticIP(name, region string, ipAddress string) (bool, error) { - addresses, err := gce.service.Addresses.List(gce.projectID, region).Do() - if err != nil { - return false, fmt.Errorf("failed to list gce IP addresses: %v", err) - } - for _, addr := range addresses.Items { - if addr.Address == ipAddress { - // This project does own the address, so return success. - return true, nil + pageToken := "" + page := 0 + for ; page == 0 || (pageToken != "" && page < maxPages); page++ { + listCall := gce.service.Addresses.List(gce.projectID, region) + if pageToken != "" { + listCall = listCall.PageToken(pageToken) } + addresses, err := listCall.Do() + if err != nil { + return false, fmt.Errorf("failed to list gce IP addresses: %v", err) + } + pageToken = addresses.NextPageToken + for _, addr := range addresses.Items { + if addr.Address == ipAddress { + // This project does own the address, so return success. + return true, nil + } + } + } + if page >= maxPages { + glog.Errorf("projectOwnsStaticIP exceeded maxPages=%d for Addresses.List; truncating.", maxPages) } return false, nil } -func (gce *GCECloud) ensureStaticIP(name, region, existingIP string) (ipAddress string, created bool, err error) { +func (gce *GCECloud) ensureStaticIP(name, serviceName, region, existingIP string) (ipAddress string, created bool, err error) { // If the address doesn't exist, this will create it. // If the existingIP exists but is ephemeral, this will promote it to static. // If the address already exists, this will harmlessly return a StatusConflict // and we'll grab the IP before returning. existed := false - addressObj := &compute.Address{Name: name} + addressObj := &compute.Address{ + Name: name, + Description: fmt.Sprintf(`{"kubernetes.io/service-name":"%s"}`, serviceName), + } if existingIP != "" { addressObj.Address = existingIP } @@ -1138,7 +1208,7 @@ func (gce *GCECloud) GetFirewall(name string) (*compute.Firewall, error) { } // CreateFirewall creates the given firewall rule. -func (gce *GCECloud) CreateFirewall(name, desc, srcRange string, ports []int64, hostNames []string) error { +func (gce *GCECloud) CreateFirewall(name, desc string, sourceRanges netsets.IPNet, ports []int64, hostNames []string) error { region, err := GetGCERegion(gce.localZone) if err != nil { return err @@ -1153,7 +1223,7 @@ func (gce *GCECloud) CreateFirewall(name, desc, srcRange string, ports []int64, if err != nil { return err } - return gce.createFirewall(name, region, desc, srcRange, svcPorts, hosts) + return gce.createFirewall(name, region, desc, sourceRanges, svcPorts, hosts) } // DeleteFirewall deletes the given firewall rule. @@ -1167,7 +1237,7 @@ func (gce *GCECloud) DeleteFirewall(name string) error { // UpdateFirewall applies the given firewall rule as an update to an existing // firewall rule with the same name. -func (gce *GCECloud) UpdateFirewall(name, desc, srcRange string, ports []int64, hostNames []string) error { +func (gce *GCECloud) UpdateFirewall(name, desc string, sourceRanges netsets.IPNet, ports []int64, hostNames []string) error { region, err := GetGCERegion(gce.localZone) if err != nil { return err @@ -1182,7 +1252,7 @@ func (gce *GCECloud) UpdateFirewall(name, desc, srcRange string, ports []int64, if err != nil { return err } - return gce.updateFirewall(name, region, desc, srcRange, svcPorts, hosts) + return gce.updateFirewall(name, region, desc, sourceRanges, svcPorts, hosts) } // Global static IP management @@ -1266,6 +1336,7 @@ func (gce *GCECloud) DeleteUrlMap(name string) error { // ListUrlMaps lists all UrlMaps in the project. func (gce *GCECloud) ListUrlMaps() (*compute.UrlMapList, error) { + // TODO: use PageToken to list all not just the first 500 return gce.service.UrlMaps.List(gce.projectID).Do() } @@ -1315,6 +1386,7 @@ func (gce *GCECloud) DeleteTargetHttpProxy(name string) error { // ListTargetHttpProxies lists all TargetHttpProxies in the project. func (gce *GCECloud) ListTargetHttpProxies() (*compute.TargetHttpProxyList, error) { + // TODO: use PageToken to list all not just the first 500 return gce.service.TargetHttpProxies.List(gce.projectID).Do() } @@ -1374,6 +1446,7 @@ func (gce *GCECloud) DeleteTargetHttpsProxy(name string) error { // ListTargetHttpsProxies lists all TargetHttpsProxies in the project. func (gce *GCECloud) ListTargetHttpsProxies() (*compute.TargetHttpsProxyList, error) { + // TODO: use PageToken to list all not just the first 500 return gce.service.TargetHttpsProxies.List(gce.projectID).Do() } @@ -1410,6 +1483,7 @@ func (gce *GCECloud) DeleteSslCertificate(name string) error { // ListSslCertificates lists all SslCertificates in the project. func (gce *GCECloud) ListSslCertificates() (*compute.SslCertificateList, error) { + // TODO: use PageToken to list all not just the first 500 return gce.service.SslCertificates.List(gce.projectID).Do() } @@ -1464,6 +1538,7 @@ func (gce *GCECloud) GetGlobalForwardingRule(name string) (*compute.ForwardingRu // ListGlobalForwardingRules lists all GlobalForwardingRules in the project. func (gce *GCECloud) ListGlobalForwardingRules() (*compute.ForwardingRuleList, error) { + // TODO: use PageToken to list all not just the first 500 return gce.service.GlobalForwardingRules.List(gce.projectID).Do() } @@ -1506,6 +1581,7 @@ func (gce *GCECloud) CreateBackendService(bg *compute.BackendService) error { // ListBackendServices lists all backend services in the project. func (gce *GCECloud) ListBackendServices() (*compute.BackendServiceList, error) { + // TODO: use PageToken to list all not just the first 500 return gce.service.BackendServices.List(gce.projectID).Do() } @@ -1556,6 +1632,7 @@ func (gce *GCECloud) CreateHttpHealthCheck(hc *compute.HttpHealthCheck) error { // ListHttpHealthCheck lists all HttpHealthChecks in the project. func (gce *GCECloud) ListHttpHealthChecks() (*compute.HttpHealthCheckList, error) { + // TODO: use PageToken to list all not just the first 500 return gce.service.HttpHealthChecks.List(gce.projectID).Do() } @@ -1586,11 +1663,13 @@ func (gce *GCECloud) DeleteInstanceGroup(name string, zone string) error { // ListInstanceGroups lists all InstanceGroups in the project and zone. func (gce *GCECloud) ListInstanceGroups(zone string) (*compute.InstanceGroupList, error) { + // TODO: use PageToken to list all not just the first 500 return gce.service.InstanceGroups.List(gce.projectID, zone).Do() } // ListInstancesInInstanceGroup lists all the instances in a given instance group and state. func (gce *GCECloud) ListInstancesInInstanceGroup(name string, zone string, state string) (*compute.InstanceGroupsListInstances, error) { + // TODO: use PageToken to list all not just the first 500 return gce.service.InstanceGroups.ListInstances( gce.projectID, zone, name, &compute.InstanceGroupsListInstancesRequest{InstanceState: state}).Do() @@ -1790,6 +1869,15 @@ func (gce *GCECloud) ExternalID(instance string) (string, error) { // InstanceID returns the cloud provider ID of the specified instance. func (gce *GCECloud) InstanceID(instanceName string) (string, error) { + if gce.useMetadataServer { + // Use metadata, if possible, to fetch ID. See issue #12000 + if gce.isCurrentInstance(instanceName) { + projectID, zone, err := getProjectAndZone() + if err == nil { + return projectID + "/" + zone + "/" + canonicalizeInstanceName(instanceName), nil + } + } + } instance, err := gce.getInstanceByName(instanceName) if err != nil { return "", err @@ -1799,6 +1887,15 @@ func (gce *GCECloud) InstanceID(instanceName string) (string, error) { // InstanceType returns the type of the specified instance. func (gce *GCECloud) InstanceType(instanceName string) (string, error) { + if gce.useMetadataServer { + // Use metadata, if possible, to fetch ID. See issue #12000 + if gce.isCurrentInstance(instanceName) { + mType, err := getCurrentMachineTypeViaMetadata() + if err == nil { + return mType, nil + } + } + } instance, err := gce.getInstanceByName(instanceName) if err != nil { return "", err @@ -1811,16 +1908,27 @@ func (gce *GCECloud) List(filter string) ([]string, error) { var instances []string // TODO: Parallelize, although O(zones) so not too bad (N <= 3 typically) for _, zone := range gce.managedZones { - listCall := gce.service.Instances.List(gce.projectID, zone) - if len(filter) > 0 { - listCall = listCall.Filter("name eq " + filter) + pageToken := "" + page := 0 + for ; page == 0 || (pageToken != "" && page < maxPages); page++ { + listCall := gce.service.Instances.List(gce.projectID, zone) + if len(filter) > 0 { + listCall = listCall.Filter("name eq " + filter) + } + if pageToken != "" { + listCall = listCall.PageToken(pageToken) + } + res, err := listCall.Do() + if err != nil { + return nil, err + } + pageToken = res.NextPageToken + for _, instance := range res.Items { + instances = append(instances, instance.Name) + } } - res, err := listCall.Do() - if err != nil { - return nil, err - } - for _, instance := range res.Items { - instances = append(instances, instance.Name) + if page >= maxPages { + glog.Errorf("List exceeded maxPages=%d for Instances.List: truncating.", maxPages) } } return instances, nil @@ -1843,31 +1951,42 @@ func truncateClusterName(clusterName string) string { } func (gce *GCECloud) ListRoutes(clusterName string) ([]*cloudprovider.Route, error) { - listCall := gce.service.Routes.List(gce.projectID) - - prefix := truncateClusterName(clusterName) - listCall = listCall.Filter("name eq " + prefix + "-.*") - - res, err := listCall.Do() - if err != nil { - return nil, err - } var routes []*cloudprovider.Route - for _, r := range res.Items { - if r.Network != gce.networkURL { - continue - } - // Not managed if route description != "k8s-node-route" - if r.Description != k8sNodeRouteTag { - continue - } - // Not managed if route name doesn't start with - if !strings.HasPrefix(r.Name, prefix) { - continue - } + pageToken := "" + page := 0 + for ; page == 0 || (pageToken != "" && page < maxPages); page++ { + listCall := gce.service.Routes.List(gce.projectID) - target := path.Base(r.NextHopInstance) - routes = append(routes, &cloudprovider.Route{Name: r.Name, TargetInstance: target, DestinationCIDR: r.DestRange}) + prefix := truncateClusterName(clusterName) + listCall = listCall.Filter("name eq " + prefix + "-.*") + if pageToken != "" { + listCall = listCall.PageToken(pageToken) + } + res, err := listCall.Do() + if err != nil { + glog.Errorf("Error getting routes from GCE: %v", err) + return nil, err + } + pageToken = res.NextPageToken + for _, r := range res.Items { + if r.Network != gce.networkURL { + continue + } + // Not managed if route description != "k8s-node-route" + if r.Description != k8sNodeRouteTag { + continue + } + // Not managed if route name doesn't start with + if !strings.HasPrefix(r.Name, prefix) { + continue + } + + target := path.Base(r.NextHopInstance) + routes = append(routes, &cloudprovider.Route{Name: r.Name, TargetInstance: target, DestinationCIDR: r.DestRange}) + } + } + if page >= maxPages { + glog.Errorf("ListRoutes exceeded maxPages=%d for Routes.List; truncating.", maxPages) } return routes, nil } @@ -2125,6 +2244,7 @@ func (gce *GCECloud) convertDiskToAttachedDisk(disk *gceDisk, readWrite string) } func (gce *GCECloud) listClustersInZone(zone string) ([]string, error) { + // TODO: use PageToken to list all not just the first 500 list, err := gce.containerService.Projects.Zones.Clusters.List(gce.projectID, zone).Do() if err != nil { return nil, err @@ -2190,28 +2310,38 @@ func (gce *GCECloud) getInstancesByNames(names []string) ([]*gceInstance, error) break } - listCall := gce.service.Instances.List(gce.projectID, zone) + pageToken := "" + page := 0 + for ; page == 0 || (pageToken != "" && page < maxPages); page++ { + listCall := gce.service.Instances.List(gce.projectID, zone) - // Add the filter for hosts - listCall = listCall.Filter("name eq (" + strings.Join(remaining, "|") + ")") + // Add the filter for hosts + listCall = listCall.Filter("name eq (" + strings.Join(remaining, "|") + ")") - listCall = listCall.Fields("items(name,id,disks,machineType)") - - res, err := listCall.Do() - if err != nil { - return nil, err - } - - for _, i := range res.Items { - name := i.Name - instance := &gceInstance{ - Zone: zone, - Name: name, - ID: i.Id, - Disks: i.Disks, - Type: lastComponent(i.MachineType), + listCall = listCall.Fields("items(name,id,disks,machineType)") + if pageToken != "" { + listCall.PageToken(pageToken) } - instances[name] = instance + + res, err := listCall.Do() + if err != nil { + return nil, err + } + pageToken = res.NextPageToken + for _, i := range res.Items { + name := i.Name + instance := &gceInstance{ + Zone: zone, + Name: name, + ID: i.Id, + Disks: i.Disks, + Type: lastComponent(i.MachineType), + } + instances[name] = instance + } + } + if page >= maxPages { + glog.Errorf("getInstancesByNames exceeded maxPages=%d for Instances.List: truncating.", maxPages) } } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_test.go deleted file mode 100644 index f1633d889..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/gce/gce_test.go +++ /dev/null @@ -1,150 +0,0 @@ -/* -Copyright 2014 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 gce - -import ( - "reflect" - "testing" -) - -func TestGetRegion(t *testing.T) { - zoneName := "us-central1-b" - regionName, err := GetGCERegion(zoneName) - if err != nil { - t.Fatalf("unexpected error from GetGCERegion: %v", err) - } - if regionName != "us-central1" { - t.Errorf("Unexpected region from GetGCERegion: %s", regionName) - } - gce := &GCECloud{ - localZone: zoneName, - region: regionName, - } - zones, ok := gce.Zones() - if !ok { - t.Fatalf("Unexpected missing zones impl") - } - zone, err := zones.GetZone() - if err != nil { - t.Fatalf("unexpected error %v", err) - } - if zone.Region != "us-central1" { - t.Errorf("Unexpected region: %s", zone.Region) - } -} - -func TestComparingHostURLs(t *testing.T) { - tests := []struct { - host1 string - zone string - name string - expectEqual bool - }{ - { - host1: "https://www.googleapis.com/compute/v1/projects/1234567/zones/us-central1-f/instances/kubernetes-node-fhx1", - zone: "us-central1-f", - name: "kubernetes-node-fhx1", - expectEqual: true, - }, - { - host1: "https://www.googleapis.com/compute/v1/projects/cool-project/zones/us-central1-f/instances/kubernetes-node-fhx1", - zone: "us-central1-f", - name: "kubernetes-node-fhx1", - expectEqual: true, - }, - { - host1: "https://www.googleapis.com/compute/v23/projects/1234567/zones/us-central1-f/instances/kubernetes-node-fhx1", - zone: "us-central1-f", - name: "kubernetes-node-fhx1", - expectEqual: true, - }, - { - host1: "https://www.googleapis.com/compute/v24/projects/1234567/regions/us-central1/zones/us-central1-f/instances/kubernetes-node-fhx1", - zone: "us-central1-f", - name: "kubernetes-node-fhx1", - expectEqual: true, - }, - { - host1: "https://www.googleapis.com/compute/v1/projects/1234567/zones/us-central1-f/instances/kubernetes-node-fhx1", - zone: "us-central1-c", - name: "kubernetes-node-fhx1", - expectEqual: false, - }, - { - host1: "https://www.googleapis.com/compute/v1/projects/1234567/zones/us-central1-f/instances/kubernetes-node-fhx", - zone: "us-central1-f", - name: "kubernetes-node-fhx1", - expectEqual: false, - }, - { - host1: "https://www.googleapis.com/compute/v1/projects/1234567/zones/us-central1-f/instances/kubernetes-node-fhx1", - zone: "us-central1-f", - name: "kubernetes-node-fhx", - expectEqual: false, - }, - } - - for _, test := range tests { - link1 := hostURLToComparablePath(test.host1) - testInstance := &gceInstance{ - Name: canonicalizeInstanceName(test.name), - Zone: test.zone, - } - link2 := testInstance.makeComparableHostPath() - if test.expectEqual && link1 != link2 { - t.Errorf("expected link1 and link2 to be equal, got %s and %s", link1, link2) - } else if !test.expectEqual && link1 == link2 { - t.Errorf("expected link1 and link2 not to be equal, got %s and %s", link1, link2) - } - } -} - -func TestScrubDNS(t *testing.T) { - tcs := []struct { - nameserversIn []string - searchesIn []string - nameserversOut []string - searchesOut []string - }{ - { - nameserversIn: []string{"1.2.3.4", "5.6.7.8"}, - nameserversOut: []string{"1.2.3.4", "5.6.7.8"}, - }, - { - searchesIn: []string{"c.prj.internal.", "12345678910.google.internal.", "google.internal."}, - searchesOut: []string{"c.prj.internal.", "google.internal."}, - }, - { - searchesIn: []string{"c.prj.internal.", "12345678910.google.internal.", "zone.c.prj.internal.", "google.internal."}, - searchesOut: []string{"c.prj.internal.", "zone.c.prj.internal.", "google.internal."}, - }, - { - searchesIn: []string{"c.prj.internal.", "12345678910.google.internal.", "zone.c.prj.internal.", "google.internal.", "unexpected"}, - searchesOut: []string{"c.prj.internal.", "zone.c.prj.internal.", "google.internal.", "unexpected"}, - }, - } - gce := &GCECloud{} - for i := range tcs { - n, s := gce.ScrubDNS(tcs[i].nameserversIn, tcs[i].searchesIn) - if !reflect.DeepEqual(n, tcs[i].nameserversOut) { - t.Errorf("Expected %v, got %v", tcs[i].nameserversOut, n) - } - if !reflect.DeepEqual(s, tcs[i].searchesOut) { - t.Errorf("Expected %v, got %v", tcs[i].searchesOut, s) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/client.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/client.go deleted file mode 100644 index 1b488bc62..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/client.go +++ /dev/null @@ -1,376 +0,0 @@ -/* -Copyright 2015 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 mesos - -import ( - "encoding/binary" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "net" - "net/http" - "sync" - "time" - - log "github.com/golang/glog" - "github.com/mesos/mesos-go/detector" - mesos "github.com/mesos/mesos-go/mesosproto" - "golang.org/x/net/context" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/resource" - utilnet "k8s.io/kubernetes/pkg/util/net" -) - -const defaultClusterName = "mesos" - -var noLeadingMasterError = errors.New("there is no current leading master available to query") - -type mesosClient struct { - masterLock sync.RWMutex - master string // host:port formatted address - httpClient *http.Client - tr *http.Transport - initialMaster <-chan struct{} // signal chan, closes once an initial, non-nil master is found - state *stateCache -} - -type slaveNode struct { - hostname string - kubeletRunning bool - resources *api.NodeResources -} - -type mesosState struct { - clusterName string - nodes map[string]*slaveNode // by hostname -} - -type stateCache struct { - sync.Mutex - expiresAt time.Time - cached *mesosState - err error - ttl time.Duration - refill func(context.Context) (*mesosState, error) -} - -// reloadCache reloads the state cache if it has expired. -func (c *stateCache) reloadCache(ctx context.Context) { - now := time.Now() - c.Lock() - defer c.Unlock() - if c.expiresAt.Before(now) { - log.V(4).Infof("Reloading cached Mesos state") - c.cached, c.err = c.refill(ctx) - c.expiresAt = now.Add(c.ttl) - } else { - log.V(4).Infof("Using cached Mesos state") - } -} - -// cachedState returns the cached Mesos state. -func (c *stateCache) cachedState(ctx context.Context) (*mesosState, error) { - c.reloadCache(ctx) - return c.cached, c.err -} - -// clusterName returns the cached Mesos cluster name. -func (c *stateCache) clusterName(ctx context.Context) (string, error) { - cached, err := c.cachedState(ctx) - if err != nil { - return "", err - } - return cached.clusterName, nil -} - -// nodes returns the cached list of slave nodes. -func (c *stateCache) nodes(ctx context.Context) (map[string]*slaveNode, error) { - cached, err := c.cachedState(ctx) - if err != nil { - return nil, err - } - return cached.nodes, nil -} - -func newMesosClient( - md detector.Master, - mesosHttpClientTimeout, stateCacheTTL time.Duration) (*mesosClient, error) { - - tr := utilnet.SetTransportDefaults(&http.Transport{}) - httpClient := &http.Client{ - Transport: tr, - Timeout: mesosHttpClientTimeout, - } - return createMesosClient(md, httpClient, tr, stateCacheTTL) -} - -func createMesosClient( - md detector.Master, - httpClient *http.Client, - tr *http.Transport, - stateCacheTTL time.Duration) (*mesosClient, error) { - - initialMaster := make(chan struct{}) - client := &mesosClient{ - httpClient: httpClient, - tr: tr, - initialMaster: initialMaster, - state: &stateCache{ - ttl: stateCacheTTL, - }, - } - client.state.refill = client.pollMasterForState - first := true - if err := md.Detect(detector.OnMasterChanged(func(info *mesos.MasterInfo) { - host, port := extractMasterAddress(info) - if len(host) > 0 { - client.masterLock.Lock() - defer client.masterLock.Unlock() - client.master = fmt.Sprintf("%s:%d", host, port) - if first { - first = false - close(initialMaster) - } - } - log.Infof("cloud master changed to '%v'", client.master) - })); err != nil { - log.V(1).Infof("detector initialization failed: %v", err) - return nil, err - } - return client, nil -} - -func extractMasterAddress(info *mesos.MasterInfo) (host string, port int) { - if info != nil { - host = info.GetAddress().GetHostname() - if host == "" { - host = info.GetAddress().GetIp() - } - - if host != "" { - // use port from Address - port = int(info.GetAddress().GetPort()) - } else { - // deprecated: get host and port directly from MasterInfo (and not Address) - host = info.GetHostname() - if host == "" { - host = unpackIPv4(info.GetIp()) - } - port = int(info.GetPort()) - } - } - return -} - -func unpackIPv4(ip uint32) string { - octets := make([]byte, 4, 4) - binary.BigEndian.PutUint32(octets, ip) - ipv4 := net.IP(octets) - return ipv4.String() -} - -// listSlaves returns a (possibly cached) map of slave nodes by hostname. -// Callers must not mutate the contents of the returned slice. -func (c *mesosClient) listSlaves(ctx context.Context) (map[string]*slaveNode, error) { - return c.state.nodes(ctx) -} - -// clusterName returns a (possibly cached) cluster name. -func (c *mesosClient) clusterName(ctx context.Context) (string, error) { - return c.state.clusterName(ctx) -} - -// pollMasterForState returns an array of slave nodes -func (c *mesosClient) pollMasterForState(ctx context.Context) (*mesosState, error) { - // wait for initial master detection - select { - case <-c.initialMaster: // noop - case <-ctx.Done(): - return nil, ctx.Err() - } - - master := func() string { - c.masterLock.RLock() - defer c.masterLock.RUnlock() - return c.master - }() - if master == "" { - return nil, noLeadingMasterError - } - - //TODO(jdef) should not assume master uses http (what about https?) - - var state *mesosState - successHandler := func(res *http.Response) error { - blob, err1 := ioutil.ReadAll(res.Body) - if err1 != nil { - return err1 - } - log.V(3).Infof("Got mesos state, content length %v", len(blob)) - state, err1 = parseMesosState(blob) - return err1 - } - // thinking here is that we may get some other status codes from mesos at some point: - // - authentication - // - redirection (possibly from http to https) - // ... - for _, tt := range []struct { - uri string - handlers map[int]func(*http.Response) error - }{ - { - uri: fmt.Sprintf("http://%s/state", master), - handlers: map[int]func(*http.Response) error{ - 200: successHandler, - }, - }, - { - uri: fmt.Sprintf("http://%s/state.json", master), - handlers: map[int]func(*http.Response) error{ - 200: successHandler, - }, - }, - } { - req, err := http.NewRequest("GET", tt.uri, nil) - if err != nil { - return nil, err - } - err = c.httpDo(ctx, req, func(res *http.Response, err error) error { - if err != nil { - return err - } - defer res.Body.Close() - if handler, ok := tt.handlers[res.StatusCode]; ok { - err1 := handler(res) - if err1 != nil { - return err1 - } - } - // no handler for this error code, proceed to the next connection type - return nil - }) - if state != nil || err != nil { - return state, err - } - } - return nil, errors.New("failed to sync with Mesos master") -} - -func parseMesosState(blob []byte) (*mesosState, error) { - type State struct { - ClusterName string `json:"cluster"` - Slaves []*struct { - Id string `json:"id"` // ex: 20150106-162714-3815890698-5050-2453-S2 - Pid string `json:"pid"` // ex: slave(1)@10.22.211.18:5051 - Hostname string `json:"hostname"` // ex: 10.22.211.18, or slave-123.nowhere.com - Resources map[string]interface{} `json:"resources"` // ex: {"mem": 123, "ports": "[31000-3200]"} - } `json:"slaves"` - Frameworks []*struct { - Id string `json:"id"` // ex: 20151105-093752-3745622208-5050-1-0000 - Pid string `json:"pid"` // ex: scheduler(1)@192.168.65.228:57124 - Executors []*struct { - SlaveId string `json:"slave_id"` // ex: 20151105-093752-3745622208-5050-1-S1 - ExecutorId string `json:"executor_id"` // ex: 6704d375c68fee1e_k8sm-executor - Name string `json:"name"` // ex: Kubelet-Executor - } `json:"executors"` - } `json:"frameworks"` - } - - state := &State{ClusterName: defaultClusterName} - if err := json.Unmarshal(blob, state); err != nil { - return nil, err - } - - executorSlaveIds := map[string]struct{}{} - for _, f := range state.Frameworks { - for _, e := range f.Executors { - // Note that this simple comparison breaks when we support more than one - // k8s instance in a cluster. At the moment this is not possible for - // a number of reasons. - // TODO(sttts): find way to detect executors of this k8s instance - if e.Name == KubernetesExecutorName { - executorSlaveIds[e.SlaveId] = struct{}{} - } - } - } - - nodes := map[string]*slaveNode{} // by hostname - for _, slave := range state.Slaves { - if slave.Hostname == "" { - continue - } - node := &slaveNode{hostname: slave.Hostname} - cap := api.ResourceList{} - if slave.Resources != nil && len(slave.Resources) > 0 { - // attempt to translate CPU (cores) and memory (MB) resources - if cpu, found := slave.Resources["cpus"]; found { - if cpuNum, ok := cpu.(float64); ok { - cap[api.ResourceCPU] = *resource.NewQuantity(int64(cpuNum), resource.DecimalSI) - } else { - log.Warningf("unexpected slave cpu resource type %T: %v", cpu, cpu) - } - } else { - log.Warningf("slave failed to report cpu resource") - } - if mem, found := slave.Resources["mem"]; found { - if memNum, ok := mem.(float64); ok { - cap[api.ResourceMemory] = *resource.NewQuantity(int64(memNum), resource.BinarySI) - } else { - log.Warningf("unexpected slave mem resource type %T: %v", mem, mem) - } - } else { - log.Warningf("slave failed to report mem resource") - } - } - if len(cap) > 0 { - node.resources = &api.NodeResources{ - Capacity: cap, - } - log.V(4).Infof("node %q reporting capacity %v", node.hostname, cap) - } - if _, ok := executorSlaveIds[slave.Id]; ok { - node.kubeletRunning = true - } - nodes[node.hostname] = node - } - - result := &mesosState{ - clusterName: state.ClusterName, - nodes: nodes, - } - - return result, nil -} - -type responseHandler func(*http.Response, error) error - -// httpDo executes an HTTP request in the given context, canceling an ongoing request if the context -// is canceled prior to completion of the request. hacked from https://blog.golang.org/context -func (c *mesosClient) httpDo(ctx context.Context, req *http.Request, f responseHandler) error { - // Run the HTTP request in a goroutine and pass the response to f. - ch := make(chan error, 1) - go func() { ch <- f(c.httpClient.Do(req)) }() - select { - case <-ctx.Done(): - c.tr.CancelRequest(req) - <-ch // Wait for f to return. - return ctx.Err() - case err := <-ch: - return err - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/client_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/client_test.go deleted file mode 100644 index 83209924b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/client_test.go +++ /dev/null @@ -1,271 +0,0 @@ -/* -Copyright 2015 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 mesos - -import ( - "fmt" - "net/http" - "net/http/httptest" - "net/url" - "reflect" - "testing" - "time" - - log "github.com/golang/glog" - "github.com/mesos/mesos-go/detector" - "github.com/mesos/mesos-go/mesosutil" - "golang.org/x/net/context" -) - -// Test data - -const ( - TEST_MASTER_ID = "master-12345" - TEST_MASTER_IP = 177048842 // 10.141.141.10 - TEST_MASTER_PORT = 5050 - - TEST_STATE_JSON = ` - { - "version": "0.22.0", - "unregistered_frameworks": [], - "started_tasks": 0, - "start_time": 1429456501.61141, - "staged_tasks": 0, - "slaves": [ - { - "resources": { - "ports": "[31000-32000]", - "mem": 15360, - "disk": 470842, - "cpus": 8 - }, - "registered_time": 1429456502.46999, - "pid": "slave(1)@mesos1.internal.company.com:5050", - "id": "20150419-081501-16777343-5050-16383-S2", - "hostname": "mesos1.internal.company.com", - "attributes": {}, - "active": true - }, - { - "resources": { - "ports": "[31000-32000]", - "mem": 15360, - "disk": 470842, - "cpus": 8 - }, - "registered_time": 1429456502.4144, - "pid": "slave(1)@mesos2.internal.company.com:5050", - "id": "20150419-081501-16777343-5050-16383-S1", - "hostname": "mesos2.internal.company.com", - "attributes": {}, - "active": true - }, - { - "resources": { - "ports": "[31000-32000]", - "mem": 15360, - "disk": 470842, - "cpus": 8 - }, - "registered_time": 1429456502.02879, - "pid": "slave(1)@mesos3.internal.company.com:5050", - "id": "20150419-081501-16777343-5050-16383-S0", - "hostname": "mesos3.internal.company.com", - "attributes": {}, - "active": true - } - ], - "pid": "master@mesos-master0.internal.company.com:5050", - "orphan_tasks": [], - "lost_tasks": 0, - "leader": "master@mesos-master0.internal.company.com:5050", - "killed_tasks": 0, - "failed_tasks": 0, - "elected_time": 1429456501.61638, - "deactivated_slaves": 0, - "completed_frameworks": [], - "build_user": "buildbot", - "build_time": 1425085311, - "build_date": "2015-02-27 17:01:51", - "activated_slaves": 3, - "finished_tasks": 0, - "flags": { - "zk_session_timeout": "10secs", - "work_dir": "/somepath/mesos/local/Lc9arz", - "webui_dir": "/usr/local/share/mesos/webui", - "version": "false", - "user_sorter": "drf", - "slave_reregister_timeout": "10mins", - "logbufsecs": "0", - "log_auto_initialize": "true", - "initialize_driver_logging": "true", - "framework_sorter": "drf", - "authenticators": "crammd5", - "authenticate_slaves": "false", - "authenticate": "false", - "allocation_interval": "1secs", - "logging_level": "INFO", - "quiet": "false", - "recovery_slave_removal_limit": "100%", - "registry": "replicated_log", - "registry_fetch_timeout": "1mins", - "registry_store_timeout": "5secs", - "registry_strict": "false", - "root_submissions": "true" - }, - "frameworks": [], - "git_branch": "refs/heads/0.22.0-rc1", - "git_sha": "46834faca67f877631e1beb7d61be5c080ec3dc2", - "git_tag": "0.22.0-rc1", - "hostname": "localhost", - "id": "20150419-081501-16777343-5050-16383" - }` -) - -// Mocks - -type FakeMasterDetector struct { - callback detector.MasterChanged - done chan struct{} -} - -func newFakeMasterDetector() *FakeMasterDetector { - return &FakeMasterDetector{ - done: make(chan struct{}), - } -} - -func (md FakeMasterDetector) Cancel() { - close(md.done) -} - -func (md FakeMasterDetector) Detect(cb detector.MasterChanged) error { - md.callback = cb - leadingMaster := mesosutil.NewMasterInfo(TEST_MASTER_ID, TEST_MASTER_IP, TEST_MASTER_PORT) - cb.OnMasterChanged(leadingMaster) - return nil -} - -func (md FakeMasterDetector) Done() <-chan struct{} { - return md.done -} - -// Auxiliary functions - -func makeHttpMocks() (*httptest.Server, *http.Client, *http.Transport) { - httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - log.V(4).Infof("Mocking response for HTTP request: %#v", r) - if r.URL.Path == "/state.json" { - w.WriteHeader(200) // OK - w.Header().Set("Content-Type", "application/json") - fmt.Fprintln(w, TEST_STATE_JSON) - } else { - w.WriteHeader(400) - fmt.Fprintln(w, "Bad Request") - } - })) - - // Intercept all client requests and feed them to the test server - transport := &http.Transport{ - Proxy: func(req *http.Request) (*url.URL, error) { - return url.Parse(httpServer.URL) - }, - } - - httpClient := &http.Client{Transport: transport} - - return httpServer, httpClient, transport -} - -// Tests - -// test mesos.parseMesosState -func Test_parseMesosState(t *testing.T) { - state, err := parseMesosState([]byte(TEST_STATE_JSON)) - - if err != nil { - t.Fatalf("parseMesosState does not yield an error") - } - if state == nil { - t.Fatalf("parseMesosState yields a non-nil state") - } - if len(state.nodes) != 3 { - t.Fatalf("parseMesosState yields a state with 3 nodes") - } -} - -// test mesos.listSlaves -func Test_listSlaves(t *testing.T) { - defer log.Flush() - md := FakeMasterDetector{} - // TODO: Uncomment next two lines and remove third line when fix #19254 - // defer httpServer.Close() - // httpServer, httpClient, httpTransport := makeHttpMocks() - _, httpClient, httpTransport := makeHttpMocks() - - cacheTTL := 500 * time.Millisecond - mesosClient, err := createMesosClient(md, httpClient, httpTransport, cacheTTL) - - if err != nil { - t.Fatalf("createMesosClient does not yield an error") - } - - slaveNodes, err := mesosClient.listSlaves(context.TODO()) - - if err != nil { - t.Fatalf("listSlaves does not yield an error") - } - if len(slaveNodes) != 3 { - t.Fatalf("listSlaves yields a collection of size 3") - } - - expectedHostnames := map[string]struct{}{ - "mesos1.internal.company.com": {}, - "mesos2.internal.company.com": {}, - "mesos3.internal.company.com": {}, - } - - actualHostnames := make(map[string]struct{}) - for _, node := range slaveNodes { - actualHostnames[node.hostname] = struct{}{} - } - - if !reflect.DeepEqual(expectedHostnames, actualHostnames) { - t.Fatalf("listSlaves yields a collection with the expected hostnames") - } -} - -// test mesos.clusterName -func Test_clusterName(t *testing.T) { - defer log.Flush() - md := FakeMasterDetector{} - // TODO: Uncomment next two lines and remove third line when fix #19254 - // defer httpServer.Close() - // httpServer, httpClient, httpTransport := makeHttpMocks() - _, httpClient, httpTransport := makeHttpMocks() - cacheTTL := 500 * time.Millisecond - mesosClient, err := createMesosClient(md, httpClient, httpTransport, cacheTTL) - - name, err := mesosClient.clusterName(context.TODO()) - - if err != nil { - t.Fatalf("clusterName does not yield an error") - } - if name != defaultClusterName { - t.Fatalf("clusterName yields the expected (default) value") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/config.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/config.go deleted file mode 100644 index c1be30e8b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/config.go +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright 2015 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 mesos - -import ( - "io" - "time" - - "github.com/scalingdata/gcfg" -) - -const ( - DefaultMesosMaster = "localhost:5050" - DefaultHttpClientTimeout = time.Duration(10) * time.Second - DefaultStateCacheTTL = time.Duration(5) * time.Second -) - -// Example Mesos cloud provider configuration file: -// -// [mesos-cloud] -// mesos-master = leader.mesos:5050 -// http-client-timeout = 500ms -// state-cache-ttl = 1h - -type ConfigWrapper struct { - Mesos_Cloud Config -} - -type Config struct { - MesosMaster string `gcfg:"mesos-master"` - MesosHttpClientTimeout Duration `gcfg:"http-client-timeout"` - StateCacheTTL Duration `gcfg:"state-cache-ttl"` -} - -type Duration struct { - Duration time.Duration `gcfg:"duration"` -} - -func (d *Duration) UnmarshalText(data []byte) error { - underlying, err := time.ParseDuration(string(data)) - if err == nil { - d.Duration = underlying - } - return err -} - -func createDefaultConfig() *Config { - return &Config{ - MesosMaster: DefaultMesosMaster, - MesosHttpClientTimeout: Duration{Duration: DefaultHttpClientTimeout}, - StateCacheTTL: Duration{Duration: DefaultStateCacheTTL}, - } -} - -func readConfig(configReader io.Reader) (*Config, error) { - config := createDefaultConfig() - wrapper := &ConfigWrapper{Mesos_Cloud: *config} - if configReader != nil { - if err := gcfg.ReadInto(wrapper, configReader); err != nil { - return nil, err - } - config = &(wrapper.Mesos_Cloud) - } - return config, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/config_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/config_test.go deleted file mode 100644 index d1013471c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/config_test.go +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2015 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 mesos - -import ( - "bytes" - "testing" - "time" - - log "github.com/golang/glog" -) - -// test mesos.createDefaultConfig -func Test_createDefaultConfig(t *testing.T) { - defer log.Flush() - - config := createDefaultConfig() - - if config.MesosMaster != DefaultMesosMaster { - t.Fatalf("Default config has the expected MesosMaster value") - } - - if config.MesosHttpClientTimeout.Duration != DefaultHttpClientTimeout { - t.Fatalf("Default config has the expected MesosHttpClientTimeout value") - } - - if config.StateCacheTTL.Duration != DefaultStateCacheTTL { - t.Fatalf("Default config has the expected StateCacheTTL value") - } -} - -// test mesos.readConfig -func Test_readConfig(t *testing.T) { - defer log.Flush() - - configString := ` -[mesos-cloud] - mesos-master = leader.mesos:5050 - http-client-timeout = 500ms - state-cache-ttl = 1h` - - reader := bytes.NewBufferString(configString) - - config, err := readConfig(reader) - - if err != nil { - t.Fatalf("Reading configuration does not yield an error: %#v", err) - } - - if config.MesosMaster != "leader.mesos:5050" { - t.Fatalf("Parsed config has the expected MesosMaster value") - } - - if config.MesosHttpClientTimeout.Duration != time.Duration(500)*time.Millisecond { - t.Fatalf("Parsed config has the expected MesosHttpClientTimeout value") - } - - if config.StateCacheTTL.Duration != time.Duration(1)*time.Hour { - t.Fatalf("Parsed config has the expected StateCacheTTL value") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/mesos.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/mesos.go deleted file mode 100644 index 20285843b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/mesos.go +++ /dev/null @@ -1,283 +0,0 @@ -/* -Copyright 2015 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 mesos - -import ( - "errors" - "fmt" - "io" - "net" - "regexp" - - log "github.com/golang/glog" - "github.com/mesos/mesos-go/detector" - "golang.org/x/net/context" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/cloudprovider" -) - -const ( - ProviderName = "mesos" - - // KubernetesExecutorName is shared between contrib/mesos and Mesos cloud provider. - // Because cloud provider -> contrib dependencies are forbidden, this constant - // is defined here, not in contrib. - KubernetesExecutorName = "Kubelet-Executor" -) - -var ( - CloudProvider *MesosCloud - - noHostNameSpecified = errors.New("No hostname specified") -) - -func init() { - cloudprovider.RegisterCloudProvider( - ProviderName, - func(configReader io.Reader) (cloudprovider.Interface, error) { - provider, err := newMesosCloud(configReader) - if err == nil { - CloudProvider = provider - } - return provider, err - }) -} - -type MesosCloud struct { - client *mesosClient - config *Config -} - -func (c *MesosCloud) MasterURI() string { - return c.config.MesosMaster -} - -func newMesosCloud(configReader io.Reader) (*MesosCloud, error) { - config, err := readConfig(configReader) - if err != nil { - return nil, err - } - - log.V(1).Infof("new mesos cloud, master='%v'", config.MesosMaster) - if d, err := detector.New(config.MesosMaster); err != nil { - log.V(1).Infof("failed to create master detector: %v", err) - return nil, err - } else if cl, err := newMesosClient(d, - config.MesosHttpClientTimeout.Duration, - config.StateCacheTTL.Duration); err != nil { - log.V(1).Infof("failed to create mesos cloud client: %v", err) - return nil, err - } else { - return &MesosCloud{client: cl, config: config}, nil - } -} - -// Implementation of Instances.CurrentNodeName -func (c *MesosCloud) CurrentNodeName(hostname string) (string, error) { - return hostname, nil -} - -func (c *MesosCloud) AddSSHKeyToAllInstances(user string, keyData []byte) error { - return errors.New("unimplemented") -} - -// Instances returns a copy of the Mesos cloud Instances implementation. -// Mesos natively provides minimal cloud-type resources. More robust cloud -// support requires a combination of Mesos and cloud-specific knowledge. -func (c *MesosCloud) Instances() (cloudprovider.Instances, bool) { - return c, true -} - -// LoadBalancer always returns nil, false in this implementation. -// Mesos does not provide any type of native load balancing by default, -// so this implementation always returns (nil, false). -func (c *MesosCloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) { - return nil, false -} - -// Zones always returns nil, false in this implementation. -// Mesos does not provide any type of native region or zone awareness, -// so this implementation always returns (nil, false). -func (c *MesosCloud) Zones() (cloudprovider.Zones, bool) { - return nil, false -} - -// Clusters returns a copy of the Mesos cloud Clusters implementation. -// Mesos does not provide support for multiple clusters. -func (c *MesosCloud) Clusters() (cloudprovider.Clusters, bool) { - return c, true -} - -// Routes always returns nil, false in this implementation. -func (c *MesosCloud) Routes() (cloudprovider.Routes, bool) { - return nil, false -} - -// ProviderName returns the cloud provider ID. -func (c *MesosCloud) ProviderName() string { - return ProviderName -} - -// ScrubDNS filters DNS settings for pods. -func (c *MesosCloud) ScrubDNS(nameservers, searches []string) (nsOut, srchOut []string) { - return nameservers, searches -} - -// ListClusters lists the names of the available Mesos clusters. -func (c *MesosCloud) ListClusters() ([]string, error) { - // Always returns a single cluster (this one!) - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() - name, err := c.client.clusterName(ctx) - return []string{name}, err -} - -// Master gets back the address (either DNS name or IP address) of the leading Mesos master node for the cluster. -func (c *MesosCloud) Master(clusterName string) (string, error) { - clusters, err := c.ListClusters() - if err != nil { - return "", err - } - for _, name := range clusters { - if name == clusterName { - if c.client.master == "" { - return "", errors.New("The currently leading master is unknown.") - } - - host, _, err := net.SplitHostPort(c.client.master) - if err != nil { - return "", err - } - - return host, nil - } - } - return "", errors.New(fmt.Sprintf("The supplied cluster '%v' does not exist", clusterName)) -} - -// ipAddress returns an IP address of the specified instance. -func ipAddress(name string) (net.IP, error) { - if name == "" { - return nil, noHostNameSpecified - } - ipaddr := net.ParseIP(name) - if ipaddr != nil { - return ipaddr, nil - } - iplist, err := net.LookupIP(name) - if err != nil { - log.V(2).Infof("failed to resolve IP from host name '%v': %v", name, err) - return nil, err - } - ipaddr = iplist[0] - log.V(2).Infof("resolved host '%v' to '%v'", name, ipaddr) - return ipaddr, nil -} - -// ExternalID returns the cloud provider ID of the specified instance (deprecated). -func (c *MesosCloud) ExternalID(instance string) (string, error) { - //TODO(jdef) use a timeout here? 15s? - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() - - nodes, err := c.client.listSlaves(ctx) - if err != nil { - return "", err - } - - node := nodes[instance] - if node == nil { - return "", cloudprovider.InstanceNotFound - } - - ip, err := ipAddress(node.hostname) - if err != nil { - return "", err - } - return ip.String(), nil -} - -// InstanceID returns the cloud provider ID of the specified instance. -func (c *MesosCloud) InstanceID(name string) (string, error) { - return "", nil -} - -// InstanceType returns the type of the specified instance. -func (c *MesosCloud) InstanceType(name string) (string, error) { - return "", nil -} - -func (c *MesosCloud) listNodes() (map[string]*slaveNode, error) { - //TODO(jdef) use a timeout here? 15s? - ctx, cancel := context.WithCancel(context.TODO()) - defer cancel() - - nodes, err := c.client.listSlaves(ctx) - if err != nil { - return nil, err - } - if len(nodes) == 0 { - log.V(2).Info("no slaves found, are any running?") - return nil, nil - } - return nodes, nil -} - -// List lists instances that match 'filter' which is a regular expression -// which must match the entire instance name (fqdn). -func (c *MesosCloud) List(filter string) ([]string, error) { - nodes, err := c.listNodes() - if err != nil { - return nil, err - } - filterRegex, err := regexp.Compile(filter) - if err != nil { - return nil, err - } - addr := []string{} - for _, node := range nodes { - if filterRegex.MatchString(node.hostname) { - addr = append(addr, node.hostname) - } - } - return addr, nil -} - -// ListWithKubelet list those instance which have no running kubelet, i.e. the -// Kubernetes executor. -func (c *MesosCloud) ListWithoutKubelet() ([]string, error) { - nodes, err := c.listNodes() - if err != nil { - return nil, err - } - addr := make([]string, 0, len(nodes)) - for _, n := range nodes { - if !n.kubeletRunning { - addr = append(addr, n.hostname) - } - } - return addr, nil -} - -// NodeAddresses returns the addresses of the specified instance. -func (c *MesosCloud) NodeAddresses(name string) ([]api.NodeAddress, error) { - ip, err := ipAddress(name) - if err != nil { - return nil, err - } - return []api.NodeAddress{{Type: api.NodeLegacyHostIP, Address: ip.String()}}, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/mesos_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/mesos_test.go deleted file mode 100644 index b504f4ef0..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/mesos_test.go +++ /dev/null @@ -1,279 +0,0 @@ -/* -Copyright 2015 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 mesos - -import ( - "bytes" - "net" - "reflect" - "testing" - "time" - - log "github.com/golang/glog" - "k8s.io/kubernetes/pkg/cloudprovider" -) - -func TestIPAddress(t *testing.T) { - expected4 := net.IPv4(127, 0, 0, 1) - ip, err := ipAddress("127.0.0.1") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !reflect.DeepEqual(ip, expected4) { - t.Fatalf("expected %#v instead of %#v", expected4, ip) - } - - expected6 := net.ParseIP("::1") - if expected6 == nil { - t.Fatalf("failed to parse ipv6 ::1") - } - ip, err = ipAddress("::1") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !reflect.DeepEqual(ip, expected6) { - t.Fatalf("expected %#v instead of %#v", expected6, ip) - } - - ip, err = ipAddress("localhost") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !reflect.DeepEqual(ip, expected4) && !reflect.DeepEqual(ip, expected6) { - t.Fatalf("expected %#v or %#v instead of %#v", expected4, expected6, ip) - } - - _, err = ipAddress("") - if err != noHostNameSpecified { - t.Fatalf("expected error noHostNameSpecified but got none") - } -} - -// test mesos.newMesosCloud with no config -func Test_newMesosCloud_NoConfig(t *testing.T) { - defer log.Flush() - mesosCloud, err := newMesosCloud(nil) - - if err != nil { - t.Fatalf("Creating a new Mesos cloud provider without config does not yield an error: %#v", err) - } - - if mesosCloud.client.httpClient.Timeout != DefaultHttpClientTimeout { - t.Fatalf("Creating a new Mesos cloud provider without config does not yield an error: %#v", err) - } - - if mesosCloud.client.state.ttl != DefaultStateCacheTTL { - t.Fatalf("Mesos client with default config has the expected state cache TTL value") - } -} - -// test mesos.newMesosCloud with custom config -func Test_newMesosCloud_WithConfig(t *testing.T) { - defer log.Flush() - - configString := ` -[mesos-cloud] - http-client-timeout = 500ms - state-cache-ttl = 1h` - - reader := bytes.NewBufferString(configString) - - mesosCloud, err := newMesosCloud(reader) - - if err != nil { - t.Fatalf("Creating a new Mesos cloud provider with a custom config does not yield an error: %#v", err) - } - - if mesosCloud.client.httpClient.Timeout != time.Duration(500)*time.Millisecond { - t.Fatalf("Mesos client with a custom config has the expected HTTP client timeout value") - } - - if mesosCloud.client.state.ttl != time.Duration(1)*time.Hour { - t.Fatalf("Mesos client with a custom config has the expected state cache TTL value") - } -} - -// tests for capability reporting functions - -// test mesos.Instances -func Test_Instances(t *testing.T) { - defer log.Flush() - mesosCloud, _ := newMesosCloud(nil) - - instances, supports_instances := mesosCloud.Instances() - - if !supports_instances || instances == nil { - t.Fatalf("MesosCloud provides an implementation of Instances") - } -} - -// test mesos.LoadBalancer -func Test_TcpLoadBalancer(t *testing.T) { - defer log.Flush() - mesosCloud, _ := newMesosCloud(nil) - - lb, supports_lb := mesosCloud.LoadBalancer() - - if supports_lb || lb != nil { - t.Fatalf("MesosCloud does not provide an implementation of LoadBalancer") - } -} - -// test mesos.Zones -func Test_Zones(t *testing.T) { - defer log.Flush() - mesosCloud, _ := newMesosCloud(nil) - - zones, supports_zones := mesosCloud.Zones() - - if supports_zones || zones != nil { - t.Fatalf("MesosCloud does not provide an implementation of Zones") - } -} - -// test mesos.Clusters -func Test_Clusters(t *testing.T) { - defer log.Flush() - mesosCloud, _ := newMesosCloud(nil) - - clusters, supports_clusters := mesosCloud.Clusters() - - if !supports_clusters || clusters == nil { - t.Fatalf("MesosCloud does not provide an implementation of Clusters") - } -} - -// test mesos.MasterURI -func Test_MasterURI(t *testing.T) { - defer log.Flush() - mesosCloud, _ := newMesosCloud(nil) - - uri := mesosCloud.MasterURI() - - if uri != DefaultMesosMaster { - t.Fatalf("MasterURI returns the expected master URI (expected \"localhost\", actual \"%s\"", uri) - } -} - -// test mesos.ListClusters -func Test_ListClusters(t *testing.T) { - defer log.Flush() - md := FakeMasterDetector{} - httpServer, httpClient, httpTransport := makeHttpMocks() - defer httpServer.Close() - cacheTTL := 500 * time.Millisecond - mesosClient, err := createMesosClient(md, httpClient, httpTransport, cacheTTL) - mesosCloud := &MesosCloud{client: mesosClient, config: createDefaultConfig()} - - clusters, err := mesosCloud.ListClusters() - - if err != nil { - t.Fatalf("ListClusters does not yield an error: %#v", err) - } - - if len(clusters) != 1 { - t.Fatalf("ListClusters should return a list of size 1: (actual: %#v)", clusters) - } - - expectedClusterNames := []string{"mesos"} - - if !reflect.DeepEqual(clusters, expectedClusterNames) { - t.Fatalf("ListClusters should return the expected list of names: (expected: %#v, actual: %#v)", - expectedClusterNames, - clusters) - } -} - -// test mesos.Master -func Test_Master(t *testing.T) { - defer log.Flush() - md := FakeMasterDetector{} - httpServer, httpClient, httpTransport := makeHttpMocks() - defer httpServer.Close() - cacheTTL := 500 * time.Millisecond - mesosClient, err := createMesosClient(md, httpClient, httpTransport, cacheTTL) - mesosCloud := &MesosCloud{client: mesosClient, config: createDefaultConfig()} - - clusters, err := mesosCloud.ListClusters() - clusterName := clusters[0] - master, err := mesosCloud.Master(clusterName) - - if err != nil { - t.Fatalf("Master does not yield an error: %#v", err) - } - - expectedMaster := unpackIPv4(TEST_MASTER_IP) - - if master != expectedMaster { - t.Fatalf("Master returns the expected value: (expected: %#v, actual: %#v", expectedMaster, master) - } -} - -// test mesos.List -func Test_List(t *testing.T) { - defer log.Flush() - md := FakeMasterDetector{} - httpServer, httpClient, httpTransport := makeHttpMocks() - defer httpServer.Close() - cacheTTL := 500 * time.Millisecond - mesosClient, err := createMesosClient(md, httpClient, httpTransport, cacheTTL) - mesosCloud := &MesosCloud{client: mesosClient, config: createDefaultConfig()} - - clusters, err := mesosCloud.List(".*") // recognizes the language of all strings - - if err != nil { - t.Fatalf("List does not yield an error: %#v", err) - } - - if len(clusters) != 3 { - t.Fatalf("List with a catch-all filter should return a list of size 3: (actual: %#v)", clusters) - } - - clusters, err = mesosCloud.List("$^") // end-of-string followed by start-of-string: recognizes the empty language - - if err != nil { - t.Fatalf("List does not yield an error: %#v", err) - } - - if len(clusters) != 0 { - t.Fatalf("List with a reject-all filter should return a list of size 0: (actual: %#v)", clusters) - } -} - -func Test_ExternalID(t *testing.T) { - defer log.Flush() - md := FakeMasterDetector{} - httpServer, httpClient, httpTransport := makeHttpMocks() - defer httpServer.Close() - cacheTTL := 500 * time.Millisecond - mesosClient, err := createMesosClient(md, httpClient, httpTransport, cacheTTL) - mesosCloud := &MesosCloud{client: mesosClient, config: createDefaultConfig()} - - _, err = mesosCloud.ExternalID("unknown") - if err != cloudprovider.InstanceNotFound { - t.Fatalf("ExternalID did not return InstanceNotFound on an unknown instance") - } - - slaveName := "mesos3.internal.company.com" - id, err := mesosCloud.ExternalID(slaveName) - if id != "" { - t.Fatalf("ExternalID should not be able to resolve %q", slaveName) - } - if err == cloudprovider.InstanceNotFound { - t.Fatalf("ExternalID should find %q", slaveName) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/plugins.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/plugins.go deleted file mode 100644 index 2baf7b47f..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/mesos/plugins.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright 2015 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 mesos - -import ( - _ "github.com/mesos/mesos-go/detector/zoo" -) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/MAINTAINERS.md b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/MAINTAINERS.md deleted file mode 100644 index f71afec99..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/MAINTAINERS.md +++ /dev/null @@ -1,6 +0,0 @@ -# Maintainers - -* [Angus Lees](https://github.com/anguslees) - - -[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/pkg/cloudprovider/providers/openstack/MAINTAINERS.md?pixel)]() diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack.go deleted file mode 100644 index 2a9376c2b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack.go +++ /dev/null @@ -1,1087 +0,0 @@ -/* -Copyright 2014 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 openstack - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "net" - "net/http" - "regexp" - "strings" - "time" - - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" - "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes" - "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach" - "github.com/rackspace/gophercloud/openstack/compute/v2/flavors" - "github.com/rackspace/gophercloud/openstack/compute/v2/servers" - "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" - "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members" - "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors" - "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools" - "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips" - "github.com/rackspace/gophercloud/pagination" - "github.com/scalingdata/gcfg" - - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/cloudprovider" - "k8s.io/kubernetes/pkg/types" -) - -const ProviderName = "openstack" - -// metadataUrl is URL to OpenStack metadata server. It's hadrcoded IPv4 -// link-local address as documented in "OpenStack Cloud Administrator Guide", -// chapter Compute - Networking with nova-network. -// http://docs.openstack.org/admin-guide-cloud/compute-networking-nova.html#metadata-service -const metadataUrl = "http://169.254.169.254/openstack/2012-08-10/meta_data.json" - -var ErrNotFound = errors.New("Failed to find object") -var ErrMultipleResults = errors.New("Multiple results where only one expected") -var ErrNoAddressFound = errors.New("No address found for host") -var ErrAttrNotFound = errors.New("Expected attribute not found") - -const ( - MiB = 1024 * 1024 - GB = 1000 * 1000 * 1000 -) - -// encoding.TextUnmarshaler interface for time.Duration -type MyDuration struct { - time.Duration -} - -func (d *MyDuration) UnmarshalText(text []byte) error { - res, err := time.ParseDuration(string(text)) - if err != nil { - return err - } - d.Duration = res - return nil -} - -type LoadBalancerOpts struct { - SubnetId string `gcfg:"subnet-id"` // required - FloatingNetworkId string `gcfg:"floating-network-id"` - LBMethod string `gfcg:"lb-method"` - CreateMonitor bool `gcfg:"create-monitor"` - MonitorDelay MyDuration `gcfg:"monitor-delay"` - MonitorTimeout MyDuration `gcfg:"monitor-timeout"` - MonitorMaxRetries uint `gcfg:"monitor-max-retries"` -} - -// OpenStack is an implementation of cloud provider Interface for OpenStack. -type OpenStack struct { - provider *gophercloud.ProviderClient - region string - lbOpts LoadBalancerOpts - // InstanceID of the server where this OpenStack object is instantiated. - localInstanceID string -} - -type Config struct { - Global struct { - AuthUrl string `gcfg:"auth-url"` - Username string - UserId string `gcfg:"user-id"` - Password string - ApiKey string `gcfg:"api-key"` - TenantId string `gcfg:"tenant-id"` - TenantName string `gcfg:"tenant-name"` - DomainId string `gcfg:"domain-id"` - DomainName string `gcfg:"domain-name"` - Region string - } - LoadBalancer LoadBalancerOpts -} - -func init() { - cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) { - cfg, err := readConfig(config) - if err != nil { - return nil, err - } - return newOpenStack(cfg) - }) -} - -func (cfg Config) toAuthOptions() gophercloud.AuthOptions { - return gophercloud.AuthOptions{ - IdentityEndpoint: cfg.Global.AuthUrl, - Username: cfg.Global.Username, - UserID: cfg.Global.UserId, - Password: cfg.Global.Password, - APIKey: cfg.Global.ApiKey, - TenantID: cfg.Global.TenantId, - TenantName: cfg.Global.TenantName, - DomainID: cfg.Global.DomainId, - DomainName: cfg.Global.DomainName, - - // Persistent service, so we need to be able to renew tokens. - AllowReauth: true, - } -} - -func readConfig(config io.Reader) (Config, error) { - if config == nil { - err := fmt.Errorf("no OpenStack cloud provider config file given") - return Config{}, err - } - - var cfg Config - err := gcfg.ReadInto(&cfg, config) - return cfg, err -} - -// parseMetadataUUID reads JSON from OpenStack metadata server and parses -// instance ID out of it. -func parseMetadataUUID(jsonData []byte) (string, error) { - // We should receive an object with { 'uuid': '' } and couple of other - // properties (which we ignore). - - obj := struct{ UUID string }{} - err := json.Unmarshal(jsonData, &obj) - if err != nil { - return "", err - } - - uuid := obj.UUID - if uuid == "" { - err = fmt.Errorf("cannot parse OpenStack metadata, got empty uuid") - return "", err - } - - return uuid, nil -} - -func readInstanceID() (string, error) { - // Try to find instance ID on the local filesystem (created by cloud-init) - const instanceIDFile = "/var/lib/cloud/data/instance-id" - idBytes, err := ioutil.ReadFile(instanceIDFile) - if err == nil { - instanceID := string(idBytes) - instanceID = strings.TrimSpace(instanceID) - glog.V(3).Infof("Got instance id from %s: %s", instanceIDFile, instanceID) - if instanceID != "" { - return instanceID, nil - } - // Fall through with empty instanceID and try metadata server. - } - glog.V(5).Infof("Cannot read %s: '%v', trying metadata server", instanceIDFile, err) - - // Try to get JSON from metdata server. - resp, err := http.Get(metadataUrl) - if err != nil { - glog.V(3).Infof("Cannot read %s: %v", metadataUrl, err) - return "", err - } - - if resp.StatusCode != 200 { - err = fmt.Errorf("got unexpected status code when reading metadata from %s: %s", metadataUrl, resp.Status) - glog.V(3).Infof("%v", err) - return "", err - } - - defer resp.Body.Close() - bodyBytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - glog.V(3).Infof("Cannot get HTTP response body from %s: %v", metadataUrl, err) - return "", err - } - instanceID, err := parseMetadataUUID(bodyBytes) - if err != nil { - glog.V(3).Infof("Cannot parse instance ID from metadata from %s: %v", metadataUrl, err) - return "", err - } - - glog.V(3).Infof("Got instance id from %s: %s", metadataUrl, instanceID) - return instanceID, nil -} - -func newOpenStack(cfg Config) (*OpenStack, error) { - provider, err := openstack.AuthenticatedClient(cfg.toAuthOptions()) - if err != nil { - return nil, err - } - - id, err := readInstanceID() - if err != nil { - return nil, err - } - - os := OpenStack{ - provider: provider, - region: cfg.Global.Region, - lbOpts: cfg.LoadBalancer, - localInstanceID: id, - } - - return &os, nil -} - -type Instances struct { - compute *gophercloud.ServiceClient - flavor_to_resource map[string]*api.NodeResources // keyed by flavor id -} - -// Instances returns an implementation of Instances for OpenStack. -func (os *OpenStack) Instances() (cloudprovider.Instances, bool) { - glog.V(4).Info("openstack.Instances() called") - - compute, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{ - Region: os.region, - }) - if err != nil { - glog.Warningf("Failed to find compute endpoint: %v", err) - return nil, false - } - - pager := flavors.ListDetail(compute, nil) - - flavor_to_resource := make(map[string]*api.NodeResources) - err = pager.EachPage(func(page pagination.Page) (bool, error) { - flavorList, err := flavors.ExtractFlavors(page) - if err != nil { - return false, err - } - for _, flavor := range flavorList { - rsrc := api.NodeResources{ - Capacity: api.ResourceList{ - api.ResourceCPU: *resource.NewQuantity(int64(flavor.VCPUs), resource.DecimalSI), - api.ResourceMemory: *resource.NewQuantity(int64(flavor.RAM)*MiB, resource.BinarySI), - "openstack.org/disk": *resource.NewQuantity(int64(flavor.Disk)*GB, resource.DecimalSI), - "openstack.org/rxTxFactor": *resource.NewMilliQuantity(int64(flavor.RxTxFactor)*1000, resource.DecimalSI), - "openstack.org/swap": *resource.NewQuantity(int64(flavor.Swap)*MiB, resource.BinarySI), - }, - } - flavor_to_resource[flavor.ID] = &rsrc - } - return true, nil - }) - if err != nil { - glog.Warningf("Failed to find compute flavors: %v", err) - return nil, false - } - - glog.V(3).Infof("Found %v compute flavors", len(flavor_to_resource)) - glog.V(1).Info("Claiming to support Instances") - - return &Instances{compute, flavor_to_resource}, true -} - -func (i *Instances) List(name_filter string) ([]string, error) { - glog.V(4).Infof("openstack List(%v) called", name_filter) - - opts := servers.ListOpts{ - Name: name_filter, - Status: "ACTIVE", - } - pager := servers.List(i.compute, opts) - - ret := make([]string, 0) - err := pager.EachPage(func(page pagination.Page) (bool, error) { - sList, err := servers.ExtractServers(page) - if err != nil { - return false, err - } - for _, server := range sList { - ret = append(ret, server.Name) - } - return true, nil - }) - if err != nil { - return nil, err - } - - glog.V(3).Infof("Found %v instances matching %v: %v", - len(ret), name_filter, ret) - - return ret, nil -} - -func getServerByName(client *gophercloud.ServiceClient, name string) (*servers.Server, error) { - opts := servers.ListOpts{ - Name: fmt.Sprintf("^%s$", regexp.QuoteMeta(name)), - Status: "ACTIVE", - } - pager := servers.List(client, opts) - - serverList := make([]servers.Server, 0, 1) - - err := pager.EachPage(func(page pagination.Page) (bool, error) { - s, err := servers.ExtractServers(page) - if err != nil { - return false, err - } - serverList = append(serverList, s...) - if len(serverList) > 1 { - return false, ErrMultipleResults - } - return true, nil - }) - if err != nil { - return nil, err - } - - if len(serverList) == 0 { - return nil, ErrNotFound - } else if len(serverList) > 1 { - return nil, ErrMultipleResults - } - - return &serverList[0], nil -} - -func getAddressesByName(client *gophercloud.ServiceClient, name string) ([]api.NodeAddress, error) { - srv, err := getServerByName(client, name) - if err != nil { - return nil, err - } - - addrs := []api.NodeAddress{} - - for network, netblob := range srv.Addresses { - list, ok := netblob.([]interface{}) - if !ok { - continue - } - - for _, item := range list { - var addressType api.NodeAddressType - - props, ok := item.(map[string]interface{}) - if !ok { - continue - } - - extIPType, ok := props["OS-EXT-IPS:type"] - if (ok && extIPType == "floating") || (!ok && network == "public") { - addressType = api.NodeExternalIP - } else { - addressType = api.NodeInternalIP - } - - tmp, ok := props["addr"] - if !ok { - continue - } - addr, ok := tmp.(string) - if !ok { - continue - } - - api.AddToNodeAddresses(&addrs, - api.NodeAddress{ - Type: addressType, - Address: addr, - }, - ) - } - } - - // AccessIPs are usually duplicates of "public" addresses. - if srv.AccessIPv4 != "" { - api.AddToNodeAddresses(&addrs, - api.NodeAddress{ - Type: api.NodeExternalIP, - Address: srv.AccessIPv4, - }, - ) - } - - if srv.AccessIPv6 != "" { - api.AddToNodeAddresses(&addrs, - api.NodeAddress{ - Type: api.NodeExternalIP, - Address: srv.AccessIPv6, - }, - ) - } - - return addrs, nil -} - -func getAddressByName(client *gophercloud.ServiceClient, name string) (string, error) { - addrs, err := getAddressesByName(client, name) - if err != nil { - return "", err - } else if len(addrs) == 0 { - return "", ErrNoAddressFound - } - - for _, addr := range addrs { - if addr.Type == api.NodeInternalIP { - return addr.Address, nil - } - } - - return addrs[0].Address, nil -} - -// Implementation of Instances.CurrentNodeName -func (i *Instances) CurrentNodeName(hostname string) (string, error) { - return hostname, nil -} - -func (i *Instances) AddSSHKeyToAllInstances(user string, keyData []byte) error { - return errors.New("unimplemented") -} - -func (i *Instances) NodeAddresses(name string) ([]api.NodeAddress, error) { - glog.V(4).Infof("NodeAddresses(%v) called", name) - - addrs, err := getAddressesByName(i.compute, name) - if err != nil { - return nil, err - } - - glog.V(4).Infof("NodeAddresses(%v) => %v", name, addrs) - return addrs, nil -} - -// ExternalID returns the cloud provider ID of the specified instance (deprecated). -func (i *Instances) ExternalID(name string) (string, error) { - srv, err := getServerByName(i.compute, name) - if err != nil { - return "", err - } - return srv.ID, nil -} - -// InstanceID returns the cloud provider ID of the specified instance. -func (i *Instances) InstanceID(name string) (string, error) { - srv, err := getServerByName(i.compute, name) - if err != nil { - return "", err - } - // In the future it is possible to also return an endpoint as: - // / - return "/" + srv.ID, nil -} - -// InstanceType returns the type of the specified instance. -func (i *Instances) InstanceType(name string) (string, error) { - return "", nil -} - -func (os *OpenStack) Clusters() (cloudprovider.Clusters, bool) { - return nil, false -} - -// ProviderName returns the cloud provider ID. -func (os *OpenStack) ProviderName() string { - return ProviderName -} - -// ScrubDNS filters DNS settings for pods. -func (os *OpenStack) ScrubDNS(nameservers, searches []string) (nsOut, srchOut []string) { - return nameservers, searches -} - -type LoadBalancer struct { - network *gophercloud.ServiceClient - compute *gophercloud.ServiceClient - opts LoadBalancerOpts -} - -func (os *OpenStack) LoadBalancer() (cloudprovider.LoadBalancer, bool) { - glog.V(4).Info("openstack.LoadBalancer() called") - - // TODO: Search for and support Rackspace loadbalancer API, and others. - network, err := openstack.NewNetworkV2(os.provider, gophercloud.EndpointOpts{ - Region: os.region, - }) - if err != nil { - glog.Warningf("Failed to find neutron endpoint: %v", err) - return nil, false - } - - compute, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{ - Region: os.region, - }) - if err != nil { - glog.Warningf("Failed to find compute endpoint: %v", err) - return nil, false - } - - glog.V(1).Info("Claiming to support LoadBalancer") - - return &LoadBalancer{network, compute, os.lbOpts}, true -} - -func isNotFound(err error) bool { - e, ok := err.(*gophercloud.UnexpectedResponseCodeError) - return ok && e.Actual == http.StatusNotFound -} - -func getPoolByName(client *gophercloud.ServiceClient, name string) (*pools.Pool, error) { - opts := pools.ListOpts{ - Name: name, - } - pager := pools.List(client, opts) - - poolList := make([]pools.Pool, 0, 1) - - err := pager.EachPage(func(page pagination.Page) (bool, error) { - p, err := pools.ExtractPools(page) - if err != nil { - return false, err - } - poolList = append(poolList, p...) - if len(poolList) > 1 { - return false, ErrMultipleResults - } - return true, nil - }) - if err != nil { - if isNotFound(err) { - return nil, ErrNotFound - } - return nil, err - } - - if len(poolList) == 0 { - return nil, ErrNotFound - } else if len(poolList) > 1 { - return nil, ErrMultipleResults - } - - return &poolList[0], nil -} - -func getVipByName(client *gophercloud.ServiceClient, name string) (*vips.VirtualIP, error) { - opts := vips.ListOpts{ - Name: name, - } - pager := vips.List(client, opts) - - vipList := make([]vips.VirtualIP, 0, 1) - - err := pager.EachPage(func(page pagination.Page) (bool, error) { - v, err := vips.ExtractVIPs(page) - if err != nil { - return false, err - } - vipList = append(vipList, v...) - if len(vipList) > 1 { - return false, ErrMultipleResults - } - return true, nil - }) - if err != nil { - if isNotFound(err) { - return nil, ErrNotFound - } - return nil, err - } - - if len(vipList) == 0 { - return nil, ErrNotFound - } else if len(vipList) > 1 { - return nil, ErrMultipleResults - } - - return &vipList[0], nil -} - -func getFloatingIPByPortID(client *gophercloud.ServiceClient, portID string) (*floatingips.FloatingIP, error) { - opts := floatingips.ListOpts{ - PortID: portID, - } - pager := floatingips.List(client, opts) - - floatingIPList := make([]floatingips.FloatingIP, 0, 1) - - err := pager.EachPage(func(page pagination.Page) (bool, error) { - f, err := floatingips.ExtractFloatingIPs(page) - if err != nil { - return false, err - } - floatingIPList = append(floatingIPList, f...) - if len(floatingIPList) > 1 { - return false, ErrMultipleResults - } - return true, nil - }) - if err != nil { - if isNotFound(err) { - return nil, ErrNotFound - } - return nil, err - } - - if len(floatingIPList) == 0 { - return nil, ErrNotFound - } else if len(floatingIPList) > 1 { - return nil, ErrMultipleResults - } - - return &floatingIPList[0], nil -} - -func (lb *LoadBalancer) GetLoadBalancer(name, region string) (*api.LoadBalancerStatus, bool, error) { - vip, err := getVipByName(lb.network, name) - if err == ErrNotFound { - return nil, false, nil - } - if vip == nil { - return nil, false, err - } - - status := &api.LoadBalancerStatus{} - status.Ingress = []api.LoadBalancerIngress{{IP: vip.Address}} - - return status, true, err -} - -// TODO: This code currently ignores 'region' and always creates a -// loadbalancer in only the current OpenStack region. We should take -// a list of regions (from config) and query/create loadbalancers in -// each region. - -func (lb *LoadBalancer) EnsureLoadBalancer(name, region string, loadBalancerIP net.IP, ports []*api.ServicePort, hosts []string, serviceName types.NamespacedName, affinity api.ServiceAffinity) (*api.LoadBalancerStatus, error) { - glog.V(4).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v, %v)", name, region, loadBalancerIP, ports, hosts, serviceName) - - if len(ports) > 1 { - return nil, fmt.Errorf("multiple ports are not yet supported in openstack load balancers") - } else if len(ports) == 0 { - return nil, fmt.Errorf("no ports provided to openstack load balancer") - } - - // The service controller verified all the protocols match on the ports, just check and use the first one - // TODO: Convert all error messages to use an event recorder - if ports[0].Protocol != api.ProtocolTCP { - return nil, fmt.Errorf("Only TCP LoadBalancer is supported for openstack load balancers") - } - - var persistence *vips.SessionPersistence - switch affinity { - case api.ServiceAffinityNone: - persistence = nil - case api.ServiceAffinityClientIP: - persistence = &vips.SessionPersistence{Type: "SOURCE_IP"} - default: - return nil, fmt.Errorf("unsupported load balancer affinity: %v", affinity) - } - - glog.V(2).Infof("Checking if openstack load balancer already exists: %s", name) - _, exists, err := lb.GetLoadBalancer(name, region) - if err != nil { - return nil, fmt.Errorf("error checking if openstack load balancer already exists: %v", err) - } - - // TODO: Implement a more efficient update strategy for common changes than delete & create - // In particular, if we implement hosts update, we can get rid of UpdateHosts - if exists { - err := lb.EnsureLoadBalancerDeleted(name, region) - if err != nil { - return nil, fmt.Errorf("error deleting existing openstack load balancer: %v", err) - } - } - - lbmethod := lb.opts.LBMethod - if lbmethod == "" { - lbmethod = pools.LBMethodRoundRobin - } - pool, err := pools.Create(lb.network, pools.CreateOpts{ - Name: name, - Protocol: pools.ProtocolTCP, - SubnetID: lb.opts.SubnetId, - LBMethod: lbmethod, - }).Extract() - if err != nil { - return nil, err - } - - for _, host := range hosts { - addr, err := getAddressByName(lb.compute, host) - if err != nil { - return nil, err - } - - _, err = members.Create(lb.network, members.CreateOpts{ - PoolID: pool.ID, - ProtocolPort: ports[0].NodePort, //TODO: need to handle multi-port - Address: addr, - }).Extract() - if err != nil { - pools.Delete(lb.network, pool.ID) - return nil, err - } - } - - var mon *monitors.Monitor - if lb.opts.CreateMonitor { - mon, err = monitors.Create(lb.network, monitors.CreateOpts{ - Type: monitors.TypeTCP, - Delay: int(lb.opts.MonitorDelay.Duration.Seconds()), - Timeout: int(lb.opts.MonitorTimeout.Duration.Seconds()), - MaxRetries: int(lb.opts.MonitorMaxRetries), - }).Extract() - if err != nil { - pools.Delete(lb.network, pool.ID) - return nil, err - } - - _, err = pools.AssociateMonitor(lb.network, pool.ID, mon.ID).Extract() - if err != nil { - monitors.Delete(lb.network, mon.ID) - pools.Delete(lb.network, pool.ID) - return nil, err - } - } - - createOpts := vips.CreateOpts{ - Name: name, - Description: fmt.Sprintf("Kubernetes external service %s", name), - Protocol: "TCP", - ProtocolPort: ports[0].Port, //TODO: need to handle multi-port - PoolID: pool.ID, - SubnetID: lb.opts.SubnetId, - Persistence: persistence, - } - if loadBalancerIP != nil { - createOpts.Address = loadBalancerIP.String() - } - - vip, err := vips.Create(lb.network, createOpts).Extract() - if err != nil { - if mon != nil { - monitors.Delete(lb.network, mon.ID) - } - pools.Delete(lb.network, pool.ID) - return nil, err - } - - status := &api.LoadBalancerStatus{} - - status.Ingress = []api.LoadBalancerIngress{{IP: vip.Address}} - - if lb.opts.FloatingNetworkId != "" { - floatIPOpts := floatingips.CreateOpts{ - FloatingNetworkID: lb.opts.FloatingNetworkId, - PortID: vip.PortID, - } - floatIP, err := floatingips.Create(lb.network, floatIPOpts).Extract() - if err != nil { - return nil, err - } - - status.Ingress = append(status.Ingress, api.LoadBalancerIngress{IP: floatIP.FloatingIP}) - } - - return status, nil - -} - -func (lb *LoadBalancer) UpdateLoadBalancer(name, region string, hosts []string) error { - glog.V(4).Infof("UpdateLoadBalancer(%v, %v, %v)", name, region, hosts) - - vip, err := getVipByName(lb.network, name) - if err != nil { - return err - } - - // Set of member (addresses) that _should_ exist - addrs := map[string]bool{} - for _, host := range hosts { - addr, err := getAddressByName(lb.compute, host) - if err != nil { - return err - } - - addrs[addr] = true - } - - // Iterate over members that _do_ exist - pager := members.List(lb.network, members.ListOpts{PoolID: vip.PoolID}) - err = pager.EachPage(func(page pagination.Page) (bool, error) { - memList, err := members.ExtractMembers(page) - if err != nil { - return false, err - } - - for _, member := range memList { - if _, found := addrs[member.Address]; found { - // Member already exists - delete(addrs, member.Address) - } else { - // Member needs to be deleted - err = members.Delete(lb.network, member.ID).ExtractErr() - if err != nil { - return false, err - } - } - } - - return true, nil - }) - if err != nil { - return err - } - - // Anything left in addrs is a new member that needs to be added - for addr := range addrs { - _, err := members.Create(lb.network, members.CreateOpts{ - PoolID: vip.PoolID, - Address: addr, - ProtocolPort: vip.ProtocolPort, - }).Extract() - if err != nil { - return err - } - } - - return nil -} - -func (lb *LoadBalancer) EnsureLoadBalancerDeleted(name, region string) error { - glog.V(4).Infof("EnsureLoadBalancerDeleted(%v, %v)", name, region) - - vip, err := getVipByName(lb.network, name) - if err != nil && err != ErrNotFound { - return err - } - - if lb.opts.FloatingNetworkId != "" && vip != nil { - floatingIP, err := getFloatingIPByPortID(lb.network, vip.PortID) - if err != nil && !isNotFound(err) { - return err - } - if floatingIP != nil { - err = floatingips.Delete(lb.network, floatingIP.ID).ExtractErr() - if err != nil && !isNotFound(err) { - return err - } - } - } - - // We have to delete the VIP before the pool can be deleted, - // so no point continuing if this fails. - if vip != nil { - err := vips.Delete(lb.network, vip.ID).ExtractErr() - if err != nil && !isNotFound(err) { - return err - } - } - - var pool *pools.Pool - if vip != nil { - pool, err = pools.Get(lb.network, vip.PoolID).Extract() - if err != nil && !isNotFound(err) { - return err - } - } else { - // The VIP is gone, but it is conceivable that a Pool - // still exists that we failed to delete on some - // previous occasion. Make a best effort attempt to - // cleanup any pools with the same name as the VIP. - pool, err = getPoolByName(lb.network, name) - if err != nil && err != ErrNotFound { - return err - } - } - - if pool != nil { - for _, monId := range pool.MonitorIDs { - _, err = pools.DisassociateMonitor(lb.network, pool.ID, monId).Extract() - if err != nil { - return err - } - - err = monitors.Delete(lb.network, monId).ExtractErr() - if err != nil && !isNotFound(err) { - return err - } - } - err = pools.Delete(lb.network, pool.ID).ExtractErr() - if err != nil && !isNotFound(err) { - return err - } - } - - return nil -} - -func (os *OpenStack) Zones() (cloudprovider.Zones, bool) { - glog.V(1).Info("Claiming to support Zones") - - return os, true -} -func (os *OpenStack) GetZone() (cloudprovider.Zone, error) { - glog.V(1).Infof("Current zone is %v", os.region) - - return cloudprovider.Zone{Region: os.region}, nil -} - -func (os *OpenStack) Routes() (cloudprovider.Routes, bool) { - return nil, false -} - -// Attaches given cinder volume to the compute running kubelet -func (os *OpenStack) AttachDisk(diskName string) (string, error) { - disk, err := os.getVolume(diskName) - if err != nil { - return "", err - } - cClient, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{ - Region: os.region, - }) - if err != nil || cClient == nil { - glog.Errorf("Unable to initialize nova client for region: %s", os.region) - return "", err - } - - if len(disk.Attachments) > 0 && disk.Attachments[0]["server_id"] != nil { - if os.localInstanceID == disk.Attachments[0]["server_id"] { - glog.V(4).Infof("Disk: %q is already attached to compute: %q", diskName, os.localInstanceID) - return disk.ID, nil - } else { - errMsg := fmt.Sprintf("Disk %q is attached to a different compute: %q, should be detached before proceeding", diskName, disk.Attachments[0]["server_id"]) - glog.Errorf(errMsg) - return "", errors.New(errMsg) - } - } - // add read only flag here if possible spothanis - _, err = volumeattach.Create(cClient, os.localInstanceID, &volumeattach.CreateOpts{ - VolumeID: disk.ID, - }).Extract() - if err != nil { - glog.Errorf("Failed to attach %s volume to %s compute", diskName, os.localInstanceID) - return "", err - } - glog.V(2).Infof("Successfully attached %s volume to %s compute", diskName, os.localInstanceID) - return disk.ID, nil -} - -// Detaches given cinder volume from the compute running kubelet -func (os *OpenStack) DetachDisk(partialDiskId string) error { - disk, err := os.getVolume(partialDiskId) - if err != nil { - return err - } - cClient, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{ - Region: os.region, - }) - if err != nil || cClient == nil { - glog.Errorf("Unable to initialize nova client for region: %s", os.region) - return err - } - if len(disk.Attachments) > 0 && disk.Attachments[0]["server_id"] != nil && os.localInstanceID == disk.Attachments[0]["server_id"] { - // This is a blocking call and effects kubelet's performance directly. - // We should consider kicking it out into a separate routine, if it is bad. - err = volumeattach.Delete(cClient, os.localInstanceID, disk.ID).ExtractErr() - if err != nil { - glog.Errorf("Failed to delete volume %s from compute %s attached %v", disk.ID, os.localInstanceID, err) - return err - } - glog.V(2).Infof("Successfully detached volume: %s from compute: %s", disk.ID, os.localInstanceID) - } else { - errMsg := fmt.Sprintf("Disk: %s has no attachments or is not attached to compute: %s", disk.Name, os.localInstanceID) - glog.Errorf(errMsg) - return errors.New(errMsg) - } - return nil -} - -// Takes a partial/full disk id or diskname -func (os *OpenStack) getVolume(diskName string) (volumes.Volume, error) { - sClient, err := openstack.NewBlockStorageV1(os.provider, gophercloud.EndpointOpts{ - Region: os.region, - }) - - var volume volumes.Volume - if err != nil || sClient == nil { - glog.Errorf("Unable to initialize cinder client for region: %s", os.region) - return volume, err - } - - err = volumes.List(sClient, nil).EachPage(func(page pagination.Page) (bool, error) { - vols, err := volumes.ExtractVolumes(page) - if err != nil { - glog.Errorf("Failed to extract volumes: %v", err) - return false, err - } else { - for _, v := range vols { - glog.V(4).Infof("%s %s %v", v.ID, v.Name, v.Attachments) - if v.Name == diskName || strings.Contains(v.ID, diskName) { - volume = v - return true, nil - } - } - } - // if it reached here then no disk with the given name was found. - errmsg := fmt.Sprintf("Unable to find disk: %s in region %s", diskName, os.region) - return false, errors.New(errmsg) - }) - if err != nil { - glog.Errorf("Error occured getting volume: %s", diskName) - return volume, err - } - return volume, err -} - -// Create a volume of given size (in GiB) -func (os *OpenStack) CreateVolume(name string, size int, tags *map[string]string) (volumeName string, err error) { - - sClient, err := openstack.NewBlockStorageV1(os.provider, gophercloud.EndpointOpts{ - Region: os.region, - }) - - if err != nil || sClient == nil { - glog.Errorf("Unable to initialize cinder client for region: %s", os.region) - return "", err - } - - opts := volumes.CreateOpts{ - Name: name, - Size: size, - } - if tags != nil { - opts.Metadata = *tags - } - vol, err := volumes.Create(sClient, opts).Extract() - if err != nil { - glog.Errorf("Failed to create a %d GB volume: %v", size, err) - return "", err - } - glog.Infof("Created volume %v", vol.ID) - return vol.ID, err -} - -func (os *OpenStack) DeleteVolume(volumeName string) error { - sClient, err := openstack.NewBlockStorageV1(os.provider, gophercloud.EndpointOpts{ - Region: os.region, - }) - - if err != nil || sClient == nil { - glog.Errorf("Unable to initialize cinder client for region: %s", os.region) - return err - } - err = volumes.Delete(sClient, volumeName).ExtractErr() - if err != nil { - glog.Errorf("Cannot delete volume %s: %v", volumeName, err) - } - return err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack_test.go deleted file mode 100644 index a4db2fafb..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/openstack/openstack_test.go +++ /dev/null @@ -1,228 +0,0 @@ -/* -Copyright 2014 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 openstack - -import ( - "os" - "strings" - "testing" - "time" - - "k8s.io/kubernetes/pkg/util/rand" - - "github.com/rackspace/gophercloud" -) - -func TestReadConfig(t *testing.T) { - _, err := readConfig(nil) - if err == nil { - t.Errorf("Should fail when no config is provided: %s", err) - } - - cfg, err := readConfig(strings.NewReader(` -[Global] -auth-url = http://auth.url -username = user -[LoadBalancer] -create-monitor = yes -monitor-delay = 1m -monitor-timeout = 30s -monitor-max-retries = 3 -`)) - if err != nil { - t.Fatalf("Should succeed when a valid config is provided: %s", err) - } - if cfg.Global.AuthUrl != "http://auth.url" { - t.Errorf("incorrect authurl: %s", cfg.Global.AuthUrl) - } - - if !cfg.LoadBalancer.CreateMonitor { - t.Errorf("incorrect lb.createmonitor: %t", cfg.LoadBalancer.CreateMonitor) - } - if cfg.LoadBalancer.MonitorDelay.Duration != 1*time.Minute { - t.Errorf("incorrect lb.monitordelay: %s", cfg.LoadBalancer.MonitorDelay) - } - if cfg.LoadBalancer.MonitorTimeout.Duration != 30*time.Second { - t.Errorf("incorrect lb.monitortimeout: %s", cfg.LoadBalancer.MonitorTimeout) - } - if cfg.LoadBalancer.MonitorMaxRetries != 3 { - t.Errorf("incorrect lb.monitormaxretries: %d", cfg.LoadBalancer.MonitorMaxRetries) - } -} - -func TestToAuthOptions(t *testing.T) { - cfg := Config{} - cfg.Global.Username = "user" - // etc. - - ao := cfg.toAuthOptions() - - if !ao.AllowReauth { - t.Errorf("Will need to be able to reauthenticate") - } - if ao.Username != cfg.Global.Username { - t.Errorf("Username %s != %s", ao.Username, cfg.Global.Username) - } -} - -// This allows acceptance testing against an existing OpenStack -// install, using the standard OS_* OpenStack client environment -// variables. -// FIXME: it would be better to hermetically test against canned JSON -// requests/responses. -func configFromEnv() (cfg Config, ok bool) { - cfg.Global.AuthUrl = os.Getenv("OS_AUTH_URL") - - cfg.Global.TenantId = os.Getenv("OS_TENANT_ID") - // Rax/nova _insists_ that we don't specify both tenant ID and name - if cfg.Global.TenantId == "" { - cfg.Global.TenantName = os.Getenv("OS_TENANT_NAME") - } - - cfg.Global.Username = os.Getenv("OS_USERNAME") - cfg.Global.Password = os.Getenv("OS_PASSWORD") - cfg.Global.ApiKey = os.Getenv("OS_API_KEY") - cfg.Global.Region = os.Getenv("OS_REGION_NAME") - cfg.Global.DomainId = os.Getenv("OS_DOMAIN_ID") - cfg.Global.DomainName = os.Getenv("OS_DOMAIN_NAME") - - ok = (cfg.Global.AuthUrl != "" && - cfg.Global.Username != "" && - (cfg.Global.Password != "" || cfg.Global.ApiKey != "") && - (cfg.Global.TenantId != "" || cfg.Global.TenantName != "" || - cfg.Global.DomainId != "" || cfg.Global.DomainName != "")) - - return -} - -func TestNewOpenStack(t *testing.T) { - cfg, ok := configFromEnv() - if !ok { - t.Skipf("No config found in environment") - } - - _, err := newOpenStack(cfg) - if err != nil { - t.Fatalf("Failed to construct/authenticate OpenStack: %s", err) - } -} - -func TestInstances(t *testing.T) { - cfg, ok := configFromEnv() - if !ok { - t.Skipf("No config found in environment") - } - - os, err := newOpenStack(cfg) - if err != nil { - t.Fatalf("Failed to construct/authenticate OpenStack: %s", err) - } - - i, ok := os.Instances() - if !ok { - t.Fatalf("Instances() returned false") - } - - srvs, err := i.List(".") - if err != nil { - t.Fatalf("Instances.List() failed: %s", err) - } - if len(srvs) == 0 { - t.Fatalf("Instances.List() returned zero servers") - } - t.Logf("Found servers (%d): %s\n", len(srvs), srvs) - - addrs, err := i.NodeAddresses(srvs[0]) - if err != nil { - t.Fatalf("Instances.NodeAddresses(%s) failed: %s", srvs[0], err) - } - t.Logf("Found NodeAddresses(%s) = %s\n", srvs[0], addrs) -} - -func TestLoadBalancer(t *testing.T) { - cfg, ok := configFromEnv() - if !ok { - t.Skipf("No config found in environment") - } - - os, err := newOpenStack(cfg) - if err != nil { - t.Fatalf("Failed to construct/authenticate OpenStack: %s", err) - } - - lb, ok := os.LoadBalancer() - if !ok { - t.Fatalf("LoadBalancer() returned false - perhaps your stack doesn't support Neutron?") - } - - _, exists, err := lb.GetLoadBalancer("noexist", "region") - if err != nil { - t.Fatalf("GetLoadBalancer(\"noexist\") returned error: %s", err) - } - if exists { - t.Fatalf("GetLoadBalancer(\"noexist\") returned exists") - } -} - -func TestZones(t *testing.T) { - os := OpenStack{ - provider: &gophercloud.ProviderClient{ - IdentityBase: "http://auth.url/", - }, - region: "myRegion", - } - - z, ok := os.Zones() - if !ok { - t.Fatalf("Zones() returned false") - } - - zone, err := z.GetZone() - if err != nil { - t.Fatalf("GetZone() returned error: %s", err) - } - - if zone.Region != "myRegion" { - t.Fatalf("GetZone() returned wrong region (%s)", zone.Region) - } -} - -func TestVolumes(t *testing.T) { - cfg, ok := configFromEnv() - if !ok { - t.Skipf("No config found in environment") - } - - os, err := newOpenStack(cfg) - if err != nil { - t.Fatalf("Failed to construct/authenticate OpenStack: %s", err) - } - - tags := map[string]string{ - "test": "value", - } - vol, err := os.CreateVolume("kubernetes-test-volume-"+rand.String(10), 1, &tags) - if err != nil { - t.Fatalf("Cannot create a new Cinder volume: %v", err) - } - - err = os.DeleteVolume(vol) - if err != nil { - t.Fatalf("Cannot delete Cinder volume %s: %v", vol, err) - } - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/ovirt/ovirt.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/ovirt/ovirt.go deleted file mode 100644 index 24b47c852..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/ovirt/ovirt.go +++ /dev/null @@ -1,291 +0,0 @@ -/* -Copyright 2014 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 ovirt - -import ( - "encoding/xml" - "errors" - "fmt" - "io" - "io/ioutil" - "net" - "net/http" - "net/url" - "path" - "sort" - "strings" - - "github.com/scalingdata/gcfg" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/cloudprovider" -) - -const ProviderName = "ovirt" - -type OVirtInstance struct { - UUID string - Name string - IPAddress string -} - -type OVirtInstanceMap map[string]OVirtInstance - -type OVirtCloud struct { - VmsRequest *url.URL - HostsRequest *url.URL -} - -type OVirtApiConfig struct { - Connection struct { - ApiEntry string `gcfg:"uri"` - Username string `gcfg:"username"` - Password string `gcfg:"password"` - } - Filters struct { - VmsQuery string `gcfg:"vms"` - } -} - -type XmlVmAddress struct { - Address string `xml:"address,attr"` -} - -type XmlVmInfo struct { - UUID string `xml:"id,attr"` - Name string `xml:"name"` - Hostname string `xml:"guest_info>fqdn"` - Addresses []XmlVmAddress `xml:"guest_info>ips>ip"` - State string `xml:"status>state"` -} - -type XmlVmsList struct { - XMLName xml.Name `xml:"vms"` - Vm []XmlVmInfo `xml:"vm"` -} - -func init() { - cloudprovider.RegisterCloudProvider(ProviderName, - func(config io.Reader) (cloudprovider.Interface, error) { - return newOVirtCloud(config) - }) -} - -func newOVirtCloud(config io.Reader) (*OVirtCloud, error) { - if config == nil { - return nil, fmt.Errorf("missing configuration file for ovirt cloud provider") - } - - oVirtConfig := OVirtApiConfig{} - - /* defaults */ - oVirtConfig.Connection.Username = "admin@internal" - - if err := gcfg.ReadInto(&oVirtConfig, config); err != nil { - return nil, err - } - - if oVirtConfig.Connection.ApiEntry == "" { - return nil, fmt.Errorf("missing ovirt uri in cloud provider configuration") - } - - request, err := url.Parse(oVirtConfig.Connection.ApiEntry) - if err != nil { - return nil, err - } - - request.Path = path.Join(request.Path, "vms") - request.User = url.UserPassword(oVirtConfig.Connection.Username, oVirtConfig.Connection.Password) - request.RawQuery = url.Values{"search": {oVirtConfig.Filters.VmsQuery}}.Encode() - - return &OVirtCloud{VmsRequest: request}, nil -} - -func (aws *OVirtCloud) Clusters() (cloudprovider.Clusters, bool) { - return nil, false -} - -// ProviderName returns the cloud provider ID. -func (v *OVirtCloud) ProviderName() string { - return ProviderName -} - -// ScrubDNS filters DNS settings for pods. -func (v *OVirtCloud) ScrubDNS(nameservers, searches []string) (nsOut, srchOut []string) { - return nameservers, searches -} - -// LoadBalancer returns an implementation of LoadBalancer for oVirt cloud -func (v *OVirtCloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) { - return nil, false -} - -// Instances returns an implementation of Instances for oVirt cloud -func (v *OVirtCloud) Instances() (cloudprovider.Instances, bool) { - return v, true -} - -// Zones returns an implementation of Zones for oVirt cloud -func (v *OVirtCloud) Zones() (cloudprovider.Zones, bool) { - return nil, false -} - -// Routes returns an implementation of Routes for oVirt cloud -func (v *OVirtCloud) Routes() (cloudprovider.Routes, bool) { - return nil, false -} - -// NodeAddresses returns the NodeAddresses of a particular machine instance -func (v *OVirtCloud) NodeAddresses(name string) ([]api.NodeAddress, error) { - instance, err := v.fetchInstance(name) - if err != nil { - return nil, err - } - - var address net.IP - - if instance.IPAddress != "" { - address = net.ParseIP(instance.IPAddress) - if address == nil { - return nil, fmt.Errorf("couldn't parse address: %s", instance.IPAddress) - } - } else { - resolved, err := net.LookupIP(name) - if err != nil || len(resolved) < 1 { - return nil, fmt.Errorf("couldn't lookup address: %s", name) - } - address = resolved[0] - } - - return []api.NodeAddress{{Type: api.NodeLegacyHostIP, Address: address.String()}}, nil -} - -// ExternalID returns the cloud provider ID of the specified instance (deprecated). -func (v *OVirtCloud) ExternalID(name string) (string, error) { - instance, err := v.fetchInstance(name) - if err != nil { - return "", err - } - return instance.UUID, nil -} - -// InstanceID returns the cloud provider ID of the specified instance. -func (v *OVirtCloud) InstanceID(name string) (string, error) { - instance, err := v.fetchInstance(name) - if err != nil { - return "", err - } - // TODO: define a way to identify the provider instance to complete - // the format /. - return "/" + instance.UUID, err -} - -// InstanceType returns the type of the specified instance. -func (v *OVirtCloud) InstanceType(name string) (string, error) { - return "", nil -} - -func getInstancesFromXml(body io.Reader) (OVirtInstanceMap, error) { - if body == nil { - return nil, fmt.Errorf("ovirt rest-api response body is missing") - } - - content, err := ioutil.ReadAll(body) - if err != nil { - return nil, err - } - - vmlist := XmlVmsList{} - - if err := xml.Unmarshal(content, &vmlist); err != nil { - return nil, err - } - - instances := make(OVirtInstanceMap) - - for _, vm := range vmlist.Vm { - // Always return only vms that are up and running - if vm.Hostname != "" && strings.ToLower(vm.State) == "up" { - address := "" - if len(vm.Addresses) > 0 { - address = vm.Addresses[0].Address - } - - instances[vm.Hostname] = OVirtInstance{ - UUID: vm.UUID, - Name: vm.Name, - IPAddress: address, - } - } - } - - return instances, nil -} - -func (v *OVirtCloud) fetchAllInstances() (OVirtInstanceMap, error) { - response, err := http.Get(v.VmsRequest.String()) - if err != nil { - return nil, err - } - - defer response.Body.Close() - - return getInstancesFromXml(response.Body) -} - -func (v *OVirtCloud) fetchInstance(name string) (*OVirtInstance, error) { - allInstances, err := v.fetchAllInstances() - if err != nil { - return nil, err - } - - instance, found := allInstances[name] - if !found { - return nil, fmt.Errorf("cannot find instance: %s", name) - } - - return &instance, nil -} - -func (m *OVirtInstanceMap) ListSortedNames() []string { - var names []string - - for k := range *m { - names = append(names, k) - } - - sort.Strings(names) - - return names -} - -// List enumerates the set of minions instances known by the cloud provider -func (v *OVirtCloud) List(filter string) ([]string, error) { - instances, err := v.fetchAllInstances() - if err != nil { - return nil, err - } - return instances.ListSortedNames(), nil -} - -// Implementation of Instances.CurrentNodeName -func (v *OVirtCloud) CurrentNodeName(hostname string) (string, error) { - return hostname, nil -} - -func (v *OVirtCloud) AddSSHKeyToAllInstances(user string, keyData []byte) error { - return errors.New("unimplemented") -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/ovirt/ovirt_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/ovirt/ovirt_test.go deleted file mode 100644 index c76bde726..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/ovirt/ovirt_test.go +++ /dev/null @@ -1,126 +0,0 @@ -/* -Copyright 2014 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 ovirt - -import ( - "io" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/cloudprovider" -) - -func TestOVirtCloudConfiguration(t *testing.T) { - config1 := (io.Reader)(nil) - - _, err1 := cloudprovider.GetCloudProvider("ovirt", config1) - if err1 == nil { - t.Fatalf("An error is expected when the configuration is missing") - } - - config2 := strings.NewReader("") - - _, err2 := cloudprovider.GetCloudProvider("ovirt", config2) - if err2 == nil { - t.Fatalf("An error is expected when the configuration is empty") - } - - config3 := strings.NewReader(` -[connection] - `) - - _, err3 := cloudprovider.GetCloudProvider("ovirt", config3) - if err3 == nil { - t.Fatalf("An error is expected when the uri is missing") - } - - config4 := strings.NewReader(` -[connection] -uri = https://localhost:8443/ovirt-engine/api -`) - - _, err4 := cloudprovider.GetCloudProvider("ovirt", config4) - if err4 != nil { - t.Fatalf("Unexpected error creating the provider: %s", err4) - } -} - -func TestOVirtCloudXmlParsing(t *testing.T) { - body1 := (io.Reader)(nil) - - _, err1 := getInstancesFromXml(body1) - if err1 == nil { - t.Fatalf("An error is expected when body is missing") - } - - body2 := strings.NewReader("") - - _, err2 := getInstancesFromXml(body2) - if err2 == nil { - t.Fatalf("An error is expected when body is empty") - } - - body3 := strings.NewReader(` - - - -`) - - instances3, err3 := getInstancesFromXml(body3) - if err3 != nil { - t.Fatalf("Unexpected error listing instances: %s", err3) - } - if len(instances3) > 0 { - t.Fatalf("Unexpected number of instance(s): %d", len(instances3)) - } - - body4 := strings.NewReader(` - - - Up - host1 - - - - - - Up - - - Down - host2 - - - Up - host3 - - -`) - - instances4, err4 := getInstancesFromXml(body4) - if err4 != nil { - t.Fatalf("Unexpected error listing instances: %s", err4) - } - if len(instances4) != 2 { - t.Fatalf("Unexpected number of instance(s): %d", len(instances4)) - } - - names := instances4.ListSortedNames() - if names[0] != "host1" || names[1] != "host3" { - t.Fatalf("Unexpected instance(s): %s", instances4) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/providers.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/providers.go deleted file mode 100644 index 6664e8903..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/providers.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2015 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 cloudprovider - -import ( - // Cloud providers - _ "k8s.io/kubernetes/pkg/cloudprovider/providers/aws" - _ "k8s.io/kubernetes/pkg/cloudprovider/providers/gce" - _ "k8s.io/kubernetes/pkg/cloudprovider/providers/mesos" - _ "k8s.io/kubernetes/pkg/cloudprovider/providers/openstack" - _ "k8s.io/kubernetes/pkg/cloudprovider/providers/ovirt" - _ "k8s.io/kubernetes/pkg/cloudprovider/providers/rackspace" -) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/rackspace/MAINTAINERS.md b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/rackspace/MAINTAINERS.md deleted file mode 100644 index 4c72c1a0a..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/rackspace/MAINTAINERS.md +++ /dev/null @@ -1,6 +0,0 @@ -# Maintainers - -* [Thom May](https://github.com/thommay) - - -[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/pkg/cloudprovider/providers/rackspace/MAINTAINERS.md?pixel)]() diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/rackspace/rackspace.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/rackspace/rackspace.go deleted file mode 100644 index fbc0d7bff..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/rackspace/rackspace.go +++ /dev/null @@ -1,393 +0,0 @@ -/* -Copyright 2014 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 rackspace - -import ( - "errors" - "fmt" - "io" - "net" - "regexp" - "time" - - "github.com/rackspace/gophercloud" - osservers "github.com/rackspace/gophercloud/openstack/compute/v2/servers" - "github.com/rackspace/gophercloud/pagination" - "github.com/rackspace/gophercloud/rackspace" - "github.com/rackspace/gophercloud/rackspace/compute/v2/servers" - "github.com/scalingdata/gcfg" - - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/cloudprovider" -) - -const ProviderName = "rackspace" - -var ErrNotFound = errors.New("Failed to find object") -var ErrMultipleResults = errors.New("Multiple results where only one expected") -var ErrNoAddressFound = errors.New("No address found for host") -var ErrAttrNotFound = errors.New("Expected attribute not found") - -// encoding.TextUnmarshaler interface for time.Duration -type MyDuration struct { - time.Duration -} - -func (d *MyDuration) UnmarshalText(text []byte) error { - res, err := time.ParseDuration(string(text)) - if err != nil { - return err - } - d.Duration = res - return nil -} - -type LoadBalancerOpts struct { - SubnetId string `gcfg:"subnet-id"` // required - CreateMonitor bool `gcfg:"create-monitor"` - MonitorDelay MyDuration `gcfg:"monitor-delay"` - MonitorTimeout MyDuration `gcfg:"monitor-timeout"` - MonitorMaxRetries uint `gcfg:"monitor-max-retries"` -} - -// Rackspace is an implementation of cloud provider Interface for Rackspace. -type Rackspace struct { - provider *gophercloud.ProviderClient - region string - lbOpts LoadBalancerOpts -} - -type Config struct { - Global struct { - AuthUrl string `gcfg:"auth-url"` - Username string - UserId string `gcfg:"user-id"` - Password string - ApiKey string `gcfg:"api-key"` - TenantId string `gcfg:"tenant-id"` - TenantName string `gcfg:"tenant-name"` - DomainId string `gcfg:"domain-id"` - DomainName string `gcfg:"domain-name"` - Region string - } - LoadBalancer LoadBalancerOpts -} - -func init() { - cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) { - cfg, err := readConfig(config) - if err != nil { - return nil, err - } - return newRackspace(cfg) - }) -} - -func (cfg Config) toAuthOptions() gophercloud.AuthOptions { - return gophercloud.AuthOptions{ - IdentityEndpoint: cfg.Global.AuthUrl, - Username: cfg.Global.Username, - UserID: cfg.Global.UserId, - Password: cfg.Global.Password, - APIKey: cfg.Global.ApiKey, - TenantID: cfg.Global.TenantId, - TenantName: cfg.Global.TenantName, - - // Persistent service, so we need to be able to renew tokens - AllowReauth: true, - } -} - -func readConfig(config io.Reader) (Config, error) { - if config == nil { - err := fmt.Errorf("no Rackspace cloud provider config file given") - return Config{}, err - } - - var cfg Config - err := gcfg.ReadInto(&cfg, config) - return cfg, err -} - -func newRackspace(cfg Config) (*Rackspace, error) { - provider, err := rackspace.AuthenticatedClient(cfg.toAuthOptions()) - if err != nil { - return nil, err - } - - os := Rackspace{ - provider: provider, - region: cfg.Global.Region, - lbOpts: cfg.LoadBalancer, - } - return &os, nil -} - -type Instances struct { - compute *gophercloud.ServiceClient -} - -// Instances returns an implementation of Instances for Rackspace. -func (os *Rackspace) Instances() (cloudprovider.Instances, bool) { - glog.V(2).Info("rackspace.Instances() called") - - compute, err := rackspace.NewComputeV2(os.provider, gophercloud.EndpointOpts{ - Region: os.region, - }) - if err != nil { - glog.Warningf("Failed to find compute endpoint: %v", err) - return nil, false - } - glog.V(1).Info("Claiming to support Instances") - - return &Instances{compute}, true -} - -func (i *Instances) List(name_filter string) ([]string, error) { - glog.V(2).Infof("rackspace List(%v) called", name_filter) - - opts := osservers.ListOpts{ - Name: name_filter, - Status: "ACTIVE", - } - pager := servers.List(i.compute, opts) - - ret := make([]string, 0) - err := pager.EachPage(func(page pagination.Page) (bool, error) { - sList, err := servers.ExtractServers(page) - if err != nil { - return false, err - } - for _, server := range sList { - ret = append(ret, server.Name) - } - return true, nil - }) - if err != nil { - return nil, err - } - - glog.V(2).Infof("Found %v entries: %v", len(ret), ret) - - return ret, nil -} - -func serverHasAddress(srv osservers.Server, ip string) bool { - if ip == firstAddr(srv.Addresses["private"]) { - return true - } - if ip == firstAddr(srv.Addresses["public"]) { - return true - } - if ip == srv.AccessIPv4 { - return true - } - if ip == srv.AccessIPv6 { - return true - } - return false -} - -func getServerByAddress(client *gophercloud.ServiceClient, name string) (*osservers.Server, error) { - pager := servers.List(client, nil) - - serverList := make([]osservers.Server, 0, 1) - - err := pager.EachPage(func(page pagination.Page) (bool, error) { - s, err := servers.ExtractServers(page) - if err != nil { - return false, err - } - for _, v := range s { - if serverHasAddress(v, name) { - serverList = append(serverList, v) - } - } - if len(serverList) > 1 { - return false, ErrMultipleResults - } - return true, nil - }) - if err != nil { - return nil, err - } - - if len(serverList) == 0 { - return nil, ErrNotFound - } else if len(serverList) > 1 { - return nil, ErrMultipleResults - } - - return &serverList[0], nil -} - -func getServerByName(client *gophercloud.ServiceClient, name string) (*osservers.Server, error) { - if net.ParseIP(name) != nil { - // we're an IP, so we'll have to walk the full list of servers to - // figure out which one we are. - return getServerByAddress(client, name) - } - opts := osservers.ListOpts{ - Name: fmt.Sprintf("^%s$", regexp.QuoteMeta(name)), - Status: "ACTIVE", - } - pager := servers.List(client, opts) - - serverList := make([]osservers.Server, 0, 1) - - err := pager.EachPage(func(page pagination.Page) (bool, error) { - s, err := servers.ExtractServers(page) - if err != nil { - return false, err - } - serverList = append(serverList, s...) - if len(serverList) > 1 { - return false, ErrMultipleResults - } - return true, nil - }) - if err != nil { - return nil, err - } - - if len(serverList) == 0 { - return nil, ErrNotFound - } else if len(serverList) > 1 { - return nil, ErrMultipleResults - } - - return &serverList[0], nil -} - -func firstAddr(netblob interface{}) string { - // Run-time types for the win :( - list, ok := netblob.([]interface{}) - if !ok || len(list) < 1 { - return "" - } - props, ok := list[0].(map[string]interface{}) - if !ok { - return "" - } - tmp, ok := props["addr"] - if !ok { - return "" - } - addr, ok := tmp.(string) - if !ok { - return "" - } - return addr -} - -func getAddressByName(api *gophercloud.ServiceClient, name string) (string, error) { - srv, err := getServerByName(api, name) - if err != nil { - return "", err - } - - var s string - if s == "" { - s = firstAddr(srv.Addresses["private"]) - } - if s == "" { - s = firstAddr(srv.Addresses["public"]) - } - if s == "" { - s = srv.AccessIPv4 - } - if s == "" { - s = srv.AccessIPv6 - } - if s == "" { - return "", ErrNoAddressFound - } - return s, nil -} - -func (i *Instances) NodeAddresses(name string) ([]api.NodeAddress, error) { - glog.V(2).Infof("NodeAddresses(%v) called", name) - - ip, err := getAddressByName(i.compute, name) - if err != nil { - return nil, err - } - - glog.V(2).Infof("NodeAddresses(%v) => %v", name, ip) - - // net.ParseIP().String() is to maintain compatibility with the old code - return []api.NodeAddress{{Type: api.NodeLegacyHostIP, Address: net.ParseIP(ip).String()}}, nil -} - -// ExternalID returns the cloud provider ID of the specified instance (deprecated). -func (i *Instances) ExternalID(name string) (string, error) { - return "", fmt.Errorf("unimplemented") -} - -// InstanceID returns the cloud provider ID of the specified instance. -func (i *Instances) InstanceID(name string) (string, error) { - return "", nil -} - -// InstanceType returns the type of the specified instance. -func (i *Instances) InstanceType(name string) (string, error) { - return "", nil -} - -func (i *Instances) AddSSHKeyToAllInstances(user string, keyData []byte) error { - return errors.New("unimplemented") -} - -// Implementation of Instances.CurrentNodeName -func (i *Instances) CurrentNodeName(hostname string) (string, error) { - return hostname, nil -} - -func (os *Rackspace) Clusters() (cloudprovider.Clusters, bool) { - return nil, false -} - -// ProviderName returns the cloud provider ID. -func (os *Rackspace) ProviderName() string { - return ProviderName -} - -// ScrubDNS filters DNS settings for pods. -func (os *Rackspace) ScrubDNS(nameservers, searches []string) (nsOut, srchOut []string) { - return nameservers, searches -} - -func (os *Rackspace) LoadBalancer() (cloudprovider.LoadBalancer, bool) { - return nil, false -} - -func (os *Rackspace) Zones() (cloudprovider.Zones, bool) { - glog.V(1).Info("Claiming to support Zones") - - return os, true -} - -func (os *Rackspace) Routes() (cloudprovider.Routes, bool) { - return nil, false -} - -func (os *Rackspace) GetZone() (cloudprovider.Zone, error) { - glog.V(1).Infof("Current zone is %v", os.region) - - return cloudprovider.Zone{Region: os.region}, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/rackspace/rackspace_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/rackspace/rackspace_test.go deleted file mode 100644 index bc3738310..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/cloudprovider/providers/rackspace/rackspace_test.go +++ /dev/null @@ -1,175 +0,0 @@ -/* -Copyright 2014 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 rackspace - -import ( - "os" - "strings" - "testing" - "time" - - "github.com/rackspace/gophercloud" -) - -func TestReadConfig(t *testing.T) { - _, err := readConfig(nil) - if err == nil { - t.Errorf("Should fail when no config is provided: %s", err) - } - - cfg, err := readConfig(strings.NewReader(` -[Global] -auth-url = http://auth.url -username = user -[LoadBalancer] -create-monitor = yes -monitor-delay = 1m -monitor-timeout = 30s -monitor-max-retries = 3 -`)) - if err != nil { - t.Fatalf("Should succeed when a valid config is provided: %s", err) - } - if cfg.Global.AuthUrl != "http://auth.url" { - t.Errorf("incorrect authurl: %s", cfg.Global.AuthUrl) - } - - if !cfg.LoadBalancer.CreateMonitor { - t.Errorf("incorrect lb.createmonitor: %t", cfg.LoadBalancer.CreateMonitor) - } - if cfg.LoadBalancer.MonitorDelay.Duration != 1*time.Minute { - t.Errorf("incorrect lb.monitordelay: %s", cfg.LoadBalancer.MonitorDelay) - } - if cfg.LoadBalancer.MonitorTimeout.Duration != 30*time.Second { - t.Errorf("incorrect lb.monitortimeout: %s", cfg.LoadBalancer.MonitorTimeout) - } - if cfg.LoadBalancer.MonitorMaxRetries != 3 { - t.Errorf("incorrect lb.monitormaxretries: %d", cfg.LoadBalancer.MonitorMaxRetries) - } -} - -func TestToAuthOptions(t *testing.T) { - cfg := Config{} - cfg.Global.Username = "user" - // etc. - - ao := cfg.toAuthOptions() - - if !ao.AllowReauth { - t.Errorf("Will need to be able to reauthenticate") - } - if ao.Username != cfg.Global.Username { - t.Errorf("Username %s != %s", ao.Username, cfg.Global.Username) - } -} - -// This allows acceptance testing against an existing Rackspace -// install, using the standard OS_* Rackspace client environment -// variables. -// FIXME: it would be better to hermetically test against canned JSON -// requests/responses. -func configFromEnv() (cfg Config, ok bool) { - cfg.Global.AuthUrl = os.Getenv("OS_AUTH_URL") - - cfg.Global.TenantId = os.Getenv("OS_TENANT_ID") - // Rax/nova _insists_ that we don't specify both tenant ID and name - if cfg.Global.TenantId == "" { - cfg.Global.TenantName = os.Getenv("OS_TENANT_NAME") - } - - cfg.Global.Username = os.Getenv("OS_USERNAME") - cfg.Global.Password = os.Getenv("OS_PASSWORD") - cfg.Global.ApiKey = os.Getenv("OS_API_KEY") - cfg.Global.Region = os.Getenv("OS_REGION_NAME") - cfg.Global.DomainId = os.Getenv("OS_DOMAIN_ID") - cfg.Global.DomainName = os.Getenv("OS_DOMAIN_NAME") - - ok = (cfg.Global.AuthUrl != "" && - cfg.Global.Username != "" && - (cfg.Global.Password != "" || cfg.Global.ApiKey != "") && - (cfg.Global.TenantId != "" || cfg.Global.TenantName != "" || - cfg.Global.DomainId != "" || cfg.Global.DomainName != "")) - - return -} - -func TestNewRackspace(t *testing.T) { - cfg, ok := configFromEnv() - if !ok { - t.Skipf("No config found in environment") - } - - _, err := newRackspace(cfg) - if err != nil { - t.Fatalf("Failed to construct/authenticate Rackspace: %s", err) - } -} - -func TestInstances(t *testing.T) { - cfg, ok := configFromEnv() - if !ok { - t.Skipf("No config found in environment") - } - - os, err := newRackspace(cfg) - if err != nil { - t.Fatalf("Failed to construct/authenticate Rackspace: %s", err) - } - - i, ok := os.Instances() - if !ok { - t.Fatalf("Instances() returned false") - } - - srvs, err := i.List(".") - if err != nil { - t.Fatalf("Instances.List() failed: %s", err) - } - if len(srvs) == 0 { - t.Fatalf("Instances.List() returned zero servers") - } - t.Logf("Found servers (%d): %s\n", len(srvs), srvs) - - addrs, err := i.NodeAddresses(srvs[0]) - if err != nil { - t.Fatalf("Instances.NodeAddresses(%s) failed: %s", srvs[0], err) - } - t.Logf("Found NodeAddresses(%s) = %s\n", srvs[0], addrs) -} - -func TestZones(t *testing.T) { - os := Rackspace{ - provider: &gophercloud.ProviderClient{ - IdentityBase: "http://auth.url/", - }, - region: "myRegion", - } - - z, ok := os.Zones() - if !ok { - t.Fatalf("Zones() returned false") - } - - zone, err := z.GetZone() - if err != nil { - t.Fatalf("GetZone() returned error: %s", err) - } - - if zone.Region != "myRegion" { - t.Fatalf("GetZone() returned wrong region (%s)", zone.Region) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/OWNERS b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/OWNERS new file mode 100644 index 000000000..35859cd8c --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/OWNERS @@ -0,0 +1,5 @@ +assignees: + - bprashanth + - davidopp + - derekwaynecarr + - mikedanese diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/controller_utils.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/controller_utils.go new file mode 100644 index 000000000..fc377134d --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/controller_utils.go @@ -0,0 +1,647 @@ +/* +Copyright 2014 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 controller + +import ( + "fmt" + "sync" + "sync/atomic" + "time" + + "github.com/golang/glog" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/validation" + "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/client/cache" + clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + "k8s.io/kubernetes/pkg/client/record" + "k8s.io/kubernetes/pkg/controller/framework" + "k8s.io/kubernetes/pkg/labels" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util" + "k8s.io/kubernetes/pkg/util/integer" + "k8s.io/kubernetes/pkg/util/sets" +) + +const ( + CreatedByAnnotation = "kubernetes.io/created-by" + + // If a watch drops a delete event for a pod, it'll take this long + // before a dormant controller waiting for those packets is woken up anyway. It is + // specifically targeted at the case where some problem prevents an update + // of expectations, without it the controller could stay asleep forever. This should + // be set based on the expected latency of watch events. + // + // Currently a controller can service (create *and* observe the watch events for said + // creation) about 10 pods a second, so it takes about 1 min to service + // 500 pods. Just creation is limited to 20qps, and watching happens with ~10-30s + // latency/pod at the scale of 3000 pods over 100 nodes. + ExpectationsTimeout = 5 * time.Minute +) + +var ( + KeyFunc = framework.DeletionHandlingMetaNamespaceKeyFunc +) + +type ResyncPeriodFunc func() time.Duration + +// Returns 0 for resyncPeriod in case resyncing is not needed. +func NoResyncPeriodFunc() time.Duration { + return 0 +} + +// StaticResyncPeriodFunc returns the resync period specified +func StaticResyncPeriodFunc(resyncPeriod time.Duration) ResyncPeriodFunc { + return func() time.Duration { + return resyncPeriod + } +} + +// Expectations are a way for controllers to tell the controller manager what they expect. eg: +// ControllerExpectations: { +// controller1: expects 2 adds in 2 minutes +// controller2: expects 2 dels in 2 minutes +// controller3: expects -1 adds in 2 minutes => controller3's expectations have already been met +// } +// +// Implementation: +// ControlleeExpectation = pair of atomic counters to track controllee's creation/deletion +// ControllerExpectationsStore = TTLStore + a ControlleeExpectation per controller +// +// * Once set expectations can only be lowered +// * A controller isn't synced till its expectations are either fulfilled, or expire +// * Controllers that don't set expectations will get woken up for every matching controllee + +// ExpKeyFunc to parse out the key from a ControlleeExpectation +var ExpKeyFunc = func(obj interface{}) (string, error) { + if e, ok := obj.(*ControlleeExpectations); ok { + return e.key, nil + } + return "", fmt.Errorf("Could not find key for obj %#v", obj) +} + +// ControllerExpectationsInterface is an interface that allows users to set and wait on expectations. +// Only abstracted out for testing. +// Warning: if using KeyFunc it is not safe to use a single ControllerExpectationsInterface with different +// types of controllers, because the keys might conflict across types. +type ControllerExpectationsInterface interface { + GetExpectations(controllerKey string) (*ControlleeExpectations, bool, error) + SatisfiedExpectations(controllerKey string) bool + DeleteExpectations(controllerKey string) + SetExpectations(controllerKey string, add, del int) error + ExpectCreations(controllerKey string, adds int) error + ExpectDeletions(controllerKey string, dels int) error + CreationObserved(controllerKey string) + DeletionObserved(controllerKey string) + RaiseExpectations(controllerKey string, add, del int) + LowerExpectations(controllerKey string, add, del int) +} + +// ControllerExpectations is a cache mapping controllers to what they expect to see before being woken up for a sync. +type ControllerExpectations struct { + cache.Store +} + +// GetExpectations returns the ControlleeExpectations of the given controller. +func (r *ControllerExpectations) GetExpectations(controllerKey string) (*ControlleeExpectations, bool, error) { + if exp, exists, err := r.GetByKey(controllerKey); err == nil && exists { + return exp.(*ControlleeExpectations), true, nil + } else { + return nil, false, err + } +} + +// DeleteExpectations deletes the expectations of the given controller from the TTLStore. +func (r *ControllerExpectations) DeleteExpectations(controllerKey string) { + if exp, exists, err := r.GetByKey(controllerKey); err == nil && exists { + if err := r.Delete(exp); err != nil { + glog.V(2).Infof("Error deleting expectations for controller %v: %v", controllerKey, err) + } + } +} + +// SatisfiedExpectations returns true if the required adds/dels for the given controller have been observed. +// Add/del counts are established by the controller at sync time, and updated as controllees are observed by the controller +// manager. +func (r *ControllerExpectations) SatisfiedExpectations(controllerKey string) bool { + if exp, exists, err := r.GetExpectations(controllerKey); exists { + if exp.Fulfilled() { + return true + } else if exp.isExpired() { + glog.V(4).Infof("Controller expectations expired %#v", exp) + return true + } else { + glog.V(4).Infof("Controller still waiting on expectations %#v", exp) + return false + } + } else if err != nil { + glog.V(2).Infof("Error encountered while checking expectations %#v, forcing sync", err) + } else { + // When a new controller is created, it doesn't have expectations. + // When it doesn't see expected watch events for > TTL, the expectations expire. + // - In this case it wakes up, creates/deletes controllees, and sets expectations again. + // When it has satisfied expectations and no controllees need to be created/destroyed > TTL, the expectations expire. + // - In this case it continues without setting expectations till it needs to create/delete controllees. + glog.V(4).Infof("Controller %v either never recorded expectations, or the ttl expired.", controllerKey) + } + // Trigger a sync if we either encountered and error (which shouldn't happen since we're + // getting from local store) or this controller hasn't established expectations. + return true +} + +// TODO: Extend ExpirationCache to support explicit expiration. +// TODO: Make this possible to disable in tests. +// TODO: Support injection of clock. +func (exp *ControlleeExpectations) isExpired() bool { + return util.RealClock{}.Since(exp.timestamp) > ExpectationsTimeout +} + +// SetExpectations registers new expectations for the given controller. Forgets existing expectations. +func (r *ControllerExpectations) SetExpectations(controllerKey string, add, del int) error { + exp := &ControlleeExpectations{add: int64(add), del: int64(del), key: controllerKey, timestamp: util.RealClock{}.Now()} + glog.V(4).Infof("Setting expectations %+v", exp) + return r.Add(exp) +} + +func (r *ControllerExpectations) ExpectCreations(controllerKey string, adds int) error { + return r.SetExpectations(controllerKey, adds, 0) +} + +func (r *ControllerExpectations) ExpectDeletions(controllerKey string, dels int) error { + return r.SetExpectations(controllerKey, 0, dels) +} + +// Decrements the expectation counts of the given controller. +func (r *ControllerExpectations) LowerExpectations(controllerKey string, add, del int) { + if exp, exists, err := r.GetExpectations(controllerKey); err == nil && exists { + exp.Add(int64(-add), int64(-del)) + // The expectations might've been modified since the update on the previous line. + glog.V(4).Infof("Lowered expectations %+v", exp) + } +} + +// Increments the expectation counts of the given controller. +func (r *ControllerExpectations) RaiseExpectations(controllerKey string, add, del int) { + if exp, exists, err := r.GetExpectations(controllerKey); err == nil && exists { + exp.Add(int64(add), int64(del)) + // The expectations might've been modified since the update on the previous line. + glog.V(4).Infof("Raised expectations %+v", exp) + } +} + +// CreationObserved atomically decrements the `add` expecation count of the given controller. +func (r *ControllerExpectations) CreationObserved(controllerKey string) { + r.LowerExpectations(controllerKey, 1, 0) +} + +// DeletionObserved atomically decrements the `del` expectation count of the given controller. +func (r *ControllerExpectations) DeletionObserved(controllerKey string) { + r.LowerExpectations(controllerKey, 0, 1) +} + +// Expectations are either fulfilled, or expire naturally. +type Expectations interface { + Fulfilled() bool +} + +// ControlleeExpectations track controllee creates/deletes. +type ControlleeExpectations struct { + add int64 + del int64 + key string + timestamp time.Time +} + +// Add increments the add and del counters. +func (e *ControlleeExpectations) Add(add, del int64) { + atomic.AddInt64(&e.add, add) + atomic.AddInt64(&e.del, del) +} + +// Fulfilled returns true if this expectation has been fulfilled. +func (e *ControlleeExpectations) Fulfilled() bool { + // TODO: think about why this line being atomic doesn't matter + return atomic.LoadInt64(&e.add) <= 0 && atomic.LoadInt64(&e.del) <= 0 +} + +// GetExpectations returns the add and del expectations of the controllee. +func (e *ControlleeExpectations) GetExpectations() (int64, int64) { + return atomic.LoadInt64(&e.add), atomic.LoadInt64(&e.del) +} + +// NewControllerExpectations returns a store for ControllerExpectations. +func NewControllerExpectations() *ControllerExpectations { + return &ControllerExpectations{cache.NewStore(ExpKeyFunc)} +} + +// UIDSetKeyFunc to parse out the key from a UIDSet. +var UIDSetKeyFunc = func(obj interface{}) (string, error) { + if u, ok := obj.(*UIDSet); ok { + return u.key, nil + } + return "", fmt.Errorf("Could not find key for obj %#v", obj) +} + +// UIDSet holds a key and a set of UIDs. Used by the +// UIDTrackingControllerExpectations to remember which UID it has seen/still +// waiting for. +type UIDSet struct { + sets.String + key string +} + +// UIDTrackingControllerExpectations tracks the UID of the pods it deletes. +// This cache is needed over plain old expectations to safely handle graceful +// deletion. The desired behavior is to treat an update that sets the +// DeletionTimestamp on an object as a delete. To do so consistenly, one needs +// to remember the expected deletes so they aren't double counted. +// TODO: Track creates as well (#22599) +type UIDTrackingControllerExpectations struct { + ControllerExpectationsInterface + // TODO: There is a much nicer way to do this that involves a single store, + // a lock per entry, and a ControlleeExpectationsInterface type. + uidStoreLock sync.Mutex + // Store used for the UIDs associated with any expectation tracked via the + // ControllerExpectationsInterface. + uidStore cache.Store +} + +// GetUIDs is a convenience method to avoid exposing the set of expected uids. +// The returned set is not thread safe, all modifications must be made holding +// the uidStoreLock. +func (u *UIDTrackingControllerExpectations) GetUIDs(controllerKey string) sets.String { + if uid, exists, err := u.uidStore.GetByKey(controllerKey); err == nil && exists { + return uid.(*UIDSet).String + } + return nil +} + +// ExpectDeletions records expectations for the given deleteKeys, against the given controller. +func (u *UIDTrackingControllerExpectations) ExpectDeletions(rcKey string, deletedKeys []string) error { + u.uidStoreLock.Lock() + defer u.uidStoreLock.Unlock() + + if existing := u.GetUIDs(rcKey); existing != nil && existing.Len() != 0 { + glog.Errorf("Clobbering existing delete keys: %+v", existing) + } + expectedUIDs := sets.NewString() + for _, k := range deletedKeys { + expectedUIDs.Insert(k) + } + glog.V(4).Infof("Controller %v waiting on deletions for: %+v", rcKey, deletedKeys) + if err := u.uidStore.Add(&UIDSet{expectedUIDs, rcKey}); err != nil { + return err + } + return u.ControllerExpectationsInterface.ExpectDeletions(rcKey, expectedUIDs.Len()) +} + +// DeletionObserved records the given deleteKey as a deletion, for the given rc. +func (u *UIDTrackingControllerExpectations) DeletionObserved(rcKey, deleteKey string) { + u.uidStoreLock.Lock() + defer u.uidStoreLock.Unlock() + + uids := u.GetUIDs(rcKey) + if uids != nil && uids.Has(deleteKey) { + glog.V(4).Infof("Controller %v received delete for pod %v", rcKey, deleteKey) + u.ControllerExpectationsInterface.DeletionObserved(rcKey) + uids.Delete(deleteKey) + } +} + +// DeleteExpectations deletes the UID set and invokes DeleteExpectations on the +// underlying ControllerExpectationsInterface. +func (u *UIDTrackingControllerExpectations) DeleteExpectations(rcKey string) { + u.uidStoreLock.Lock() + defer u.uidStoreLock.Unlock() + + u.ControllerExpectationsInterface.DeleteExpectations(rcKey) + if uidExp, exists, err := u.uidStore.GetByKey(rcKey); err == nil && exists { + if err := u.uidStore.Delete(uidExp); err != nil { + glog.V(2).Infof("Error deleting uid expectations for controller %v: %v", rcKey, err) + } + } +} + +// NewUIDTrackingControllerExpectations returns a wrapper around +// ControllerExpectations that is aware of deleteKeys. +func NewUIDTrackingControllerExpectations(ce ControllerExpectationsInterface) *UIDTrackingControllerExpectations { + return &UIDTrackingControllerExpectations{ControllerExpectationsInterface: ce, uidStore: cache.NewStore(UIDSetKeyFunc)} +} + +// PodControlInterface is an interface that knows how to add or delete pods +// created as an interface to allow testing. +type PodControlInterface interface { + // CreatePods creates new pods according to the spec. + CreatePods(namespace string, template *api.PodTemplateSpec, object runtime.Object) error + // CreatePodsOnNode creates a new pod accorting to the spec on the specified node. + CreatePodsOnNode(nodeName, namespace string, template *api.PodTemplateSpec, object runtime.Object) error + // DeletePod deletes the pod identified by podID. + DeletePod(namespace string, podID string, object runtime.Object) error +} + +// RealPodControl is the default implementation of PodControlInterface. +type RealPodControl struct { + KubeClient clientset.Interface + Recorder record.EventRecorder +} + +var _ PodControlInterface = &RealPodControl{} + +func getPodsLabelSet(template *api.PodTemplateSpec) labels.Set { + desiredLabels := make(labels.Set) + for k, v := range template.Labels { + desiredLabels[k] = v + } + return desiredLabels +} + +func getPodsAnnotationSet(template *api.PodTemplateSpec, object runtime.Object) (labels.Set, error) { + desiredAnnotations := make(labels.Set) + for k, v := range template.Annotations { + desiredAnnotations[k] = v + } + createdByRef, err := api.GetReference(object) + if err != nil { + return desiredAnnotations, fmt.Errorf("unable to get controller reference: %v", err) + } + + // TODO: this code was not safe previously - as soon as new code came along that switched to v2, old clients + // would be broken upon reading it. This is explicitly hardcoded to v1 to guarantee predictable deployment. + // We need to consistently handle this case of annotation versioning. + codec := api.Codecs.LegacyCodec(unversioned.GroupVersion{Group: api.GroupName, Version: "v1"}) + + createdByRefJson, err := runtime.Encode(codec, &api.SerializedReference{ + Reference: *createdByRef, + }) + if err != nil { + return desiredAnnotations, fmt.Errorf("unable to serialize controller reference: %v", err) + } + desiredAnnotations[CreatedByAnnotation] = string(createdByRefJson) + return desiredAnnotations, nil +} + +func getPodsPrefix(controllerName string) string { + // use the dash (if the name isn't too long) to make the pod name a bit prettier + prefix := fmt.Sprintf("%s-", controllerName) + if ok, _ := validation.ValidatePodName(prefix, true); !ok { + prefix = controllerName + } + return prefix +} + +func (r RealPodControl) CreatePods(namespace string, template *api.PodTemplateSpec, object runtime.Object) error { + return r.createPods("", namespace, template, object) +} + +func (r RealPodControl) CreatePodsOnNode(nodeName, namespace string, template *api.PodTemplateSpec, object runtime.Object) error { + return r.createPods(nodeName, namespace, template, object) +} + +func (r RealPodControl) createPods(nodeName, namespace string, template *api.PodTemplateSpec, object runtime.Object) error { + desiredLabels := getPodsLabelSet(template) + desiredAnnotations, err := getPodsAnnotationSet(template, object) + if err != nil { + return err + } + meta, err := api.ObjectMetaFor(object) + if err != nil { + return fmt.Errorf("object does not have ObjectMeta, %v", err) + } + prefix := getPodsPrefix(meta.Name) + + pod := &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Labels: desiredLabels, + Annotations: desiredAnnotations, + GenerateName: prefix, + }, + } + if err := api.Scheme.Convert(&template.Spec, &pod.Spec); err != nil { + return fmt.Errorf("unable to convert pod template: %v", err) + } + if len(nodeName) != 0 { + pod.Spec.NodeName = nodeName + } + if labels.Set(pod.Labels).AsSelector().Empty() { + return fmt.Errorf("unable to create pods, no labels") + } + if newPod, err := r.KubeClient.Core().Pods(namespace).Create(pod); err != nil { + r.Recorder.Eventf(object, api.EventTypeWarning, "FailedCreate", "Error creating: %v", err) + return fmt.Errorf("unable to create pods: %v", err) + } else { + glog.V(4).Infof("Controller %v created pod %v", meta.Name, newPod.Name) + r.Recorder.Eventf(object, api.EventTypeNormal, "SuccessfulCreate", "Created pod: %v", newPod.Name) + } + return nil +} + +func (r RealPodControl) DeletePod(namespace string, podID string, object runtime.Object) error { + meta, err := api.ObjectMetaFor(object) + if err != nil { + return fmt.Errorf("object does not have ObjectMeta, %v", err) + } + if err := r.KubeClient.Core().Pods(namespace).Delete(podID, nil); err != nil { + r.Recorder.Eventf(object, api.EventTypeWarning, "FailedDelete", "Error deleting: %v", err) + return fmt.Errorf("unable to delete pods: %v", err) + } else { + glog.V(4).Infof("Controller %v deleted pod %v", meta.Name, podID) + r.Recorder.Eventf(object, api.EventTypeNormal, "SuccessfulDelete", "Deleted pod: %v", podID) + } + return nil +} + +type FakePodControl struct { + sync.Mutex + Templates []api.PodTemplateSpec + DeletePodName []string + Err error +} + +var _ PodControlInterface = &FakePodControl{} + +func (f *FakePodControl) CreatePods(namespace string, spec *api.PodTemplateSpec, object runtime.Object) error { + f.Lock() + defer f.Unlock() + if f.Err != nil { + return f.Err + } + f.Templates = append(f.Templates, *spec) + return nil +} + +func (f *FakePodControl) CreatePodsOnNode(nodeName, namespace string, template *api.PodTemplateSpec, object runtime.Object) error { + f.Lock() + defer f.Unlock() + if f.Err != nil { + return f.Err + } + f.Templates = append(f.Templates, *template) + return nil +} + +func (f *FakePodControl) DeletePod(namespace string, podID string, object runtime.Object) error { + f.Lock() + defer f.Unlock() + if f.Err != nil { + return f.Err + } + f.DeletePodName = append(f.DeletePodName, podID) + return nil +} + +func (f *FakePodControl) Clear() { + f.Lock() + defer f.Unlock() + f.DeletePodName = []string{} + f.Templates = []api.PodTemplateSpec{} +} + +// ActivePods type allows custom sorting of pods so a controller can pick the best ones to delete. +type ActivePods []*api.Pod + +func (s ActivePods) Len() int { return len(s) } +func (s ActivePods) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +func (s ActivePods) Less(i, j int) bool { + // 1. Unassigned < assigned + // If only one of the pods is unassigned, the unassigned one is smaller + if s[i].Spec.NodeName != s[j].Spec.NodeName && (len(s[i].Spec.NodeName) == 0 || len(s[j].Spec.NodeName) == 0) { + return len(s[i].Spec.NodeName) == 0 + } + // 2. PodPending < PodUnknown < PodRunning + m := map[api.PodPhase]int{api.PodPending: 0, api.PodUnknown: 1, api.PodRunning: 2} + if m[s[i].Status.Phase] != m[s[j].Status.Phase] { + return m[s[i].Status.Phase] < m[s[j].Status.Phase] + } + // 3. Not ready < ready + // If only one of the pods is not ready, the not ready one is smaller + if api.IsPodReady(s[i]) != api.IsPodReady(s[j]) { + return !api.IsPodReady(s[i]) + } + // TODO: take availability into account when we push minReadySeconds information from deployment into pods, + // see https://github.com/kubernetes/kubernetes/issues/22065 + // 4. Been ready for empty time < less time < more time + // If both pods are ready, the latest ready one is smaller + if api.IsPodReady(s[i]) && api.IsPodReady(s[j]) && !podReadyTime(s[i]).Equal(podReadyTime(s[j])) { + return afterOrZero(podReadyTime(s[i]), podReadyTime(s[j])) + } + // 5. Pods with containers with higher restart counts < lower restart counts + if maxContainerRestarts(s[i]) != maxContainerRestarts(s[j]) { + return maxContainerRestarts(s[i]) > maxContainerRestarts(s[j]) + } + // 6. Empty creation time pods < newer pods < older pods + if !s[i].CreationTimestamp.Equal(s[j].CreationTimestamp) { + return afterOrZero(s[i].CreationTimestamp, s[j].CreationTimestamp) + } + return false +} + +// afterOrZero checks if time t1 is after time t2; if one of them +// is zero, the zero time is seen as after non-zero time. +func afterOrZero(t1, t2 unversioned.Time) bool { + if t1.Time.IsZero() || t2.Time.IsZero() { + return t1.Time.IsZero() + } + return t1.After(t2.Time) +} + +func podReadyTime(pod *api.Pod) unversioned.Time { + if api.IsPodReady(pod) { + for _, c := range pod.Status.Conditions { + // we only care about pod ready conditions + if c.Type == api.PodReady && c.Status == api.ConditionTrue { + return c.LastTransitionTime + } + } + } + return unversioned.Time{} +} + +func maxContainerRestarts(pod *api.Pod) int { + maxRestarts := 0 + for _, c := range pod.Status.ContainerStatuses { + maxRestarts = integer.IntMax(maxRestarts, c.RestartCount) + } + return maxRestarts +} + +// FilterActivePods returns pods that have not terminated. +func FilterActivePods(pods []api.Pod) []*api.Pod { + var result []*api.Pod + for i := range pods { + p := pods[i] + if IsPodActive(p) { + result = append(result, &p) + } else { + glog.V(4).Infof("Ignoring inactive pod %v/%v in state %v, deletion time %v", + p.Namespace, p.Name, p.Status.Phase, p.DeletionTimestamp) + } + } + return result +} + +func IsPodActive(p api.Pod) bool { + return api.PodSucceeded != p.Status.Phase && + api.PodFailed != p.Status.Phase && + p.DeletionTimestamp == nil +} + +// FilterActiveReplicaSets returns replica sets that have (or at least ought to have) pods. +func FilterActiveReplicaSets(replicaSets []*extensions.ReplicaSet) []*extensions.ReplicaSet { + active := []*extensions.ReplicaSet{} + for i := range replicaSets { + if replicaSets[i].Spec.Replicas > 0 { + active = append(active, replicaSets[i]) + } + } + return active +} + +// PodKey returns a key unique to the given pod within a cluster. +// It's used so we consistently use the same key scheme in this module. +// It does exactly what cache.MetaNamespaceKeyFunc would have done +// expcept there's not possibility for error since we know the exact type. +func PodKey(pod *api.Pod) string { + return fmt.Sprintf("%v/%v", pod.Namespace, pod.Name) +} + +// ControllersByCreationTimestamp sorts a list of ReplicationControllers by creation timestamp, using their names as a tie breaker. +type ControllersByCreationTimestamp []*api.ReplicationController + +func (o ControllersByCreationTimestamp) Len() int { return len(o) } +func (o ControllersByCreationTimestamp) Swap(i, j int) { o[i], o[j] = o[j], o[i] } + +func (o ControllersByCreationTimestamp) Less(i, j int) bool { + if o[i].CreationTimestamp.Equal(o[j].CreationTimestamp) { + return o[i].Name < o[j].Name + } + return o[i].CreationTimestamp.Before(o[j].CreationTimestamp) +} + +// ReplicaSetsByCreationTimestamp sorts a list of ReplicationSets by creation timestamp, using their names as a tie breaker. +type ReplicaSetsByCreationTimestamp []*extensions.ReplicaSet + +func (o ReplicaSetsByCreationTimestamp) Len() int { return len(o) } +func (o ReplicaSetsByCreationTimestamp) Swap(i, j int) { o[i], o[j] = o[j], o[i] } + +func (o ReplicaSetsByCreationTimestamp) Less(i, j int) bool { + if o[i].CreationTimestamp.Equal(o[j].CreationTimestamp) { + return o[i].Name < o[j].Name + } + return o[i].CreationTimestamp.Before(o[j].CreationTimestamp) +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/chown/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/doc.go similarity index 84% rename from Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/chown/doc.go rename to Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/doc.go index 3c41f5ed3..1e310b466 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/chown/doc.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/doc.go @@ -14,5 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package chown provides utilities to chown a path -package chown +// Package controller contains code for controllers (like the replication +// controller). +package controller diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/framework/controller_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/framework/controller_test.go deleted file mode 100644 index dbede3423..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/framework/controller_test.go +++ /dev/null @@ -1,404 +0,0 @@ -/* -Copyright 2015 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 framework_test - -import ( - "fmt" - "math/rand" - "sync" - "testing" - "time" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/cache" - "k8s.io/kubernetes/pkg/controller/framework" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/sets" - - "github.com/google/gofuzz" -) - -func Example() { - // source simulates an apiserver object endpoint. - source := framework.NewFakeControllerSource() - - // This will hold the downstream state, as we know it. - downstream := cache.NewStore(framework.DeletionHandlingMetaNamespaceKeyFunc) - - // This will hold incoming changes. Note how we pass downstream in as a - // KeyLister, that way resync operations will result in the correct set - // of update/delete deltas. - fifo := cache.NewDeltaFIFO(cache.MetaNamespaceKeyFunc, nil, downstream) - - // Let's do threadsafe output to get predictable test results. - deletionCounter := make(chan string, 1000) - - cfg := &framework.Config{ - Queue: fifo, - ListerWatcher: source, - ObjectType: &api.Pod{}, - FullResyncPeriod: time.Millisecond * 100, - RetryOnError: false, - - // Let's implement a simple controller that just deletes - // everything that comes in. - Process: func(obj interface{}) error { - // Obj is from the Pop method of the Queue we make above. - newest := obj.(cache.Deltas).Newest() - - if newest.Type != cache.Deleted { - // Update our downstream store. - err := downstream.Add(newest.Object) - if err != nil { - return err - } - - // Delete this object. - source.Delete(newest.Object.(runtime.Object)) - } else { - // Update our downstream store. - err := downstream.Delete(newest.Object) - if err != nil { - return err - } - - // fifo's KeyOf is easiest, because it handles - // DeletedFinalStateUnknown markers. - key, err := fifo.KeyOf(newest.Object) - if err != nil { - return err - } - - // Report this deletion. - deletionCounter <- key - } - return nil - }, - } - - // Create the controller and run it until we close stop. - stop := make(chan struct{}) - defer close(stop) - go framework.New(cfg).Run(stop) - - // Let's add a few objects to the source. - testIDs := []string{"a-hello", "b-controller", "c-framework"} - for _, name := range testIDs { - // Note that these pods are not valid-- the fake source doesn't - // call validation or anything. - source.Add(&api.Pod{ObjectMeta: api.ObjectMeta{Name: name}}) - } - - // Let's wait for the controller to process the things we just added. - outputSet := sets.String{} - for i := 0; i < len(testIDs); i++ { - outputSet.Insert(<-deletionCounter) - } - - for _, key := range outputSet.List() { - fmt.Println(key) - } - // Output: - // a-hello - // b-controller - // c-framework -} - -func ExampleInformer() { - // source simulates an apiserver object endpoint. - source := framework.NewFakeControllerSource() - - // Let's do threadsafe output to get predictable test results. - deletionCounter := make(chan string, 1000) - - // Make a controller that immediately deletes anything added to it, and - // logs anything deleted. - _, controller := framework.NewInformer( - source, - &api.Pod{}, - time.Millisecond*100, - framework.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - source.Delete(obj.(runtime.Object)) - }, - DeleteFunc: func(obj interface{}) { - key, err := framework.DeletionHandlingMetaNamespaceKeyFunc(obj) - if err != nil { - key = "oops something went wrong with the key" - } - - // Report this deletion. - deletionCounter <- key - }, - }, - ) - - // Run the controller and run it until we close stop. - stop := make(chan struct{}) - defer close(stop) - go controller.Run(stop) - - // Let's add a few objects to the source. - testIDs := []string{"a-hello", "b-controller", "c-framework"} - for _, name := range testIDs { - // Note that these pods are not valid-- the fake source doesn't - // call validation or anything. - source.Add(&api.Pod{ObjectMeta: api.ObjectMeta{Name: name}}) - } - - // Let's wait for the controller to process the things we just added. - outputSet := sets.String{} - for i := 0; i < len(testIDs); i++ { - outputSet.Insert(<-deletionCounter) - } - - for _, key := range outputSet.List() { - fmt.Println(key) - } - // Output: - // a-hello - // b-controller - // c-framework -} - -func TestHammerController(t *testing.T) { - // This test executes a bunch of requests through the fake source and - // controller framework to make sure there's no locking/threading - // errors. If an error happens, it should hang forever or trigger the - // race detector. - - // source simulates an apiserver object endpoint. - source := framework.NewFakeControllerSource() - - // Let's do threadsafe output to get predictable test results. - outputSetLock := sync.Mutex{} - // map of key to operations done on the key - outputSet := map[string][]string{} - - recordFunc := func(eventType string, obj interface{}) { - key, err := framework.DeletionHandlingMetaNamespaceKeyFunc(obj) - if err != nil { - t.Errorf("something wrong with key: %v", err) - key = "oops something went wrong with the key" - } - - // Record some output when items are deleted. - outputSetLock.Lock() - defer outputSetLock.Unlock() - outputSet[key] = append(outputSet[key], eventType) - } - - // Make a controller which just logs all the changes it gets. - _, controller := framework.NewInformer( - source, - &api.Pod{}, - time.Millisecond*100, - framework.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { recordFunc("add", obj) }, - UpdateFunc: func(oldObj, newObj interface{}) { recordFunc("update", newObj) }, - DeleteFunc: func(obj interface{}) { recordFunc("delete", obj) }, - }, - ) - - if controller.HasSynced() { - t.Errorf("Expected HasSynced() to return false before we started the controller") - } - - // Run the controller and run it until we close stop. - stop := make(chan struct{}) - go controller.Run(stop) - - // Let's wait for the controller to do its initial sync - time.Sleep(100 * time.Millisecond) - if !controller.HasSynced() { - t.Errorf("Expected HasSynced() to return true after the initial sync") - } - - wg := sync.WaitGroup{} - const threads = 3 - wg.Add(threads) - for i := 0; i < threads; i++ { - go func() { - defer wg.Done() - // Let's add a few objects to the source. - currentNames := sets.String{} - rs := rand.NewSource(rand.Int63()) - f := fuzz.New().NilChance(.5).NumElements(0, 2).RandSource(rs) - r := rand.New(rs) // Mustn't use r and f concurrently! - for i := 0; i < 100; i++ { - var name string - var isNew bool - if currentNames.Len() == 0 || r.Intn(3) == 1 { - f.Fuzz(&name) - isNew = true - } else { - l := currentNames.List() - name = l[r.Intn(len(l))] - } - - pod := &api.Pod{} - f.Fuzz(pod) - pod.ObjectMeta.Name = name - pod.ObjectMeta.Namespace = "default" - // Add, update, or delete randomly. - // Note that these pods are not valid-- the fake source doesn't - // call validation or perform any other checking. - if isNew { - currentNames.Insert(name) - source.Add(pod) - continue - } - switch r.Intn(2) { - case 0: - currentNames.Insert(name) - source.Modify(pod) - case 1: - currentNames.Delete(name) - source.Delete(pod) - } - } - }() - } - wg.Wait() - - // Let's wait for the controller to finish processing the things we just added. - time.Sleep(100 * time.Millisecond) - close(stop) - - outputSetLock.Lock() - t.Logf("got: %#v", outputSet) -} - -func TestUpdate(t *testing.T) { - // This test is going to exercise the various paths that result in a - // call to update. - - // source simulates an apiserver object endpoint. - source := framework.NewFakeControllerSource() - - const ( - FROM = "from" - ADD_MISSED = "missed the add event" - TO = "to" - ) - - // These are the transitions we expect to see; because this is - // asynchronous, there are a lot of valid possibilities. - type pair struct{ from, to string } - allowedTransitions := map[pair]bool{ - pair{FROM, TO}: true, - pair{FROM, ADD_MISSED}: true, - pair{ADD_MISSED, TO}: true, - - // Because a resync can happen when we've already observed one - // of the above but before the item is deleted. - pair{TO, TO}: true, - // Because a resync could happen before we observe an update. - pair{FROM, FROM}: true, - } - - pod := func(name, check string, final bool) *api.Pod { - p := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: name, - Labels: map[string]string{"check": check}, - }, - } - if final { - p.Labels["final"] = "true" - } - return p - } - deletePod := func(p *api.Pod) bool { - return p.Labels["final"] == "true" - } - - tests := []func(string){ - func(name string) { - name = "a-" + name - source.Add(pod(name, FROM, false)) - source.Modify(pod(name, TO, true)) - }, - func(name string) { - name = "b-" + name - source.Add(pod(name, FROM, false)) - source.ModifyDropWatch(pod(name, TO, true)) - }, - func(name string) { - name = "c-" + name - source.AddDropWatch(pod(name, FROM, false)) - source.Modify(pod(name, ADD_MISSED, false)) - source.Modify(pod(name, TO, true)) - }, - func(name string) { - name = "d-" + name - source.Add(pod(name, FROM, true)) - }, - } - - const threads = 3 - - var testDoneWG sync.WaitGroup - testDoneWG.Add(threads * len(tests)) - - // Make a controller that deletes things once it observes an update. - // It calls Done() on the wait group on deletions so we can tell when - // everything we've added has been deleted. - _, controller := framework.NewInformer( - source, - &api.Pod{}, - time.Millisecond*1, - framework.ResourceEventHandlerFuncs{ - UpdateFunc: func(oldObj, newObj interface{}) { - o, n := oldObj.(*api.Pod), newObj.(*api.Pod) - from, to := o.Labels["check"], n.Labels["check"] - if !allowedTransitions[pair{from, to}] { - t.Errorf("observed transition %q -> %q for %v", from, to, n.Name) - } - if deletePod(n) { - source.Delete(n) - } - }, - DeleteFunc: func(obj interface{}) { - testDoneWG.Done() - }, - }, - ) - - // Run the controller and run it until we close stop. - // Once Run() is called, calls to testDoneWG.Done() might start, so - // all testDoneWG.Add() calls must happen before this point - stop := make(chan struct{}) - go controller.Run(stop) - - // run every test a few times, in parallel - var wg sync.WaitGroup - wg.Add(threads * len(tests)) - for i := 0; i < threads; i++ { - for j, f := range tests { - go func(name string, f func(string)) { - defer wg.Done() - f(name) - }(fmt.Sprintf("%v-%v", i, j), f) - } - } - wg.Wait() - - // Let's wait for the controller to process the things we just added. - testDoneWG.Wait() - close(stop) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/framework/fake_controller_source_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/framework/fake_controller_source_test.go deleted file mode 100644 index 01269ce64..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/framework/fake_controller_source_test.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright 2015 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 framework - -import ( - "sync" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/watch" -) - -// ensure the watch delivers the requested and only the requested items. -func consume(t *testing.T, w watch.Interface, rvs []string, done *sync.WaitGroup) { - defer done.Done() - for _, rv := range rvs { - got, ok := <-w.ResultChan() - if !ok { - t.Errorf("%#v: unexpected channel close, wanted %v", rvs, rv) - return - } - gotRV := got.Object.(*api.Pod).ObjectMeta.ResourceVersion - if e, a := rv, gotRV; e != a { - t.Errorf("wanted %v, got %v", e, a) - } else { - t.Logf("Got %v as expected", gotRV) - } - } - // We should not get anything else. - got, open := <-w.ResultChan() - if open { - t.Errorf("%#v: unwanted object %#v", rvs, got) - } -} - -func TestRCNumber(t *testing.T) { - pod := func(name string) *api.Pod { - return &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: name, - }, - } - } - - wg := &sync.WaitGroup{} - wg.Add(3) - - source := NewFakeControllerSource() - source.Add(pod("foo")) - source.Modify(pod("foo")) - source.Modify(pod("foo")) - - w, err := source.Watch(api.ListOptions{ResourceVersion: "1"}) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - go consume(t, w, []string{"2", "3"}, wg) - - list, err := source.List(api.ListOptions{}) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if e, a := "3", list.(*api.List).ResourceVersion; e != a { - t.Errorf("wanted %v, got %v", e, a) - } - - w2, err := source.Watch(api.ListOptions{ResourceVersion: "2"}) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - go consume(t, w2, []string{"3"}, wg) - - w3, err := source.Watch(api.ListOptions{ResourceVersion: "3"}) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - go consume(t, w3, []string{}, wg) - source.Shutdown() - wg.Wait() -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/lookup_cache.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/lookup_cache.go new file mode 100644 index 000000000..5d82908be --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/controller/lookup_cache.go @@ -0,0 +1,90 @@ +/* +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 controller + +import ( + "hash/adler32" + "sync" + + "github.com/golang/groupcache/lru" + "k8s.io/kubernetes/pkg/api/meta" + hashutil "k8s.io/kubernetes/pkg/util/hash" +) + +type objectWithMeta interface { + meta.Object +} + +// keyFunc returns the key of an object, which is used to look up in the cache for it's matching object. +// Since we match objects by namespace and Labels/Selector, so if two objects have the same namespace and labels, +// they will have the same key. +func keyFunc(obj objectWithMeta) uint64 { + hash := adler32.New() + hashutil.DeepHashObject(hash, &equivalenceLabelObj{ + namespace: obj.GetNamespace(), + labels: obj.GetLabels(), + }) + return uint64(hash.Sum32()) +} + +type equivalenceLabelObj struct { + namespace string + labels map[string]string +} + +// MatchingCache save label and selector matching relationship +type MatchingCache struct { + mutex sync.RWMutex + cache *lru.Cache +} + +// NewMatchingCache return a NewMatchingCache, which save label and selector matching relationship. +func NewMatchingCache(maxCacheEntries int) *MatchingCache { + return &MatchingCache{ + cache: lru.New(maxCacheEntries), + } +} + +// Add will add matching information to the cache. +func (c *MatchingCache) Add(labelObj objectWithMeta, selectorObj objectWithMeta) { + key := keyFunc(labelObj) + c.mutex.Lock() + defer c.mutex.Unlock() + c.cache.Add(key, selectorObj) +} + +// GetMatchingObject lookup the matching object for a given object. +// Note: the cache information may be invalid since the controller may be deleted or updated, +// we need check in the external request to ensure the cache data is not dirty. +func (c *MatchingCache) GetMatchingObject(labelObj objectWithMeta) (controller interface{}, exists bool) { + key := keyFunc(labelObj) + c.mutex.Lock() + defer c.mutex.Unlock() + return c.cache.Get(key) +} + +// Update update the cached matching information. +func (c *MatchingCache) Update(labelObj objectWithMeta, selectorObj objectWithMeta) { + c.Add(labelObj, selectorObj) +} + +// InvalidateAll invalidate the whole cache. +func (c *MatchingCache) InvalidateAll() { + c.mutex.Lock() + defer c.mutex.Unlock() + c.cache = lru.New(c.cache.MaxEntries) +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/OWNERS b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/OWNERS new file mode 100644 index 000000000..a046efc0c --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/OWNERS @@ -0,0 +1,5 @@ +assignees: + - derekwaynecarr + - lavalamp + - smarterclayton + - wojtek-t diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/converter_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/converter_test.go deleted file mode 100644 index c639492a7..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/converter_test.go +++ /dev/null @@ -1,847 +0,0 @@ -/* -Copyright 2014 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 conversion - -import ( - "encoding/json" - "fmt" - "reflect" - "strconv" - "strings" - "testing" - - "github.com/google/gofuzz" - flag "github.com/spf13/pflag" - - "k8s.io/kubernetes/pkg/util" -) - -var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.") - -// Test a weird version/kind embedding format. -type MyWeirdCustomEmbeddedVersionKindField struct { - ID string `json:"ID,omitempty"` - APIVersion string `json:"myVersionKey,omitempty"` - ObjectKind string `json:"myKindKey,omitempty"` - Z string `json:"Z,omitempty"` - Y uint64 `json:"Y,omitempty"` -} - -type TestType1 struct { - MyWeirdCustomEmbeddedVersionKindField `json:",inline"` - A string `json:"A,omitempty"` - B int `json:"B,omitempty"` - C int8 `json:"C,omitempty"` - D int16 `json:"D,omitempty"` - E int32 `json:"E,omitempty"` - F int64 `json:"F,omitempty"` - G uint `json:"G,omitempty"` - H uint8 `json:"H,omitempty"` - I uint16 `json:"I,omitempty"` - J uint32 `json:"J,omitempty"` - K uint64 `json:"K,omitempty"` - L bool `json:"L,omitempty"` - M map[string]int `json:"M,omitempty"` - N map[string]TestType2 `json:"N,omitempty"` - O *TestType2 `json:"O,omitempty"` - P []TestType2 `json:"Q,omitempty"` -} - -type TestType2 struct { - A string `json:"A,omitempty"` - B int `json:"B,omitempty"` -} - -type ExternalTestType2 struct { - A string `json:"A,omitempty"` - B int `json:"B,omitempty"` -} -type ExternalTestType1 struct { - MyWeirdCustomEmbeddedVersionKindField `json:",inline"` - A string `json:"A,omitempty"` - B int `json:"B,omitempty"` - C int8 `json:"C,omitempty"` - D int16 `json:"D,omitempty"` - E int32 `json:"E,omitempty"` - F int64 `json:"F,omitempty"` - G uint `json:"G,omitempty"` - H uint8 `json:"H,omitempty"` - I uint16 `json:"I,omitempty"` - J uint32 `json:"J,omitempty"` - K uint64 `json:"K,omitempty"` - L bool `json:"L,omitempty"` - M map[string]int `json:"M,omitempty"` - N map[string]ExternalTestType2 `json:"N,omitempty"` - O *ExternalTestType2 `json:"O,omitempty"` - P []ExternalTestType2 `json:"Q,omitempty"` -} - -func testLogger(t *testing.T) DebugLogger { - // We don't set logger to eliminate rubbish logs in tests. - // If you want to switch it, simply switch it to: "return t" - return nil -} - -func TestConverter_byteSlice(t *testing.T) { - c := NewConverter(DefaultNameFunc) - src := []byte{1, 2, 3} - dest := []byte{} - err := c.Convert(&src, &dest, 0, nil) - if err != nil { - t.Fatalf("expected no error") - } - if e, a := src, dest; !reflect.DeepEqual(e, a) { - t.Errorf("expected %#v, got %#v", e, a) - } -} - -func TestConverter_MismatchedTypes(t *testing.T) { - c := NewConverter(DefaultNameFunc) - - err := c.RegisterConversionFunc( - func(in *[]string, out *int, s Scope) error { - if str, err := strconv.Atoi((*in)[0]); err != nil { - return err - } else { - *out = str - return nil - } - }, - ) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - src := []string{"5"} - var dest *int - err = c.Convert(&src, &dest, 0, nil) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if e, a := 5, *dest; e != a { - t.Errorf("expected %#v, got %#v", e, a) - } -} - -func TestConverter_DefaultConvert(t *testing.T) { - type A struct { - Foo string - Baz int - } - type B struct { - Bar string - Baz int - } - c := NewConverter(DefaultNameFunc) - c.Debug = testLogger(t) - c.nameFunc = func(t reflect.Type) string { return "MyType" } - - // Ensure conversion funcs can call DefaultConvert to get default behavior, - // then fixup remaining fields manually - err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error { - if err := s.DefaultConvert(in, out, IgnoreMissingFields); err != nil { - return err - } - out.Bar = in.Foo - return nil - }) - if err != nil { - t.Fatalf("unexpected error %v", err) - } - - x := A{"hello, intrepid test reader!", 3} - y := B{} - - err = c.Convert(&x, &y, 0, nil) - if err != nil { - t.Fatalf("unexpected error %v", err) - } - if e, a := x.Foo, y.Bar; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := x.Baz, y.Baz; e != a { - t.Errorf("expected %v, got %v", e, a) - } -} - -func TestConverter_DeepCopy(t *testing.T) { - type A struct { - Foo *string - Bar []string - Baz interface{} - Qux map[string]string - } - c := NewConverter(DefaultNameFunc) - c.Debug = testLogger(t) - - foo, baz := "foo", "baz" - x := A{ - Foo: &foo, - Bar: []string{"bar"}, - Baz: &baz, - Qux: map[string]string{"qux": "qux"}, - } - y := A{} - - if err := c.Convert(&x, &y, 0, nil); err != nil { - t.Fatalf("unexpected error %v", err) - } - *x.Foo = "foo2" - x.Bar[0] = "bar2" - *x.Baz.(*string) = "baz2" - x.Qux["qux"] = "qux2" - if e, a := *x.Foo, *y.Foo; e == a { - t.Errorf("expected difference between %v and %v", e, a) - } - if e, a := x.Bar, y.Bar; reflect.DeepEqual(e, a) { - t.Errorf("expected difference between %v and %v", e, a) - } - if e, a := *x.Baz.(*string), *y.Baz.(*string); e == a { - t.Errorf("expected difference between %v and %v", e, a) - } - if e, a := x.Qux, y.Qux; reflect.DeepEqual(e, a) { - t.Errorf("expected difference between %v and %v", e, a) - } -} - -func TestConverter_CallsRegisteredFunctions(t *testing.T) { - type A struct { - Foo string - Baz int - } - type B struct { - Bar string - Baz int - } - type C struct{} - c := NewConverter(DefaultNameFunc) - c.Debug = testLogger(t) - err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error { - out.Bar = in.Foo - return s.Convert(&in.Baz, &out.Baz, 0) - }) - if err != nil { - t.Fatalf("unexpected error %v", err) - } - err = c.RegisterConversionFunc(func(in *B, out *A, s Scope) error { - out.Foo = in.Bar - return s.Convert(&in.Baz, &out.Baz, 0) - }) - if err != nil { - t.Fatalf("unexpected error %v", err) - } - - x := A{"hello, intrepid test reader!", 3} - y := B{} - - err = c.Convert(&x, &y, 0, nil) - if err != nil { - t.Fatalf("unexpected error %v", err) - } - if e, a := x.Foo, y.Bar; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := x.Baz, y.Baz; e != a { - t.Errorf("expected %v, got %v", e, a) - } - - z := B{"all your test are belong to us", 42} - w := A{} - - err = c.Convert(&z, &w, 0, nil) - if err != nil { - t.Fatalf("unexpected error %v", err) - } - if e, a := z.Bar, w.Foo; e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := z.Baz, w.Baz; e != a { - t.Errorf("expected %v, got %v", e, a) - } - - err = c.RegisterConversionFunc(func(in *A, out *C, s Scope) error { - return fmt.Errorf("C can't store an A, silly") - }) - if err != nil { - t.Fatalf("unexpected error %v", err) - } - err = c.Convert(&A{}, &C{}, 0, nil) - if err == nil { - t.Errorf("unexpected non-error") - } -} - -func TestConverter_IgnoredConversion(t *testing.T) { - type A struct{} - type B struct{} - - count := 0 - c := NewConverter(DefaultNameFunc) - if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error { - count++ - return nil - }); err != nil { - t.Fatalf("unexpected error %v", err) - } - if err := c.RegisterIgnoredConversion(&A{}, &B{}); err != nil { - t.Fatal(err) - } - a := A{} - b := B{} - if err := c.Convert(&a, &b, 0, nil); err != nil { - t.Errorf("%v", err) - } - if count != 0 { - t.Errorf("unexpected number of conversion invocations") - } -} - -func TestConverter_IgnoredConversionNested(t *testing.T) { - type C string - type A struct { - C C - } - type B struct { - C C - } - - c := NewConverter(DefaultNameFunc) - typed := C("") - if err := c.RegisterIgnoredConversion(&typed, &typed); err != nil { - t.Fatal(err) - } - a := A{C: C("test")} - b := B{C: C("other")} - if err := c.Convert(&a, &b, AllowDifferentFieldTypeNames, nil); err != nil { - t.Errorf("%v", err) - } - if b.C != C("other") { - t.Errorf("expected no conversion of field C: %#v", b) - } -} - -func TestConverter_GeneratedConversionOverriden(t *testing.T) { - type A struct{} - type B struct{} - c := NewConverter(DefaultNameFunc) - if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error { - return nil - }); err != nil { - t.Fatalf("unexpected error %v", err) - } - if err := c.RegisterGeneratedConversionFunc(func(in *A, out *B, s Scope) error { - return fmt.Errorf("generated function should be overriden") - }); err != nil { - t.Fatalf("unexpected error %v", err) - } - - a := A{} - b := B{} - if err := c.Convert(&a, &b, 0, nil); err != nil { - t.Errorf("%v", err) - } -} - -func TestConverter_WithConversionOverriden(t *testing.T) { - type A struct{} - type B struct{} - c := NewConverter(DefaultNameFunc) - if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error { - return fmt.Errorf("conversion function should be overriden") - }); err != nil { - t.Fatalf("unexpected error %v", err) - } - if err := c.RegisterGeneratedConversionFunc(func(in *A, out *B, s Scope) error { - return fmt.Errorf("generated function should be overriden") - }); err != nil { - t.Fatalf("unexpected error %v", err) - } - - ext := NewConversionFuncs() - ext.Add(func(in *A, out *B, s Scope) error { - return nil - }) - newc := c.WithConversions(ext) - - a := A{} - b := B{} - if err := c.Convert(&a, &b, 0, nil); err == nil || err.Error() != "conversion function should be overriden" { - t.Errorf("unexpected error: %v", err) - } - if err := newc.Convert(&a, &b, 0, nil); err != nil { - t.Errorf("%v", err) - } -} - -func TestConverter_MapsStringArrays(t *testing.T) { - type A struct { - Foo string - Baz int - Other string - } - c := NewConverter(DefaultNameFunc) - c.Debug = testLogger(t) - if err := c.RegisterConversionFunc(func(input *[]string, out *string, s Scope) error { - if len(*input) == 0 { - *out = "" - } - *out = (*input)[0] - return nil - }); err != nil { - t.Fatalf("unexpected error %v", err) - } - - x := map[string][]string{ - "Foo": {"bar"}, - "Baz": {"1"}, - "Other": {"", "test"}, - "other": {"wrong"}, - } - y := A{"test", 2, "something"} - - if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames, nil); err == nil { - t.Error("unexpected non-error") - } - - if err := c.RegisterConversionFunc(func(input *[]string, out *int, s Scope) error { - if len(*input) == 0 { - *out = 0 - } - str := (*input)[0] - i, err := strconv.Atoi(str) - if err != nil { - return err - } - *out = i - return nil - }); err != nil { - t.Fatalf("unexpected error %v", err) - } - - if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames, nil); err != nil { - t.Fatalf("unexpected error %v", err) - } - if !reflect.DeepEqual(y, A{"bar", 1, ""}) { - t.Errorf("unexpected result: %#v", y) - } -} - -func TestConverter_MapsStringArraysWithMappingKey(t *testing.T) { - type A struct { - Foo string `json:"test"` - Baz int - Other string - } - c := NewConverter(DefaultNameFunc) - c.Debug = testLogger(t) - if err := c.RegisterConversionFunc(func(input *[]string, out *string, s Scope) error { - if len(*input) == 0 { - *out = "" - } - *out = (*input)[0] - return nil - }); err != nil { - t.Fatalf("unexpected error %v", err) - } - - x := map[string][]string{ - "Foo": {"bar"}, - "test": {"baz"}, - } - y := A{"", 0, ""} - - if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames|IgnoreMissingFields, &Meta{}); err != nil { - t.Fatalf("unexpected error %v", err) - } - if !reflect.DeepEqual(y, A{"bar", 0, ""}) { - t.Errorf("unexpected result: %#v", y) - } - - mapping := func(key string, sourceTag, destTag reflect.StructTag) (source string, dest string) { - if s := destTag.Get("json"); len(s) > 0 { - return strings.SplitN(s, ",", 2)[0], key - } - return key, key - } - - if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames|IgnoreMissingFields, &Meta{KeyNameMapping: mapping}); err != nil { - t.Fatalf("unexpected error %v", err) - } - if !reflect.DeepEqual(y, A{"baz", 0, ""}) { - t.Errorf("unexpected result: %#v", y) - } -} - -func TestConverter_fuzz(t *testing.T) { - // Use the same types from the scheme test. - table := []struct { - from, to, check interface{} - }{ - {&TestType1{}, &ExternalTestType1{}, &TestType1{}}, - {&ExternalTestType1{}, &TestType1{}, &ExternalTestType1{}}, - } - - f := fuzz.New().NilChance(.5).NumElements(0, 100) - c := NewConverter(DefaultNameFunc) - c.nameFunc = func(t reflect.Type) string { - // Hide the fact that we don't have separate packages for these things. - return map[reflect.Type]string{ - reflect.TypeOf(TestType1{}): "TestType1", - reflect.TypeOf(ExternalTestType1{}): "TestType1", - reflect.TypeOf(TestType2{}): "TestType2", - reflect.TypeOf(ExternalTestType2{}): "TestType2", - }[t] - } - c.Debug = testLogger(t) - - for i, item := range table { - for j := 0; j < *fuzzIters; j++ { - f.Fuzz(item.from) - err := c.Convert(item.from, item.to, 0, nil) - if err != nil { - t.Errorf("(%v, %v): unexpected error: %v", i, j, err) - continue - } - err = c.Convert(item.to, item.check, 0, nil) - if err != nil { - t.Errorf("(%v, %v): unexpected error: %v", i, j, err) - continue - } - if e, a := item.from, item.check; !reflect.DeepEqual(e, a) { - t.Errorf("(%v, %v): unexpected diff: %v", i, j, objDiff(e, a)) - } - } - } -} - -func TestConverter_MapElemAddr(t *testing.T) { - type Foo struct { - A map[int]int - } - type Bar struct { - A map[string]string - } - c := NewConverter(DefaultNameFunc) - c.Debug = testLogger(t) - err := c.RegisterConversionFunc( - func(in *int, out *string, s Scope) error { - *out = fmt.Sprintf("%v", *in) - return nil - }, - ) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - err = c.RegisterConversionFunc( - func(in *string, out *int, s Scope) error { - if str, err := strconv.Atoi(*in); err != nil { - return err - } else { - *out = str - return nil - } - }, - ) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - f := fuzz.New().NilChance(0).NumElements(3, 3) - first := Foo{} - second := Bar{} - f.Fuzz(&first) - err = c.Convert(&first, &second, AllowDifferentFieldTypeNames, nil) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - third := Foo{} - err = c.Convert(&second, &third, AllowDifferentFieldTypeNames, nil) - if e, a := first, third; !reflect.DeepEqual(e, a) { - t.Errorf("Unexpected diff: %v", objDiff(e, a)) - } -} - -func TestConverter_tags(t *testing.T) { - type Foo struct { - A string `test:"foo"` - } - type Bar struct { - A string `test:"bar"` - } - c := NewConverter(DefaultNameFunc) - c.Debug = testLogger(t) - err := c.RegisterConversionFunc( - func(in *string, out *string, s Scope) error { - if e, a := "foo", s.SrcTag().Get("test"); e != a { - t.Errorf("expected %v, got %v", e, a) - } - if e, a := "bar", s.DestTag().Get("test"); e != a { - t.Errorf("expected %v, got %v", e, a) - } - return nil - }, - ) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - err = c.Convert(&Foo{}, &Bar{}, AllowDifferentFieldTypeNames, nil) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } -} - -func TestConverter_meta(t *testing.T) { - type Foo struct{ A string } - type Bar struct{ A string } - c := NewConverter(DefaultNameFunc) - c.Debug = testLogger(t) - checks := 0 - err := c.RegisterConversionFunc( - func(in *Foo, out *Bar, s Scope) error { - if s.Meta() == nil || s.Meta().SrcVersion != "test" || s.Meta().DestVersion != "passes" { - t.Errorf("Meta did not get passed!") - } - checks++ - s.Convert(&in.A, &out.A, 0) - return nil - }, - ) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - err = c.RegisterConversionFunc( - func(in *string, out *string, s Scope) error { - if s.Meta() == nil || s.Meta().SrcVersion != "test" || s.Meta().DestVersion != "passes" { - t.Errorf("Meta did not get passed a second time!") - } - checks++ - return nil - }, - ) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - err = c.Convert(&Foo{}, &Bar{}, 0, &Meta{SrcVersion: "test", DestVersion: "passes"}) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if checks != 2 { - t.Errorf("Registered functions did not get called.") - } -} - -func TestConverter_flags(t *testing.T) { - type Foo struct{ A string } - type Bar struct{ A string } - table := []struct { - from, to interface{} - flags FieldMatchingFlags - shouldSucceed bool - }{ - // Check that DestFromSource allows extra fields only in source. - { - from: &struct{ A string }{}, - to: &struct{ A, B string }{}, - flags: DestFromSource, - shouldSucceed: false, - }, { - from: &struct{ A, B string }{}, - to: &struct{ A string }{}, - flags: DestFromSource, - shouldSucceed: true, - }, - - // Check that SourceToDest allows for extra fields only in dest. - { - from: &struct{ A string }{}, - to: &struct{ A, B string }{}, - flags: SourceToDest, - shouldSucceed: true, - }, { - from: &struct{ A, B string }{}, - to: &struct{ A string }{}, - flags: SourceToDest, - shouldSucceed: false, - }, - - // Check that IgnoreMissingFields makes the above failure cases pass. - { - from: &struct{ A string }{}, - to: &struct{ A, B string }{}, - flags: DestFromSource | IgnoreMissingFields, - shouldSucceed: true, - }, { - from: &struct{ A, B string }{}, - to: &struct{ A string }{}, - flags: SourceToDest | IgnoreMissingFields, - shouldSucceed: true, - }, - - // Check that the field type name must match unless - // AllowDifferentFieldTypeNames is specified. - { - from: &struct{ A, B Foo }{}, - to: &struct{ A Bar }{}, - flags: DestFromSource, - shouldSucceed: false, - }, { - from: &struct{ A Foo }{}, - to: &struct{ A, B Bar }{}, - flags: SourceToDest, - shouldSucceed: false, - }, { - from: &struct{ A, B Foo }{}, - to: &struct{ A Bar }{}, - flags: DestFromSource | AllowDifferentFieldTypeNames, - shouldSucceed: true, - }, { - from: &struct{ A Foo }{}, - to: &struct{ A, B Bar }{}, - flags: SourceToDest | AllowDifferentFieldTypeNames, - shouldSucceed: true, - }, - } - f := fuzz.New().NilChance(.5).NumElements(0, 100) - c := NewConverter(DefaultNameFunc) - c.Debug = testLogger(t) - - for i, item := range table { - for j := 0; j < *fuzzIters; j++ { - f.Fuzz(item.from) - err := c.Convert(item.from, item.to, item.flags, nil) - if item.shouldSucceed && err != nil { - t.Errorf("(%v, %v): unexpected error: %v", i, j, err) - continue - } - if !item.shouldSucceed && err == nil { - t.Errorf("(%v, %v): unexpected non-error", i, j) - continue - } - } - } -} - -func TestConverter_FieldRename(t *testing.T) { - type WeirdMeta struct { - Name string - Type string - } - type NameMeta struct { - Name string - } - type TypeMeta struct { - Type string - } - type A struct { - WeirdMeta - } - type B struct { - TypeMeta - NameMeta - } - - c := NewConverter(DefaultNameFunc) - err := c.SetStructFieldCopy(WeirdMeta{}, "WeirdMeta", TypeMeta{}, "TypeMeta") - if err != nil { - t.Fatalf("unexpected error %v", err) - } - err = c.SetStructFieldCopy(WeirdMeta{}, "WeirdMeta", NameMeta{}, "NameMeta") - if err != nil { - t.Fatalf("unexpected error %v", err) - } - err = c.SetStructFieldCopy(TypeMeta{}, "TypeMeta", WeirdMeta{}, "WeirdMeta") - if err != nil { - t.Fatalf("unexpected error %v", err) - } - err = c.SetStructFieldCopy(NameMeta{}, "NameMeta", WeirdMeta{}, "WeirdMeta") - if err != nil { - t.Fatalf("unexpected error %v", err) - } - c.Debug = testLogger(t) - - aVal := &A{ - WeirdMeta: WeirdMeta{ - Name: "Foo", - Type: "Bar", - }, - } - - bVal := &B{ - TypeMeta: TypeMeta{"Bar"}, - NameMeta: NameMeta{"Foo"}, - } - - table := map[string]struct { - from, to, expect interface{} - flags FieldMatchingFlags - }{ - "to": { - aVal, - &B{}, - bVal, - AllowDifferentFieldTypeNames | SourceToDest | IgnoreMissingFields, - }, - "from": { - bVal, - &A{}, - aVal, - AllowDifferentFieldTypeNames | SourceToDest, - }, - "toDestFirst": { - aVal, - &B{}, - bVal, - AllowDifferentFieldTypeNames, - }, - "fromDestFirst": { - bVal, - &A{}, - aVal, - AllowDifferentFieldTypeNames | IgnoreMissingFields, - }, - } - - for name, item := range table { - err := c.Convert(item.from, item.to, item.flags, nil) - if err != nil { - t.Errorf("%v: unexpected error: %v", name, err) - continue - } - if e, a := item.expect, item.to; !reflect.DeepEqual(e, a) { - t.Errorf("%v: unexpected diff: %v", name, objDiff(e, a)) - } - } -} - -func objDiff(a, b interface{}) string { - ab, err := json.Marshal(a) - if err != nil { - panic("a") - } - bb, err := json.Marshal(b) - if err != nil { - panic("b") - } - return util.StringDiff(string(ab), string(bb)) - - // An alternate diff attempt, in case json isn't showing you - // the difference. (reflect.DeepEqual makes a distinction between - // nil and empty slices, for example.) - //return util.StringDiff( - // fmt.Sprintf("%#v", a), - // fmt.Sprintf("%#v", b), - //) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/deep_copy_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/deep_copy_test.go deleted file mode 100644 index a1cd65308..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/deep_copy_test.go +++ /dev/null @@ -1,161 +0,0 @@ -/* -Copyright 2015 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 conversion - -import ( - "math/rand" - "reflect" - "testing" - - "github.com/google/gofuzz" -) - -func TestDeepCopy(t *testing.T) { - semantic := EqualitiesOrDie() - f := fuzz.New().NilChance(.5).NumElements(0, 100) - table := []interface{}{ - map[string]string{}, - int(5), - "hello world", - struct { - A, B, C struct { - D map[string]int - } - X []int - Y []byte - }{}, - } - for _, obj := range table { - obj2, err := NewCloner().DeepCopy(obj) - if err != nil { - t.Errorf("Error: couldn't copy %#v", obj) - continue - } - if e, a := obj, obj2; !semantic.DeepEqual(e, a) { - t.Errorf("expected %#v\ngot %#v", e, a) - } - - obj3 := reflect.New(reflect.TypeOf(obj)).Interface() - f.Fuzz(obj3) - obj4, err := NewCloner().DeepCopy(obj3) - if err != nil { - t.Errorf("Error: couldn't copy %#v", obj) - continue - } - if e, a := obj3, obj4; !semantic.DeepEqual(e, a) { - t.Errorf("expected %#v\ngot %#v", e, a) - } - f.Fuzz(obj3) - } -} - -func copyOrDie(t *testing.T, in interface{}) interface{} { - out, err := NewCloner().DeepCopy(in) - if err != nil { - t.Fatalf("DeepCopy failed: %#q: %v", in, err) - } - return out -} - -func TestDeepCopySliceSeparate(t *testing.T) { - x := []int{5} - y := copyOrDie(t, x).([]int) - x[0] = 3 - if y[0] == 3 { - t.Errorf("deep copy wasn't deep: %#q %#q", x, y) - } -} - -func TestDeepCopyArraySeparate(t *testing.T) { - x := [1]int{5} - y := copyOrDie(t, x).([1]int) - x[0] = 3 - if y[0] == 3 { - t.Errorf("deep copy wasn't deep: %#q %#q", x, y) - } -} - -func TestDeepCopyMapSeparate(t *testing.T) { - x := map[string]int{"foo": 5} - y := copyOrDie(t, x).(map[string]int) - x["foo"] = 3 - if y["foo"] == 3 { - t.Errorf("deep copy wasn't deep: %#q %#q", x, y) - } -} - -func TestDeepCopyPointerSeparate(t *testing.T) { - z := 5 - x := &z - y := copyOrDie(t, x).(*int) - *x = 3 - if *y == 3 { - t.Errorf("deep copy wasn't deep: %#q %#q", x, y) - } -} - -func TestDeepCopyStruct(t *testing.T) { - type Foo struct { - A int - } - type Bar struct { - Foo - F *Foo - } - a := &Bar{Foo{1}, &Foo{2}} - b := copyOrDie(t, a).(*Bar) - a.A = 3 - a.F.A = 4 - - if b.A != 1 || b.F.A != 2 { - t.Errorf("deep copy wasn't deep: %#v, %#v", a, b) - } -} - -var result interface{} - -func BenchmarkDeepCopy(b *testing.B) { - table := []interface{}{ - map[string]string{}, - int(5), - "hello world", - struct { - A, B, C struct { - D map[string]int - } - X []int - Y []byte - }{}, - } - - f := fuzz.New().RandSource(rand.NewSource(1)).NilChance(.5).NumElements(0, 100) - for i := range table { - out := table[i] - obj := reflect.New(reflect.TypeOf(out)).Interface() - f.Fuzz(obj) - table[i] = obj - } - - b.ResetTimer() - var r interface{} - for i := 0; i < b.N; i++ { - for j := range table { - r, _ = NewCloner().DeepCopy(table[j]) - } - } - result = r -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/helper_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/helper_test.go deleted file mode 100644 index 69fef3334..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/helper_test.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2014 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 conversion - -import "testing" - -func TestInvalidPtrValueKind(t *testing.T) { - var simple interface{} - switch obj := simple.(type) { - default: - _, err := EnforcePtr(obj) - if err == nil { - t.Errorf("Expected error on invalid kind") - } - } -} - -func TestEnforceNilPtr(t *testing.T) { - var nilPtr *struct{} - _, err := EnforcePtr(nilPtr) - if err == nil { - t.Errorf("Expected error on nil pointer") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/queryparams/convert.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/queryparams/convert.go index 0f04e7bb4..63c545697 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/queryparams/convert.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/queryparams/convert.go @@ -23,6 +23,16 @@ import ( "strings" ) +// Marshaler converts an object to a query parameter string representation +type Marshaler interface { + MarshalQueryParameter() (string, error) +} + +// Unmarshaler converts a string representation to an object +type Unmarshaler interface { + UnmarshalQueryParameter(string) error +} + func jsonTag(field reflect.StructField) (string, bool) { structTag := field.Tag.Get("json") if len(structTag) == 0 { @@ -72,6 +82,31 @@ func zeroValue(value reflect.Value) bool { return reflect.DeepEqual(reflect.Zero(value.Type()).Interface(), value.Interface()) } +func customMarshalValue(value reflect.Value) (reflect.Value, bool) { + // Return unless we implement a custom query marshaler + if !value.CanInterface() { + return reflect.Value{}, false + } + + marshaler, ok := value.Interface().(Marshaler) + if !ok { + return reflect.Value{}, false + } + + // Don't invoke functions on nil pointers + // If the type implements MarshalQueryParameter, AND the tag is not omitempty, AND the value is a nil pointer, "" seems like a reasonable response + if isPointerKind(value.Kind()) && zeroValue(value) { + return reflect.ValueOf(""), true + } + + // Get the custom marshalled value + v, err := marshaler.MarshalQueryParameter() + if err != nil { + return reflect.Value{}, false + } + return reflect.ValueOf(v), true +} + func addParam(values url.Values, tag string, omitempty bool, value reflect.Value) { if omitempty && zeroValue(value) { return @@ -128,7 +163,8 @@ func convertStruct(result url.Values, st reflect.Type, sv reflect.Value) { kind := ft.Kind() if isPointerKind(kind) { - kind = ft.Elem().Kind() + ft = ft.Elem() + kind = ft.Kind() if !field.IsNil() { field = reflect.Indirect(field) } @@ -142,7 +178,11 @@ func convertStruct(result url.Values, st reflect.Type, sv reflect.Value) { addListOfParams(result, tag, omitempty, field) } case isStructKind(kind) && !(zeroValue(field) && omitempty): - convertStruct(result, ft, field) + if marshalValue, ok := customMarshalValue(field); ok { + addParam(result, tag, omitempty, marshalValue) + } else { + convertStruct(result, ft, field) + } } } } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/queryparams/convert_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/queryparams/convert_test.go deleted file mode 100644 index 405357557..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/queryparams/convert_test.go +++ /dev/null @@ -1,174 +0,0 @@ -/* -Copyright 2014 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 queryparams_test - -import ( - "net/url" - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/conversion/queryparams" -) - -type namedString string -type namedBool bool - -type bar struct { - Float1 float32 `json:"float1"` - Float2 float64 `json:"float2"` - Int1 int64 `json:"int1,omitempty"` - Int2 int32 `json:"int2,omitempty"` - Int3 int16 `json:"int3,omitempty"` - Str1 string `json:"str1,omitempty"` - Ignored int - Ignored2 string -} - -func (obj *bar) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind } - -type foo struct { - Str string `json:"str"` - Integer int `json:"integer,omitempty"` - Slice []string `json:"slice,omitempty"` - Boolean bool `json:"boolean,omitempty"` - NamedStr namedString `json:"namedStr,omitempty"` - NamedBool namedBool `json:"namedBool,omitempty"` - Foobar bar `json:"foobar,omitempty"` - Testmap map[string]string `json:"testmap,omitempty"` -} - -func (obj *foo) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind } - -type baz struct { - Ptr *int `json:"ptr"` - Bptr *bool `json:"bptr,omitempty"` -} - -func (obj *baz) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind } - -func validateResult(t *testing.T, input interface{}, actual, expected url.Values) { - local := url.Values{} - for k, v := range expected { - local[k] = v - } - for k, v := range actual { - if ev, ok := local[k]; !ok || !reflect.DeepEqual(ev, v) { - if !ok { - t.Errorf("%#v: actual value key %s not found in expected map", input, k) - } else { - t.Errorf("%#v: values don't match: actual: %#v, expected: %#v", input, v, ev) - } - break - } - delete(local, k) - } - if len(local) > 0 { - t.Errorf("%#v: expected map has keys that were not found in actual map: %#v", input, local) - } -} - -func TestConvert(t *testing.T) { - tests := []struct { - input interface{} - expected url.Values - }{ - { - input: &foo{ - Str: "hello", - }, - expected: url.Values{"str": {"hello"}}, - }, - { - input: &foo{ - Str: "test string", - Slice: []string{"one", "two", "three"}, - Integer: 234, - Boolean: true, - }, - expected: url.Values{"str": {"test string"}, "slice": {"one", "two", "three"}, "integer": {"234"}, "boolean": {"true"}}, - }, - { - input: &foo{ - Str: "named types", - NamedStr: "value1", - NamedBool: true, - }, - expected: url.Values{"str": {"named types"}, "namedStr": {"value1"}, "namedBool": {"true"}}, - }, - { - input: &foo{ - Str: "don't ignore embedded struct", - Foobar: bar{ - Float1: 5.0, - }, - }, - expected: url.Values{"str": {"don't ignore embedded struct"}, "float1": {"5"}, "float2": {"0"}}, - }, - { - // Ignore untagged fields - input: &bar{ - Float1: 23.5, - Float2: 100.7, - Int1: 1, - Int2: 2, - Int3: 3, - Ignored: 1, - Ignored2: "ignored", - }, - expected: url.Values{"float1": {"23.5"}, "float2": {"100.7"}, "int1": {"1"}, "int2": {"2"}, "int3": {"3"}}, - }, - { - // include fields that are not tagged omitempty - input: &foo{ - NamedStr: "named str", - }, - expected: url.Values{"str": {""}, "namedStr": {"named str"}}, - }, - { - input: &baz{ - Ptr: intp(5), - Bptr: boolp(true), - }, - expected: url.Values{"ptr": {"5"}, "bptr": {"true"}}, - }, - { - input: &baz{ - Bptr: boolp(true), - }, - expected: url.Values{"ptr": {""}, "bptr": {"true"}}, - }, - { - input: &baz{ - Ptr: intp(5), - }, - expected: url.Values{"ptr": {"5"}}, - }, - } - - for _, test := range tests { - result, err := queryparams.Convert(test.input) - if err != nil { - t.Errorf("Unexpected error while converting %#v: %v", test.input, err) - } - validateResult(t, test.input, result, test.expected) - } -} - -func intp(n int) *int { return &n } - -func boolp(b bool) *bool { return &b } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/OWNERS b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/OWNERS new file mode 100644 index 000000000..766c481bd --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/OWNERS @@ -0,0 +1,3 @@ +assignees: + - erictune + - liggitt diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/aws/aws_credentials.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/aws/aws_credentials.go deleted file mode 100644 index 395c438ed..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/aws/aws_credentials.go +++ /dev/null @@ -1,163 +0,0 @@ -/* -Copyright 2014 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 aws_credentials - -import ( - "encoding/base64" - "strings" - "time" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ecr" - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/cloudprovider" - "k8s.io/kubernetes/pkg/credentialprovider" -) - -var registryUrls = []string{"*.dkr.ecr.*.amazonaws.com"} - -// awsHandlerLogger is a handler that logs all AWS SDK requests -// Copied from cloudprovider/aws/log_handler.go -func awsHandlerLogger(req *request.Request) { - service := req.ClientInfo.ServiceName - - name := "?" - if req.Operation != nil { - name = req.Operation.Name - } - - glog.V(4).Infof("AWS request: %s %s", service, name) -} - -// An interface for testing purposes. -type tokenGetter interface { - GetAuthorizationToken(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) -} - -// The canonical implementation -type ecrTokenGetter struct { - svc *ecr.ECR -} - -func (p *ecrTokenGetter) GetAuthorizationToken(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { - return p.svc.GetAuthorizationToken(input) -} - -// ecrProvider is a DockerConfigProvider that gets and refreshes 12-hour tokens -// from AWS to access ECR. -type ecrProvider struct { - getter tokenGetter -} - -// Not using the package init() function: this module should be initialized only -// if using the AWS cloud provider. This way, we avoid timeouts waiting for a -// non-existent provider. -func Init() { - credentialprovider.RegisterCredentialProvider("aws-ecr-key", - &credentialprovider.CachingDockerConfigProvider{ - Provider: &ecrProvider{}, - // Refresh credentials a little earlier before they expire - Lifetime: 11*time.Hour + 55*time.Minute, - }) -} - -// Enabled implements DockerConfigProvider.Enabled for the AWS token-based implementation. -// For now, it gets activated only if AWS was chosen as the cloud provider. -// TODO: figure how to enable it manually for deployments that are not on AWS but still -// use ECR somehow? -func (p *ecrProvider) Enabled() bool { - provider, err := cloudprovider.GetCloudProvider("aws", nil) - if err != nil { - glog.Errorf("while initializing AWS cloud provider %v", err) - return false - } - if provider == nil { - return false - } - - zones, ok := provider.Zones() - if !ok { - glog.Errorf("couldn't get Zones() interface") - return false - } - zone, err := zones.GetZone() - if err != nil { - glog.Errorf("while getting zone %v", err) - return false - } - if zone.Region == "" { - glog.Errorf("Region information is empty") - return false - } - - getter := &ecrTokenGetter{svc: ecr.New(session.New(&aws.Config{ - Credentials: nil, - Region: &zone.Region, - }))} - getter.svc.Handlers.Sign.PushFrontNamed(request.NamedHandler{ - Name: "k8s/logger", - Fn: awsHandlerLogger, - }) - p.getter = getter - - return true -} - -// Provide implements DockerConfigProvider.Provide, refreshing ECR tokens on demand -func (p *ecrProvider) Provide() credentialprovider.DockerConfig { - cfg := credentialprovider.DockerConfig{} - - // TODO: fill in RegistryIds? - params := &ecr.GetAuthorizationTokenInput{} - output, err := p.getter.GetAuthorizationToken(params) - if err != nil { - glog.Errorf("while requesting ECR authorization token %v", err) - return cfg - } - if output == nil { - glog.Errorf("Got back no ECR token") - return cfg - } - - for _, data := range output.AuthorizationData { - if data.ProxyEndpoint != nil && - data.AuthorizationToken != nil { - decodedToken, err := base64.StdEncoding.DecodeString(aws.StringValue(data.AuthorizationToken)) - if err != nil { - glog.Errorf("while decoding token for endpoint %s %v", data.ProxyEndpoint, err) - return cfg - } - parts := strings.SplitN(string(decodedToken), ":", 2) - user := parts[0] - password := parts[1] - entry := credentialprovider.DockerConfigEntry{ - Username: user, - Password: password, - // ECR doesn't care and Docker is about to obsolete it - Email: "not@val.id", - } - - // Add our entry for each of the supported container registry URLs - for _, k := range registryUrls { - cfg[k] = entry - } - } - } - return cfg -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/aws/aws_credentials_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/aws/aws_credentials_test.go deleted file mode 100644 index a07493993..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/aws/aws_credentials_test.go +++ /dev/null @@ -1,108 +0,0 @@ -/* -Copyright 2014 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 aws_credentials - -import ( - "encoding/base64" - "fmt" - "path" - "testing" - "time" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecr" - - "k8s.io/kubernetes/pkg/credentialprovider" -) - -const user = "foo" -const password = "1234567890abcdef" -const email = "not@val.id" - -// Mock implementation -type testTokenGetter struct { - user string - password string - endpoint string -} - -func (p *testTokenGetter) GetAuthorizationToken(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { - - expiration := time.Now().Add(1 * time.Hour) - creds := []byte(fmt.Sprintf("%s:%s", p.user, p.password)) - data := &ecr.AuthorizationData{ - AuthorizationToken: aws.String(base64.StdEncoding.EncodeToString(creds)), - ExpiresAt: &expiration, - ProxyEndpoint: aws.String(p.endpoint), - } - output := &ecr.GetAuthorizationTokenOutput{ - AuthorizationData: []*ecr.AuthorizationData{data}, - } - - return output, nil //p.svc.GetAuthorizationToken(input) -} - -func TestEcrProvide(t *testing.T) { - registry := "123456789012.dkr.ecr.lala-land-1.amazonaws.com" - otherRegistries := []string{"private.registry.com", - "gcr.io", - } - image := "foo/bar" - - provider := &ecrProvider{ - getter: &testTokenGetter{ - user: user, - password: password, - endpoint: registry}, - } - - keyring := &credentialprovider.BasicDockerKeyring{} - keyring.Add(provider.Provide()) - - // Verify that we get the expected username/password combo for - // an ECR image name. - fullImage := path.Join(registry, image) - creds, ok := keyring.Lookup(fullImage) - if !ok { - t.Errorf("Didn't find expected URL: %s", fullImage) - return - } - if len(creds) > 1 { - t.Errorf("Got more hits than expected: %s", creds) - } - val := creds[0] - - if user != val.Username { - t.Errorf("Unexpected username value, want: _token, got: %s", val.Username) - } - if password != val.Password { - t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password) - } - if email != val.Email { - t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email) - } - - // Verify that we get an error for other images. - for _, otherRegistry := range otherRegistries { - fullImage = path.Join(otherRegistry, image) - creds, ok = keyring.Lookup(fullImage) - if ok { - t.Errorf("Unexpectedly found image: %s", fullImage) - return - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/config_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/config_test.go deleted file mode 100644 index 587879fe9..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/config_test.go +++ /dev/null @@ -1,225 +0,0 @@ -/* -Copyright 2014 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 credentialprovider - -import ( - "encoding/json" - "reflect" - "testing" -) - -func TestDockerConfigJsonJSONDecode(t *testing.T) { - input := []byte(`{"auths": {"http://foo.example.com":{"username": "foo", "password": "bar", "email": "foo@example.com"}, "http://bar.example.com":{"username": "bar", "password": "baz", "email": "bar@example.com"}}}`) - - expect := DockerConfigJson{ - Auths: DockerConfig(map[string]DockerConfigEntry{ - "http://foo.example.com": { - Username: "foo", - Password: "bar", - Email: "foo@example.com", - }, - "http://bar.example.com": { - Username: "bar", - Password: "baz", - Email: "bar@example.com", - }, - }), - } - - var output DockerConfigJson - err := json.Unmarshal(input, &output) - if err != nil { - t.Errorf("Received unexpected error: %v", err) - } - - if !reflect.DeepEqual(expect, output) { - t.Errorf("Received unexpected output. Expected %#v, got %#v", expect, output) - } -} - -func TestDockerConfigJSONDecode(t *testing.T) { - input := []byte(`{"http://foo.example.com":{"username": "foo", "password": "bar", "email": "foo@example.com"}, "http://bar.example.com":{"username": "bar", "password": "baz", "email": "bar@example.com"}}`) - - expect := DockerConfig(map[string]DockerConfigEntry{ - "http://foo.example.com": { - Username: "foo", - Password: "bar", - Email: "foo@example.com", - }, - "http://bar.example.com": { - Username: "bar", - Password: "baz", - Email: "bar@example.com", - }, - }) - - var output DockerConfig - err := json.Unmarshal(input, &output) - if err != nil { - t.Errorf("Received unexpected error: %v", err) - } - - if !reflect.DeepEqual(expect, output) { - t.Errorf("Received unexpected output. Expected %#v, got %#v", expect, output) - } -} - -func TestDockerConfigEntryJSONDecode(t *testing.T) { - tests := []struct { - input []byte - expect DockerConfigEntry - fail bool - }{ - // simple case, just decode the fields - { - input: []byte(`{"username": "foo", "password": "bar", "email": "foo@example.com"}`), - expect: DockerConfigEntry{ - Username: "foo", - Password: "bar", - Email: "foo@example.com", - }, - fail: false, - }, - - // auth field decodes to username & password - { - input: []byte(`{"auth": "Zm9vOmJhcg==", "email": "foo@example.com"}`), - expect: DockerConfigEntry{ - Username: "foo", - Password: "bar", - Email: "foo@example.com", - }, - fail: false, - }, - - // auth field overrides username & password - { - input: []byte(`{"username": "foo", "password": "bar", "auth": "cGluZzpwb25n", "email": "foo@example.com"}`), - expect: DockerConfigEntry{ - Username: "ping", - Password: "pong", - Email: "foo@example.com", - }, - fail: false, - }, - - // poorly-formatted auth causes failure - { - input: []byte(`{"auth": "pants", "email": "foo@example.com"}`), - expect: DockerConfigEntry{ - Username: "", - Password: "", - Email: "foo@example.com", - }, - fail: true, - }, - - // invalid JSON causes failure - { - input: []byte(`{"email": false}`), - expect: DockerConfigEntry{ - Username: "", - Password: "", - Email: "", - }, - fail: true, - }, - } - - for i, tt := range tests { - var output DockerConfigEntry - err := json.Unmarshal(tt.input, &output) - if (err != nil) != tt.fail { - t.Errorf("case %d: expected fail=%t, got err=%v", i, tt.fail, err) - } - - if !reflect.DeepEqual(tt.expect, output) { - t.Errorf("case %d: expected output %#v, got %#v", i, tt.expect, output) - } - } -} - -func TestDecodeDockerConfigFieldAuth(t *testing.T) { - tests := []struct { - input string - username string - password string - fail bool - }{ - // auth field decodes to username & password - { - input: "Zm9vOmJhcg==", - username: "foo", - password: "bar", - }, - - // good base64 data, but no colon separating username & password - { - input: "cGFudHM=", - fail: true, - }, - - // bad base64 data - { - input: "pants", - fail: true, - }, - } - - for i, tt := range tests { - username, password, err := decodeDockerConfigFieldAuth(tt.input) - if (err != nil) != tt.fail { - t.Errorf("case %d: expected fail=%t, got err=%v", i, tt.fail, err) - } - - if tt.username != username { - t.Errorf("case %d: expected username %q, got %q", i, tt.username, username) - } - - if tt.password != password { - t.Errorf("case %d: expected password %q, got %q", i, tt.password, password) - } - } -} - -func TestDockerConfigEntryJSONCompatibleEncode(t *testing.T) { - tests := []struct { - input DockerConfigEntry - expect []byte - }{ - // simple case, just decode the fields - { - expect: []byte(`{"username":"foo","password":"bar","email":"foo@example.com","auth":"Zm9vOmJhcg=="}`), - input: DockerConfigEntry{ - Username: "foo", - Password: "bar", - Email: "foo@example.com", - }, - }, - } - - for i, tt := range tests { - actual, err := json.Marshal(tt.input) - if err != nil { - t.Errorf("case %d: unexpected error: %v", i, err) - } - - if string(tt.expect) != string(actual) { - t.Errorf("case %d: expected %v, got %v", i, string(tt.expect), string(actual)) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/doc.go deleted file mode 100644 index e6f2beba8..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2014 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 gcp_credentials contains implementations of DockerConfigProvider -// for Google Cloud Platform. -package gcp_credentials diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/jwt.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/jwt.go deleted file mode 100644 index 3c1a05d4b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/jwt.go +++ /dev/null @@ -1,111 +0,0 @@ -/* -Copyright 2014 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 gcp_credentials - -import ( - "io/ioutil" - "time" - - "github.com/golang/glog" - "golang.org/x/oauth2" - "golang.org/x/oauth2/google" - "golang.org/x/oauth2/jwt" - "k8s.io/kubernetes/pkg/credentialprovider" - - "github.com/spf13/pflag" -) - -const ( - storageReadOnlyScope = "https://www.googleapis.com/auth/devstorage.read_only" -) - -var ( - flagJwtFile = pflag.String("google-json-key", "", - "The Google Cloud Platform Service Account JSON Key to use for authentication.") -) - -// A DockerConfigProvider that reads its configuration from Google -// Compute Engine metadata. -type jwtProvider struct { - path *string - config *jwt.Config - tokenUrl string -} - -// init registers the various means by which credentials may -// be resolved on GCP. -func init() { - credentialprovider.RegisterCredentialProvider("google-jwt-key", - &credentialprovider.CachingDockerConfigProvider{ - Provider: &jwtProvider{ - path: flagJwtFile, - }, - Lifetime: 30 * time.Minute, - }) -} - -// Enabled implements DockerConfigProvider for the JSON Key based implementation. -func (j *jwtProvider) Enabled() bool { - if *j.path == "" { - return false - } - - data, err := ioutil.ReadFile(*j.path) - if err != nil { - glog.Errorf("while reading file %s got %v", *j.path, err) - return false - } - config, err := google.JWTConfigFromJSON(data, storageReadOnlyScope) - if err != nil { - glog.Errorf("while parsing %s data got %v", *j.path, err) - return false - } - - j.config = config - if j.tokenUrl != "" { - j.config.TokenURL = j.tokenUrl - } - return true -} - -// Provide implements DockerConfigProvider -func (j *jwtProvider) Provide() credentialprovider.DockerConfig { - cfg := credentialprovider.DockerConfig{} - - ts := j.config.TokenSource(oauth2.NoContext) - token, err := ts.Token() - if err != nil { - glog.Errorf("while exchanging json key %s for access token %v", *j.path, err) - return cfg - } - if !token.Valid() { - glog.Errorf("Got back invalid token: %v", token) - return cfg - } - - entry := credentialprovider.DockerConfigEntry{ - Username: "_token", - Password: token.AccessToken, - Email: j.config.Email, - } - - // Add our entry for each of the supported container registry URLs - for _, k := range containerRegistryUrls { - cfg[k] = entry - } - return cfg -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/jwt_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/jwt_test.go deleted file mode 100644 index 8aa2ad733..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/jwt_test.go +++ /dev/null @@ -1,127 +0,0 @@ -/* -Copyright 2014 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 gcp_credentials - -import ( - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - "os" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/credentialprovider" -) - -const email = "foo@bar.com" - -// From oauth2/jwt_test.go -var ( - dummyPrivateKey = `-----BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAx4fm7dngEmOULNmAs1IGZ9Apfzh+BkaQ1dzkmbUgpcoghucE -DZRnAGd2aPyB6skGMXUytWQvNYav0WTR00wFtX1ohWTfv68HGXJ8QXCpyoSKSSFY -fuP9X36wBSkSX9J5DVgiuzD5VBdzUISSmapjKm+DcbRALjz6OUIPEWi1Tjl6p5RK -1w41qdbmt7E5/kGhKLDuT7+M83g4VWhgIvaAXtnhklDAggilPPa8ZJ1IFe31lNlr -k4DRk38nc6sEutdf3RL7QoH7FBusI7uXV03DC6dwN1kP4GE7bjJhcRb/7jYt7CQ9 -/E9Exz3c0yAp0yrTg0Fwh+qxfH9dKwN52S7SBwIDAQABAoIBAQCaCs26K07WY5Jt -3a2Cw3y2gPrIgTCqX6hJs7O5ByEhXZ8nBwsWANBUe4vrGaajQHdLj5OKfsIDrOvn -2NI1MqflqeAbu/kR32q3tq8/Rl+PPiwUsW3E6Pcf1orGMSNCXxeducF2iySySzh3 -nSIhCG5uwJDWI7a4+9KiieFgK1pt/Iv30q1SQS8IEntTfXYwANQrfKUVMmVF9aIK -6/WZE2yd5+q3wVVIJ6jsmTzoDCX6QQkkJICIYwCkglmVy5AeTckOVwcXL0jqw5Kf -5/soZJQwLEyBoQq7Kbpa26QHq+CJONetPP8Ssy8MJJXBT+u/bSseMb3Zsr5cr43e -DJOhwsThAoGBAPY6rPKl2NT/K7XfRCGm1sbWjUQyDShscwuWJ5+kD0yudnT/ZEJ1 -M3+KS/iOOAoHDdEDi9crRvMl0UfNa8MAcDKHflzxg2jg/QI+fTBjPP5GOX0lkZ9g -z6VePoVoQw2gpPFVNPPTxKfk27tEzbaffvOLGBEih0Kb7HTINkW8rIlzAoGBAM9y -1yr+jvfS1cGFtNU+Gotoihw2eMKtIqR03Yn3n0PK1nVCDKqwdUqCypz4+ml6cxRK -J8+Pfdh7D+ZJd4LEG6Y4QRDLuv5OA700tUoSHxMSNn3q9As4+T3MUyYxWKvTeu3U -f2NWP9ePU0lV8ttk7YlpVRaPQmc1qwooBA/z/8AdAoGAW9x0HWqmRICWTBnpjyxx -QGlW9rQ9mHEtUotIaRSJ6K/F3cxSGUEkX1a3FRnp6kPLcckC6NlqdNgNBd6rb2rA -cPl/uSkZP42Als+9YMoFPU/xrrDPbUhu72EDrj3Bllnyb168jKLa4VBOccUvggxr -Dm08I1hgYgdN5huzs7y6GeUCgYEAj+AZJSOJ6o1aXS6rfV3mMRve9bQ9yt8jcKXw -5HhOCEmMtaSKfnOF1Ziih34Sxsb7O2428DiX0mV/YHtBnPsAJidL0SdLWIapBzeg -KHArByIRkwE6IvJvwpGMdaex1PIGhx5i/3VZL9qiq/ElT05PhIb+UXgoWMabCp84 -OgxDK20CgYAeaFo8BdQ7FmVX2+EEejF+8xSge6WVLtkaon8bqcn6P0O8lLypoOhd -mJAYH8WU+UAy9pecUnDZj14LAGNVmYcse8HFX71MoshnvCTFEPVo4rZxIAGwMpeJ -5jgQ3slYLpqrGlcbLgUXBUgzEO684Wk/UV9DFPlHALVqCfXQ9dpJPg== ------END RSA PRIVATE KEY-----` - - jsonKey = fmt.Sprintf(`{"private_key":"%[1]s", "client_email":"%[2]s"}`, - strings.Replace(dummyPrivateKey, "\n", "\\n", -1), email) -) - -func TestJwtProvider(t *testing.T) { - token := "asdhflkjsdfkjhsdf" - - // Modeled after oauth2/jwt_test.go - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte(fmt.Sprintf(`{ - "access_token": "%[1]s", - "scope": "user", - "token_type": "bearer", - "expires_in": 3600 - }`, token))) - })) - // TODO: Uncomment when fix #19254 - // defer ts.Close() - - file, err := ioutil.TempFile(os.TempDir(), "temp") - if err != nil { - t.Fatalf("Error creating temp file: %v", err) - } - - filename := file.Name() - _, err = file.WriteString(jsonKey) - if err != nil { - t.Fatalf("Error writing temp file: %v", err) - } - - provider := &jwtProvider{ - path: &filename, - tokenUrl: ts.URL, - } - if !provider.Enabled() { - t.Fatalf("Provider is unexpectedly disabled") - } - - keyring := &credentialprovider.BasicDockerKeyring{} - keyring.Add(provider.Provide()) - - // Verify that we get the expected username/password combo for - // a gcr.io image name. - registryUrl := "gcr.io/foo/bar" - creds, ok := keyring.Lookup(registryUrl) - if !ok { - t.Errorf("Didn't find expected URL: %s", registryUrl) - return - } - if len(creds) > 1 { - t.Errorf("Got more hits than expected: %s", creds) - } - val := creds[0] - - if "_token" != val.Username { - t.Errorf("Unexpected username value, want: _token, got: %s", val.Username) - } - if token != val.Password { - t.Errorf("Unexpected password value, want: %s, got: %s", token, val.Password) - } - if email != val.Email { - t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/metadata.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/metadata.go deleted file mode 100644 index 8ab929315..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/metadata.go +++ /dev/null @@ -1,202 +0,0 @@ -/* -Copyright 2014 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 gcp_credentials - -import ( - "encoding/json" - "net/http" - "strings" - "time" - - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/credentialprovider" -) - -const ( - metadataUrl = "http://metadata.google.internal./computeMetadata/v1/" - metadataAttributes = metadataUrl + "instance/attributes/" - dockerConfigKey = metadataAttributes + "google-dockercfg" - dockerConfigUrlKey = metadataAttributes + "google-dockercfg-url" - metadataScopes = metadataUrl + "instance/service-accounts/default/scopes" - metadataToken = metadataUrl + "instance/service-accounts/default/token" - metadataEmail = metadataUrl + "instance/service-accounts/default/email" - storageScopePrefix = "https://www.googleapis.com/auth/devstorage" - cloudPlatformScopePrefix = "https://www.googleapis.com/auth/cloud-platform" -) - -// For these urls, the parts of the host name can be glob, for example '*.gcr.io" will match -// "foo.gcr.io" and "bar.gcr.io". -var containerRegistryUrls = []string{"container.cloud.google.com", "gcr.io", "*.gcr.io"} - -var metadataHeader = &http.Header{ - "Metadata-Flavor": []string{"Google"}, -} - -// A DockerConfigProvider that reads its configuration from Google -// Compute Engine metadata. -type metadataProvider struct { - Client *http.Client -} - -// A DockerConfigProvider that reads its configuration from a specific -// Google Compute Engine metadata key: 'google-dockercfg'. -type dockerConfigKeyProvider struct { - metadataProvider -} - -// A DockerConfigProvider that reads its configuration from a URL read from -// a specific Google Compute Engine metadata key: 'google-dockercfg-url'. -type dockerConfigUrlKeyProvider struct { - metadataProvider -} - -// A DockerConfigProvider that provides a dockercfg with: -// Username: "_token" -// Password: "{access token from metadata}" -type containerRegistryProvider struct { - metadataProvider -} - -// init registers the various means by which credentials may -// be resolved on GCP. -func init() { - credentialprovider.RegisterCredentialProvider("google-dockercfg", - &credentialprovider.CachingDockerConfigProvider{ - Provider: &dockerConfigKeyProvider{ - metadataProvider{Client: http.DefaultClient}, - }, - Lifetime: 60 * time.Second, - }) - - credentialprovider.RegisterCredentialProvider("google-dockercfg-url", - &credentialprovider.CachingDockerConfigProvider{ - Provider: &dockerConfigUrlKeyProvider{ - metadataProvider{Client: http.DefaultClient}, - }, - Lifetime: 60 * time.Second, - }) - - credentialprovider.RegisterCredentialProvider("google-container-registry", - // Never cache this. The access token is already - // cached by the metadata service. - &containerRegistryProvider{ - metadataProvider{Client: http.DefaultClient}, - }) -} - -// Enabled implements DockerConfigProvider for all of the Google implementations. -func (g *metadataProvider) Enabled() bool { - _, err := credentialprovider.ReadUrl(metadataUrl, g.Client, metadataHeader) - return err == nil -} - -// Provide implements DockerConfigProvider -func (g *dockerConfigKeyProvider) Provide() credentialprovider.DockerConfig { - // Read the contents of the google-dockercfg metadata key and - // parse them as an alternate .dockercfg - if cfg, err := credentialprovider.ReadDockerConfigFileFromUrl(dockerConfigKey, g.Client, metadataHeader); err != nil { - glog.Errorf("while reading 'google-dockercfg' metadata: %v", err) - } else { - return cfg - } - - return credentialprovider.DockerConfig{} -} - -// Provide implements DockerConfigProvider -func (g *dockerConfigUrlKeyProvider) Provide() credentialprovider.DockerConfig { - // Read the contents of the google-dockercfg-url key and load a .dockercfg from there - if url, err := credentialprovider.ReadUrl(dockerConfigUrlKey, g.Client, metadataHeader); err != nil { - glog.Errorf("while reading 'google-dockercfg-url' metadata: %v", err) - } else { - if strings.HasPrefix(string(url), "http") { - if cfg, err := credentialprovider.ReadDockerConfigFileFromUrl(string(url), g.Client, nil); err != nil { - glog.Errorf("while reading 'google-dockercfg-url'-specified url: %s, %v", string(url), err) - } else { - return cfg - } - } else { - // TODO(mattmoor): support reading alternate scheme URLs (e.g. gs:// or s3://) - glog.Errorf("Unsupported URL scheme: %s", string(url)) - } - } - - return credentialprovider.DockerConfig{} -} - -// Enabled implements a special metadata-based check, which verifies the -// storage scope is available on the GCE VM. -func (g *containerRegistryProvider) Enabled() bool { - value, err := credentialprovider.ReadUrl(metadataScopes+"?alt=json", g.Client, metadataHeader) - if err != nil { - return false - } - var scopes []string - if err := json.Unmarshal([]byte(value), &scopes); err != nil { - return false - } - - for _, v := range scopes { - // cloudPlatformScope implies storage scope. - if strings.HasPrefix(v, storageScopePrefix) || strings.HasPrefix(v, cloudPlatformScopePrefix) { - return true - } - } - glog.Warningf("Google container registry is disabled, no storage scope is available: %s", value) - return false -} - -// tokenBlob is used to decode the JSON blob containing an access token -// that is returned by GCE metadata. -type tokenBlob struct { - AccessToken string `json:"access_token"` -} - -// Provide implements DockerConfigProvider -func (g *containerRegistryProvider) Provide() credentialprovider.DockerConfig { - cfg := credentialprovider.DockerConfig{} - - tokenJsonBlob, err := credentialprovider.ReadUrl(metadataToken, g.Client, metadataHeader) - if err != nil { - glog.Errorf("while reading access token endpoint: %v", err) - return cfg - } - - email, err := credentialprovider.ReadUrl(metadataEmail, g.Client, metadataHeader) - if err != nil { - glog.Errorf("while reading email endpoint: %v", err) - return cfg - } - - var parsedBlob tokenBlob - if err := json.Unmarshal([]byte(tokenJsonBlob), &parsedBlob); err != nil { - glog.Errorf("while parsing json blob %s: %v", tokenJsonBlob, err) - return cfg - } - - entry := credentialprovider.DockerConfigEntry{ - Username: "_token", - Password: parsedBlob.AccessToken, - Email: string(email), - } - - // Add our entry for each of the supported container registry URLs - for _, k := range containerRegistryUrls { - cfg[k] = entry - } - return cfg -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/metadata_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/metadata_test.go deleted file mode 100644 index 5da1bcd3b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/gcp/metadata_test.go +++ /dev/null @@ -1,347 +0,0 @@ -/* -Copyright 2014 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 gcp_credentials - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "net/url" - "reflect" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/credentialprovider" -) - -func TestDockerKeyringFromGoogleDockerConfigMetadata(t *testing.T) { - registryUrl := "hello.kubernetes.io" - email := "foo@bar.baz" - username := "foo" - password := "bar" - auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password))) - sampleDockerConfig := fmt.Sprintf(`{ - "https://%s": { - "email": %q, - "auth": %q - } -}`, registryUrl, email, auth) - - const probeEndpoint = "/computeMetadata/v1/" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Only serve the one metadata key. - if probeEndpoint == r.URL.Path { - w.WriteHeader(http.StatusOK) - } else if strings.HasSuffix(dockerConfigKey, r.URL.Path) { - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "application/json") - fmt.Fprintln(w, sampleDockerConfig) - } else { - w.WriteHeader(http.StatusNotFound) - } - })) - // TODO: Uncomment when fix #19254 - // defer server.Close() - - // Make a transport that reroutes all traffic to the example server - transport := &http.Transport{ - Proxy: func(req *http.Request) (*url.URL, error) { - return url.Parse(server.URL + req.URL.Path) - }, - } - - keyring := &credentialprovider.BasicDockerKeyring{} - provider := &dockerConfigKeyProvider{ - metadataProvider{Client: &http.Client{Transport: transport}}, - } - - if !provider.Enabled() { - t.Errorf("Provider is unexpectedly disabled") - } - - keyring.Add(provider.Provide()) - - creds, ok := keyring.Lookup(registryUrl) - if !ok { - t.Errorf("Didn't find expected URL: %s", registryUrl) - return - } - if len(creds) > 1 { - t.Errorf("Got more hits than expected: %s", creds) - } - val := creds[0] - - if username != val.Username { - t.Errorf("Unexpected username value, want: %s, got: %s", username, val.Username) - } - if password != val.Password { - t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password) - } - if email != val.Email { - t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email) - } -} - -func TestDockerKeyringFromGoogleDockerConfigMetadataUrl(t *testing.T) { - registryUrl := "hello.kubernetes.io" - email := "foo@bar.baz" - username := "foo" - password := "bar" - auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password))) - sampleDockerConfig := fmt.Sprintf(`{ - "https://%s": { - "email": %q, - "auth": %q - } -}`, registryUrl, email, auth) - - const probeEndpoint = "/computeMetadata/v1/" - const valueEndpoint = "/my/value" - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Only serve the URL key and the value endpoint - if probeEndpoint == r.URL.Path { - w.WriteHeader(http.StatusOK) - } else if valueEndpoint == r.URL.Path { - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "application/json") - fmt.Fprintln(w, sampleDockerConfig) - } else if strings.HasSuffix(dockerConfigUrlKey, r.URL.Path) { - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "application/text") - fmt.Fprint(w, "http://foo.bar.com"+valueEndpoint) - } else { - w.WriteHeader(http.StatusNotFound) - } - })) - // TODO: Uncomment when fix #19254 - // defer server.Close() - - // Make a transport that reroutes all traffic to the example server - transport := &http.Transport{ - Proxy: func(req *http.Request) (*url.URL, error) { - return url.Parse(server.URL + req.URL.Path) - }, - } - - keyring := &credentialprovider.BasicDockerKeyring{} - provider := &dockerConfigUrlKeyProvider{ - metadataProvider{Client: &http.Client{Transport: transport}}, - } - - if !provider.Enabled() { - t.Errorf("Provider is unexpectedly disabled") - } - - keyring.Add(provider.Provide()) - - creds, ok := keyring.Lookup(registryUrl) - if !ok { - t.Errorf("Didn't find expected URL: %s", registryUrl) - return - } - if len(creds) > 1 { - t.Errorf("Got more hits than expected: %s", creds) - } - val := creds[0] - - if username != val.Username { - t.Errorf("Unexpected username value, want: %s, got: %s", username, val.Username) - } - if password != val.Password { - t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password) - } - if email != val.Email { - t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email) - } -} - -func TestContainerRegistryBasics(t *testing.T) { - registryUrl := "container.cloud.google.com" - email := "1234@project.gserviceaccount.com" - token := &tokenBlob{AccessToken: "ya26.lots-of-indiscernible-garbage"} - - const ( - defaultEndpoint = "/computeMetadata/v1/instance/service-accounts/default/" - scopeEndpoint = defaultEndpoint + "scopes" - emailEndpoint = defaultEndpoint + "email" - tokenEndpoint = defaultEndpoint + "token" - ) - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Only serve the URL key and the value endpoint - if scopeEndpoint == r.URL.Path { - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "application/json") - fmt.Fprintf(w, `["%s.read_write"]`, storageScopePrefix) - } else if emailEndpoint == r.URL.Path { - w.WriteHeader(http.StatusOK) - fmt.Fprint(w, email) - } else if tokenEndpoint == r.URL.Path { - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "application/json") - bytes, err := json.Marshal(token) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - fmt.Fprintln(w, string(bytes)) - } else { - w.WriteHeader(http.StatusNotFound) - } - })) - // TODO: Uncomment when fix #19254 - // defer server.Close() - - // Make a transport that reroutes all traffic to the example server - transport := &http.Transport{ - Proxy: func(req *http.Request) (*url.URL, error) { - return url.Parse(server.URL + req.URL.Path) - }, - } - - keyring := &credentialprovider.BasicDockerKeyring{} - provider := &containerRegistryProvider{ - metadataProvider{Client: &http.Client{Transport: transport}}, - } - - if !provider.Enabled() { - t.Errorf("Provider is unexpectedly disabled") - } - - keyring.Add(provider.Provide()) - - creds, ok := keyring.Lookup(registryUrl) - if !ok { - t.Errorf("Didn't find expected URL: %s", registryUrl) - return - } - if len(creds) > 1 { - t.Errorf("Got more hits than expected: %s", creds) - } - val := creds[0] - - if "_token" != val.Username { - t.Errorf("Unexpected username value, want: %s, got: %s", "_token", val.Username) - } - if token.AccessToken != val.Password { - t.Errorf("Unexpected password value, want: %s, got: %s", token.AccessToken, val.Password) - } - if email != val.Email { - t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email) - } -} - -func TestContainerRegistryNoStorageScope(t *testing.T) { - const ( - defaultEndpoint = "/computeMetadata/v1/instance/service-accounts/default/" - scopeEndpoint = defaultEndpoint + "scopes" - ) - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Only serve the URL key and the value endpoint - if scopeEndpoint == r.URL.Path { - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "application/json") - fmt.Fprint(w, `["https://www.googleapis.com/auth/compute.read_write"]`) - } else { - w.WriteHeader(http.StatusNotFound) - } - })) - // TODO: Uncomment when fix #19254 - // defer server.Close() - - // Make a transport that reroutes all traffic to the example server - transport := &http.Transport{ - Proxy: func(req *http.Request) (*url.URL, error) { - return url.Parse(server.URL + req.URL.Path) - }, - } - - provider := &containerRegistryProvider{ - metadataProvider{Client: &http.Client{Transport: transport}}, - } - - if provider.Enabled() { - t.Errorf("Provider is unexpectedly enabled") - } -} - -func TestComputePlatformScopeSubstitutesStorageScope(t *testing.T) { - const ( - defaultEndpoint = "/computeMetadata/v1/instance/service-accounts/default/" - scopeEndpoint = defaultEndpoint + "scopes" - ) - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Only serve the URL key and the value endpoint - if scopeEndpoint == r.URL.Path { - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "application/json") - fmt.Fprint(w, `["https://www.googleapis.com/auth/compute.read_write","https://www.googleapis.com/auth/cloud-platform.read-only"]`) - } else { - w.WriteHeader(http.StatusNotFound) - } - })) - // TODO: Uncomment when fix #19254 - // defer server.Close() - - // Make a transport that reroutes all traffic to the example server - transport := &http.Transport{ - Proxy: func(req *http.Request) (*url.URL, error) { - return url.Parse(server.URL + req.URL.Path) - }, - } - - provider := &containerRegistryProvider{ - metadataProvider{Client: &http.Client{Transport: transport}}, - } - - if !provider.Enabled() { - t.Errorf("Provider is unexpectedly disabled") - } -} - -func TestAllProvidersNoMetadata(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotFound) - })) - // TODO: Uncomment when fix #19254 - // defer server.Close() - - // Make a transport that reroutes all traffic to the example server - transport := &http.Transport{ - Proxy: func(req *http.Request) (*url.URL, error) { - return url.Parse(server.URL + req.URL.Path) - }, - } - - providers := []credentialprovider.DockerConfigProvider{ - &dockerConfigKeyProvider{ - metadataProvider{Client: &http.Client{Transport: transport}}, - }, - &dockerConfigUrlKeyProvider{ - metadataProvider{Client: &http.Client{Transport: transport}}, - }, - &containerRegistryProvider{ - metadataProvider{Client: &http.Client{Transport: transport}}, - }, - } - - for _, provider := range providers { - if provider.Enabled() { - t.Errorf("Provider %s is unexpectedly enabled", reflect.TypeOf(provider).String()) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/keyring.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/keyring.go index 0ea079aee..2378156dc 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/keyring.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/keyring.go @@ -67,7 +67,11 @@ func (dk *BasicDockerKeyring) Add(cfg DockerConfig) { Email: ident.Email, } - parsed, err := url.Parse(loc) + value := loc + if !strings.HasPrefix(value, "https://") && !strings.HasPrefix(value, "http://") { + value = "https://" + value + } + parsed, err := url.Parse(value) if err != nil { glog.Errorf("Entry %q in dockercfg invalid (%v), ignoring", loc, err) continue @@ -77,17 +81,20 @@ func (dk *BasicDockerKeyring) Add(cfg DockerConfig) { // foo.bar.com/namespace // Or hostname matches: // foo.bar.com + // It also considers /v2/ and /v1/ equivalent to the hostname // See ResolveAuthConfig in docker/registry/auth.go. - if parsed.Host != "" { - // NOTE: foo.bar.com comes through as Path. - dk.creds[parsed.Host] = append(dk.creds[parsed.Host], creds) - dk.index = append(dk.index, parsed.Host) + effectivePath := parsed.Path + if strings.HasPrefix(effectivePath, "/v2/") || strings.HasPrefix(effectivePath, "/v1/") { + effectivePath = effectivePath[3:] } - if (len(parsed.Path) > 0) && (parsed.Path != "/") { - key := parsed.Host + parsed.Path - dk.creds[key] = append(dk.creds[key], creds) - dk.index = append(dk.index, key) + var key string + if (len(effectivePath) > 0) && (effectivePath != "/") { + key = parsed.Host + effectivePath + } else { + key = parsed.Host } + dk.creds[key] = append(dk.creds[key], creds) + dk.index = append(dk.index, key) } eliminateDupes := sets.NewString(dk.index...) @@ -100,7 +107,10 @@ func (dk *BasicDockerKeyring) Add(cfg DockerConfig) { sort.Sort(sort.Reverse(sort.StringSlice(dk.index))) } -const defaultRegistryHost = "index.docker.io/v1/" +const ( + defaultRegistryHost = "index.docker.io" + defaultRegistryKey = defaultRegistryHost + "/v1/" +) // isDefaultRegistryMatch determines whether the given image will // pull from the default registry (DockerHub) based on the @@ -223,8 +233,10 @@ func (dk *BasicDockerKeyring) Lookup(image string) ([]docker.AuthConfiguration, } // Use credentials for the default registry if provided, and appropriate - if auth, ok := dk.creds[defaultRegistryHost]; ok && isDefaultRegistryMatch(image) { - return auth, true + if isDefaultRegistryMatch(image) { + if auth, ok := dk.creds[defaultRegistryHost]; ok { + return auth, true + } } return []docker.AuthConfiguration{}, false diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/keyring_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/keyring_test.go deleted file mode 100644 index cd92d79b2..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/keyring_test.go +++ /dev/null @@ -1,476 +0,0 @@ -/* -Copyright 2014 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 credentialprovider - -import ( - "encoding/base64" - "fmt" - "testing" -) - -func TestUrlsMatch(t *testing.T) { - tests := []struct { - globUrl string - targetUrl string - matchExpected bool - }{ - // match when there is no path component - { - globUrl: "*.kubernetes.io", - targetUrl: "prefix.kubernetes.io", - matchExpected: true, - }, - { - globUrl: "prefix.*.io", - targetUrl: "prefix.kubernetes.io", - matchExpected: true, - }, - { - globUrl: "prefix.kubernetes.*", - targetUrl: "prefix.kubernetes.io", - matchExpected: true, - }, - { - globUrl: "*-good.kubernetes.io", - targetUrl: "prefix-good.kubernetes.io", - matchExpected: true, - }, - // match with path components - { - globUrl: "*.kubernetes.io/blah", - targetUrl: "prefix.kubernetes.io/blah", - matchExpected: true, - }, - { - globUrl: "prefix.*.io/foo", - targetUrl: "prefix.kubernetes.io/foo/bar", - matchExpected: true, - }, - // match with path components and ports - { - globUrl: "*.kubernetes.io:1111/blah", - targetUrl: "prefix.kubernetes.io:1111/blah", - matchExpected: true, - }, - { - globUrl: "prefix.*.io:1111/foo", - targetUrl: "prefix.kubernetes.io:1111/foo/bar", - matchExpected: true, - }, - // no match when number of parts mismatch - { - globUrl: "*.kubernetes.io", - targetUrl: "kubernetes.io", - matchExpected: false, - }, - { - globUrl: "*.*.kubernetes.io", - targetUrl: "prefix.kubernetes.io", - matchExpected: false, - }, - { - globUrl: "*.*.kubernetes.io", - targetUrl: "kubernetes.io", - matchExpected: false, - }, - // no match when some parts mismatch - { - globUrl: "kubernetes.io", - targetUrl: "kubernetes.com", - matchExpected: false, - }, - { - globUrl: "k*.io", - targetUrl: "quay.io", - matchExpected: false, - }, - // no match when ports mismatch - { - globUrl: "*.kubernetes.io:1234/blah", - targetUrl: "prefix.kubernetes.io:1111/blah", - matchExpected: false, - }, - { - globUrl: "prefix.*.io/foo", - targetUrl: "prefix.kubernetes.io:1111/foo/bar", - matchExpected: false, - }, - } - for _, test := range tests { - matched, _ := urlsMatchStr(test.globUrl, test.targetUrl) - if matched != test.matchExpected { - t.Errorf("Expected match result of %s and %s to be %t, but was %t", - test.globUrl, test.targetUrl, test.matchExpected, matched) - } - } -} - -func TestDockerKeyringForGlob(t *testing.T) { - tests := []struct { - globUrl string - targetUrl string - }{ - { - globUrl: "hello.kubernetes.io", - targetUrl: "hello.kubernetes.io", - }, - { - globUrl: "*.docker.io", - targetUrl: "prefix.docker.io", - }, - { - globUrl: "prefix.*.io", - targetUrl: "prefix.docker.io", - }, - { - globUrl: "prefix.docker.*", - targetUrl: "prefix.docker.io", - }, - { - globUrl: "*.docker.io/path", - targetUrl: "prefix.docker.io/path", - }, - { - globUrl: "prefix.*.io/path", - targetUrl: "prefix.docker.io/path/subpath", - }, - { - globUrl: "prefix.docker.*/path", - targetUrl: "prefix.docker.io/path", - }, - { - globUrl: "*.docker.io:8888", - targetUrl: "prefix.docker.io:8888", - }, - { - globUrl: "prefix.*.io:8888", - targetUrl: "prefix.docker.io:8888", - }, - { - globUrl: "prefix.docker.*:8888", - targetUrl: "prefix.docker.io:8888", - }, - { - globUrl: "*.docker.io/path:1111", - targetUrl: "prefix.docker.io/path:1111", - }, - { - globUrl: "prefix.*.io/path:1111", - targetUrl: "prefix.docker.io/path/subpath:1111", - }, - { - globUrl: "prefix.docker.*/path:1111", - targetUrl: "prefix.docker.io/path:1111", - }, - } - for _, test := range tests { - email := "foo@bar.baz" - username := "foo" - password := "bar" - auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password))) - sampleDockerConfig := fmt.Sprintf(`{ - "https://%s": { - "email": %q, - "auth": %q - } -}`, test.globUrl, email, auth) - - keyring := &BasicDockerKeyring{} - if cfg, err := readDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil { - t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err) - } else { - keyring.Add(cfg) - } - - creds, ok := keyring.Lookup(test.targetUrl + "/foo/bar") - if !ok { - t.Errorf("Didn't find expected URL: %s", test.targetUrl) - return - } - val := creds[0] - - if username != val.Username { - t.Errorf("Unexpected username value, want: %s, got: %s", username, val.Username) - } - if password != val.Password { - t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password) - } - if email != val.Email { - t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email) - } - } -} - -func TestKeyringMiss(t *testing.T) { - tests := []struct { - globUrl string - lookupUrl string - }{ - { - globUrl: "hello.kubernetes.io", - lookupUrl: "world.mesos.org/foo/bar", - }, - { - globUrl: "*.docker.com", - lookupUrl: "prefix.docker.io", - }, - { - globUrl: "suffix.*.io", - lookupUrl: "prefix.docker.io", - }, - { - globUrl: "prefix.docker.c*", - lookupUrl: "prefix.docker.io", - }, - } - for _, test := range tests { - email := "foo@bar.baz" - username := "foo" - password := "bar" - auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password))) - sampleDockerConfig := fmt.Sprintf(`{ - "https://%s": { - "email": %q, - "auth": %q - } -}`, test.globUrl, email, auth) - - keyring := &BasicDockerKeyring{} - if cfg, err := readDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil { - t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err) - } else { - keyring.Add(cfg) - } - - _, ok := keyring.Lookup(test.lookupUrl + "/foo/bar") - if ok { - t.Errorf("Expected not to find URL %s, but found", test.lookupUrl) - } - } - -} - -func TestKeyringMissWithDockerHubCredentials(t *testing.T) { - url := defaultRegistryHost - email := "foo@bar.baz" - username := "foo" - password := "bar" - auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password))) - sampleDockerConfig := fmt.Sprintf(`{ - "https://%s": { - "email": %q, - "auth": %q - } -}`, url, email, auth) - - keyring := &BasicDockerKeyring{} - if cfg, err := readDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil { - t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err) - } else { - keyring.Add(cfg) - } - - val, ok := keyring.Lookup("world.mesos.org/foo/bar") - if ok { - t.Errorf("Found unexpected credential: %+v", val) - } -} - -func TestKeyringHitWithUnqualifiedDockerHub(t *testing.T) { - url := defaultRegistryHost - email := "foo@bar.baz" - username := "foo" - password := "bar" - auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password))) - sampleDockerConfig := fmt.Sprintf(`{ - "https://%s": { - "email": %q, - "auth": %q - } -}`, url, email, auth) - - keyring := &BasicDockerKeyring{} - if cfg, err := readDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil { - t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err) - } else { - keyring.Add(cfg) - } - - creds, ok := keyring.Lookup("google/docker-registry") - if !ok { - t.Errorf("Didn't find expected URL: %s", url) - return - } - if len(creds) > 1 { - t.Errorf("Got more hits than expected: %s", creds) - } - val := creds[0] - - if username != val.Username { - t.Errorf("Unexpected username value, want: %s, got: %s", username, val.Username) - } - if password != val.Password { - t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password) - } - if email != val.Email { - t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email) - } -} - -func TestKeyringHitWithUnqualifiedLibraryDockerHub(t *testing.T) { - url := defaultRegistryHost - email := "foo@bar.baz" - username := "foo" - password := "bar" - auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password))) - sampleDockerConfig := fmt.Sprintf(`{ - "https://%s": { - "email": %q, - "auth": %q - } -}`, url, email, auth) - - keyring := &BasicDockerKeyring{} - if cfg, err := readDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil { - t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err) - } else { - keyring.Add(cfg) - } - - creds, ok := keyring.Lookup("jenkins") - if !ok { - t.Errorf("Didn't find expected URL: %s", url) - return - } - if len(creds) > 1 { - t.Errorf("Got more hits than expected: %s", creds) - } - val := creds[0] - - if username != val.Username { - t.Errorf("Unexpected username value, want: %s, got: %s", username, val.Username) - } - if password != val.Password { - t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password) - } - if email != val.Email { - t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email) - } -} - -func TestKeyringHitWithQualifiedDockerHub(t *testing.T) { - url := defaultRegistryHost - email := "foo@bar.baz" - username := "foo" - password := "bar" - auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password))) - sampleDockerConfig := fmt.Sprintf(`{ - "https://%s": { - "email": %q, - "auth": %q - } -}`, url, email, auth) - - keyring := &BasicDockerKeyring{} - if cfg, err := readDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil { - t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err) - } else { - keyring.Add(cfg) - } - - creds, ok := keyring.Lookup(url + "/google/docker-registry") - if !ok { - t.Errorf("Didn't find expected URL: %s", url) - return - } - if len(creds) > 2 { - t.Errorf("Got more hits than expected: %s", creds) - } - val := creds[0] - - if username != val.Username { - t.Errorf("Unexpected username value, want: %s, got: %s", username, val.Username) - } - if password != val.Password { - t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password) - } - if email != val.Email { - t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email) - } -} - -func TestIsDefaultRegistryMatch(t *testing.T) { - samples := []map[bool]string{ - {true: "foo/bar"}, - {true: "docker.io/foo/bar"}, - {true: "index.docker.io/foo/bar"}, - {true: "foo"}, - {false: ""}, - {false: "registry.tld/foo/bar"}, - {false: "registry:5000/foo/bar"}, - {false: "myhostdocker.io/foo/bar"}, - } - for _, sample := range samples { - for expected, imageName := range sample { - if got := isDefaultRegistryMatch(imageName); got != expected { - t.Errorf("Expected '%s' to be %t, got %t", imageName, expected, got) - } - } - } -} - -type testProvider struct { - Count int -} - -// Enabled implements dockerConfigProvider -func (d *testProvider) Enabled() bool { - return true -} - -// Provide implements dockerConfigProvider -func (d *testProvider) Provide() DockerConfig { - d.Count += 1 - return DockerConfig{} -} - -func TestLazyKeyring(t *testing.T) { - provider := &testProvider{ - Count: 0, - } - lazy := &lazyDockerKeyring{ - Providers: []DockerConfigProvider{ - provider, - }, - } - - if provider.Count != 0 { - t.Errorf("Unexpected number of Provide calls: %v", provider.Count) - } - lazy.Lookup("foo") - if provider.Count != 1 { - t.Errorf("Unexpected number of Provide calls: %v", provider.Count) - } - lazy.Lookup("foo") - if provider.Count != 2 { - t.Errorf("Unexpected number of Provide calls: %v", provider.Count) - } - lazy.Lookup("foo") - if provider.Count != 3 { - t.Errorf("Unexpected number of Provide calls: %v", provider.Count) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/provider_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/provider_test.go deleted file mode 100644 index 099d839fb..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/credentialprovider/provider_test.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright 2014 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 credentialprovider - -import ( - "testing" - "time" -) - -func TestCachingProvider(t *testing.T) { - provider := &testProvider{ - Count: 0, - } - - cache := &CachingDockerConfigProvider{ - Provider: provider, - Lifetime: 1 * time.Second, - } - - if provider.Count != 0 { - t.Errorf("Unexpected number of Provide calls: %v", provider.Count) - } - cache.Provide() - cache.Provide() - cache.Provide() - cache.Provide() - if provider.Count != 1 { - t.Errorf("Unexpected number of Provide calls: %v", provider.Count) - } - - time.Sleep(cache.Lifetime) - cache.Provide() - cache.Provide() - cache.Provide() - cache.Provide() - if provider.Count != 2 { - t.Errorf("Unexpected number of Provide calls: %v", provider.Count) - } - - time.Sleep(cache.Lifetime) - cache.Provide() - cache.Provide() - cache.Provide() - cache.Provide() - if provider.Count != 3 { - t.Errorf("Unexpected number of Provide calls: %v", provider.Count) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/fieldpath/fieldpath_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/fieldpath/fieldpath_test.go deleted file mode 100644 index d510426ee..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/fieldpath/fieldpath_test.go +++ /dev/null @@ -1,117 +0,0 @@ -/* -Copyright 2015 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 fieldpath - -import ( - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api" -) - -func TestExtractFieldPathAsString(t *testing.T) { - cases := []struct { - name string - fieldPath string - obj interface{} - expectedValue string - expectedMessageFragment string - }{ - { - name: "not an API object", - fieldPath: "metadata.name", - obj: "", - expectedMessageFragment: "expected struct", - }, - { - name: "ok - namespace", - fieldPath: "metadata.namespace", - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Namespace: "object-namespace", - }, - }, - expectedValue: "object-namespace", - }, - { - name: "ok - name", - fieldPath: "metadata.name", - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "object-name", - }, - }, - expectedValue: "object-name", - }, - { - name: "ok - labels", - fieldPath: "metadata.labels", - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"key": "value"}, - }, - }, - expectedValue: "key=\"value\"\n", - }, - { - name: "ok - labels bslash n", - fieldPath: "metadata.labels", - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"key": "value\n"}, - }, - }, - expectedValue: "key=\"value\\n\"\n", - }, - { - name: "ok - annotations", - fieldPath: "metadata.annotations", - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{"builder": "john-doe"}, - }, - }, - expectedValue: "builder=\"john-doe\"\n", - }, - - { - name: "invalid expression", - fieldPath: "metadata.whoops", - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Namespace: "object-namespace", - }, - }, - expectedMessageFragment: "Unsupported fieldPath", - }, - } - - for _, tc := range cases { - actual, err := ExtractFieldPathAsString(tc.obj, tc.fieldPath) - if err != nil { - if tc.expectedMessageFragment != "" { - if !strings.Contains(err.Error(), tc.expectedMessageFragment) { - t.Errorf("%v: Unexpected error message: %q, expected to contain %q", tc.name, err, tc.expectedMessageFragment) - } - } else { - t.Errorf("%v: unexpected error: %v", tc.name, err) - } - } else if e := tc.expectedValue; e != "" && e != actual { - t.Errorf("%v: Unexpected result; got %q, expected %q", tc.name, actual, e) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/fields/fields_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/fields/fields_test.go deleted file mode 100644 index 9f6f3fc35..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/fields/fields_test.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright 2015 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 fields - -import ( - "testing" -) - -func matches(t *testing.T, ls Set, want string) { - if ls.String() != want { - t.Errorf("Expected '%s', but got '%s'", want, ls.String()) - } -} - -func TestSetString(t *testing.T) { - matches(t, Set{"x": "y"}, "x=y") - matches(t, Set{"foo": "bar"}, "foo=bar") - matches(t, Set{"foo": "bar", "baz": "qup"}, "baz=qup,foo=bar") -} - -func TestFieldHas(t *testing.T) { - fieldHasTests := []struct { - Ls Fields - Key string - Has bool - }{ - {Set{"x": "y"}, "x", true}, - {Set{"x": ""}, "x", true}, - {Set{"x": "y"}, "foo", false}, - } - for _, lh := range fieldHasTests { - if has := lh.Ls.Has(lh.Key); has != lh.Has { - t.Errorf("%#v.Has(%#v) => %v, expected %v", lh.Ls, lh.Key, has, lh.Has) - } - } -} - -func TestFieldGet(t *testing.T) { - ls := Set{"x": "y"} - if ls.Get("x") != "y" { - t.Errorf("Set.Get is broken") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/fields/selector_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/fields/selector_test.go deleted file mode 100644 index 7651ae6bb..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/fields/selector_test.go +++ /dev/null @@ -1,208 +0,0 @@ -/* -Copyright 2015 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 fields - -import ( - "testing" -) - -func TestSelectorParse(t *testing.T) { - testGoodStrings := []string{ - "x=a,y=b,z=c", - "", - "x!=a,y=b", - } - testBadStrings := []string{ - "x=a||y=b", - "x==a==b", - } - for _, test := range testGoodStrings { - lq, err := ParseSelector(test) - if err != nil { - t.Errorf("%v: error %v (%#v)\n", test, err, err) - } - if test != lq.String() { - t.Errorf("%v restring gave: %v\n", test, lq.String()) - } - } - for _, test := range testBadStrings { - _, err := ParseSelector(test) - if err == nil { - t.Errorf("%v: did not get expected error\n", test) - } - } -} - -func TestDeterministicParse(t *testing.T) { - s1, err := ParseSelector("x=a,a=x") - s2, err2 := ParseSelector("a=x,x=a") - if err != nil || err2 != nil { - t.Errorf("Unexpected parse error") - } - if s1.String() != s2.String() { - t.Errorf("Non-deterministic parse") - } -} - -func expectMatch(t *testing.T, selector string, ls Set) { - lq, err := ParseSelector(selector) - if err != nil { - t.Errorf("Unable to parse %v as a selector\n", selector) - return - } - if !lq.Matches(ls) { - t.Errorf("Wanted %s to match '%s', but it did not.\n", selector, ls) - } -} - -func expectNoMatch(t *testing.T, selector string, ls Set) { - lq, err := ParseSelector(selector) - if err != nil { - t.Errorf("Unable to parse %v as a selector\n", selector) - return - } - if lq.Matches(ls) { - t.Errorf("Wanted '%s' to not match '%s', but it did.", selector, ls) - } -} - -func TestEverything(t *testing.T) { - if !Everything().Matches(Set{"x": "y"}) { - t.Errorf("Nil selector didn't match") - } - if !Everything().Empty() { - t.Errorf("Everything was not empty") - } -} - -func TestSelectorMatches(t *testing.T) { - expectMatch(t, "", Set{"x": "y"}) - expectMatch(t, "x=y", Set{"x": "y"}) - expectMatch(t, "x=y,z=w", Set{"x": "y", "z": "w"}) - expectMatch(t, "x!=y,z!=w", Set{"x": "z", "z": "a"}) - expectMatch(t, "notin=in", Set{"notin": "in"}) // in and notin in exactMatch - expectNoMatch(t, "x=y", Set{"x": "z"}) - expectNoMatch(t, "x=y,z=w", Set{"x": "w", "z": "w"}) - expectNoMatch(t, "x!=y,z!=w", Set{"x": "z", "z": "w"}) - - labelset := Set{ - "foo": "bar", - "baz": "blah", - } - expectMatch(t, "foo=bar", labelset) - expectMatch(t, "baz=blah", labelset) - expectMatch(t, "foo=bar,baz=blah", labelset) - expectNoMatch(t, "foo=blah", labelset) - expectNoMatch(t, "baz=bar", labelset) - expectNoMatch(t, "foo=bar,foobar=bar,baz=blah", labelset) -} - -func TestOneTermEqualSelector(t *testing.T) { - if !OneTermEqualSelector("x", "y").Matches(Set{"x": "y"}) { - t.Errorf("No match when match expected.") - } - if OneTermEqualSelector("x", "y").Matches(Set{"x": "z"}) { - t.Errorf("Match when none expected.") - } -} - -func expectMatchDirect(t *testing.T, selector, ls Set) { - if !SelectorFromSet(selector).Matches(ls) { - t.Errorf("Wanted %s to match '%s', but it did not.\n", selector, ls) - } -} - -func expectNoMatchDirect(t *testing.T, selector, ls Set) { - if SelectorFromSet(selector).Matches(ls) { - t.Errorf("Wanted '%s' to not match '%s', but it did.", selector, ls) - } -} - -func TestSetMatches(t *testing.T) { - labelset := Set{ - "foo": "bar", - "baz": "blah", - } - expectMatchDirect(t, Set{}, labelset) - expectMatchDirect(t, Set{"foo": "bar"}, labelset) - expectMatchDirect(t, Set{"baz": "blah"}, labelset) - expectMatchDirect(t, Set{"foo": "bar", "baz": "blah"}, labelset) - expectNoMatchDirect(t, Set{"foo": "=blah"}, labelset) - expectNoMatchDirect(t, Set{"baz": "=bar"}, labelset) - expectNoMatchDirect(t, Set{"foo": "=bar", "foobar": "bar", "baz": "blah"}, labelset) -} - -func TestNilMapIsValid(t *testing.T) { - selector := Set(nil).AsSelector() - if selector == nil { - t.Errorf("Selector for nil set should be Everything") - } - if !selector.Empty() { - t.Errorf("Selector for nil set should be Empty") - } -} - -func TestSetIsEmpty(t *testing.T) { - if !(Set{}).AsSelector().Empty() { - t.Errorf("Empty set should be empty") - } - if !(andTerm(nil)).Empty() { - t.Errorf("Nil andTerm should be empty") - } - if (&hasTerm{}).Empty() { - t.Errorf("hasTerm should not be empty") - } - if (¬HasTerm{}).Empty() { - t.Errorf("notHasTerm should not be empty") - } - if !(andTerm{andTerm{}}).Empty() { - t.Errorf("Nested andTerm should be empty") - } - if (andTerm{&hasTerm{"a", "b"}}).Empty() { - t.Errorf("Nested andTerm should not be empty") - } -} - -func TestRequiresExactMatch(t *testing.T) { - testCases := map[string]struct { - S Selector - Label string - Value string - Found bool - }{ - "empty set": {Set{}.AsSelector(), "test", "", false}, - "nil andTerm": {andTerm(nil), "test", "", false}, - "empty hasTerm": {&hasTerm{}, "test", "", false}, - "skipped hasTerm": {&hasTerm{"a", "b"}, "test", "", false}, - "valid hasTerm": {&hasTerm{"test", "b"}, "test", "b", true}, - "valid hasTerm no value": {&hasTerm{"test", ""}, "test", "", true}, - "valid notHasTerm": {¬HasTerm{"test", "b"}, "test", "", false}, - "valid notHasTerm no value": {¬HasTerm{"test", ""}, "test", "", false}, - "nested andTerm": {andTerm{andTerm{}}, "test", "", false}, - "nested andTerm matches": {andTerm{&hasTerm{"test", "b"}}, "test", "b", true}, - "andTerm with non-match": {andTerm{&hasTerm{}, &hasTerm{"test", "b"}}, "test", "b", true}, - } - for k, v := range testCases { - value, found := v.S.RequiresExactMatch(v.Label) - if value != v.Value { - t.Errorf("%s: expected value %s, got %s", k, v.Value, value) - } - if found != v.Found { - t.Errorf("%s: expected found %t, got %t", k, v.Found, found) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/OWNERS b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/OWNERS new file mode 100644 index 000000000..66431ddd0 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/OWNERS @@ -0,0 +1,7 @@ +assignees: + - bgrant0607 + - brendandburns + - deads2k + - janetkuo + - jlowdermilk + - smarterclayton diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/annotate.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/annotate.go deleted file mode 100644 index daebf57d1..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/annotate.go +++ /dev/null @@ -1,350 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "strings" - - "github.com/golang/glog" - "github.com/spf13/cobra" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/strategicpatch" -) - -// AnnotateOptions have the data required to perform the annotate operation -type AnnotateOptions struct { - resources []string - newAnnotations map[string]string - removeAnnotations []string - builder *resource.Builder - filenames []string - selector string - - overwrite bool - all bool - resourceVersion string - - changeCause string - recordChangeCause bool - - f *cmdutil.Factory - out io.Writer - cmd *cobra.Command -} - -const ( - annotate_long = `Update the annotations on one or more resources. - -An annotation is a key/value pair that can hold larger (compared to a label), and possibly not human-readable, data. -It is intended to store non-identifying auxiliary data, especially data manipulated by tools and system extensions. -If --overwrite is true, then existing annotations can be overwritten, otherwise attempting to overwrite an annotation will result in an error. -If --resource-version is specified, then updates will use this resource version, otherwise the existing resource-version will be used. - -Possible resources include (case insensitive): pods (po), services (svc), -replicationcontrollers (rc), nodes (no), events (ev), componentstatuses (cs), -limitranges (limits), persistentvolumes (pv), persistentvolumeclaims (pvc), -horizontalpodautoscalers (hpa), resourcequotas (quota) or secrets.` - annotate_example = `# Update pod 'foo' with the annotation 'description' and the value 'my frontend'. -# If the same annotation is set multiple times, only the last value will be applied -$ kubectl annotate pods foo description='my frontend' - -# Update a pod identified by type and name in "pod.json" -$ kubectl annotate -f pod.json description='my frontend' - -# Update pod 'foo' with the annotation 'description' and the value 'my frontend running nginx', overwriting any existing value. -$ kubectl annotate --overwrite pods foo description='my frontend running nginx' - -# Update all pods in the namespace -$ kubectl annotate pods --all description='my frontend running nginx' - -# Update pod 'foo' only if the resource is unchanged from version 1. -$ kubectl annotate pods foo description='my frontend running nginx' --resource-version=1 - -# Update pod 'foo' by removing an annotation named 'description' if it exists. -# Does not require the --overwrite flag. -$ kubectl annotate pods foo description-` -) - -func NewCmdAnnotate(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &AnnotateOptions{} - - cmd := &cobra.Command{ - Use: "annotate [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]", - Short: "Update the annotations on a resource", - Long: annotate_long, - Example: annotate_example, - Run: func(cmd *cobra.Command, args []string) { - if err := options.Complete(f, out, cmd, args); err != nil { - cmdutil.CheckErr(err) - } - if err := options.Validate(args); err != nil { - cmdutil.CheckErr(cmdutil.UsageError(cmd, err.Error())) - } - if err := options.RunAnnotate(); err != nil { - cmdutil.CheckErr(err) - } - }, - } - cmdutil.AddPrinterFlags(cmd) - cmd.Flags().StringVarP(&options.selector, "selector", "l", "", "Selector (label query) to filter on") - cmd.Flags().BoolVar(&options.overwrite, "overwrite", false, "If true, allow annotations to be overwritten, otherwise reject annotation updates that overwrite existing annotations.") - cmd.Flags().BoolVar(&options.all, "all", false, "select all resources in the namespace of the specified resource types") - cmd.Flags().StringVar(&options.resourceVersion, "resource-version", "", "If non-empty, the annotation update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource.") - usage := "Filename, directory, or URL to a file identifying the resource to update the annotation" - kubectl.AddJsonFilenameFlag(cmd, &options.filenames, usage) - cmdutil.AddRecordFlag(cmd) - return cmd -} - -// Complete adapts from the command line args and factory to the data required. -func (o *AnnotateOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) (err error) { - - namespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - // retrieves resource and annotation args from args - // also checks args to verify that all resources are specified before annotations - annotationArgs := []string{} - metAnnotaionArg := false - for _, s := range args { - isAnnotation := strings.Contains(s, "=") || strings.HasSuffix(s, "-") - switch { - case !metAnnotaionArg && isAnnotation: - metAnnotaionArg = true - fallthrough - case metAnnotaionArg && isAnnotation: - annotationArgs = append(annotationArgs, s) - case !metAnnotaionArg && !isAnnotation: - o.resources = append(o.resources, s) - case metAnnotaionArg && !isAnnotation: - return fmt.Errorf("all resources must be specified before annotation changes: %s", s) - } - } - if len(o.resources) < 1 && len(o.filenames) == 0 { - return fmt.Errorf("one or more resources must be specified as or /") - } - if len(annotationArgs) < 1 { - return fmt.Errorf("at least one annotation update is required") - } - - if o.newAnnotations, o.removeAnnotations, err = parseAnnotations(annotationArgs); err != nil { - return err - } - - o.recordChangeCause = cmdutil.GetRecordFlag(cmd) - o.changeCause = f.Command() - - mapper, typer := f.Object() - o.builder = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - ContinueOnError(). - NamespaceParam(namespace).DefaultNamespace(). - FilenameParam(enforceNamespace, o.filenames...). - SelectorParam(o.selector). - ResourceTypeOrNameArgs(o.all, o.resources...). - Flatten(). - Latest() - - o.f = f - o.out = out - o.cmd = cmd - - return nil -} - -// Validate checks to the AnnotateOptions to see if there is sufficient information run the command. -func (o AnnotateOptions) Validate(args []string) error { - if err := validateAnnotations(o.removeAnnotations, o.newAnnotations); err != nil { - return err - } - - // only apply resource version locking on a single resource - if len(o.resources) > 1 && len(o.resourceVersion) > 0 { - return fmt.Errorf("--resource-version may only be used with a single resource") - } - - return nil -} - -// RunAnnotate does the work -func (o AnnotateOptions) RunAnnotate() error { - r := o.builder.Do() - if err := r.Err(); err != nil { - return err - } - - return r.Visit(func(info *resource.Info, err error) error { - if err != nil { - return err - } - - name, namespace, obj := info.Name, info.Namespace, info.Object - oldData, err := json.Marshal(obj) - if err != nil { - return err - } - // If we should record change-cause, add it to new annotations - if cmdutil.ContainsChangeCause(info) || o.recordChangeCause { - o.newAnnotations[kubectl.ChangeCauseAnnotation] = o.changeCause - } - if err := o.updateAnnotations(obj); err != nil { - return err - } - newData, err := json.Marshal(obj) - if err != nil { - return err - } - patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, obj) - createdPatch := err == nil - if err != nil { - glog.V(2).Infof("couldn't compute patch: %v", err) - } - - mapping := info.ResourceMapping() - client, err := o.f.ClientForMapping(mapping) - if err != nil { - return err - } - helper := resource.NewHelper(client, mapping) - - var outputObj runtime.Object - if createdPatch { - outputObj, err = helper.Patch(namespace, name, api.StrategicMergePatchType, patchBytes) - } else { - outputObj, err = helper.Replace(namespace, name, false, obj) - } - if err != nil { - return err - } - outputFormat := cmdutil.GetFlagString(o.cmd, "output") - if outputFormat != "" { - return o.f.PrintObject(o.cmd, outputObj, o.out) - } - mapper, _ := o.f.Object() - cmdutil.PrintSuccess(mapper, false, o.out, info.Mapping.Resource, info.Name, "annotated") - return nil - }) -} - -// parseAnnotations retrieves new and remove annotations from annotation args -func parseAnnotations(annotationArgs []string) (map[string]string, []string, error) { - var invalidBuf bytes.Buffer - newAnnotations := map[string]string{} - removeAnnotations := []string{} - for _, annotationArg := range annotationArgs { - if strings.Index(annotationArg, "=") != -1 { - parts := strings.SplitN(annotationArg, "=", 2) - if len(parts) != 2 || len(parts[1]) == 0 { - if invalidBuf.Len() > 0 { - invalidBuf.WriteString(", ") - } - invalidBuf.WriteString(fmt.Sprintf(annotationArg)) - } else { - newAnnotations[parts[0]] = parts[1] - } - } else if strings.HasSuffix(annotationArg, "-") { - removeAnnotations = append(removeAnnotations, annotationArg[:len(annotationArg)-1]) - } else { - if invalidBuf.Len() > 0 { - invalidBuf.WriteString(", ") - } - invalidBuf.WriteString(fmt.Sprintf(annotationArg)) - } - } - if invalidBuf.Len() > 0 { - return newAnnotations, removeAnnotations, fmt.Errorf("invalid annotation format: %s", invalidBuf.String()) - } - - return newAnnotations, removeAnnotations, nil -} - -// validateAnnotations checks the format of annotation args and checks removed annotations aren't in the new annotations map -func validateAnnotations(removeAnnotations []string, newAnnotations map[string]string) error { - var modifyRemoveBuf bytes.Buffer - for _, removeAnnotation := range removeAnnotations { - if _, found := newAnnotations[removeAnnotation]; found { - if modifyRemoveBuf.Len() > 0 { - modifyRemoveBuf.WriteString(", ") - } - modifyRemoveBuf.WriteString(fmt.Sprintf(removeAnnotation)) - } - } - if modifyRemoveBuf.Len() > 0 { - return fmt.Errorf("can not both modify and remove the following annotation(s) in the same command: %s", modifyRemoveBuf.String()) - } - - return nil -} - -// validateNoAnnotationOverwrites validates that when overwrite is false, to-be-updated annotations don't exist in the object annotation map (yet) -func validateNoAnnotationOverwrites(meta *api.ObjectMeta, annotations map[string]string) error { - var buf bytes.Buffer - for key := range annotations { - // change-cause annotation can always be overwritten - if key == kubectl.ChangeCauseAnnotation { - continue - } - if value, found := meta.Annotations[key]; found { - if buf.Len() > 0 { - buf.WriteString("; ") - } - buf.WriteString(fmt.Sprintf("'%s' already has a value (%s)", key, value)) - } - } - if buf.Len() > 0 { - return fmt.Errorf("--overwrite is false but found the following declared annotation(s): %s", buf.String()) - } - return nil -} - -// updateAnnotations updates annotations of obj -func (o AnnotateOptions) updateAnnotations(obj runtime.Object) error { - meta, err := api.ObjectMetaFor(obj) - if err != nil { - return err - } - if !o.overwrite { - if err := validateNoAnnotationOverwrites(meta, o.newAnnotations); err != nil { - return err - } - } - - if meta.Annotations == nil { - meta.Annotations = make(map[string]string) - } - - for key, value := range o.newAnnotations { - meta.Annotations[key] = value - } - for _, annotation := range o.removeAnnotations { - delete(meta.Annotations, annotation) - } - - if len(o.resourceVersion) != 0 { - meta.ResourceVersion = o.resourceVersion - } - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/annotate_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/annotate_test.go deleted file mode 100644 index d2f58bf65..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/annotate_test.go +++ /dev/null @@ -1,571 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "net/http" - "reflect" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/fake" - "k8s.io/kubernetes/pkg/runtime" -) - -func TestValidateAnnotationOverwrites(t *testing.T) { - tests := []struct { - meta *api.ObjectMeta - annotations map[string]string - expectErr bool - scenario string - }{ - { - meta: &api.ObjectMeta{ - Annotations: map[string]string{ - "a": "A", - "b": "B", - }, - }, - annotations: map[string]string{ - "a": "a", - "c": "C", - }, - scenario: "share first annotation", - expectErr: true, - }, - { - meta: &api.ObjectMeta{ - Annotations: map[string]string{ - "a": "A", - "c": "C", - }, - }, - annotations: map[string]string{ - "b": "B", - "c": "c", - }, - scenario: "share second annotation", - expectErr: true, - }, - { - meta: &api.ObjectMeta{ - Annotations: map[string]string{ - "a": "A", - "c": "C", - }, - }, - annotations: map[string]string{ - "b": "B", - "d": "D", - }, - scenario: "no overlap", - }, - { - meta: &api.ObjectMeta{}, - annotations: map[string]string{ - "a": "A", - "b": "B", - }, - scenario: "no annotations", - }, - } - for _, test := range tests { - err := validateNoAnnotationOverwrites(test.meta, test.annotations) - if test.expectErr && err == nil { - t.Errorf("%s: unexpected non-error", test.scenario) - } else if !test.expectErr && err != nil { - t.Errorf("%s: unexpected error: %v", test.scenario, err) - } - } -} - -func TestParseAnnotations(t *testing.T) { - testURL := "https://test.com/index.htm?id=123#u=user-name" - testJSON := `'{"kind":"SerializedReference","apiVersion":"v1","reference":{"kind":"ReplicationController","namespace":"default","name":"my-nginx","uid":"c544ee78-2665-11e5-8051-42010af0c213","apiVersion":"v1","resourceVersion":"61368"}}'` - tests := []struct { - annotations []string - expected map[string]string - expectedRemove []string - scenario string - expectedErr string - expectErr bool - }{ - { - annotations: []string{"a=b", "c=d"}, - expected: map[string]string{"a": "b", "c": "d"}, - expectedRemove: []string{}, - scenario: "add two annotations", - expectErr: false, - }, - { - annotations: []string{"url=" + testURL, "kubernetes.io/created-by=" + testJSON}, - expected: map[string]string{"url": testURL, "kubernetes.io/created-by": testJSON}, - expectedRemove: []string{}, - scenario: "add annotations with special characters", - expectErr: false, - }, - { - annotations: []string{}, - expected: map[string]string{}, - expectedRemove: []string{}, - scenario: "add no annotations", - expectErr: false, - }, - { - annotations: []string{"a=b", "c=d", "e-"}, - expected: map[string]string{"a": "b", "c": "d"}, - expectedRemove: []string{"e"}, - scenario: "add two annotations, remove one", - expectErr: false, - }, - { - annotations: []string{"ab", "c=d"}, - expectedErr: "invalid annotation format: ab", - scenario: "incorrect annotation input (missing =value)", - expectErr: true, - }, - { - annotations: []string{"a="}, - expectedErr: "invalid annotation format: a=", - scenario: "incorrect annotation input (missing value)", - expectErr: true, - }, - { - annotations: []string{"ab", "a="}, - expectedErr: "invalid annotation format: ab, a=", - scenario: "incorrect multiple annotation input (missing value)", - expectErr: true, - }, - } - for _, test := range tests { - annotations, remove, err := parseAnnotations(test.annotations) - switch { - case test.expectErr && err == nil: - t.Errorf("%s: unexpected non-error, should return %v", test.scenario, test.expectedErr) - case test.expectErr && err.Error() != test.expectedErr: - t.Errorf("%s: unexpected error %v, expected %v", test.scenario, err, test.expectedErr) - case !test.expectErr && err != nil: - t.Errorf("%s: unexpected error %v", test.scenario, err) - case !test.expectErr && !reflect.DeepEqual(annotations, test.expected): - t.Errorf("%s: expected %v, got %v", test.scenario, test.expected, annotations) - case !test.expectErr && !reflect.DeepEqual(remove, test.expectedRemove): - t.Errorf("%s: expected %v, got %v", test.scenario, test.expectedRemove, remove) - } - } -} - -func TestValidateAnnotations(t *testing.T) { - tests := []struct { - removeAnnotations []string - newAnnotations map[string]string - expectedErr string - scenario string - }{ - { - expectedErr: "can not both modify and remove the following annotation(s) in the same command: a", - removeAnnotations: []string{"a"}, - newAnnotations: map[string]string{"a": "b", "c": "d"}, - scenario: "remove an added annotation", - }, - { - expectedErr: "can not both modify and remove the following annotation(s) in the same command: a, c", - removeAnnotations: []string{"a", "c"}, - newAnnotations: map[string]string{"a": "b", "c": "d"}, - scenario: "remove added annotations", - }, - } - for _, test := range tests { - if err := validateAnnotations(test.removeAnnotations, test.newAnnotations); err == nil { - t.Errorf("%s: unexpected non-error", test.scenario) - } else if err.Error() != test.expectedErr { - t.Errorf("%s: expected error %s, got %s", test.scenario, test.expectedErr, err.Error()) - } - } -} - -func TestUpdateAnnotations(t *testing.T) { - tests := []struct { - obj runtime.Object - overwrite bool - version string - annotations map[string]string - remove []string - expected runtime.Object - expectErr bool - }{ - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{"a": "b"}, - }, - }, - annotations: map[string]string{"a": "b"}, - expectErr: true, - }, - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{"a": "b"}, - }, - }, - annotations: map[string]string{"a": "c"}, - overwrite: true, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{"a": "c"}, - }, - }, - }, - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{"a": "b"}, - }, - }, - annotations: map[string]string{"c": "d"}, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{"a": "b", "c": "d"}, - }, - }, - }, - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{"a": "b"}, - }, - }, - annotations: map[string]string{"c": "d"}, - version: "2", - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{"a": "b", "c": "d"}, - ResourceVersion: "2", - }, - }, - }, - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{"a": "b"}, - }, - }, - annotations: map[string]string{}, - remove: []string{"a"}, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{}, - }, - }, - }, - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{"a": "b", "c": "d"}, - }, - }, - annotations: map[string]string{"e": "f"}, - remove: []string{"a"}, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{ - "c": "d", - "e": "f", - }, - }, - }, - }, - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{"a": "b", "c": "d"}, - }, - }, - annotations: map[string]string{"e": "f"}, - remove: []string{"g"}, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{ - "a": "b", - "c": "d", - "e": "f", - }, - }, - }, - }, - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{"a": "b", "c": "d"}, - }, - }, - remove: []string{"e"}, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{ - "a": "b", - "c": "d", - }, - }, - }, - }, - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{}, - }, - annotations: map[string]string{"a": "b"}, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{"a": "b"}, - }, - }, - }, - } - for _, test := range tests { - options := &AnnotateOptions{ - overwrite: test.overwrite, - newAnnotations: test.annotations, - removeAnnotations: test.remove, - resourceVersion: test.version, - } - err := options.updateAnnotations(test.obj) - if test.expectErr { - if err == nil { - t.Errorf("unexpected non-error: %v", test) - } - continue - } - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v %v", err, test) - } - if !reflect.DeepEqual(test.obj, test.expected) { - t.Errorf("expected: %v, got %v", test.expected, test.obj) - } - } -} - -func TestAnnotateErrors(t *testing.T) { - testCases := map[string]struct { - args []string - flags map[string]string - errFn func(error) bool - }{ - "no args": { - args: []string{}, - errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") }, - }, - "not enough annotations": { - args: []string{"pods"}, - errFn: func(err error) bool { - return strings.Contains(err.Error(), "at least one annotation update is required") - }, - }, - "no resources remove annotations": { - args: []string{"pods-"}, - errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") }, - }, - "no resources add annotations": { - args: []string{"pods=bar"}, - errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") }, - }, - } - - for k, testCase := range testCases { - f, tf, _ := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} - - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdAnnotate(f, buf) - cmd.SetOutput(buf) - - for k, v := range testCase.flags { - cmd.Flags().Set(k, v) - } - options := &AnnotateOptions{} - err := options.Complete(f, buf, cmd, testCase.args) - if !testCase.errFn(err) { - t.Errorf("%s: unexpected error: %v", k, err) - continue - } - if tf.Printer.(*testPrinter).Objects != nil { - t.Errorf("unexpected print to default printer") - } - if buf.Len() > 0 { - t.Errorf("buffer should be empty: %s", string(buf.Bytes())) - } - } -} - -func TestAnnotateObject(t *testing.T) { - pods, _, _ := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch req.Method { - case "GET": - switch req.URL.Path { - case "/namespaces/test/pods/foo": - return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - case "PATCH": - switch req.URL.Path { - case "/namespaces/test/pods/foo": - return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - default: - t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} - - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdAnnotate(f, buf) - cmd.SetOutput(buf) - options := &AnnotateOptions{} - args := []string{"pods/foo", "a=b", "c-"} - if err := options.Complete(f, buf, cmd, args); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if err := options.Validate(args); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if err := options.RunAnnotate(); err != nil { - t.Fatalf("unexpected error: %v", err) - } -} - -func TestAnnotateObjectFromFile(t *testing.T) { - pods, _, _ := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch req.Method { - case "GET": - switch req.URL.Path { - case "/namespaces/test/pods/cassandra": - return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - case "PATCH": - switch req.URL.Path { - case "/namespaces/test/pods/cassandra": - return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - default: - t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} - - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdAnnotate(f, buf) - cmd.SetOutput(buf) - options := &AnnotateOptions{} - options.filenames = []string{"../../../examples/cassandra/cassandra.yaml"} - args := []string{"a=b", "c-"} - if err := options.Complete(f, buf, cmd, args); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if err := options.Validate(args); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if err := options.RunAnnotate(); err != nil { - t.Fatalf("unexpected error: %v", err) - } -} - -func TestAnnotateMultipleObjects(t *testing.T) { - pods, _, _ := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch req.Method { - case "GET": - switch req.URL.Path { - case "/namespaces/test/pods": - return &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - case "PATCH": - switch req.URL.Path { - case "/namespaces/test/pods/foo": - return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil - case "/namespaces/test/pods/bar": - return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[1])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - default: - t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} - - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdAnnotate(f, buf) - cmd.SetOutput(buf) - options := &AnnotateOptions{} - options.all = true - args := []string{"pods", "a=b", "c-"} - if err := options.Complete(f, buf, cmd, args); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if err := options.Validate(args); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if err := options.RunAnnotate(); err != nil { - t.Fatalf("unexpected error: %v", err) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/apiversions.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/apiversions.go deleted file mode 100644 index bc04d3eb5..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/apiversions.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - "os" - "sort" - - "github.com/spf13/cobra" - - unversioned_client "k8s.io/kubernetes/pkg/client/unversioned" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" -) - -func NewCmdApiVersions(f *cmdutil.Factory, out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "api-versions", - // apiversions is deprecated. - Aliases: []string{"apiversions"}, - Short: "Print the supported API versions on the server, in the form of \"group/version\".", - Run: func(cmd *cobra.Command, args []string) { - err := RunApiVersions(f, out) - cmdutil.CheckErr(err) - }, - } - return cmd -} - -func RunApiVersions(f *cmdutil.Factory, w io.Writer) error { - if len(os.Args) > 1 && os.Args[1] == "apiversions" { - printDeprecationWarning("api-versions", "apiversions") - } - - client, err := f.Client() - if err != nil { - return err - } - - groupList, err := client.Discovery().ServerGroups() - if err != nil { - return fmt.Errorf("Couldn't get available api versions from server: %v\n", err) - } - apiVersions := unversioned_client.ExtractGroupVersions(groupList) - sort.Strings(apiVersions) - for _, v := range apiVersions { - fmt.Fprintln(w, v) - } - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/apply.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/apply.go deleted file mode 100644 index 54aabc86b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/apply.go +++ /dev/null @@ -1,213 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/strategicpatch" -) - -// ApplyOptions stores cmd.Flag values for apply. As new fields are added, -// add them here instead of referencing the cmd.Flags() -type ApplyOptions struct { - Filenames []string -} - -const ( - apply_long = `Apply a configuration to a resource by filename or stdin. -The resource will be created if it doesn't exist yet. - -JSON and YAML formats are accepted.` - apply_example = `# Apply the configuration in pod.json to a pod. -$ kubectl apply -f ./pod.json - -# Apply the JSON passed into stdin to a pod. -$ cat pod.json | kubectl apply -f -` -) - -func NewCmdApply(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &ApplyOptions{} - - cmd := &cobra.Command{ - Use: "apply -f FILENAME", - Short: "Apply a configuration to a resource by filename or stdin", - Long: apply_long, - Example: apply_example, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(validateArgs(cmd, args)) - cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) - cmdutil.CheckErr(RunApply(f, cmd, out, options)) - }, - } - - usage := "Filename, directory, or URL to file that contains the configuration to apply" - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) - cmd.MarkFlagRequired("filename") - cmdutil.AddValidateFlags(cmd) - cmdutil.AddOutputFlagsForMutation(cmd) - cmdutil.AddRecordFlag(cmd) - return cmd -} - -func validateArgs(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return cmdutil.UsageError(cmd, "Unexpected args: %v", args) - } - - return nil -} - -func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *ApplyOptions) error { - shortOutput := cmdutil.GetFlagString(cmd, "output") == "name" - schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir")) - if err != nil { - return err - } - - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - Schema(schema). - ContinueOnError(). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). - Flatten(). - Do() - err = r.Err() - if err != nil { - return err - } - - encoder := f.JSONEncoder() - decoder := f.Decoder(false) - - count := 0 - err = r.Visit(func(info *resource.Info, err error) error { - // In this method, info.Object contains the object retrieved from the server - // and info.VersionedObject contains the object decoded from the input source. - if err != nil { - return err - } - - // Get the modified configuration of the object. Embed the result - // as an annotation in the modified configuration, so that it will appear - // in the patch sent to the server. - modified, err := kubectl.GetModifiedConfiguration(info, true, encoder) - if err != nil { - return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving modified configuration from:\n%v\nfor:", info), info.Source, err) - } - - if err := info.Get(); err != nil { - if !errors.IsNotFound(err) { - return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%v\nfrom server for:", info), info.Source, err) - } - // Create the resource if it doesn't exist - // First, update the annotation used by kubectl apply - if err := kubectl.CreateApplyAnnotation(info, encoder); err != nil { - return cmdutil.AddSourceToErr("creating", info.Source, err) - } - - if cmdutil.ShouldRecord(cmd, info) { - if err := cmdutil.RecordChangeCause(info.Object, f.Command()); err != nil { - return cmdutil.AddSourceToErr("creating", info.Source, err) - } - } - - // Then create the resource and skip the three-way merge - if err := createAndRefresh(info); err != nil { - return cmdutil.AddSourceToErr("creating", info.Source, err) - } - count++ - cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, "created") - return nil - } - - // Serialize the current configuration of the object from the server. - current, err := runtime.Encode(encoder, info.Object) - if err != nil { - return cmdutil.AddSourceToErr(fmt.Sprintf("serializing current configuration from:\n%v\nfor:", info), info.Source, err) - } - - // Retrieve the original configuration of the object from the annotation. - original, err := kubectl.GetOriginalConfiguration(info) - if err != nil { - return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving original configuration from:\n%v\nfor:", info), info.Source, err) - } - - // Create the versioned struct from the original from the server for - // strategic patch. - // TODO: Move all structs in apply to use raw data. Can be done once - // builder has a RawResult method which delivers raw data instead of - // internal objects. - versionedObject, _, err := decoder.Decode(current, nil, nil) - if err != nil { - return cmdutil.AddSourceToErr(fmt.Sprintf("converting encoded server-side object back to versioned struct:\n%v\nfor:", info), info.Source, err) - } - - // Compute a three way strategic merge patch to send to server. - patch, err := strategicpatch.CreateThreeWayMergePatch(original, modified, current, versionedObject, true) - if err != nil { - format := "creating patch with:\noriginal:\n%s\nmodified:\n%s\ncurrent:\n%s\nfrom:\n%v\nfor:" - return cmdutil.AddSourceToErr(fmt.Sprintf(format, original, modified, current, info), info.Source, err) - } - - helper := resource.NewHelper(info.Client, info.Mapping) - _, err = helper.Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch) - if err != nil { - return cmdutil.AddSourceToErr(fmt.Sprintf("applying patch:\n%s\nto:\n%v\nfor:", patch, info), info.Source, err) - } - - if cmdutil.ShouldRecord(cmd, info) { - patch, err = cmdutil.ChangeResourcePatch(info, f.Command()) - if err != nil { - return err - } - _, err = helper.Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch) - if err != nil { - return cmdutil.AddSourceToErr(fmt.Sprintf("applying patch:\n%s\nto:\n%v\nfor:", patch, info), info.Source, err) - } - } - - count++ - cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, "configured") - return nil - }) - - if err != nil { - return err - } - - if count == 0 { - return fmt.Errorf("no objects passed to apply") - } - - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/apply_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/apply_test.go deleted file mode 100644 index 93a4d304e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/apply_test.go +++ /dev/null @@ -1,313 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "encoding/json" - "io/ioutil" - "net/http" - "os" - "testing" - - "github.com/ghodss/yaml" - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/unversioned/fake" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/runtime" -) - -func TestApplyExtraArgsFail(t *testing.T) { - buf := bytes.NewBuffer([]byte{}) - - f, _, _ := NewAPIFactory() - c := NewCmdApply(f, buf) - if validateApplyArgs(c, []string{"rc"}) == nil { - t.Fatalf("unexpected non-error") - } -} - -func validateApplyArgs(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return cmdutil.UsageError(cmd, "Unexpected args: %v", args) - } - return nil -} - -const ( - filenameRC = "../../../test/fixtures/pkg/kubectl/cmd/apply/rc.yaml" - filenameSVC = "../../../test/fixtures/pkg/kubectl/cmd/apply/service.yaml" - filenameRCSVC = "../../../test/fixtures/pkg/kubectl/cmd/apply/rc-service.yaml" -) - -func readBytesFromFile(t *testing.T, filename string) []byte { - file, err := os.Open(filename) - if err != nil { - t.Fatal(err) - } - - data, err := ioutil.ReadAll(file) - if err != nil { - t.Fatal(err) - } - - return data -} - -func readReplicationControllerFromFile(t *testing.T, filename string) *api.ReplicationController { - data := readBytesFromFile(t, filename) - rc := api.ReplicationController{} - // TODO(jackgr): Replace with a call to testapi.Codec().Decode(). - if err := yaml.Unmarshal(data, &rc); err != nil { - t.Fatal(err) - } - - return &rc -} - -func readServiceFromFile(t *testing.T, filename string) *api.Service { - data := readBytesFromFile(t, filename) - svc := api.Service{} - // TODO(jackgr): Replace with a call to testapi.Codec().Decode(). - if err := yaml.Unmarshal(data, &svc); err != nil { - t.Fatal(err) - } - - return &svc -} - -func annotateRuntimeObject(t *testing.T, originalObj, currentObj runtime.Object, kind string) (string, []byte) { - originalMeta, err := api.ObjectMetaFor(originalObj) - if err != nil { - t.Fatal(err) - } - - originalMeta.Labels["DELETE_ME"] = "DELETE_ME" - original, err := json.Marshal(originalObj) - if err != nil { - t.Fatal(err) - } - - currentMeta, err := api.ObjectMetaFor(currentObj) - if err != nil { - t.Fatal(err) - } - - if currentMeta.Annotations == nil { - currentMeta.Annotations = map[string]string{} - } - - currentMeta.Annotations[kubectl.LastAppliedConfigAnnotation] = string(original) - current, err := json.Marshal(currentObj) - if err != nil { - t.Fatal(err) - } - - return currentMeta.Name, current -} - -func readAndAnnotateReplicationController(t *testing.T, filename string) (string, []byte) { - rc1 := readReplicationControllerFromFile(t, filename) - rc2 := readReplicationControllerFromFile(t, filename) - return annotateRuntimeObject(t, rc1, rc2, "ReplicationController") -} - -func readAndAnnotateService(t *testing.T, filename string) (string, []byte) { - svc1 := readServiceFromFile(t, filename) - svc2 := readServiceFromFile(t, filename) - return annotateRuntimeObject(t, svc1, svc2, "Service") -} - -func validatePatchApplication(t *testing.T, req *http.Request) { - patch, err := ioutil.ReadAll(req.Body) - if err != nil { - t.Fatal(err) - } - - patchMap := map[string]interface{}{} - if err := json.Unmarshal(patch, &patchMap); err != nil { - t.Fatal(err) - } - - annotationsMap := walkMapPath(t, patchMap, []string{"metadata", "annotations"}) - if _, ok := annotationsMap[kubectl.LastAppliedConfigAnnotation]; !ok { - t.Fatalf("patch does not contain annotation:\n%s\n", patch) - } - - labelMap := walkMapPath(t, patchMap, []string{"metadata", "labels"}) - if deleteMe, ok := labelMap["DELETE_ME"]; !ok || deleteMe != nil { - t.Fatalf("patch does not remove deleted key: DELETE_ME:\n%s\n", patch) - } -} - -func walkMapPath(t *testing.T, start map[string]interface{}, path []string) map[string]interface{} { - finish := start - for i := 0; i < len(path); i++ { - var ok bool - finish, ok = finish[path[i]].(map[string]interface{}) - if !ok { - t.Fatalf("key:%s of path:%v not found in map:%v", path[i], path, start) - } - } - - return finish -} - -func TestApplyObject(t *testing.T) { - initTestErrorHandler(t) - nameRC, currentRC := readAndAnnotateReplicationController(t, filenameRC) - pathRC := "/namespaces/test/replicationcontrollers/" + nameRC - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == pathRC && m == "GET": - bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC)) - return &http.Response{StatusCode: 200, Body: bodyRC}, nil - case p == pathRC && m == "PATCH": - validatePatchApplication(t, req) - bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC)) - return &http.Response{StatusCode: 200, Body: bodyRC}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdApply(f, buf) - cmd.Flags().Set("filename", filenameRC) - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - // uses the name from the file, not the response - expectRC := "replicationcontroller/" + nameRC + "\n" - if buf.String() != expectRC { - t.Fatalf("unexpected output: %s\nexpected: %s", buf.String(), expectRC) - } -} - -func TestApplyNonExistObject(t *testing.T) { - nameRC, currentRC := readAndAnnotateReplicationController(t, filenameRC) - pathRC := "/namespaces/test/replicationcontrollers" - pathNameRC := pathRC + "/" + nameRC - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == pathNameRC && m == "GET": - return &http.Response{StatusCode: 404, Body: ioutil.NopCloser(bytes.NewReader(nil))}, nil - case p == pathRC && m == "POST": - bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC)) - return &http.Response{StatusCode: 201, Body: bodyRC}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdApply(f, buf) - cmd.Flags().Set("filename", filenameRC) - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - // uses the name from the file, not the response - expectRC := "replicationcontroller/" + nameRC + "\n" - if buf.String() != expectRC { - t.Errorf("unexpected output: %s\nexpected: %s", buf.String(), expectRC) - } -} - -func TestApplyMultipleObjectsAsList(t *testing.T) { - testApplyMultipleObjects(t, true) -} - -func TestApplyMultipleObjectsAsFiles(t *testing.T) { - testApplyMultipleObjects(t, false) -} - -func testApplyMultipleObjects(t *testing.T, asList bool) { - nameRC, currentRC := readAndAnnotateReplicationController(t, filenameRC) - pathRC := "/namespaces/test/replicationcontrollers/" + nameRC - - nameSVC, currentSVC := readAndAnnotateService(t, filenameSVC) - pathSVC := "/namespaces/test/services/" + nameSVC - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == pathRC && m == "GET": - bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC)) - return &http.Response{StatusCode: 200, Body: bodyRC}, nil - case p == pathRC && m == "PATCH": - validatePatchApplication(t, req) - bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC)) - return &http.Response{StatusCode: 200, Body: bodyRC}, nil - case p == pathSVC && m == "GET": - bodySVC := ioutil.NopCloser(bytes.NewReader(currentSVC)) - return &http.Response{StatusCode: 200, Body: bodySVC}, nil - case p == pathSVC && m == "PATCH": - validatePatchApplication(t, req) - bodySVC := ioutil.NopCloser(bytes.NewReader(currentSVC)) - return &http.Response{StatusCode: 200, Body: bodySVC}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdApply(f, buf) - if asList { - cmd.Flags().Set("filename", filenameRCSVC) - } else { - cmd.Flags().Set("filename", filenameRC) - cmd.Flags().Set("filename", filenameSVC) - } - cmd.Flags().Set("output", "name") - - cmd.Run(cmd, []string{}) - - // Names should come from the REST response, NOT the files - expectRC := "replicationcontroller/" + nameRC + "\n" - expectSVC := "service/" + nameSVC + "\n" - // Test both possible orders since output is non-deterministic. - expectOne := expectRC + expectSVC - expectTwo := expectSVC + expectRC - if buf.String() != expectOne && buf.String() != expectTwo { - t.Fatalf("unexpected output: %s\nexpected: %s OR %s", buf.String(), expectOne, expectTwo) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/attach.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/attach.go deleted file mode 100644 index 22f1ddb3b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/attach.go +++ /dev/null @@ -1,251 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - "net/url" - "os" - "os/signal" - "syscall" - - "github.com/docker/docker/pkg/term" - "github.com/golang/glog" - "github.com/spf13/cobra" - "k8s.io/kubernetes/pkg/api" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/remotecommand" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - utilerrors "k8s.io/kubernetes/pkg/util/errors" -) - -const ( - attach_example = `# Get output from running pod 123456-7890, using the first container by default -$ kubectl attach 123456-7890 - -# Get output from ruby-container from pod 123456-7890 -$ kubectl attach 123456-7890 -c ruby-container - -# Switch to raw terminal mode, sends stdin to 'bash' in ruby-container from pod 123456-7890 -# and sends stdout/stderr from 'bash' back to the client -$ kubectl attach 123456-7890 -c ruby-container -i -t` -) - -func NewCmdAttach(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command { - options := &AttachOptions{ - In: cmdIn, - Out: cmdOut, - Err: cmdErr, - - Attach: &DefaultRemoteAttach{}, - } - cmd := &cobra.Command{ - Use: "attach POD -c CONTAINER", - Short: "Attach to a running container.", - Long: "Attach to a process that is already running inside an existing container.", - Example: attach_example, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(options.Complete(f, cmd, args)) - cmdutil.CheckErr(options.Validate()) - cmdutil.CheckErr(options.Run()) - }, - } - // TODO support UID - cmd.Flags().StringVarP(&options.ContainerName, "container", "c", "", "Container name. If omitted, the first container in the pod will be chosen") - cmd.Flags().BoolVarP(&options.Stdin, "stdin", "i", false, "Pass stdin to the container") - cmd.Flags().BoolVarP(&options.TTY, "tty", "t", false, "Stdin is a TTY") - return cmd -} - -// RemoteAttach defines the interface accepted by the Attach command - provided for test stubbing -type RemoteAttach interface { - Attach(method string, url *url.URL, config *client.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool) error -} - -// DefaultRemoteAttach is the standard implementation of attaching -type DefaultRemoteAttach struct{} - -func (*DefaultRemoteAttach) Attach(method string, url *url.URL, config *client.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool) error { - exec, err := remotecommand.NewExecutor(config, method, url) - if err != nil { - return err - } - return exec.Stream(stdin, stdout, stderr, tty) -} - -// AttachOptions declare the arguments accepted by the Exec command -type AttachOptions struct { - Namespace string - PodName string - ContainerName string - Stdin bool - TTY bool - - In io.Reader - Out io.Writer - Err io.Writer - - Attach RemoteAttach - Client *client.Client - Config *client.Config -} - -// Complete verifies command line arguments and loads data from the command environment -func (p *AttachOptions) Complete(f *cmdutil.Factory, cmd *cobra.Command, argsIn []string) error { - if len(argsIn) == 0 { - return cmdutil.UsageError(cmd, "POD is required for attach") - } - if len(argsIn) > 1 { - return cmdutil.UsageError(cmd, fmt.Sprintf("expected a single argument: POD, saw %d: %s", len(argsIn), argsIn)) - } - p.PodName = argsIn[0] - - namespace, _, err := f.DefaultNamespace() - if err != nil { - return err - } - p.Namespace = namespace - - config, err := f.ClientConfig() - if err != nil { - return err - } - p.Config = config - - client, err := f.Client() - if err != nil { - return err - } - p.Client = client - - return nil -} - -// Validate checks that the provided attach options are specified. -func (p *AttachOptions) Validate() error { - allErrs := []error{} - if len(p.PodName) == 0 { - allErrs = append(allErrs, fmt.Errorf("pod name must be specified")) - } - if p.Out == nil || p.Err == nil { - allErrs = append(allErrs, fmt.Errorf("both output and error output must be provided")) - } - if p.Attach == nil || p.Client == nil || p.Config == nil { - allErrs = append(allErrs, fmt.Errorf("client, client config, and attach must be provided")) - } - return utilerrors.NewAggregate(allErrs) -} - -// Run executes a validated remote execution against a pod. -func (p *AttachOptions) Run() error { - pod, err := p.Client.Pods(p.Namespace).Get(p.PodName) - if err != nil { - return err - } - - if pod.Status.Phase != api.PodRunning { - return fmt.Errorf("pod %s is not running and cannot be attached to; current phase is %s", p.PodName, pod.Status.Phase) - } - - var stdin io.Reader - tty := p.TTY - - containerToAttach := p.GetContainer(pod) - if tty && !containerToAttach.TTY { - tty = false - fmt.Fprintf(p.Err, "Unable to use a TTY - container %s doesn't allocate one\n", containerToAttach.Name) - } - - // TODO: refactor with terminal helpers from the edit utility once that is merged - if p.Stdin { - stdin = p.In - if tty { - if file, ok := stdin.(*os.File); ok { - inFd := file.Fd() - if term.IsTerminal(inFd) { - oldState, err := term.SetRawTerminal(inFd) - if err != nil { - glog.Fatal(err) - } - fmt.Fprintln(p.Out, "\nHit enter for command prompt") - // this handles a clean exit, where the command finished - defer term.RestoreTerminal(inFd, oldState) - - // SIGINT is handled by term.SetRawTerminal (it runs a goroutine that listens - // for SIGINT and restores the terminal before exiting) - - // this handles SIGTERM - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGTERM) - go func() { - <-sigChan - term.RestoreTerminal(inFd, oldState) - os.Exit(0) - }() - } else { - fmt.Fprintln(p.Err, "STDIN is not a terminal") - } - } else { - tty = false - fmt.Fprintln(p.Err, "Unable to use a TTY - input is not the right kind of file") - } - } - } - - // TODO: consider abstracting into a client invocation or client helper - req := p.Client.RESTClient.Post(). - Resource("pods"). - Name(pod.Name). - Namespace(pod.Namespace). - SubResource("attach") - req.VersionedParams(&api.PodAttachOptions{ - Container: containerToAttach.Name, - Stdin: stdin != nil, - Stdout: p.Out != nil, - Stderr: p.Err != nil, - TTY: tty, - }, api.ParameterCodec) - - err = p.Attach.Attach("POST", req.URL(), p.Config, stdin, p.Out, p.Err, tty) - if err != nil { - return err - } - if p.Stdin && tty && pod.Spec.RestartPolicy == api.RestartPolicyAlways { - fmt.Fprintf(p.Out, "Session ended, resume using 'kubectl attach %s -c %s -i -t' command when the pod is running\n", pod.Name, containerToAttach.Name) - } - return nil -} - -// GetContainer returns the container to attach to, with a fallback. -func (p *AttachOptions) GetContainer(pod *api.Pod) api.Container { - if len(p.ContainerName) > 0 { - for _, container := range pod.Spec.Containers { - if container.Name == p.ContainerName { - return container - } - } - } - - glog.V(4).Infof("defaulting container name to %s", pod.Spec.Containers[0].Name) - return pod.Spec.Containers[0] -} - -// GetContainerName returns the name of the container to attach to, with a fallback. -func (p *AttachOptions) GetContainerName(pod *api.Pod) string { - return p.GetContainer(pod).Name -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/attach_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/attach_test.go deleted file mode 100644 index 7fc4b7fe6..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/attach_test.go +++ /dev/null @@ -1,279 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "fmt" - "io" - "net/http" - "net/url" - "strings" - "testing" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/fake" -) - -type fakeRemoteAttach struct { - method string - url *url.URL - attachErr error -} - -func (f *fakeRemoteAttach) Attach(method string, url *url.URL, config *client.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool) error { - f.method = method - f.url = url - return f.attachErr -} - -func TestPodAndContainerAttach(t *testing.T) { - tests := []struct { - args []string - p *AttachOptions - name string - expectError bool - expectedPod string - expectedContainer string - }{ - { - p: &AttachOptions{}, - expectError: true, - name: "empty", - }, - { - p: &AttachOptions{}, - args: []string{"foo", "bar"}, - expectError: true, - name: "too many args", - }, - { - p: &AttachOptions{}, - args: []string{"foo"}, - expectedPod: "foo", - name: "no container, no flags", - }, - { - p: &AttachOptions{ContainerName: "bar"}, - args: []string{"foo"}, - expectedPod: "foo", - expectedContainer: "bar", - name: "container in flag", - }, - } - for _, test := range tests { - f, tf, codec := NewAPIFactory() - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { return nil, nil }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{} - - cmd := &cobra.Command{} - options := test.p - err := options.Complete(f, cmd, test.args) - if test.expectError && err == nil { - t.Errorf("unexpected non-error (%s)", test.name) - } - if !test.expectError && err != nil { - t.Errorf("unexpected error: %v (%s)", err, test.name) - } - if err != nil { - continue - } - if options.PodName != test.expectedPod { - t.Errorf("expected: %s, got: %s (%s)", test.expectedPod, options.PodName, test.name) - } - if options.ContainerName != test.expectedContainer { - t.Errorf("expected: %s, got: %s (%s)", test.expectedContainer, options.ContainerName, test.name) - } - } -} - -func TestAttach(t *testing.T) { - version := testapi.Default.GroupVersion().Version - tests := []struct { - name, version, podPath, attachPath, container string - pod *api.Pod - attachErr bool - }{ - { - name: "pod attach", - version: version, - podPath: "/api/" + version + "/namespaces/test/pods/foo", - attachPath: "/api/" + version + "/namespaces/test/pods/foo/attach", - pod: attachPod(), - }, - { - name: "pod attach error", - version: version, - podPath: "/api/" + version + "/namespaces/test/pods/foo", - attachPath: "/api/" + version + "/namespaces/test/pods/foo/attach", - pod: attachPod(), - attachErr: true, - }, - } - for _, test := range tests { - f, tf, codec := NewAPIFactory() - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == test.podPath && m == "GET": - body := objBody(codec, test.pod) - return &http.Response{StatusCode: 200, Body: body}, nil - default: - // Ensures no GET is performed when deleting by name - t.Errorf("%s: unexpected request: %s %#v\n%#v", test.name, req.Method, req.URL, req) - return nil, fmt.Errorf("unexpected request") - } - }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: &unversioned.GroupVersion{Version: test.version}}} - bufOut := bytes.NewBuffer([]byte{}) - bufErr := bytes.NewBuffer([]byte{}) - bufIn := bytes.NewBuffer([]byte{}) - ex := &fakeRemoteAttach{} - if test.attachErr { - ex.attachErr = fmt.Errorf("attach error") - } - params := &AttachOptions{ - ContainerName: "bar", - In: bufIn, - Out: bufOut, - Err: bufErr, - Attach: ex, - } - cmd := &cobra.Command{} - if err := params.Complete(f, cmd, []string{"foo"}); err != nil { - t.Fatal(err) - } - err := params.Run() - if test.attachErr && err != ex.attachErr { - t.Errorf("%s: Unexpected exec error: %v", test.name, err) - continue - } - if !test.attachErr && err != nil { - t.Errorf("%s: Unexpected error: %v", test.name, err) - continue - } - if test.attachErr { - continue - } - if ex.url.Path != test.attachPath { - t.Errorf("%s: Did not get expected path for exec request", test.name) - continue - } - if ex.method != "POST" { - t.Errorf("%s: Did not get method for attach request: %s", test.name, ex.method) - } - if ex.url.Query().Get("container") != "bar" { - t.Errorf("%s: Did not have query parameters: %s", test.name, ex.url.Query()) - } - } -} - -func TestAttachWarnings(t *testing.T) { - version := testapi.Default.GroupVersion().Version - tests := []struct { - name, container, version, podPath, expectedErr, expectedOut string - pod *api.Pod - stdin, tty bool - }{ - { - name: "fallback tty if not supported", - version: version, - podPath: "/api/" + version + "/namespaces/test/pods/foo", - pod: attachPod(), - stdin: true, - tty: true, - expectedErr: "Unable to use a TTY - container bar doesn't allocate one", - }, - } - for _, test := range tests { - f, tf, codec := NewAPIFactory() - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == test.podPath && m == "GET": - body := objBody(codec, test.pod) - return &http.Response{StatusCode: 200, Body: body}, nil - default: - t.Errorf("%s: unexpected request: %s %#v\n%#v", test.name, req.Method, req.URL, req) - return nil, fmt.Errorf("unexpected request") - } - }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: &unversioned.GroupVersion{Version: test.version}}} - bufOut := bytes.NewBuffer([]byte{}) - bufErr := bytes.NewBuffer([]byte{}) - bufIn := bytes.NewBuffer([]byte{}) - ex := &fakeRemoteAttach{} - params := &AttachOptions{ - ContainerName: test.container, - In: bufIn, - Out: bufOut, - Err: bufErr, - Stdin: test.stdin, - TTY: test.tty, - Attach: ex, - } - cmd := &cobra.Command{} - if err := params.Complete(f, cmd, []string{"foo"}); err != nil { - t.Fatal(err) - } - if err := params.Run(); err != nil { - t.Fatal(err) - } - - if test.stdin && test.tty { - if !test.pod.Spec.Containers[0].TTY { - if !strings.Contains(bufErr.String(), test.expectedErr) { - t.Errorf("%s: Expected TTY fallback warning for attach request: %s", test.name, bufErr.String()) - continue - } - } - } - } -} - -func attachPod() *api.Pod { - return &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{ - { - Name: "bar", - }, - }, - }, - Status: api.PodStatus{ - Phase: api.PodRunning, - }, - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/autoscale.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/autoscale.go deleted file mode 100644 index 343c3a8a5..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/autoscale.go +++ /dev/null @@ -1,180 +0,0 @@ -/* -Copyright 2015 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 cmd - -import ( - "fmt" - "io" - - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/util/errors" - - "github.com/spf13/cobra" -) - -const ( - autoscaleLong = `Creates an autoscaler that automatically chooses and sets the number of pods that run in a kubernetes cluster. - -Looks up a deployment or replication controller by name and creates an autoscaler that uses this deployment or replication controller as a reference. -An autoscaler can automatically increase or decrease number of pods deployed within the system as needed.` - - autoscaleExample = `# Auto scale a deployment "foo", with the number of pods between 2 to 10, target CPU utilization at a default value that server applies: -$ kubectl autoscale deployment foo --min=2 --max=10 - -# Auto scale a replication controller "foo", with the number of pods between 1 to 5, target CPU utilization at 80%: -$ kubectl autoscale rc foo --max=5 --cpu-percent=80` -) - -func NewCmdAutoscale(f *cmdutil.Factory, out io.Writer) *cobra.Command { - filenames := []string{} - cmd := &cobra.Command{ - Use: "autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS] --max=MAXPODS [--cpu-percent=CPU] [flags]", - Short: "Auto-scale a deployment or replication controller", - Long: autoscaleLong, - Example: autoscaleExample, - Run: func(cmd *cobra.Command, args []string) { - err := RunAutoscale(f, out, cmd, args, filenames) - cmdutil.CheckErr(err) - }, - } - cmdutil.AddPrinterFlags(cmd) - cmd.Flags().String("generator", "horizontalpodautoscaler/v1beta1", "The name of the API generator to use. Currently there is only 1 generator.") - cmd.Flags().Int("min", -1, "The lower limit for the number of pods that can be set by the autoscaler. If it's not specified or negative, the server will apply a default value.") - cmd.Flags().Int("max", -1, "The upper limit for the number of pods that can be set by the autoscaler. Required.") - cmd.MarkFlagRequired("max") - cmd.Flags().Int("cpu-percent", -1, fmt.Sprintf("The target average CPU utilization (represented as a percent of requested CPU) over all the pods. If it's not specified or negative, the server will apply a default value.")) - cmd.Flags().String("name", "", "The name for the newly created object. If not specified, the name of the input resource will be used.") - cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without creating it.") - usage := "Filename, directory, or URL to a file identifying the resource to autoscale." - kubectl.AddJsonFilenameFlag(cmd, &filenames, usage) - cmdutil.AddApplyAnnotationFlags(cmd) - cmdutil.AddRecordFlag(cmd) - return cmd -} - -func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, filenames []string) error { - namespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - // validate flags - if err := validateFlags(cmd); err != nil { - return err - } - - mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - ContinueOnError(). - NamespaceParam(namespace).DefaultNamespace(). - FilenameParam(enforceNamespace, filenames...). - ResourceTypeOrNameArgs(false, args...). - Flatten(). - Do() - infos, err := r.Infos() - if err != nil { - return err - } - if len(infos) > 1 { - return fmt.Errorf("multiple resources provided: %v", args) - } - info := infos[0] - mapping := info.ResourceMapping() - if err := f.CanBeAutoscaled(mapping.GroupVersionKind.GroupKind()); err != nil { - return err - } - - // Get the generator, setup and validate all required parameters - generatorName := cmdutil.GetFlagString(cmd, "generator") - generators := f.Generators("autoscale") - generator, found := generators[generatorName] - if !found { - return cmdutil.UsageError(cmd, fmt.Sprintf("generator %q not found.", generatorName)) - } - names := generator.ParamNames() - params := kubectl.MakeParams(cmd, names) - name := info.Name - params["default-name"] = name - - params["scaleRef-kind"] = mapping.GroupVersionKind.Kind - params["scaleRef-name"] = name - params["scaleRef-apiVersion"] = mapping.GroupVersionKind.GroupVersion().String() - - if err = kubectl.ValidateParams(names, params); err != nil { - return err - } - // Check for invalid flags used against the present generator. - if err := kubectl.EnsureFlagsValid(cmd, generators, generatorName); err != nil { - return err - } - - // Generate new object - object, err := generator.Generate(params) - if err != nil { - return err - } - - resourceMapper := &resource.Mapper{ - ObjectTyper: typer, - RESTMapper: mapper, - ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), - Decoder: f.Decoder(true), - } - hpa, err := resourceMapper.InfoForObject(object) - if err != nil { - return err - } - if cmdutil.ShouldRecord(cmd, hpa) { - if err := cmdutil.RecordChangeCause(hpa.Object, f.Command()); err != nil { - return err - } - object = hpa.Object - } - // TODO: extract this flag to a central location, when such a location exists. - if cmdutil.GetFlagBool(cmd, "dry-run") { - return f.PrintObject(cmd, object, out) - } - - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), hpa, f.JSONEncoder()); err != nil { - return err - } - - object, err = resource.NewHelper(hpa.Client, hpa.Mapping).Create(namespace, false, object) - if err != nil { - return err - } - - if len(cmdutil.GetFlagString(cmd, "output")) > 0 { - return f.PrintObject(cmd, object, out) - } - cmdutil.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, "autoscaled") - return nil -} - -func validateFlags(cmd *cobra.Command) error { - errs := []error{} - max, min, cpu := cmdutil.GetFlagInt(cmd, "max"), cmdutil.GetFlagInt(cmd, "min"), cmdutil.GetFlagInt(cmd, "cpu-percent") - if max < 1 || max < min { - errs = append(errs, fmt.Errorf("--max=MAXPODS is required, and must be at least 1 and --min=MINPODS")) - } - if cpu > 100 { - errs = append(errs, fmt.Errorf("CPU utilization (%%) cannot exceed 100")) - } - return errors.NewAggregate(errs) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo.go deleted file mode 100644 index c76e28f7c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/clusterinfo.go +++ /dev/null @@ -1,117 +0,0 @@ -/* -Copyright 2015 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 cmd - -import ( - "fmt" - "io" - "os" - "strconv" - - "k8s.io/kubernetes/pkg/api" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - - "github.com/daviddengcn/go-colortext" - "github.com/spf13/cobra" -) - -func NewCmdClusterInfo(f *cmdutil.Factory, out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "cluster-info", - // clusterinfo is deprecated. - Aliases: []string{"clusterinfo"}, - Short: "Display cluster info", - Long: "Display addresses of the master and services with label kubernetes.io/cluster-service=true", - Run: func(cmd *cobra.Command, args []string) { - err := RunClusterInfo(f, out, cmd) - cmdutil.CheckErr(err) - }, - } - return cmd -} - -func RunClusterInfo(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command) error { - if len(os.Args) > 1 && os.Args[1] == "clusterinfo" { - printDeprecationWarning("cluster-info", "clusterinfo") - } - - client, err := f.ClientConfig() - if err != nil { - return err - } - printService(out, "Kubernetes master", client.Host) - - mapper, typer := f.Object() - cmdNamespace := cmdutil.GetFlagString(cmd, "namespace") - if cmdNamespace == "" { - cmdNamespace = api.NamespaceSystem - } - - // TODO use generalized labels once they are implemented (#341) - b := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - NamespaceParam(cmdNamespace).DefaultNamespace(). - SelectorParam("kubernetes.io/cluster-service=true"). - ResourceTypeOrNameArgs(false, []string{"services"}...). - Latest() - b.Do().Visit(func(r *resource.Info, err error) error { - if err != nil { - return err - } - services := r.Object.(*api.ServiceList).Items - for _, service := range services { - var link string - if len(service.Status.LoadBalancer.Ingress) > 0 { - ingress := service.Status.LoadBalancer.Ingress[0] - ip := ingress.IP - if ip == "" { - ip = ingress.Hostname - } - for _, port := range service.Spec.Ports { - link += "http://" + ip + ":" + strconv.Itoa(port.Port) + " " - } - } else { - if len(client.GroupVersion.Group) == 0 { - link = client.Host + "/api/" + client.GroupVersion.Version + "/proxy/namespaces/" + service.ObjectMeta.Namespace + "/services/" + service.ObjectMeta.Name - } else { - link = client.Host + "/api/" + client.GroupVersion.Group + "/" + client.GroupVersion.Version + "/proxy/namespaces/" + service.ObjectMeta.Namespace + "/services/" + service.ObjectMeta.Name - - } - } - name := service.ObjectMeta.Labels["kubernetes.io/name"] - if len(name) == 0 { - name = service.ObjectMeta.Name - } - printService(out, name, link) - } - return nil - }) - return nil - - // TODO consider printing more information about cluster -} - -func printService(out io.Writer, name, link string) { - ct.ChangeColor(ct.Green, false, ct.None, false) - fmt.Fprint(out, name) - ct.ResetColor() - fmt.Fprintf(out, " is running at ") - ct.ChangeColor(ct.Yellow, false, ct.None, false) - fmt.Fprint(out, link) - ct.ResetColor() - fmt.Fprintln(out, "") -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/cmd.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/cmd.go deleted file mode 100644 index 2abffaf37..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/cmd.go +++ /dev/null @@ -1,201 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "io" - - "github.com/golang/glog" - cmdconfig "k8s.io/kubernetes/pkg/kubectl/cmd/config" - "k8s.io/kubernetes/pkg/kubectl/cmd/rollout" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/util" - - "github.com/spf13/cobra" -) - -const ( - bash_completion_func = `# call kubectl get $1, -__kubectl_parse_get() -{ - local template - template="{{ range .items }}{{ .metadata.name }} {{ end }}" - local kubectl_out - if kubectl_out=$(kubectl get -o template --template="${template}" "$1" 2>/dev/null); then - COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) ) - fi -} - -__kubectl_get_resource() -{ - if [[ ${#nouns[@]} -eq 0 ]]; then - return 1 - fi - __kubectl_parse_get "${nouns[${#nouns[@]} -1]}" -} - -__kubectl_get_resource_pod() -{ - __kubectl_parse_get "pod" -} - -__kubectl_get_resource_rc() -{ - __kubectl_parse_get "rc" -} - -# $1 is the name of the pod we want to get the list of containers inside -__kubectl_get_containers() -{ - local template - template="{{ range .spec.containers }}{{ .name }} {{ end }}" - __debug "${FUNCNAME} nouns are ${nouns[*]}" - - local len="${#nouns[@]}" - if [[ ${len} -ne 1 ]]; then - return - fi - local last=${nouns[${len} -1]} - local kubectl_out - if kubectl_out=$(kubectl get -o template --template="${template}" pods "${last}" 2>/dev/null); then - COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) ) - fi -} - -# Require both a pod and a container to be specified -__kubectl_require_pod_and_container() -{ - if [[ ${#nouns[@]} -eq 0 ]]; then - __kubectl_parse_get pods - return 0 - fi; - __kubectl_get_containers - return 0 -} - -__custom_func() { - case ${last_command} in - kubectl_get | kubectl_describe | kubectl_delete | kubectl_label | kubectl_stop) - __kubectl_get_resource - return - ;; - kubectl_logs) - __kubectl_require_pod_and_container - return - ;; - kubectl_exec) - __kubectl_get_resource_pod - return - ;; - kubectl_rolling-update) - __kubectl_get_resource_rc - return - ;; - *) - ;; - esac -} -` - valid_resources = `Valid resource types include: - * componentstatuses (aka 'cs') - * events (aka 'ev') - * endpoints (aka 'ep') - * horizontalpodautoscalers (aka 'hpa') - * ingress (aka 'ing') - * jobs - * limitranges (aka 'limits') - * nodes (aka 'no') - * namespaces (aka 'ns') - * pods (aka 'po') - * persistentvolumes (aka 'pv') - * persistentvolumeclaims (aka 'pvc') - * quota - * resourcequotas (aka 'quota') - * replicasets (aka 'rs') - * replicationcontrollers (aka 'rc') - * secrets - * serviceaccounts - * services (aka 'svc') -` -) - -// NewKubectlCommand creates the `kubectl` command and its nested children. -func NewKubectlCommand(f *cmdutil.Factory, in io.Reader, out, err io.Writer) *cobra.Command { - // Parent command to which all subcommands are added. - cmds := &cobra.Command{ - Use: "kubectl", - Short: "kubectl controls the Kubernetes cluster manager", - Long: `kubectl controls the Kubernetes cluster manager. - -Find more information at https://github.com/kubernetes/kubernetes.`, - Run: runHelp, - BashCompletionFunction: bash_completion_func, - } - - f.BindFlags(cmds.PersistentFlags()) - - // From this point and forward we get warnings on flags that contain "_" separators - cmds.SetGlobalNormalizationFunc(util.WarnWordSepNormalizeFunc) - - cmds.AddCommand(NewCmdGet(f, out)) - cmds.AddCommand(NewCmdDescribe(f, out)) - cmds.AddCommand(NewCmdCreate(f, out)) - cmds.AddCommand(NewCmdReplace(f, out)) - cmds.AddCommand(NewCmdPatch(f, out)) - cmds.AddCommand(NewCmdDelete(f, out)) - cmds.AddCommand(NewCmdEdit(f, out)) - cmds.AddCommand(NewCmdApply(f, out)) - - cmds.AddCommand(NewCmdNamespace(out)) - cmds.AddCommand(NewCmdLogs(f, out)) - cmds.AddCommand(NewCmdRollingUpdate(f, out)) - cmds.AddCommand(NewCmdScale(f, out)) - cmds.AddCommand(NewCmdCordon(f, out)) - cmds.AddCommand(NewCmdDrain(f, out)) - cmds.AddCommand(NewCmdUncordon(f, out)) - - cmds.AddCommand(NewCmdAttach(f, in, out, err)) - cmds.AddCommand(NewCmdExec(f, in, out, err)) - cmds.AddCommand(NewCmdPortForward(f)) - cmds.AddCommand(NewCmdProxy(f, out)) - - cmds.AddCommand(NewCmdRun(f, in, out, err)) - cmds.AddCommand(NewCmdStop(f, out)) - cmds.AddCommand(NewCmdExposeService(f, out)) - cmds.AddCommand(NewCmdAutoscale(f, out)) - cmds.AddCommand(rollout.NewCmdRollout(f, out)) - - cmds.AddCommand(NewCmdLabel(f, out)) - cmds.AddCommand(NewCmdAnnotate(f, out)) - - cmds.AddCommand(cmdconfig.NewCmdConfig(cmdconfig.NewDefaultPathOptions(), out)) - cmds.AddCommand(NewCmdClusterInfo(f, out)) - cmds.AddCommand(NewCmdApiVersions(f, out)) - cmds.AddCommand(NewCmdVersion(f, out)) - cmds.AddCommand(NewCmdExplain(f, out)) - cmds.AddCommand(NewCmdConvert(f, out)) - - return cmds -} - -func runHelp(cmd *cobra.Command, args []string) { - cmd.Help() -} - -func printDeprecationWarning(command, alias string) { - glog.Warningf("%s is DEPRECATED and will be removed in a future version. Use %s instead.", alias, command) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/cmd_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/cmd_test.go deleted file mode 100644 index 82783c34f..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/cmd_test.go +++ /dev/null @@ -1,709 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "reflect" - "testing" - "time" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/validation" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/fake" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/runtime/serializer" - "k8s.io/kubernetes/pkg/util" -) - -func initTestErrorHandler(t *testing.T) { - cmdutil.BehaviorOnFatal(func(str string) { - t.Errorf("Error running command: %s", str) - }) -} - -type internalType struct { - Kind string - APIVersion string - - Name string -} - -type externalType struct { - Kind string `json:"kind"` - APIVersion string `json:"apiVersion"` - - Name string `json:"name"` -} - -type ExternalType2 struct { - Kind string `json:"kind"` - APIVersion string `json:"apiVersion"` - - Name string `json:"name"` -} - -func (obj *internalType) GetObjectKind() unversioned.ObjectKind { return obj } -func (obj *internalType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind() -} -func (obj *internalType) GroupVersionKind() *unversioned.GroupVersionKind { - return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind) -} -func (obj *externalType) GetObjectKind() unversioned.ObjectKind { return obj } -func (obj *externalType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind() -} -func (obj *externalType) GroupVersionKind() *unversioned.GroupVersionKind { - return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind) -} -func (obj *ExternalType2) GetObjectKind() unversioned.ObjectKind { return obj } -func (obj *ExternalType2) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind() -} -func (obj *ExternalType2) GroupVersionKind() *unversioned.GroupVersionKind { - return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind) -} - -var versionErr = errors.New("not a version") - -func versionErrIfFalse(b bool) error { - if b { - return nil - } - return versionErr -} - -var validVersion = testapi.Default.GroupVersion().Version -var internalGV = unversioned.GroupVersion{Group: "apitest", Version: runtime.APIVersionInternal} -var unlikelyGV = unversioned.GroupVersion{Group: "apitest", Version: "unlikelyversion"} -var validVersionGV = unversioned.GroupVersion{Group: "apitest", Version: validVersion} - -func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) { - scheme := runtime.NewScheme() - scheme.AddKnownTypeWithName(internalGV.WithKind("Type"), &internalType{}) - scheme.AddKnownTypeWithName(unlikelyGV.WithKind("Type"), &externalType{}) - //This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name. - scheme.AddKnownTypeWithName(validVersionGV.WithKind("Type"), &ExternalType2{}) - - codecs := serializer.NewCodecFactory(scheme) - codec := codecs.LegacyCodec(unlikelyGV) - mapper := meta.NewDefaultRESTMapper([]unversioned.GroupVersion{unlikelyGV, validVersionGV}, func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) { - return &meta.VersionInterfaces{ - ObjectConvertor: scheme, - MetadataAccessor: meta.NewAccessor(), - }, versionErrIfFalse(version == validVersionGV || version == unlikelyGV) - }) - for _, gv := range []unversioned.GroupVersion{unlikelyGV, validVersionGV} { - for kind := range scheme.KnownTypes(gv) { - gvk := gv.WithKind(kind) - - scope := meta.RESTScopeNamespace - mapper.Add(gvk, scope) - } - } - - return scheme, mapper, codec -} - -type testPrinter struct { - Objects []runtime.Object - Err error -} - -func (t *testPrinter) PrintObj(obj runtime.Object, out io.Writer) error { - t.Objects = append(t.Objects, obj) - fmt.Fprintf(out, "%#v", obj) - return t.Err -} - -// TODO: implement HandledResources() -func (t *testPrinter) HandledResources() []string { - return []string{} -} - -type testDescriber struct { - Name, Namespace string - Output string - Err error -} - -func (t *testDescriber) Describe(namespace, name string) (output string, err error) { - t.Namespace, t.Name = namespace, name - return t.Output, t.Err -} - -type testFactory struct { - Mapper meta.RESTMapper - Typer runtime.ObjectTyper - Client kubectl.RESTClient - Describer kubectl.Describer - Printer kubectl.ResourcePrinter - Validator validation.Schema - Namespace string - ClientConfig *client.Config - Err error -} - -func NewTestFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) { - scheme, mapper, codec := newExternalScheme() - t := &testFactory{ - Validator: validation.NullSchema{}, - Mapper: mapper, - Typer: scheme, - } - return &cmdutil.Factory{ - Object: func() (meta.RESTMapper, runtime.ObjectTyper) { - return t.Mapper, t.Typer - }, - ClientForMapping: func(*meta.RESTMapping) (resource.RESTClient, error) { - return t.Client, t.Err - }, - Decoder: func(bool) runtime.Decoder { - return codec - }, - JSONEncoder: func() runtime.Encoder { - return codec - }, - Describer: func(*meta.RESTMapping) (kubectl.Describer, error) { - return t.Describer, t.Err - }, - Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, showLabels bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { - return t.Printer, t.Err - }, - Validator: func(validate bool, cacheDir string) (validation.Schema, error) { - return t.Validator, t.Err - }, - DefaultNamespace: func() (string, bool, error) { - return t.Namespace, false, t.Err - }, - ClientConfig: func() (*client.Config, error) { - return t.ClientConfig, t.Err - }, - }, t, codec -} - -func NewMixedFactory(apiClient resource.RESTClient) (*cmdutil.Factory, *testFactory, runtime.Codec) { - f, t, c := NewTestFactory() - f.Object = func() (meta.RESTMapper, runtime.ObjectTyper) { - return meta.MultiRESTMapper{t.Mapper, testapi.Default.RESTMapper()}, runtime.MultiObjectTyper{t.Typer, api.Scheme} - } - f.ClientForMapping = func(m *meta.RESTMapping) (resource.RESTClient, error) { - if m.ObjectConvertor == api.Scheme { - return apiClient, t.Err - } - return t.Client, t.Err - } - return f, t, c -} - -func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) { - t := &testFactory{ - Validator: validation.NullSchema{}, - } - - f := &cmdutil.Factory{ - Object: func() (meta.RESTMapper, runtime.ObjectTyper) { - return testapi.Default.RESTMapper(), api.Scheme - }, - Client: func() (*client.Client, error) { - // Swap out the HTTP client out of the client with the fake's version. - fakeClient := t.Client.(*fake.RESTClient) - c := client.NewOrDie(t.ClientConfig) - c.Client = fakeClient.Client - c.ExtensionsClient.Client = fakeClient.Client - return c, t.Err - }, - ClientForMapping: func(*meta.RESTMapping) (resource.RESTClient, error) { - return t.Client, t.Err - }, - Decoder: func(bool) runtime.Decoder { - return testapi.Default.Codec() - }, - JSONEncoder: func() runtime.Encoder { - return testapi.Default.Codec() - }, - Describer: func(*meta.RESTMapping) (kubectl.Describer, error) { - return t.Describer, t.Err - }, - Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, showLabels bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { - return t.Printer, t.Err - }, - Validator: func(validate bool, cacheDir string) (validation.Schema, error) { - return t.Validator, t.Err - }, - DefaultNamespace: func() (string, bool, error) { - return t.Namespace, false, t.Err - }, - ClientConfig: func() (*client.Config, error) { - return t.ClientConfig, t.Err - }, - Generators: func(cmdName string) map[string]kubectl.Generator { - return cmdutil.DefaultGenerators(cmdName) - }, - LogsForObject: func(object, options runtime.Object) (*client.Request, error) { - fakeClient := t.Client.(*fake.RESTClient) - c := client.NewOrDie(t.ClientConfig) - c.Client = fakeClient.Client - - switch t := object.(type) { - case *api.Pod: - opts, ok := options.(*api.PodLogOptions) - if !ok { - return nil, errors.New("provided options object is not a PodLogOptions") - } - return c.Pods(t.Namespace).GetLogs(t.Name, opts), nil - default: - fqKind, err := api.Scheme.ObjectKind(object) - if err != nil { - return nil, err - } - return nil, fmt.Errorf("cannot get the logs from %v", fqKind) - } - }, - } - rf := cmdutil.NewFactory(nil) - f.PodSelectorForObject = rf.PodSelectorForObject - f.MapBasedSelectorForObject = rf.MapBasedSelectorForObject - f.PortsForObject = rf.PortsForObject - f.LabelsForObject = rf.LabelsForObject - f.CanBeExposed = rf.CanBeExposed - return f, t, testapi.Default.Codec() -} - -func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser { - return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj)))) -} - -func stringBody(body string) io.ReadCloser { - return ioutil.NopCloser(bytes.NewReader([]byte(body))) -} - -// TODO(jlowdermilk): refactor the Factory so we can test client versions properly, -// with different client/server version skew scenarios. -// Verify that resource.RESTClients constructed from a factory respect mapping.APIVersion -//func TestClientVersions(t *testing.T) { -// f := cmdutil.NewFactory(nil) -// -// version := testapi.Default.Version() -// mapping := &meta.RESTMapping{ -// APIVersion: version, -// } -// c, err := f.ClientForMapping(mapping) -// if err != nil { -// t.Errorf("unexpected error: %v", err) -// } -// client := c.(*client.RESTClient) -// if client.APIVersion() != version { -// t.Errorf("unexpected Client APIVersion: %s %v", client.APIVersion, client) -// } -//} - -func ExamplePrintReplicationControllerWithNamespace() { - f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false, true, false, false, false, false, []string{}) - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: nil, - } - cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr) - ctrl := &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "beep", - Labels: map[string]string{"foo": "bar"}, - CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)}, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 1, - Selector: map[string]string{"foo": "bar"}, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"foo": "bar"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - }, - }, - }, - }, - }, - } - err := f.PrintObject(cmd, ctrl, os.Stdout) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - // Output: - // NAMESPACE CONTROLLER REPLICAS AGE - // beep foo 1 10y -} - -func ExamplePrintReplicationControllerWithWide() { - f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false, false, true, false, false, false, []string{}) - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: nil, - } - cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr) - ctrl := &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "bar"}, - CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)}, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 1, - Selector: map[string]string{"foo": "bar"}, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"foo": "bar"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - }, - }, - }, - }, - }, - } - err := f.PrintObject(cmd, ctrl, os.Stdout) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - // Output: - // CONTROLLER REPLICAS AGE CONTAINER(S) IMAGE(S) SELECTOR - // foo 1 10y foo someimage foo=bar -} - -func ExamplePrintPodWithWideFormat() { - f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false, false, true, false, false, false, []string{}) - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: nil, - } - nodeName := "kubernetes-minion-abcd" - cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr) - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "test1", - CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)}, - }, - Spec: api.PodSpec{ - Containers: make([]api.Container, 2), - NodeName: nodeName, - }, - Status: api.PodStatus{ - Phase: "podPhase", - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - } - err := f.PrintObject(cmd, pod, os.Stdout) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - // Output: - // NAME READY STATUS RESTARTS AGE NODE - // test1 1/2 podPhase 6 10y kubernetes-minion-abcd -} - -func ExamplePrintPodWithShowLabels() { - f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false, false, false, false, true, false, []string{}) - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: nil, - } - nodeName := "kubernetes-minion-abcd" - cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr) - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "test1", - CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)}, - Labels: map[string]string{ - "l1": "key", - "l2": "value", - }, - }, - Spec: api.PodSpec{ - Containers: make([]api.Container, 2), - NodeName: nodeName, - }, - Status: api.PodStatus{ - Phase: "podPhase", - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - } - err := f.PrintObject(cmd, pod, os.Stdout) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - // Output: - // NAME READY STATUS RESTARTS AGE LABELS - // test1 1/2 podPhase 6 10y l1=key,l2=value -} - -func newAllPhasePodList() *api.PodList { - nodeName := "kubernetes-minion-abcd" - return &api.PodList{ - Items: []api.Pod{ - { - ObjectMeta: api.ObjectMeta{ - Name: "test1", - CreationTimestamp: unversioned.Time{time.Now().AddDate(-10, 0, 0)}, - }, - Spec: api.PodSpec{ - Containers: make([]api.Container, 2), - NodeName: nodeName, - }, - Status: api.PodStatus{ - Phase: api.PodPending, - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "test2", - CreationTimestamp: unversioned.Time{time.Now().AddDate(-10, 0, 0)}, - }, - Spec: api.PodSpec{ - Containers: make([]api.Container, 2), - NodeName: nodeName, - }, - Status: api.PodStatus{ - Phase: api.PodRunning, - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "test3", - CreationTimestamp: unversioned.Time{time.Now().AddDate(-10, 0, 0)}, - }, - Spec: api.PodSpec{ - Containers: make([]api.Container, 2), - NodeName: nodeName, - }, - Status: api.PodStatus{ - Phase: api.PodSucceeded, - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "test4", - CreationTimestamp: unversioned.Time{time.Now().AddDate(-10, 0, 0)}, - }, - Spec: api.PodSpec{ - Containers: make([]api.Container, 2), - NodeName: nodeName, - }, - Status: api.PodStatus{ - Phase: api.PodFailed, - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "test5", - CreationTimestamp: unversioned.Time{time.Now().AddDate(-10, 0, 0)}, - }, - Spec: api.PodSpec{ - Containers: make([]api.Container, 2), - NodeName: nodeName, - }, - Status: api.PodStatus{ - Phase: api.PodUnknown, - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - }}, - } -} - -func ExamplePrintPodHideTerminated() { - f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false, false, false, false, false, false, []string{}) - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: nil, - } - cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr) - podList := newAllPhasePodList() - err := f.PrintObject(cmd, podList, os.Stdout) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - // Output: - // NAME READY STATUS RESTARTS AGE - // test1 1/2 Pending 6 10y - // test2 1/2 Running 6 10y - // test5 1/2 Unknown 6 10y -} - -func ExamplePrintPodShowAll() { - f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false, false, false, true, false, false, []string{}) - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: nil, - } - cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr) - podList := newAllPhasePodList() - err := f.PrintObject(cmd, podList, os.Stdout) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - // Output: - // NAME READY STATUS RESTARTS AGE - // test1 1/2 Pending 6 10y - // test2 1/2 Running 6 10y - // test3 1/2 Succeeded 6 10y - // test4 1/2 Failed 6 10y - // test5 1/2 Unknown 6 10y -} - -func ExamplePrintServiceWithNamespacesAndLabels() { - f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false, true, false, false, false, false, []string{"l1"}) - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: nil, - } - cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr) - svc := &api.ServiceList{ - Items: []api.Service{ - { - ObjectMeta: api.ObjectMeta{ - Name: "svc1", - Namespace: "ns1", - CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)}, - Labels: map[string]string{ - "l1": "value", - }, - }, - Spec: api.ServiceSpec{ - Ports: []api.ServicePort{ - {Protocol: "UDP", Port: 53}, - {Protocol: "TCP", Port: 53}, - }, - Selector: map[string]string{ - "s": "magic", - }, - ClusterIP: "10.1.1.1", - }, - Status: api.ServiceStatus{}, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "svc2", - Namespace: "ns2", - CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)}, - Labels: map[string]string{ - "l1": "dolla-bill-yall", - }, - }, - Spec: api.ServiceSpec{ - Ports: []api.ServicePort{ - {Protocol: "TCP", Port: 80}, - {Protocol: "TCP", Port: 8080}, - }, - Selector: map[string]string{ - "s": "kazam", - }, - ClusterIP: "10.1.1.2", - }, - Status: api.ServiceStatus{}, - }}, - } - ld := util.NewLineDelimiter(os.Stdout, "|") - defer ld.Flush() - err := f.PrintObject(cmd, svc, ld) - if err != nil { - fmt.Printf("Unexpected error: %v", err) - } - // Output: - // |NAMESPACE NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE L1| - // |ns1 svc1 10.1.1.1 unknown 53/UDP,53/TCP 10y value| - // |ns2 svc2 10.1.1.2 unknown 80/TCP,8080/TCP 10y dolla-bill-yall| - // || -} - -func TestNormalizationFuncGlobalExistence(t *testing.T) { - // This test can be safely deleted when we will not support multiple flag formats - root := NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, os.Stdout, os.Stderr) - - if root.Parent() != nil { - t.Fatal("We expect the root command to be returned") - } - if root.GlobalNormalizationFunc() == nil { - t.Fatal("We expect that root command has a global normalization function") - } - - if reflect.ValueOf(root.GlobalNormalizationFunc()).Pointer() != reflect.ValueOf(root.Flags().GetNormalizeFunc()).Pointer() { - t.Fatal("root command seems to have a wrong normalization function") - } - - sub := root - for sub.HasSubCommands() { - sub = sub.Commands()[0] - } - - // In case of failure of this test check this PR: spf13/cobra#110 - if reflect.ValueOf(sub.Flags().GetNormalizeFunc()).Pointer() != reflect.ValueOf(root.Flags().GetNormalizeFunc()).Pointer() { - t.Fatal("child and root commands should have the same normalization functions") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/config.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/config.go deleted file mode 100644 index 47eeecf5c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/config.go +++ /dev/null @@ -1,448 +0,0 @@ -/* -Copyright 2014 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 config - -import ( - "errors" - "io" - "os" - "path" - "path/filepath" - "reflect" - "strconv" - - "github.com/golang/glog" - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" -) - -type PathOptions struct { - // GlobalFile is the full path to the file to load as the global (final) option - GlobalFile string - // EnvVar is the env var name that points to the list of kubeconfig files to load - EnvVar string - // ExplicitFileFlag is the name of the flag to use for prompting for the kubeconfig file - ExplicitFileFlag string - - // GlobalFileSubpath is an optional value used for displaying help - GlobalFileSubpath string - - LoadingRules *clientcmd.ClientConfigLoadingRules -} - -// ConfigAccess is used by subcommands and methods in this package to load and modify the appropriate config files -type ConfigAccess interface { - // GetLoadingPrecedence returns the slice of files that should be used for loading and inspecting the config - GetLoadingPrecedence() []string - // GetStartingConfig returns the config that subcommands should being operating against. It may or may not be merged depending on loading rules - GetStartingConfig() (*clientcmdapi.Config, error) - // GetDefaultFilename returns the name of the file you should write into (create if necessary), if you're trying to create a new stanza as opposed to updating an existing one. - GetDefaultFilename() string - // IsExplicitFile indicates whether or not this command is interested in exactly one file. This implementation only ever does that via a flag, but implementations that handle local, global, and flags may have more - IsExplicitFile() bool - // GetExplicitFile returns the particular file this command is operating against. This implementation only ever has one, but implementations that handle local, global, and flags may have more - GetExplicitFile() string -} - -func NewCmdConfig(pathOptions *PathOptions, out io.Writer) *cobra.Command { - if len(pathOptions.ExplicitFileFlag) == 0 { - pathOptions.ExplicitFileFlag = clientcmd.RecommendedConfigPathFlag - } - - cmd := &cobra.Command{ - Use: "config SUBCOMMAND", - Short: "config modifies kubeconfig files", - Long: `config modifies kubeconfig files using subcommands like "kubectl config set current-context my-context" - -The loading order follows these rules: -1. If the --` + pathOptions.ExplicitFileFlag + ` flag is set, then only that file is loaded. The flag may only be set once and no merging takes place. -2. If $` + pathOptions.EnvVar + ` environment variable is set, then it is used a list of paths (normal path delimitting rules for your system). These paths are merged together. When a value is modified, it is modified in the file that defines the stanza. When a value is created, it is created in the first file that exists. If no files in the chain exist, then it creates the last file in the list. -3. Otherwise, ` + path.Join("${HOME}", pathOptions.GlobalFileSubpath) + ` is used and no merging takes place. -`, - Run: func(cmd *cobra.Command, args []string) { - cmd.Help() - }, - } - - // file paths are common to all sub commands - cmd.PersistentFlags().StringVar(&pathOptions.LoadingRules.ExplicitPath, pathOptions.ExplicitFileFlag, pathOptions.LoadingRules.ExplicitPath, "use a particular kubeconfig file") - - cmd.AddCommand(NewCmdConfigView(out, pathOptions)) - cmd.AddCommand(NewCmdConfigSetCluster(out, pathOptions)) - cmd.AddCommand(NewCmdConfigSetAuthInfo(out, pathOptions)) - cmd.AddCommand(NewCmdConfigSetContext(out, pathOptions)) - cmd.AddCommand(NewCmdConfigSet(out, pathOptions)) - cmd.AddCommand(NewCmdConfigUnset(out, pathOptions)) - cmd.AddCommand(NewCmdConfigCurrentContext(out, pathOptions)) - cmd.AddCommand(NewCmdConfigUseContext(out, pathOptions)) - - return cmd -} - -func NewDefaultPathOptions() *PathOptions { - ret := &PathOptions{ - GlobalFile: clientcmd.RecommendedHomeFile, - EnvVar: clientcmd.RecommendedConfigPathEnvVar, - ExplicitFileFlag: clientcmd.RecommendedConfigPathFlag, - - GlobalFileSubpath: path.Join(clientcmd.RecommendedHomeDir, clientcmd.RecommendedFileName), - - LoadingRules: clientcmd.NewDefaultClientConfigLoadingRules(), - } - ret.LoadingRules.DoNotResolvePaths = true - - return ret -} - -func (o *PathOptions) GetEnvVarFiles() []string { - if len(o.EnvVar) == 0 { - return []string{} - } - - envVarValue := os.Getenv(o.EnvVar) - if len(envVarValue) == 0 { - return []string{} - } - - return filepath.SplitList(envVarValue) -} - -func (o *PathOptions) GetLoadingPrecedence() []string { - if envVarFiles := o.GetEnvVarFiles(); len(envVarFiles) > 0 { - return envVarFiles - } - - return []string{o.GlobalFile} -} - -func (o *PathOptions) GetStartingConfig() (*clientcmdapi.Config, error) { - // don't mutate the original - loadingRules := *o.LoadingRules - loadingRules.Precedence = o.GetLoadingPrecedence() - - clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, &clientcmd.ConfigOverrides{}) - rawConfig, err := clientConfig.RawConfig() - if os.IsNotExist(err) { - return clientcmdapi.NewConfig(), nil - } - if err != nil { - return nil, err - } - - return &rawConfig, nil -} - -func (o *PathOptions) GetDefaultFilename() string { - if o.IsExplicitFile() { - return o.GetExplicitFile() - } - - if envVarFiles := o.GetEnvVarFiles(); len(envVarFiles) > 0 { - if len(envVarFiles) == 1 { - return envVarFiles[0] - } - - // if any of the envvar files already exists, return it - for _, envVarFile := range envVarFiles { - if _, err := os.Stat(envVarFile); err == nil { - return envVarFile - } - } - - // otherwise, return the last one in the list - return envVarFiles[len(envVarFiles)-1] - } - - return o.GlobalFile -} - -func (o *PathOptions) IsExplicitFile() bool { - if len(o.LoadingRules.ExplicitPath) > 0 { - return true - } - - return false -} - -func (o *PathOptions) GetExplicitFile() string { - return o.LoadingRules.ExplicitPath -} - -// ModifyConfig takes a Config object, iterates through Clusters, AuthInfos, and Contexts, uses the LocationOfOrigin if specified or -// uses the default destination file to write the results into. This results in multiple file reads, but it's very easy to follow. -// Preferences and CurrentContext should always be set in the default destination file. Since we can't distinguish between empty and missing values -// (no nil strings), we're forced have separate handling for them. In the kubeconfig cases, newConfig should have at most one difference, -// that means that this code will only write into a single file. If you want to relativizePaths, you must provide a fully qualified path in any -// modified element. -func ModifyConfig(configAccess ConfigAccess, newConfig clientcmdapi.Config, relativizePaths bool) error { - startingConfig, err := configAccess.GetStartingConfig() - if err != nil { - return err - } - - // We need to find all differences, locate their original files, read a partial config to modify only that stanza and write out the file. - // Special case the test for current context and preferences since those always write to the default file. - if reflect.DeepEqual(*startingConfig, newConfig) { - // nothing to do - return nil - } - - if startingConfig.CurrentContext != newConfig.CurrentContext { - if err := writeCurrentContext(configAccess, newConfig.CurrentContext); err != nil { - return err - } - } - - if !reflect.DeepEqual(startingConfig.Preferences, newConfig.Preferences) { - if err := writePreferences(configAccess, newConfig.Preferences); err != nil { - return err - } - } - - // Search every cluster, authInfo, and context. First from new to old for differences, then from old to new for deletions - for key, cluster := range newConfig.Clusters { - startingCluster, exists := startingConfig.Clusters[key] - if !reflect.DeepEqual(cluster, startingCluster) || !exists { - destinationFile := cluster.LocationOfOrigin - if len(destinationFile) == 0 { - destinationFile = configAccess.GetDefaultFilename() - } - - configToWrite := getConfigFromFileOrDie(destinationFile) - t := *cluster - - configToWrite.Clusters[key] = &t - configToWrite.Clusters[key].LocationOfOrigin = destinationFile - if relativizePaths { - if err := clientcmd.RelativizeClusterLocalPaths(configToWrite.Clusters[key]); err != nil { - return err - } - } - - if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil { - return err - } - } - } - - for key, context := range newConfig.Contexts { - startingContext, exists := startingConfig.Contexts[key] - if !reflect.DeepEqual(context, startingContext) || !exists { - destinationFile := context.LocationOfOrigin - if len(destinationFile) == 0 { - destinationFile = configAccess.GetDefaultFilename() - } - - configToWrite := getConfigFromFileOrDie(destinationFile) - configToWrite.Contexts[key] = context - - if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil { - return err - } - } - } - - for key, authInfo := range newConfig.AuthInfos { - startingAuthInfo, exists := startingConfig.AuthInfos[key] - if !reflect.DeepEqual(authInfo, startingAuthInfo) || !exists { - destinationFile := authInfo.LocationOfOrigin - if len(destinationFile) == 0 { - destinationFile = configAccess.GetDefaultFilename() - } - - configToWrite := getConfigFromFileOrDie(destinationFile) - t := *authInfo - configToWrite.AuthInfos[key] = &t - configToWrite.AuthInfos[key].LocationOfOrigin = destinationFile - if relativizePaths { - if err := clientcmd.RelativizeAuthInfoLocalPaths(configToWrite.AuthInfos[key]); err != nil { - return err - } - } - - if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil { - return err - } - } - } - - for key, cluster := range startingConfig.Clusters { - if _, exists := newConfig.Clusters[key]; !exists { - destinationFile := cluster.LocationOfOrigin - if len(destinationFile) == 0 { - destinationFile = configAccess.GetDefaultFilename() - } - - configToWrite := getConfigFromFileOrDie(destinationFile) - delete(configToWrite.Clusters, key) - - if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil { - return err - } - } - } - - for key, context := range startingConfig.Contexts { - if _, exists := newConfig.Contexts[key]; !exists { - destinationFile := context.LocationOfOrigin - if len(destinationFile) == 0 { - destinationFile = configAccess.GetDefaultFilename() - } - - configToWrite := getConfigFromFileOrDie(destinationFile) - delete(configToWrite.Contexts, key) - - if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil { - return err - } - } - } - - for key, authInfo := range startingConfig.AuthInfos { - if _, exists := newConfig.AuthInfos[key]; !exists { - destinationFile := authInfo.LocationOfOrigin - if len(destinationFile) == 0 { - destinationFile = configAccess.GetDefaultFilename() - } - - configToWrite := getConfigFromFileOrDie(destinationFile) - delete(configToWrite.AuthInfos, key) - - if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil { - return err - } - } - } - - return nil -} - -// writeCurrentContext takes three possible paths. -// If newCurrentContext is the same as the startingConfig's current context, then we exit. -// If newCurrentContext has a value, then that value is written into the default destination file. -// If newCurrentContext is empty, then we find the config file that is setting the CurrentContext and clear the value from that file -func writeCurrentContext(configAccess ConfigAccess, newCurrentContext string) error { - if startingConfig, err := configAccess.GetStartingConfig(); err != nil { - return err - } else if startingConfig.CurrentContext == newCurrentContext { - return nil - } - - if configAccess.IsExplicitFile() { - file := configAccess.GetExplicitFile() - currConfig := getConfigFromFileOrDie(file) - currConfig.CurrentContext = newCurrentContext - if err := clientcmd.WriteToFile(*currConfig, file); err != nil { - return err - } - - return nil - } - - if len(newCurrentContext) > 0 { - destinationFile := configAccess.GetDefaultFilename() - config := getConfigFromFileOrDie(destinationFile) - config.CurrentContext = newCurrentContext - - if err := clientcmd.WriteToFile(*config, destinationFile); err != nil { - return err - } - - return nil - } - - // we're supposed to be clearing the current context. We need to find the first spot in the chain that is setting it and clear it - for _, file := range configAccess.GetLoadingPrecedence() { - if _, err := os.Stat(file); err == nil { - currConfig := getConfigFromFileOrDie(file) - - if len(currConfig.CurrentContext) > 0 { - currConfig.CurrentContext = newCurrentContext - if err := clientcmd.WriteToFile(*currConfig, file); err != nil { - return err - } - - return nil - } - } - } - - return errors.New("no config found to write context") -} - -func writePreferences(configAccess ConfigAccess, newPrefs clientcmdapi.Preferences) error { - if startingConfig, err := configAccess.GetStartingConfig(); err != nil { - return err - } else if reflect.DeepEqual(startingConfig.Preferences, newPrefs) { - return nil - } - - if configAccess.IsExplicitFile() { - file := configAccess.GetExplicitFile() - currConfig := getConfigFromFileOrDie(file) - currConfig.Preferences = newPrefs - if err := clientcmd.WriteToFile(*currConfig, file); err != nil { - return err - } - - return nil - } - - for _, file := range configAccess.GetLoadingPrecedence() { - currConfig := getConfigFromFileOrDie(file) - - if !reflect.DeepEqual(currConfig.Preferences, newPrefs) { - currConfig.Preferences = newPrefs - if err := clientcmd.WriteToFile(*currConfig, file); err != nil { - return err - } - - return nil - } - } - - return errors.New("no config found to write preferences") -} - -// getConfigFromFileOrDie tries to read a kubeconfig file and if it can't, it calls exit. One exception, missing files result in empty configs, not an exit -func getConfigFromFileOrDie(filename string) *clientcmdapi.Config { - config, err := clientcmd.LoadFromFile(filename) - if err != nil && !os.IsNotExist(err) { - glog.FatalDepth(1, err) - } - - if config == nil { - return clientcmdapi.NewConfig() - } - - return config -} - -func toBool(propertyValue string) (bool, error) { - boolValue := false - if len(propertyValue) != 0 { - var err error - boolValue, err = strconv.ParseBool(propertyValue) - if err != nil { - return false, err - } - } - - return boolValue, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/config_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/config_test.go deleted file mode 100644 index 8f33734a0..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/config_test.go +++ /dev/null @@ -1,783 +0,0 @@ -/* -Copyright 2014 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 config - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "path" - "reflect" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" - "k8s.io/kubernetes/pkg/util" -) - -func newRedFederalCowHammerConfig() clientcmdapi.Config { - return clientcmdapi.Config{ - AuthInfos: map[string]*clientcmdapi.AuthInfo{ - "red-user": {Token: "red-token"}}, - Clusters: map[string]*clientcmdapi.Cluster{ - "cow-cluster": {Server: "http://cow.org:8080"}}, - Contexts: map[string]*clientcmdapi.Context{ - "federal-context": {AuthInfo: "red-user", Cluster: "cow-cluster"}}, - CurrentContext: "federal-context", - } -} - -func ExampleView() { - expectedConfig := newRedFederalCowHammerConfig() - test := configCommandTest{ - args: []string{"view"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - output := test.run(nil) - fmt.Printf("%v", output) - // Output: - // apiVersion: v1 - // clusters: - // - cluster: - // server: http://cow.org:8080 - // name: cow-cluster - // contexts: - // - context: - // cluster: cow-cluster - // user: red-user - // name: federal-context - // current-context: federal-context - // kind: Config - // preferences: {} - // users: - // - name: red-user - // user: - // token: red-token -} - -func TestCurrentContext(t *testing.T) { - startingConfig := newRedFederalCowHammerConfig() - test := configCommandTest{ - args: []string{"current-context"}, - startingConfig: startingConfig, - expectedConfig: startingConfig, - expectedOutputs: []string{startingConfig.CurrentContext}, - } - test.run(t) -} - -func TestSetCurrentContext(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - startingConfig := newRedFederalCowHammerConfig() - - newContextName := "the-new-context" - - startingConfig.Contexts[newContextName] = clientcmdapi.NewContext() - expectedConfig.Contexts[newContextName] = clientcmdapi.NewContext() - - expectedConfig.CurrentContext = newContextName - - test := configCommandTest{ - args: []string{"use-context", "the-new-context"}, - startingConfig: startingConfig, - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestSetNonExistentContext(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - test := configCommandTest{ - args: []string{"use-context", "non-existent-config"}, - startingConfig: expectedConfig, - expectedConfig: expectedConfig, - expectedOutputs: []string{`no context exists with the name: "non-existent-config"`}, - } - test.run(t) -} - -func TestSetIntoExistingStruct(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - expectedConfig.AuthInfos["red-user"].Password = "new-path-value" - test := configCommandTest{ - args: []string{"set", "users.red-user.password", "new-path-value"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestSetWithPathPrefixIntoExistingStruct(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - expectedConfig.Clusters["cow-cluster"].Server = "http://cow.org:8080/foo/baz" - test := configCommandTest{ - args: []string{"set", "clusters.cow-cluster.server", "http://cow.org:8080/foo/baz"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) - - dc := clientcmd.NewDefaultClientConfig(expectedConfig, &clientcmd.ConfigOverrides{}) - dcc, err := dc.ClientConfig() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - expectedHost := "http://cow.org:8080/foo/baz" - if expectedHost != dcc.Host { - t.Fatalf("expected client.Config.Host = %q instead of %q", expectedHost, dcc.Host) - } -} - -func TestUnsetStruct(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - delete(expectedConfig.AuthInfos, "red-user") - test := configCommandTest{ - args: []string{"unset", "users.red-user"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestUnsetField(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - expectedConfig.AuthInfos["red-user"] = clientcmdapi.NewAuthInfo() - test := configCommandTest{ - args: []string{"unset", "users.red-user.token"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestSetIntoNewStruct(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - cluster := clientcmdapi.NewCluster() - cluster.Server = "new-server-value" - expectedConfig.Clusters["big-cluster"] = cluster - test := configCommandTest{ - args: []string{"set", "clusters.big-cluster.server", "new-server-value"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestSetBoolean(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - cluster := clientcmdapi.NewCluster() - cluster.InsecureSkipTLSVerify = true - expectedConfig.Clusters["big-cluster"] = cluster - test := configCommandTest{ - args: []string{"set", "clusters.big-cluster.insecure-skip-tls-verify", "true"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestSetIntoNewConfig(t *testing.T) { - expectedConfig := *clientcmdapi.NewConfig() - context := clientcmdapi.NewContext() - context.AuthInfo = "fake-user" - expectedConfig.Contexts["new-context"] = context - test := configCommandTest{ - args: []string{"set", "contexts.new-context.user", "fake-user"}, - startingConfig: *clientcmdapi.NewConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestNewEmptyAuth(t *testing.T) { - expectedConfig := *clientcmdapi.NewConfig() - expectedConfig.AuthInfos["the-user-name"] = clientcmdapi.NewAuthInfo() - test := configCommandTest{ - args: []string{"set-credentials", "the-user-name"}, - startingConfig: *clientcmdapi.NewConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestAdditionalAuth(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - authInfo := clientcmdapi.NewAuthInfo() - authInfo.Token = "token" - expectedConfig.AuthInfos["another-user"] = authInfo - test := configCommandTest{ - args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagBearerToken + "=token"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestEmbedClientCert(t *testing.T) { - fakeCertFile, _ := ioutil.TempFile("", "") - defer os.Remove(fakeCertFile.Name()) - fakeData := []byte("fake-data") - ioutil.WriteFile(fakeCertFile.Name(), fakeData, 0600) - expectedConfig := newRedFederalCowHammerConfig() - authInfo := clientcmdapi.NewAuthInfo() - authInfo.ClientCertificateData = fakeData - expectedConfig.AuthInfos["another-user"] = authInfo - - test := configCommandTest{ - args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagCertFile + "=" + fakeCertFile.Name(), "--" + clientcmd.FlagEmbedCerts + "=true"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestEmbedClientKey(t *testing.T) { - fakeKeyFile, _ := ioutil.TempFile("", "") - defer os.Remove(fakeKeyFile.Name()) - fakeData := []byte("fake-data") - ioutil.WriteFile(fakeKeyFile.Name(), fakeData, 0600) - expectedConfig := newRedFederalCowHammerConfig() - authInfo := clientcmdapi.NewAuthInfo() - authInfo.ClientKeyData = fakeData - expectedConfig.AuthInfos["another-user"] = authInfo - - test := configCommandTest{ - args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagKeyFile + "=" + fakeKeyFile.Name(), "--" + clientcmd.FlagEmbedCerts + "=true"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestEmbedNoKeyOrCertDisallowed(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - test := configCommandTest{ - args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagEmbedCerts + "=true"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - expectedOutputs: []string{"--client-certificate", "--client-key", "embed"}, - } - - test.run(t) -} - -func TestEmptyTokenAndCertAllowed(t *testing.T) { - fakeCertFile, _ := ioutil.TempFile("", "cert-file") - - expectedConfig := newRedFederalCowHammerConfig() - authInfo := clientcmdapi.NewAuthInfo() - authInfo.ClientCertificate = path.Base(fakeCertFile.Name()) - expectedConfig.AuthInfos["another-user"] = authInfo - - test := configCommandTest{ - args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagCertFile + "=" + fakeCertFile.Name(), "--" + clientcmd.FlagBearerToken + "="}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestTokenAndCertAllowed(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - authInfo := clientcmdapi.NewAuthInfo() - authInfo.Token = "token" - authInfo.ClientCertificate = "/cert-file" - expectedConfig.AuthInfos["another-user"] = authInfo - test := configCommandTest{ - args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagCertFile + "=/cert-file", "--" + clientcmd.FlagBearerToken + "=token"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestTokenAndBasicDisallowed(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - test := configCommandTest{ - args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagUsername + "=myuser", "--" + clientcmd.FlagBearerToken + "=token"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - expectedOutputs: []string{"--token", "--username"}, - } - - test.run(t) -} - -func TestBasicClearsToken(t *testing.T) { - authInfoWithToken := clientcmdapi.NewAuthInfo() - authInfoWithToken.Token = "token" - - authInfoWithBasic := clientcmdapi.NewAuthInfo() - authInfoWithBasic.Username = "myuser" - authInfoWithBasic.Password = "mypass" - - startingConfig := newRedFederalCowHammerConfig() - startingConfig.AuthInfos["another-user"] = authInfoWithToken - - expectedConfig := newRedFederalCowHammerConfig() - expectedConfig.AuthInfos["another-user"] = authInfoWithBasic - - test := configCommandTest{ - args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagUsername + "=myuser", "--" + clientcmd.FlagPassword + "=mypass"}, - startingConfig: startingConfig, - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestTokenClearsBasic(t *testing.T) { - authInfoWithBasic := clientcmdapi.NewAuthInfo() - authInfoWithBasic.Username = "myuser" - authInfoWithBasic.Password = "mypass" - - authInfoWithToken := clientcmdapi.NewAuthInfo() - authInfoWithToken.Token = "token" - - startingConfig := newRedFederalCowHammerConfig() - startingConfig.AuthInfos["another-user"] = authInfoWithBasic - - expectedConfig := newRedFederalCowHammerConfig() - expectedConfig.AuthInfos["another-user"] = authInfoWithToken - - test := configCommandTest{ - args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagBearerToken + "=token"}, - startingConfig: startingConfig, - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestTokenLeavesCert(t *testing.T) { - authInfoWithCerts := clientcmdapi.NewAuthInfo() - authInfoWithCerts.ClientCertificate = "cert" - authInfoWithCerts.ClientCertificateData = []byte("certdata") - authInfoWithCerts.ClientKey = "key" - authInfoWithCerts.ClientKeyData = []byte("keydata") - - authInfoWithTokenAndCerts := clientcmdapi.NewAuthInfo() - authInfoWithTokenAndCerts.Token = "token" - authInfoWithTokenAndCerts.ClientCertificate = "cert" - authInfoWithTokenAndCerts.ClientCertificateData = []byte("certdata") - authInfoWithTokenAndCerts.ClientKey = "key" - authInfoWithTokenAndCerts.ClientKeyData = []byte("keydata") - - startingConfig := newRedFederalCowHammerConfig() - startingConfig.AuthInfos["another-user"] = authInfoWithCerts - - expectedConfig := newRedFederalCowHammerConfig() - expectedConfig.AuthInfos["another-user"] = authInfoWithTokenAndCerts - - test := configCommandTest{ - args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagBearerToken + "=token"}, - startingConfig: startingConfig, - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestCertLeavesToken(t *testing.T) { - authInfoWithToken := clientcmdapi.NewAuthInfo() - authInfoWithToken.Token = "token" - - authInfoWithTokenAndCerts := clientcmdapi.NewAuthInfo() - authInfoWithTokenAndCerts.Token = "token" - authInfoWithTokenAndCerts.ClientCertificate = "/cert" - authInfoWithTokenAndCerts.ClientKey = "/key" - - startingConfig := newRedFederalCowHammerConfig() - startingConfig.AuthInfos["another-user"] = authInfoWithToken - - expectedConfig := newRedFederalCowHammerConfig() - expectedConfig.AuthInfos["another-user"] = authInfoWithTokenAndCerts - - test := configCommandTest{ - args: []string{"set-credentials", "another-user", "--" + clientcmd.FlagCertFile + "=/cert", "--" + clientcmd.FlagKeyFile + "=/key"}, - startingConfig: startingConfig, - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestCAClearsInsecure(t *testing.T) { - fakeCAFile, _ := ioutil.TempFile("", "ca-file") - - clusterInfoWithInsecure := clientcmdapi.NewCluster() - clusterInfoWithInsecure.InsecureSkipTLSVerify = true - - clusterInfoWithCA := clientcmdapi.NewCluster() - clusterInfoWithCA.CertificateAuthority = path.Base(fakeCAFile.Name()) - - startingConfig := newRedFederalCowHammerConfig() - startingConfig.Clusters["another-cluster"] = clusterInfoWithInsecure - - expectedConfig := newRedFederalCowHammerConfig() - expectedConfig.Clusters["another-cluster"] = clusterInfoWithCA - - test := configCommandTest{ - args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagCAFile + "=" + fakeCAFile.Name()}, - startingConfig: startingConfig, - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestCAClearsCAData(t *testing.T) { - clusterInfoWithCAData := clientcmdapi.NewCluster() - clusterInfoWithCAData.CertificateAuthorityData = []byte("cadata") - - clusterInfoWithCA := clientcmdapi.NewCluster() - clusterInfoWithCA.CertificateAuthority = "/cafile" - - startingConfig := newRedFederalCowHammerConfig() - startingConfig.Clusters["another-cluster"] = clusterInfoWithCAData - - expectedConfig := newRedFederalCowHammerConfig() - expectedConfig.Clusters["another-cluster"] = clusterInfoWithCA - - test := configCommandTest{ - args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagCAFile + "=/cafile", "--" + clientcmd.FlagInsecure + "=false"}, - startingConfig: startingConfig, - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestInsecureClearsCA(t *testing.T) { - clusterInfoWithInsecure := clientcmdapi.NewCluster() - clusterInfoWithInsecure.InsecureSkipTLSVerify = true - - clusterInfoWithCA := clientcmdapi.NewCluster() - clusterInfoWithCA.CertificateAuthority = "cafile" - clusterInfoWithCA.CertificateAuthorityData = []byte("cadata") - - startingConfig := newRedFederalCowHammerConfig() - startingConfig.Clusters["another-cluster"] = clusterInfoWithCA - - expectedConfig := newRedFederalCowHammerConfig() - expectedConfig.Clusters["another-cluster"] = clusterInfoWithInsecure - - test := configCommandTest{ - args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagInsecure + "=true"}, - startingConfig: startingConfig, - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestCADataClearsCA(t *testing.T) { - fakeCAFile, _ := ioutil.TempFile("", "") - defer os.Remove(fakeCAFile.Name()) - fakeData := []byte("cadata") - ioutil.WriteFile(fakeCAFile.Name(), fakeData, 0600) - - clusterInfoWithCAData := clientcmdapi.NewCluster() - clusterInfoWithCAData.CertificateAuthorityData = fakeData - - clusterInfoWithCA := clientcmdapi.NewCluster() - clusterInfoWithCA.CertificateAuthority = "cafile" - - startingConfig := newRedFederalCowHammerConfig() - startingConfig.Clusters["another-cluster"] = clusterInfoWithCA - - expectedConfig := newRedFederalCowHammerConfig() - expectedConfig.Clusters["another-cluster"] = clusterInfoWithCAData - - test := configCommandTest{ - args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagCAFile + "=" + fakeCAFile.Name(), "--" + clientcmd.FlagEmbedCerts + "=true"}, - startingConfig: startingConfig, - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestEmbedNoCADisallowed(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - test := configCommandTest{ - args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagEmbedCerts + "=true"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - expectedOutputs: []string{"--certificate-authority", "embed"}, - } - - test.run(t) -} - -func TestCAAndInsecureDisallowed(t *testing.T) { - test := configCommandTest{ - args: []string{"set-cluster", "another-cluster", "--" + clientcmd.FlagCAFile + "=cafile", "--" + clientcmd.FlagInsecure + "=true"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: newRedFederalCowHammerConfig(), - expectedOutputs: []string{"certificate", "insecure"}, - } - - test.run(t) -} - -func TestMergeExistingAuth(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - authInfo := expectedConfig.AuthInfos["red-user"] - authInfo.ClientKey = "/key" - expectedConfig.AuthInfos["red-user"] = authInfo - test := configCommandTest{ - args: []string{"set-credentials", "red-user", "--" + clientcmd.FlagKeyFile + "=/key"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestNewEmptyCluster(t *testing.T) { - expectedConfig := *clientcmdapi.NewConfig() - expectedConfig.Clusters["new-cluster"] = clientcmdapi.NewCluster() - test := configCommandTest{ - args: []string{"set-cluster", "new-cluster"}, - startingConfig: *clientcmdapi.NewConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestAdditionalCluster(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - cluster := clientcmdapi.NewCluster() - cluster.APIVersion = testapi.Default.GroupVersion().String() - cluster.CertificateAuthority = "/ca-location" - cluster.InsecureSkipTLSVerify = false - cluster.Server = "serverlocation" - expectedConfig.Clusters["different-cluster"] = cluster - test := configCommandTest{ - args: []string{"set-cluster", "different-cluster", "--" + clientcmd.FlagAPIServer + "=serverlocation", "--" + clientcmd.FlagInsecure + "=false", "--" + clientcmd.FlagCAFile + "=/ca-location", "--" + clientcmd.FlagAPIVersion + "=" + testapi.Default.GroupVersion().String()}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestOverwriteExistingCluster(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - cluster := clientcmdapi.NewCluster() - cluster.Server = "serverlocation" - expectedConfig.Clusters["cow-cluster"] = cluster - - test := configCommandTest{ - args: []string{"set-cluster", "cow-cluster", "--" + clientcmd.FlagAPIServer + "=serverlocation"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestNewEmptyContext(t *testing.T) { - expectedConfig := *clientcmdapi.NewConfig() - expectedConfig.Contexts["new-context"] = clientcmdapi.NewContext() - test := configCommandTest{ - args: []string{"set-context", "new-context"}, - startingConfig: *clientcmdapi.NewConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestAdditionalContext(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - context := clientcmdapi.NewContext() - context.Cluster = "some-cluster" - context.AuthInfo = "some-user" - context.Namespace = "different-namespace" - expectedConfig.Contexts["different-context"] = context - test := configCommandTest{ - args: []string{"set-context", "different-context", "--" + clientcmd.FlagClusterName + "=some-cluster", "--" + clientcmd.FlagAuthInfoName + "=some-user", "--" + clientcmd.FlagNamespace + "=different-namespace"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestMergeExistingContext(t *testing.T) { - expectedConfig := newRedFederalCowHammerConfig() - context := expectedConfig.Contexts["federal-context"] - context.Namespace = "hammer" - expectedConfig.Contexts["federal-context"] = context - - test := configCommandTest{ - args: []string{"set-context", "federal-context", "--" + clientcmd.FlagNamespace + "=hammer"}, - startingConfig: newRedFederalCowHammerConfig(), - expectedConfig: expectedConfig, - } - - test.run(t) -} - -func TestToBool(t *testing.T) { - type test struct { - in string - out bool - err string - } - - tests := []test{ - {"", false, ""}, - {"true", true, ""}, - {"on", false, `strconv.ParseBool: parsing "on": invalid syntax`}, - } - - for _, curr := range tests { - b, err := toBool(curr.in) - if (len(curr.err) != 0) && err == nil { - t.Errorf("Expected error: %v, but got nil", curr.err) - } - if (len(curr.err) == 0) && err != nil { - t.Errorf("Unexpected error: %v", err) - } - if (err != nil) && (err.Error() != curr.err) { - t.Errorf("Expected %v, got %v", curr.err, err) - - } - if b != curr.out { - t.Errorf("Expected %v, got %v", curr.out, b) - } - } - -} - -func testConfigCommand(args []string, startingConfig clientcmdapi.Config, t *testing.T) (string, clientcmdapi.Config) { - fakeKubeFile, _ := ioutil.TempFile("", "") - defer os.Remove(fakeKubeFile.Name()) - err := clientcmd.WriteToFile(startingConfig, fakeKubeFile.Name()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - argsToUse := make([]string, 0, 2+len(args)) - argsToUse = append(argsToUse, "--kubeconfig="+fakeKubeFile.Name()) - argsToUse = append(argsToUse, args...) - - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdConfig(NewDefaultPathOptions(), buf) - cmd.SetArgs(argsToUse) - cmd.Execute() - - // outBytes, _ := ioutil.ReadFile(fakeKubeFile.Name()) - config := getConfigFromFileOrDie(fakeKubeFile.Name()) - - return buf.String(), *config -} - -type configCommandTest struct { - args []string - startingConfig clientcmdapi.Config - expectedConfig clientcmdapi.Config - expectedOutputs []string -} - -func (test configCommandTest) run(t *testing.T) string { - out, actualConfig := testConfigCommand(test.args, test.startingConfig, t) - - testSetNilMapsToEmpties(reflect.ValueOf(&test.expectedConfig)) - testSetNilMapsToEmpties(reflect.ValueOf(&actualConfig)) - testClearLocationOfOrigin(&actualConfig) - - if !api.Semantic.DeepEqual(test.expectedConfig, actualConfig) { - t.Errorf("diff: %v", util.ObjectDiff(test.expectedConfig, actualConfig)) - t.Errorf("expected: %#v\n actual: %#v", test.expectedConfig, actualConfig) - } - - for _, expectedOutput := range test.expectedOutputs { - if !strings.Contains(out, expectedOutput) { - t.Errorf("expected '%s' in output, got '%s'", expectedOutput, out) - } - } - - return out -} -func testClearLocationOfOrigin(config *clientcmdapi.Config) { - for key, obj := range config.AuthInfos { - obj.LocationOfOrigin = "" - config.AuthInfos[key] = obj - } - for key, obj := range config.Clusters { - obj.LocationOfOrigin = "" - config.Clusters[key] = obj - } - for key, obj := range config.Contexts { - obj.LocationOfOrigin = "" - config.Contexts[key] = obj - } -} -func testSetNilMapsToEmpties(curr reflect.Value) { - actualCurrValue := curr - if curr.Kind() == reflect.Ptr { - actualCurrValue = curr.Elem() - } - - switch actualCurrValue.Kind() { - case reflect.Map: - for _, mapKey := range actualCurrValue.MapKeys() { - currMapValue := actualCurrValue.MapIndex(mapKey) - testSetNilMapsToEmpties(currMapValue) - } - - case reflect.Struct: - for fieldIndex := 0; fieldIndex < actualCurrValue.NumField(); fieldIndex++ { - currFieldValue := actualCurrValue.Field(fieldIndex) - - if currFieldValue.Kind() == reflect.Map && currFieldValue.IsNil() { - newValue := reflect.MakeMap(currFieldValue.Type()) - currFieldValue.Set(newValue) - } else { - testSetNilMapsToEmpties(currFieldValue.Addr()) - } - } - - } - -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_authinfo.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_authinfo.go deleted file mode 100644 index c61cd8c12..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_authinfo.go +++ /dev/null @@ -1,234 +0,0 @@ -/* -Copyright 2014 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 config - -import ( - "errors" - "fmt" - "io" - "io/ioutil" - "path/filepath" - "strings" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" - "k8s.io/kubernetes/pkg/util" -) - -type createAuthInfoOptions struct { - configAccess ConfigAccess - name string - authPath util.StringFlag - clientCertificate util.StringFlag - clientKey util.StringFlag - token util.StringFlag - username util.StringFlag - password util.StringFlag - embedCertData util.BoolFlag -} - -var create_authinfo_long = fmt.Sprintf(`Sets a user entry in kubeconfig -Specifying a name that already exists will merge new fields on top of existing values. - - Client-certificate flags: - --%v=certfile --%v=keyfile - - Bearer token flags: - --%v=bearer_token - - Basic auth flags: - --%v=basic_user --%v=basic_password - - Bearer token and basic auth are mutually exclusive. -`, clientcmd.FlagCertFile, clientcmd.FlagKeyFile, clientcmd.FlagBearerToken, clientcmd.FlagUsername, clientcmd.FlagPassword) - -const create_authinfo_example = `# Set only the "client-key" field on the "cluster-admin" -# entry, without touching other values: -$ kubectl config set-credentials cluster-admin --client-key=~/.kube/admin.key - -# Set basic auth for the "cluster-admin" entry -$ kubectl config set-credentials cluster-admin --username=admin --password=uXFGweU9l35qcif - -# Embed client certificate data in the "cluster-admin" entry -$ kubectl config set-credentials cluster-admin --client-certificate=~/.kube/admin.crt --embed-certs=true` - -func NewCmdConfigSetAuthInfo(out io.Writer, configAccess ConfigAccess) *cobra.Command { - options := &createAuthInfoOptions{configAccess: configAccess} - - cmd := &cobra.Command{ - Use: fmt.Sprintf("set-credentials NAME [--%v=path/to/certfile] [--%v=path/to/keyfile] [--%v=bearer_token] [--%v=basic_user] [--%v=basic_password]", clientcmd.FlagCertFile, clientcmd.FlagKeyFile, clientcmd.FlagBearerToken, clientcmd.FlagUsername, clientcmd.FlagPassword), - Short: "Sets a user entry in kubeconfig", - Long: create_authinfo_long, - Example: create_authinfo_example, - Run: func(cmd *cobra.Command, args []string) { - if !options.complete(cmd) { - return - } - - err := options.run() - if err != nil { - fmt.Fprintf(out, "%v\n", err) - } else { - fmt.Fprintf(out, "user %q set.\n", options.name) - } - }, - } - - cmd.Flags().Var(&options.clientCertificate, clientcmd.FlagCertFile, "path to "+clientcmd.FlagCertFile+" for the user entry in kubeconfig") - cmd.Flags().Var(&options.clientKey, clientcmd.FlagKeyFile, "path to "+clientcmd.FlagKeyFile+" for the user entry in kubeconfig") - cmd.Flags().Var(&options.token, clientcmd.FlagBearerToken, clientcmd.FlagBearerToken+" for the user entry in kubeconfig") - cmd.Flags().Var(&options.username, clientcmd.FlagUsername, clientcmd.FlagUsername+" for the user entry in kubeconfig") - cmd.Flags().Var(&options.password, clientcmd.FlagPassword, clientcmd.FlagPassword+" for the user entry in kubeconfig") - f := cmd.Flags().VarPF(&options.embedCertData, clientcmd.FlagEmbedCerts, "", "embed client cert/key for the user entry in kubeconfig") - f.NoOptDefVal = "true" - - return cmd -} - -func (o createAuthInfoOptions) run() error { - err := o.validate() - if err != nil { - return err - } - - config, err := o.configAccess.GetStartingConfig() - if err != nil { - return err - } - - startingStanza, exists := config.AuthInfos[o.name] - if !exists { - startingStanza = clientcmdapi.NewAuthInfo() - } - authInfo := o.modifyAuthInfo(*startingStanza) - config.AuthInfos[o.name] = &authInfo - - if err := ModifyConfig(o.configAccess, *config, true); err != nil { - return err - } - - return nil -} - -// authInfo builds an AuthInfo object from the options -func (o *createAuthInfoOptions) modifyAuthInfo(existingAuthInfo clientcmdapi.AuthInfo) clientcmdapi.AuthInfo { - modifiedAuthInfo := existingAuthInfo - - var setToken, setBasic bool - - if o.clientCertificate.Provided() { - certPath := o.clientCertificate.Value() - if o.embedCertData.Value() { - modifiedAuthInfo.ClientCertificateData, _ = ioutil.ReadFile(certPath) - modifiedAuthInfo.ClientCertificate = "" - } else { - certPath, _ = filepath.Abs(certPath) - modifiedAuthInfo.ClientCertificate = certPath - if len(modifiedAuthInfo.ClientCertificate) > 0 { - modifiedAuthInfo.ClientCertificateData = nil - } - } - } - if o.clientKey.Provided() { - keyPath := o.clientKey.Value() - if o.embedCertData.Value() { - modifiedAuthInfo.ClientKeyData, _ = ioutil.ReadFile(keyPath) - modifiedAuthInfo.ClientKey = "" - } else { - keyPath, _ = filepath.Abs(keyPath) - modifiedAuthInfo.ClientKey = keyPath - if len(modifiedAuthInfo.ClientKey) > 0 { - modifiedAuthInfo.ClientKeyData = nil - } - } - } - - if o.token.Provided() { - modifiedAuthInfo.Token = o.token.Value() - setToken = len(modifiedAuthInfo.Token) > 0 - } - - if o.username.Provided() { - modifiedAuthInfo.Username = o.username.Value() - setBasic = setBasic || len(modifiedAuthInfo.Username) > 0 - } - if o.password.Provided() { - modifiedAuthInfo.Password = o.password.Value() - setBasic = setBasic || len(modifiedAuthInfo.Password) > 0 - } - - // If any auth info was set, make sure any other existing auth types are cleared - if setToken || setBasic { - if !setToken { - modifiedAuthInfo.Token = "" - } - if !setBasic { - modifiedAuthInfo.Username = "" - modifiedAuthInfo.Password = "" - } - } - - return modifiedAuthInfo -} - -func (o *createAuthInfoOptions) complete(cmd *cobra.Command) bool { - args := cmd.Flags().Args() - if len(args) != 1 { - cmd.Help() - return false - } - - o.name = args[0] - return true -} - -func (o createAuthInfoOptions) validate() error { - if len(o.name) == 0 { - return errors.New("you must specify a non-empty user name") - } - methods := []string{} - if len(o.token.Value()) > 0 { - methods = append(methods, fmt.Sprintf("--%v", clientcmd.FlagBearerToken)) - } - if len(o.username.Value()) > 0 || len(o.password.Value()) > 0 { - methods = append(methods, fmt.Sprintf("--%v/--%v", clientcmd.FlagUsername, clientcmd.FlagPassword)) - } - if len(methods) > 1 { - return fmt.Errorf("you cannot specify more than one authentication method at the same time: %v", strings.Join(methods, ", ")) - } - if o.embedCertData.Value() { - certPath := o.clientCertificate.Value() - keyPath := o.clientKey.Value() - if certPath == "" && keyPath == "" { - return fmt.Errorf("you must specify a --%s or --%s to embed", clientcmd.FlagCertFile, clientcmd.FlagKeyFile) - } - if certPath != "" { - if _, err := ioutil.ReadFile(certPath); err != nil { - return fmt.Errorf("error reading %s data from %s: %v", clientcmd.FlagCertFile, certPath, err) - } - } - if keyPath != "" { - if _, err := ioutil.ReadFile(keyPath); err != nil { - return fmt.Errorf("error reading %s data from %s: %v", clientcmd.FlagKeyFile, keyPath, err) - } - } - } - - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_cluster.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_cluster.go deleted file mode 100644 index 6f463a1b4..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_cluster.go +++ /dev/null @@ -1,183 +0,0 @@ -/* -Copyright 2014 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 config - -import ( - "errors" - "fmt" - "io" - "io/ioutil" - "path/filepath" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" - "k8s.io/kubernetes/pkg/util" -) - -type createClusterOptions struct { - configAccess ConfigAccess - name string - server util.StringFlag - apiVersion util.StringFlag - insecureSkipTLSVerify util.BoolFlag - certificateAuthority util.StringFlag - embedCAData util.BoolFlag -} - -const ( - create_cluster_long = `Sets a cluster entry in kubeconfig. -Specifying a name that already exists will merge new fields on top of existing values for those fields.` - create_cluster_example = `# Set only the server field on the e2e cluster entry without touching other values. -$ kubectl config set-cluster e2e --server=https://1.2.3.4 - -# Embed certificate authority data for the e2e cluster entry -$ kubectl config set-cluster e2e --certificate-authority=~/.kube/e2e/kubernetes.ca.crt - -# Disable cert checking for the dev cluster entry -$ kubectl config set-cluster e2e --insecure-skip-tls-verify=true` -) - -func NewCmdConfigSetCluster(out io.Writer, configAccess ConfigAccess) *cobra.Command { - options := &createClusterOptions{configAccess: configAccess} - - cmd := &cobra.Command{ - Use: fmt.Sprintf("set-cluster NAME [--%v=server] [--%v=path/to/certficate/authority] [--%v=apiversion] [--%v=true]", clientcmd.FlagAPIServer, clientcmd.FlagCAFile, clientcmd.FlagAPIVersion, clientcmd.FlagInsecure), - Short: "Sets a cluster entry in kubeconfig", - Long: create_cluster_long, - Example: create_cluster_example, - Run: func(cmd *cobra.Command, args []string) { - if !options.complete(cmd) { - return - } - - err := options.run() - if err != nil { - fmt.Fprintf(out, "%v\n", err) - } else { - fmt.Fprintf(out, "cluster %q set.\n", options.name) - } - }, - } - - options.insecureSkipTLSVerify.Default(false) - - cmd.Flags().Var(&options.server, clientcmd.FlagAPIServer, clientcmd.FlagAPIServer+" for the cluster entry in kubeconfig") - cmd.Flags().Var(&options.apiVersion, clientcmd.FlagAPIVersion, clientcmd.FlagAPIVersion+" for the cluster entry in kubeconfig") - f := cmd.Flags().VarPF(&options.insecureSkipTLSVerify, clientcmd.FlagInsecure, "", clientcmd.FlagInsecure+" for the cluster entry in kubeconfig") - f.NoOptDefVal = "true" - cmd.Flags().Var(&options.certificateAuthority, clientcmd.FlagCAFile, "path to "+clientcmd.FlagCAFile+" for the cluster entry in kubeconfig") - f = cmd.Flags().VarPF(&options.embedCAData, clientcmd.FlagEmbedCerts, "", clientcmd.FlagEmbedCerts+" for the cluster entry in kubeconfig") - f.NoOptDefVal = "true" - - return cmd -} - -func (o createClusterOptions) run() error { - err := o.validate() - if err != nil { - return err - } - - config, err := o.configAccess.GetStartingConfig() - if err != nil { - return err - } - - startingStanza, exists := config.Clusters[o.name] - if !exists { - startingStanza = clientcmdapi.NewCluster() - } - cluster := o.modifyCluster(*startingStanza) - config.Clusters[o.name] = &cluster - - if err := ModifyConfig(o.configAccess, *config, true); err != nil { - return err - } - - return nil -} - -// cluster builds a Cluster object from the options -func (o *createClusterOptions) modifyCluster(existingCluster clientcmdapi.Cluster) clientcmdapi.Cluster { - modifiedCluster := existingCluster - - if o.server.Provided() { - modifiedCluster.Server = o.server.Value() - } - if o.apiVersion.Provided() { - modifiedCluster.APIVersion = o.apiVersion.Value() - } - if o.insecureSkipTLSVerify.Provided() { - modifiedCluster.InsecureSkipTLSVerify = o.insecureSkipTLSVerify.Value() - // Specifying insecure mode clears any certificate authority - if modifiedCluster.InsecureSkipTLSVerify { - modifiedCluster.CertificateAuthority = "" - modifiedCluster.CertificateAuthorityData = nil - } - } - if o.certificateAuthority.Provided() { - caPath := o.certificateAuthority.Value() - if o.embedCAData.Value() { - modifiedCluster.CertificateAuthorityData, _ = ioutil.ReadFile(caPath) - modifiedCluster.InsecureSkipTLSVerify = false - modifiedCluster.CertificateAuthority = "" - } else { - caPath, _ = filepath.Abs(caPath) - modifiedCluster.CertificateAuthority = caPath - // Specifying a certificate authority file clears certificate authority data and insecure mode - if caPath != "" { - modifiedCluster.InsecureSkipTLSVerify = false - modifiedCluster.CertificateAuthorityData = nil - } - } - } - - return modifiedCluster -} - -func (o *createClusterOptions) complete(cmd *cobra.Command) bool { - args := cmd.Flags().Args() - if len(args) != 1 { - cmd.Help() - return false - } - - o.name = args[0] - return true -} - -func (o createClusterOptions) validate() error { - if len(o.name) == 0 { - return errors.New("you must specify a non-empty cluster name") - } - if o.insecureSkipTLSVerify.Value() && o.certificateAuthority.Value() != "" { - return errors.New("you cannot specify a certificate authority and insecure mode at the same time") - } - if o.embedCAData.Value() { - caPath := o.certificateAuthority.Value() - if caPath == "" { - return fmt.Errorf("you must specify a --%s to embed", clientcmd.FlagCAFile) - } - if _, err := ioutil.ReadFile(caPath); err != nil { - return fmt.Errorf("could not read %s data from %s: %v", clientcmd.FlagCAFile, caPath, err) - } - } - - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_context.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_context.go deleted file mode 100644 index 093ce944c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/create_context.go +++ /dev/null @@ -1,133 +0,0 @@ -/* -Copyright 2014 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 config - -import ( - "errors" - "fmt" - "io" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" - "k8s.io/kubernetes/pkg/util" -) - -type createContextOptions struct { - configAccess ConfigAccess - name string - cluster util.StringFlag - authInfo util.StringFlag - namespace util.StringFlag -} - -const ( - create_context_long = `Sets a context entry in kubeconfig -Specifying a name that already exists will merge new fields on top of existing values for those fields.` - create_context_example = `# Set the user field on the gce context entry without touching other values -$ kubectl config set-context gce --user=cluster-admin` -) - -func NewCmdConfigSetContext(out io.Writer, configAccess ConfigAccess) *cobra.Command { - options := &createContextOptions{configAccess: configAccess} - - cmd := &cobra.Command{ - Use: fmt.Sprintf("set-context NAME [--%v=cluster_nickname] [--%v=user_nickname] [--%v=namespace]", clientcmd.FlagClusterName, clientcmd.FlagAuthInfoName, clientcmd.FlagNamespace), - Short: "Sets a context entry in kubeconfig", - Long: create_context_long, - Example: create_context_example, - Run: func(cmd *cobra.Command, args []string) { - if !options.complete(cmd) { - return - } - - err := options.run() - if err != nil { - fmt.Fprintf(out, "%v\n", err) - } else { - fmt.Fprintf(out, "context %q set.\n", options.name) - } - }, - } - - cmd.Flags().Var(&options.cluster, clientcmd.FlagClusterName, clientcmd.FlagClusterName+" for the context entry in kubeconfig") - cmd.Flags().Var(&options.authInfo, clientcmd.FlagAuthInfoName, clientcmd.FlagAuthInfoName+" for the context entry in kubeconfig") - cmd.Flags().Var(&options.namespace, clientcmd.FlagNamespace, clientcmd.FlagNamespace+" for the context entry in kubeconfig") - - return cmd -} - -func (o createContextOptions) run() error { - err := o.validate() - if err != nil { - return err - } - - config, err := o.configAccess.GetStartingConfig() - if err != nil { - return err - } - - startingStanza, exists := config.Contexts[o.name] - if !exists { - startingStanza = clientcmdapi.NewContext() - } - context := o.modifyContext(*startingStanza) - config.Contexts[o.name] = &context - - if err := ModifyConfig(o.configAccess, *config, true); err != nil { - return err - } - - return nil -} - -func (o *createContextOptions) modifyContext(existingContext clientcmdapi.Context) clientcmdapi.Context { - modifiedContext := existingContext - - if o.cluster.Provided() { - modifiedContext.Cluster = o.cluster.Value() - } - if o.authInfo.Provided() { - modifiedContext.AuthInfo = o.authInfo.Value() - } - if o.namespace.Provided() { - modifiedContext.Namespace = o.namespace.Value() - } - - return modifiedContext -} - -func (o *createContextOptions) complete(cmd *cobra.Command) bool { - args := cmd.Flags().Args() - if len(args) != 1 { - cmd.Help() - return false - } - - o.name = args[0] - return true -} - -func (o createContextOptions) validate() error { - if len(o.name) == 0 { - return errors.New("you must specify a non-empty context name") - } - - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/current_context.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/current_context.go deleted file mode 100644 index 66e8f78b3..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/current_context.go +++ /dev/null @@ -1,67 +0,0 @@ -/* -Copyright 2014 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 config - -import ( - "fmt" - "io" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - - "github.com/spf13/cobra" -) - -type CurrentContextOptions struct { - ConfigAccess ConfigAccess -} - -const ( - current_context_long = `Displays the current-context` - current_context_example = `# Display the current-context -$ kubectl config current-context` -) - -func NewCmdConfigCurrentContext(out io.Writer, configAccess ConfigAccess) *cobra.Command { - options := &CurrentContextOptions{ConfigAccess: configAccess} - - cmd := &cobra.Command{ - Use: "current-context", - Short: "Displays the current-context", - Long: current_context_long, - Example: current_context_example, - Run: func(cmd *cobra.Command, args []string) { - err := RunCurrentContext(out, args, options) - cmdutil.CheckErr(err) - }, - } - - return cmd -} - -func RunCurrentContext(out io.Writer, args []string, options *CurrentContextOptions) error { - config, err := options.ConfigAccess.GetStartingConfig() - if err != nil { - return err - } - - if config.CurrentContext == "" { - err = fmt.Errorf("current-context is not set\n") - return err - } - - fmt.Fprintf(out, "%s\n", config.CurrentContext) - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/current_context_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/current_context_test.go deleted file mode 100644 index 169c65443..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/current_context_test.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright 2014 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 config - -import ( - "bytes" - "io/ioutil" - "os" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" -) - -type currentContextTest struct { - startingConfig clientcmdapi.Config - expectedError string -} - -func newFederalContextConfig() clientcmdapi.Config { - return clientcmdapi.Config{ - CurrentContext: "federal-context", - } -} - -func TestCurrentContextWithSetContext(t *testing.T) { - test := currentContextTest{ - startingConfig: newFederalContextConfig(), - expectedError: "", - } - - test.run(t) -} - -func TestCurrentContextWithUnsetContext(t *testing.T) { - test := currentContextTest{ - startingConfig: *clientcmdapi.NewConfig(), - expectedError: "current-context is not set", - } - - test.run(t) -} - -func (test currentContextTest) run(t *testing.T) { - fakeKubeFile, _ := ioutil.TempFile("", "") - defer os.Remove(fakeKubeFile.Name()) - err := clientcmd.WriteToFile(test.startingConfig, fakeKubeFile.Name()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - pathOptions := NewDefaultPathOptions() - pathOptions.GlobalFile = fakeKubeFile.Name() - pathOptions.EnvVar = "" - options := CurrentContextOptions{ - ConfigAccess: pathOptions, - } - - buf := bytes.NewBuffer([]byte{}) - err = RunCurrentContext(buf, []string{}, &options) - if len(test.expectedError) != 0 { - if err == nil { - t.Errorf("Did not get %v", test.expectedError) - } else { - if !strings.Contains(err.Error(), test.expectedError) { - t.Errorf("Expected %v, but got %v", test.expectedError, err) - } - } - return - } - - if err != nil { - t.Errorf("Unexpected error: %v", err) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser.go deleted file mode 100644 index 0be5f241e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser.go +++ /dev/null @@ -1,152 +0,0 @@ -/* -Copyright 2014 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 config - -import ( - "fmt" - "reflect" - "strings" - - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" - "k8s.io/kubernetes/pkg/util/sets" -) - -type navigationSteps struct { - steps []navigationStep - currentStepIndex int -} - -type navigationStep struct { - stepValue string - stepType reflect.Type -} - -func newNavigationSteps(path string) (*navigationSteps, error) { - steps := []navigationStep{} - individualParts := strings.Split(path, ".") - - currType := reflect.TypeOf(clientcmdapi.Config{}) - currPartIndex := 0 - for currPartIndex < len(individualParts) { - switch currType.Kind() { - case reflect.Map: - // if we're in a map, we need to locate a name. That name may contain dots, so we need to know what tokens are legal for the map's value type - // for example, we could have a set request like: `set clusters.10.10.12.56.insecure-skip-tls-verify true`. We enter this case with - // steps representing 10, 10, 12, 56, insecure-skip-tls-verify. The name is "10.10.12.56", so we want to collect all those parts together and - // store them as a single step. In order to do that, we need to determine what set of tokens is a legal step AFTER the name of the map key - // This set of reflective code pulls the type of the map values, uses that type to look up the set of legal tags. Those legal tags are used to - // walk the list of remaining parts until we find a match to a legal tag or the end of the string. That name is used to burn all the used parts. - mapValueType := currType.Elem().Elem() - mapValueOptions, err := getPotentialTypeValues(mapValueType) - if err != nil { - return nil, err - } - nextPart := findNameStep(individualParts[currPartIndex:], sets.StringKeySet(mapValueOptions)) - - steps = append(steps, navigationStep{nextPart, mapValueType}) - currPartIndex += len(strings.Split(nextPart, ".")) - currType = mapValueType - - case reflect.Struct: - nextPart := individualParts[currPartIndex] - - options, err := getPotentialTypeValues(currType) - if err != nil { - return nil, err - } - fieldType, exists := options[nextPart] - if !exists { - return nil, fmt.Errorf("unable to parse %v after %v at %v", path, steps, currType) - } - - steps = append(steps, navigationStep{nextPart, fieldType}) - currPartIndex += len(strings.Split(nextPart, ".")) - currType = fieldType - } - } - - return &navigationSteps{steps, 0}, nil -} - -func (s *navigationSteps) pop() navigationStep { - if s.moreStepsRemaining() { - s.currentStepIndex++ - return s.steps[s.currentStepIndex-1] - } - return navigationStep{} -} - -func (s *navigationSteps) peek() navigationStep { - if s.moreStepsRemaining() { - return s.steps[s.currentStepIndex] - } - return navigationStep{} -} - -func (s *navigationSteps) moreStepsRemaining() bool { - return len(s.steps) > s.currentStepIndex -} - -// findNameStep takes the list of parts and a set of valid tags that can be used after the name. It then walks the list of parts -// until it find a valid "next" tag or until it reaches the end of the parts and then builds the name back up out of the individual parts -func findNameStep(parts []string, typeOptions sets.String) string { - if len(parts) == 0 { - return "" - } - - numberOfPartsInStep := findKnownValue(parts[1:], typeOptions) + 1 - // if we didn't find a known value, then the entire thing must be a name - if numberOfPartsInStep == 0 { - numberOfPartsInStep = len(parts) - } - nextParts := parts[0:numberOfPartsInStep] - - return strings.Join(nextParts, ".") -} - -// getPotentialTypeValues takes a type and looks up the tags used to represent its fields when serialized. -func getPotentialTypeValues(typeValue reflect.Type) (map[string]reflect.Type, error) { - if typeValue.Kind() == reflect.Ptr { - typeValue = typeValue.Elem() - } - - if typeValue.Kind() != reflect.Struct { - return nil, fmt.Errorf("%v is not of type struct", typeValue) - } - - ret := make(map[string]reflect.Type) - - for fieldIndex := 0; fieldIndex < typeValue.NumField(); fieldIndex++ { - fieldType := typeValue.Field(fieldIndex) - yamlTag := fieldType.Tag.Get("json") - yamlTagName := strings.Split(yamlTag, ",")[0] - - ret[yamlTagName] = fieldType.Type - } - - return ret, nil -} - -func findKnownValue(parts []string, valueOptions sets.String) int { - for i := range parts { - if valueOptions.Has(parts[i]) { - return i - } - } - - return -1 -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser_test.go deleted file mode 100644 index 3ec3c40ff..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser_test.go +++ /dev/null @@ -1,96 +0,0 @@ -/* -Copyright 2014 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 config - -import ( - "reflect" - "strings" - "testing" - - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" - "k8s.io/kubernetes/pkg/util" -) - -type stepParserTest struct { - path string - expectedNavigationSteps navigationSteps - expectedError string -} - -func TestParseWithDots(t *testing.T) { - test := stepParserTest{ - path: "clusters.my.dot.delimited.name.server", - expectedNavigationSteps: navigationSteps{ - steps: []navigationStep{ - {"clusters", reflect.TypeOf(make(map[string]*clientcmdapi.Cluster))}, - {"my.dot.delimited.name", reflect.TypeOf(clientcmdapi.Cluster{})}, - {"server", reflect.TypeOf("")}, - }, - }, - } - - test.run(t) -} - -func TestParseWithDotsEndingWithName(t *testing.T) { - test := stepParserTest{ - path: "contexts.10.12.12.12", - expectedNavigationSteps: navigationSteps{ - steps: []navigationStep{ - {"contexts", reflect.TypeOf(make(map[string]*clientcmdapi.Context))}, - {"10.12.12.12", reflect.TypeOf(clientcmdapi.Context{})}, - }, - }, - } - - test.run(t) -} - -func TestParseWithBadValue(t *testing.T) { - test := stepParserTest{ - path: "user.bad", - expectedNavigationSteps: navigationSteps{ - steps: []navigationStep{}, - }, - expectedError: "unable to parse user.bad after [] at api.Config", - } - - test.run(t) -} - -func (test stepParserTest) run(t *testing.T) { - actualSteps, err := newNavigationSteps(test.path) - if len(test.expectedError) != 0 { - if err == nil { - t.Errorf("Did not get %v", test.expectedError) - } else { - if !strings.Contains(err.Error(), test.expectedError) { - t.Errorf("Expected %v, but got %v", test.expectedError, err) - } - } - return - } - - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - if !reflect.DeepEqual(test.expectedNavigationSteps, *actualSteps) { - t.Errorf("diff: %v", util.ObjectDiff(test.expectedNavigationSteps, *actualSteps)) - t.Errorf("expected: %#v\n actual: %#v", test.expectedNavigationSteps, *actualSteps) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/set.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/set.go deleted file mode 100644 index c175a23c2..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/set.go +++ /dev/null @@ -1,208 +0,0 @@ -/* -Copyright 2014 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 config - -import ( - "errors" - "fmt" - "io" - "reflect" - "strings" - - "github.com/spf13/cobra" -) - -const ( - cannotHaveStepsAfterError = "Cannot have steps after %v. %v are remaining" - additionStepRequiredUnlessUnsettingError = "Must have additional steps after %v unless you are unsetting it" -) - -type setOptions struct { - configAccess ConfigAccess - propertyName string - propertyValue string -} - -const set_long = `Sets an individual value in a kubeconfig file -PROPERTY_NAME is a dot delimited name where each token represents either a attribute name or a map key. Map keys may not contain dots. -PROPERTY_VALUE is the new value you wish to set.` - -func NewCmdConfigSet(out io.Writer, configAccess ConfigAccess) *cobra.Command { - options := &setOptions{configAccess: configAccess} - - cmd := &cobra.Command{ - Use: "set PROPERTY_NAME PROPERTY_VALUE", - Short: "Sets an individual value in a kubeconfig file", - Long: set_long, - Run: func(cmd *cobra.Command, args []string) { - if !options.complete(cmd) { - return - } - - err := options.run() - if err != nil { - fmt.Fprintf(out, "%v\n", err) - } else { - fmt.Fprintf(out, "property %q set.\n", options.propertyName) - } - }, - } - - return cmd -} - -func (o setOptions) run() error { - err := o.validate() - if err != nil { - return err - } - - config, err := o.configAccess.GetStartingConfig() - if err != nil { - return err - } - steps, err := newNavigationSteps(o.propertyName) - if err != nil { - return err - } - err = modifyConfig(reflect.ValueOf(config), steps, o.propertyValue, false) - if err != nil { - return err - } - - if err := ModifyConfig(o.configAccess, *config, false); err != nil { - return err - } - - return nil -} - -func (o *setOptions) complete(cmd *cobra.Command) bool { - endingArgs := cmd.Flags().Args() - if len(endingArgs) != 2 { - cmd.Help() - return false - } - - o.propertyValue = endingArgs[1] - o.propertyName = endingArgs[0] - return true -} - -func (o setOptions) validate() error { - if len(o.propertyValue) == 0 { - return errors.New("you cannot use set to unset a property") - } - - if len(o.propertyName) == 0 { - return errors.New("you must specify a property") - } - - return nil -} - -func modifyConfig(curr reflect.Value, steps *navigationSteps, propertyValue string, unset bool) error { - currStep := steps.pop() - - actualCurrValue := curr - if curr.Kind() == reflect.Ptr { - actualCurrValue = curr.Elem() - } - - switch actualCurrValue.Kind() { - case reflect.Map: - if !steps.moreStepsRemaining() && !unset { - return fmt.Errorf("can't set a map to a value: %v", actualCurrValue) - } - - mapKey := reflect.ValueOf(currStep.stepValue) - mapValueType := curr.Type().Elem().Elem() - - if !steps.moreStepsRemaining() && unset { - actualCurrValue.SetMapIndex(mapKey, reflect.Value{}) - return nil - } - - currMapValue := actualCurrValue.MapIndex(mapKey) - - needToSetNewMapValue := currMapValue.Kind() == reflect.Invalid - if needToSetNewMapValue { - currMapValue = reflect.New(mapValueType.Elem()).Elem().Addr() - actualCurrValue.SetMapIndex(mapKey, currMapValue) - } - - err := modifyConfig(currMapValue, steps, propertyValue, unset) - if err != nil { - return err - } - - return nil - - case reflect.String: - if steps.moreStepsRemaining() { - return fmt.Errorf("can't have more steps after a string. %v", steps) - } - actualCurrValue.SetString(propertyValue) - return nil - - case reflect.Bool: - if steps.moreStepsRemaining() { - return fmt.Errorf("can't have more steps after a bool. %v", steps) - } - boolValue, err := toBool(propertyValue) - if err != nil { - return err - } - actualCurrValue.SetBool(boolValue) - return nil - - case reflect.Struct: - for fieldIndex := 0; fieldIndex < actualCurrValue.NumField(); fieldIndex++ { - currFieldValue := actualCurrValue.Field(fieldIndex) - currFieldType := actualCurrValue.Type().Field(fieldIndex) - currYamlTag := currFieldType.Tag.Get("json") - currFieldTypeYamlName := strings.Split(currYamlTag, ",")[0] - - if currFieldTypeYamlName == currStep.stepValue { - thisMapHasNoValue := (currFieldValue.Kind() == reflect.Map && currFieldValue.IsNil()) - - if thisMapHasNoValue { - newValue := reflect.MakeMap(currFieldValue.Type()) - currFieldValue.Set(newValue) - - if !steps.moreStepsRemaining() && unset { - return nil - } - } - - if !steps.moreStepsRemaining() && unset { - // if we're supposed to unset the value or if the value is a map that doesn't exist, create a new value and overwrite - newValue := reflect.New(currFieldValue.Type()).Elem() - currFieldValue.Set(newValue) - return nil - } - - return modifyConfig(currFieldValue.Addr(), steps, propertyValue, unset) - } - } - - return fmt.Errorf("unable to locate path %#v under %v", currStep, actualCurrValue) - - } - - panic(fmt.Errorf("unrecognized type: %v", actualCurrValue)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/unset.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/unset.go deleted file mode 100644 index 555309ccb..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/unset.go +++ /dev/null @@ -1,104 +0,0 @@ -/* -Copyright 2014 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 config - -import ( - "errors" - "fmt" - "io" - "reflect" - - "github.com/spf13/cobra" -) - -type unsetOptions struct { - configAccess ConfigAccess - propertyName string -} - -const unset_long = `Unsets an individual value in a kubeconfig file -PROPERTY_NAME is a dot delimited name where each token represents either a attribute name or a map key. Map keys may not contain dots.` - -func NewCmdConfigUnset(out io.Writer, configAccess ConfigAccess) *cobra.Command { - options := &unsetOptions{configAccess: configAccess} - - cmd := &cobra.Command{ - Use: "unset PROPERTY_NAME", - Short: "Unsets an individual value in a kubeconfig file", - Long: unset_long, - Run: func(cmd *cobra.Command, args []string) { - if !options.complete(cmd) { - return - } - - err := options.run() - if err != nil { - fmt.Fprintf(out, "%v\n", err) - } else { - fmt.Fprintf(out, "property %q unset.\n", options.propertyName) - } - }, - } - - return cmd -} - -func (o unsetOptions) run() error { - err := o.validate() - if err != nil { - return err - } - - config, err := o.configAccess.GetStartingConfig() - if err != nil { - return err - } - - steps, err := newNavigationSteps(o.propertyName) - if err != nil { - return err - } - err = modifyConfig(reflect.ValueOf(config), steps, "", true) - if err != nil { - return err - } - - if err := ModifyConfig(o.configAccess, *config, false); err != nil { - return err - } - - return nil -} - -func (o *unsetOptions) complete(cmd *cobra.Command) bool { - endingArgs := cmd.Flags().Args() - if len(endingArgs) != 1 { - cmd.Help() - return false - } - - o.propertyName = endingArgs[0] - return true -} - -func (o unsetOptions) validate() error { - if len(o.propertyName) == 0 { - return errors.New("you must specify a property") - } - - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/use_context.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/use_context.go deleted file mode 100644 index a6a5c26d5..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/use_context.go +++ /dev/null @@ -1,101 +0,0 @@ -/* -Copyright 2014 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 config - -import ( - "errors" - "fmt" - "io" - - "github.com/spf13/cobra" - - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" -) - -type useContextOptions struct { - configAccess ConfigAccess - contextName string -} - -func NewCmdConfigUseContext(out io.Writer, configAccess ConfigAccess) *cobra.Command { - options := &useContextOptions{configAccess: configAccess} - - cmd := &cobra.Command{ - Use: "use-context CONTEXT_NAME", - Short: "Sets the current-context in a kubeconfig file", - Long: `Sets the current-context in a kubeconfig file`, - Run: func(cmd *cobra.Command, args []string) { - if !options.complete(cmd) { - return - } - - err := options.run() - if err != nil { - fmt.Fprintf(out, "%v\n", err) - } else { - fmt.Fprintf(out, "switched to context %q.\n", options.contextName) - } - }, - } - - return cmd -} - -func (o useContextOptions) run() error { - config, err := o.configAccess.GetStartingConfig() - if err != nil { - return err - } - - err = o.validate(config) - if err != nil { - return err - } - - config.CurrentContext = o.contextName - - if err := ModifyConfig(o.configAccess, *config, true); err != nil { - return err - } - - return nil -} - -func (o *useContextOptions) complete(cmd *cobra.Command) bool { - endingArgs := cmd.Flags().Args() - if len(endingArgs) != 1 { - cmd.Help() - return false - } - - o.contextName = endingArgs[0] - return true -} - -func (o useContextOptions) validate(config *clientcmdapi.Config) error { - if len(o.contextName) == 0 { - return errors.New("you must specify a current-context") - } - - for name := range config.Contexts { - if name == o.contextName { - return nil - } - } - - return fmt.Errorf("no context exists with the name: %q.", o.contextName) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/view.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/view.go deleted file mode 100644 index 50bdb30ad..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/config/view.go +++ /dev/null @@ -1,158 +0,0 @@ -/* -Copyright 2014 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 config - -import ( - "errors" - "fmt" - "io" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" - "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/latest" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/util" -) - -type ViewOptions struct { - ConfigAccess ConfigAccess - Merge util.BoolFlag - Flatten bool - Minify bool - RawByteData bool -} - -const ( - view_long = `Displays merged kubeconfig settings or a specified kubeconfig file. - -You can use --output jsonpath={...} to extract specific values using a jsonpath expression.` - view_example = `# Show Merged kubeconfig settings. -$ kubectl config view - -# Get the password for the e2e user -$ kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}'` -) - -func NewCmdConfigView(out io.Writer, ConfigAccess ConfigAccess) *cobra.Command { - options := &ViewOptions{ConfigAccess: ConfigAccess} - // Default to yaml - defaultOutputFormat := "yaml" - - cmd := &cobra.Command{ - Use: "view", - Short: "Displays merged kubeconfig settings or a specified kubeconfig file.", - Long: view_long, - Example: view_example, - Run: func(cmd *cobra.Command, args []string) { - options.Complete() - outputFormat := cmdutil.GetFlagString(cmd, "output") - if outputFormat == "wide" { - fmt.Printf("--output wide is not available in kubectl config view; reset to default output format (%s)\n\n", defaultOutputFormat) - cmd.Flags().Set("output", defaultOutputFormat) - } - - printer, _, err := cmdutil.PrinterForCommand(cmd) - cmdutil.CheckErr(err) - version, err := cmdutil.OutputVersion(cmd, &latest.ExternalVersion) - cmdutil.CheckErr(err) - printer = kubectl.NewVersionedPrinter(printer, clientcmdapi.Scheme, version) - - cmdutil.CheckErr(options.Run(out, printer)) - }, - } - - cmdutil.AddPrinterFlags(cmd) - cmd.Flags().Set("output", defaultOutputFormat) - - options.Merge.Default(true) - f := cmd.Flags().VarPF(&options.Merge, "merge", "", "merge together the full hierarchy of kubeconfig files") - f.NoOptDefVal = "true" - cmd.Flags().BoolVar(&options.RawByteData, "raw", false, "display raw byte data") - cmd.Flags().BoolVar(&options.Flatten, "flatten", false, "flatten the resulting kubeconfig file into self contained output (useful for creating portable kubeconfig files)") - cmd.Flags().BoolVar(&options.Minify, "minify", false, "remove all information not used by current-context from the output") - return cmd -} - -func (o ViewOptions) Run(out io.Writer, printer kubectl.ResourcePrinter) error { - config, err := o.loadConfig() - if err != nil { - return err - } - - if o.Minify { - if err := clientcmdapi.MinifyConfig(config); err != nil { - return err - } - } - - if o.Flatten { - if err := clientcmdapi.FlattenConfig(config); err != nil { - return err - } - } else if !o.RawByteData { - clientcmdapi.ShortenConfig(config) - } - - err = printer.PrintObj(config, out) - if err != nil { - return err - } - - return nil -} - -func (o *ViewOptions) Complete() bool { - if o.ConfigAccess.IsExplicitFile() { - if !o.Merge.Provided() { - o.Merge.Set("false") - } - } - - return true -} - -func (o ViewOptions) loadConfig() (*clientcmdapi.Config, error) { - err := o.Validate() - if err != nil { - return nil, err - } - - config, err := o.getStartingConfig() - return config, err -} - -func (o ViewOptions) Validate() error { - if !o.Merge.Value() && !o.ConfigAccess.IsExplicitFile() { - return errors.New("if merge==false a precise file must to specified") - } - - return nil -} - -// getStartingConfig returns the Config object built from the sources specified by the options, the filename read (only if it was a single file), and an error if something goes wrong -func (o *ViewOptions) getStartingConfig() (*clientcmdapi.Config, error) { - switch { - case !o.Merge.Value(): - return clientcmd.LoadFromFile(o.ConfigAccess.GetExplicitFile()) - - default: - return o.ConfigAccess.GetStartingConfig() - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/convert.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/convert.go deleted file mode 100644 index e7e04d5b9..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/convert.go +++ /dev/null @@ -1,164 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apimachinery/registered" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/runtime" - - "github.com/spf13/cobra" -) - -const ( - convert_long = `Convert config files between different API versions. Both YAML -and JSON formats are accepted. - -The command takes filename, directory, or URL as input, and convert it into format -of version specified by --output-version flag. If target version is not specified or -not supported, convert to latest version. - -The default output will be printed to stdout in YAML format. One can use -o option -to change to output destination. -` - convert_example = `# Convert 'pod.yaml' to latest version and print to stdout. -$ kubectl convert -f pod.yaml - -# Convert the live state of the resource specified by 'pod.yaml' to the latest version -# and print to stdout in json format. -$ kubectl convert -f pod.yaml --local -o json - -# Convert all files under current directory to latest version and create them all. -$ kubectl convert -f . | kubectl create -f - -` -) - -// NewCmdConvert creates a command object for the generic "convert" action, which -// translates the config file into a given version. -func NewCmdConvert(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &ConvertOptions{} - - cmd := &cobra.Command{ - Use: "convert -f FILENAME", - Short: "Convert config files between different API versions", - Long: convert_long, - Example: convert_example, - Run: func(cmd *cobra.Command, args []string) { - err := options.Complete(f, out, cmd, args) - cmdutil.CheckErr(err) - err = options.RunConvert() - cmdutil.CheckErr(err) - }, - } - - usage := "Filename, directory, or URL to file to need to get converted." - kubectl.AddJsonFilenameFlag(cmd, &options.filenames, usage) - cmd.MarkFlagRequired("filename") - cmdutil.AddValidateFlags(cmd) - cmdutil.AddPrinterFlags(cmd) - cmd.Flags().BoolVar(&options.local, "local", true, "If true, convert will NOT try to contact api-server but run locally.") - - return cmd -} - -// ConvertOptions have the data required to perform the convert operation -type ConvertOptions struct { - builder *resource.Builder - filenames []string - local bool - - encoder runtime.Encoder - out io.Writer - printer kubectl.ResourcePrinter - - outputVersion unversioned.GroupVersion -} - -// Complete collects information required to run Convert command from command line. -func (o *ConvertOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) (err error) { - o.outputVersion, err = cmdutil.OutputVersion(cmd, ®istered.EnabledVersionsForGroup(api.GroupName)[0]) - if err != nil { - return err - } - if !registered.IsEnabledVersion(o.outputVersion) { - cmdutil.UsageError(cmd, "'%s' is not a registered version.", o.outputVersion) - } - - // build the builder - mapper, typer := f.Object() - clientMapper := resource.ClientMapperFunc(f.ClientForMapping) - if o.local { - fmt.Fprintln(out, "running in local mode...") - o.builder = resource.NewBuilder(mapper, typer, resource.DisabledClientForMapping{clientMapper}, f.Decoder(true)) - } else { - o.builder = resource.NewBuilder(mapper, typer, clientMapper, f.Decoder(true)) - schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir")) - if err != nil { - return err - } - o.builder = o.builder.Schema(schema) - } - cmdNamespace, _, err := f.DefaultNamespace() - if err != nil { - return err - } - o.builder = o.builder.NamespaceParam(cmdNamespace). - ContinueOnError(). - FilenameParam(false, o.filenames...). - Flatten() - - // build the printer - o.out = out - outputFormat := cmdutil.GetFlagString(cmd, "output") - templateFile := cmdutil.GetFlagString(cmd, "template") - if len(outputFormat) == 0 { - if len(templateFile) == 0 { - outputFormat = "yaml" - } else { - outputFormat = "template" - } - } - o.encoder = f.JSONEncoder() - o.printer, _, err = kubectl.GetPrinter(outputFormat, templateFile) - if err != nil { - return err - } - - return nil -} - -// RunConvert implements the generic Convert command -func (o *ConvertOptions) RunConvert() error { - infos, err := o.builder.Do().Infos() - if err != nil { - return err - } - - objects, err := resource.AsVersionedObject(infos, false, o.outputVersion.String(), o.encoder) - if err != nil { - return err - } - - return o.printer.PrintObj(objects, o.out) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create.go deleted file mode 100644 index 612ebd191..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create.go +++ /dev/null @@ -1,258 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - "strings" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/runtime" -) - -// CreateOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of -// referencing the cmd.Flags() -type CreateOptions struct { - Filenames []string -} - -const ( - create_long = `Create a resource by filename or stdin. - -JSON and YAML formats are accepted.` - create_example = `# Create a pod using the data in pod.json. -$ kubectl create -f ./pod.json - -# Create a pod based on the JSON passed into stdin. -$ cat pod.json | kubectl create -f -` -) - -func NewCmdCreate(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &CreateOptions{} - - cmd := &cobra.Command{ - Use: "create -f FILENAME", - Short: "Create a resource by filename or stdin", - Long: create_long, - Example: create_example, - Run: func(cmd *cobra.Command, args []string) { - if len(options.Filenames) == 0 { - cmd.Help() - return - } - cmdutil.CheckErr(ValidateArgs(cmd, args)) - cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) - cmdutil.CheckErr(RunCreate(f, cmd, out, options)) - }, - } - - usage := "Filename, directory, or URL to file to use to create the resource" - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) - cmd.MarkFlagRequired("filename") - cmdutil.AddValidateFlags(cmd) - cmdutil.AddOutputFlagsForMutation(cmd) - cmdutil.AddApplyAnnotationFlags(cmd) - cmdutil.AddRecordFlag(cmd) - - // create subcommands - cmd.AddCommand(NewCmdCreateNamespace(f, out)) - cmd.AddCommand(NewCmdCreateSecret(f, out)) - cmd.AddCommand(NewCmdCreateConfigMap(f, out)) - return cmd -} - -func ValidateArgs(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return cmdutil.UsageError(cmd, "Unexpected args: %v", args) - } - return nil -} - -func RunCreate(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *CreateOptions) error { - schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir")) - if err != nil { - return err - } - - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - Schema(schema). - ContinueOnError(). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). - Flatten(). - Do() - err = r.Err() - if err != nil { - return err - } - - count := 0 - err = r.Visit(func(info *resource.Info, err error) error { - if err != nil { - return err - } - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { - return cmdutil.AddSourceToErr("creating", info.Source, err) - } - - if cmdutil.ShouldRecord(cmd, info) { - if err := cmdutil.RecordChangeCause(info.Object, f.Command()); err != nil { - return cmdutil.AddSourceToErr("creating", info.Source, err) - } - } - - if err := createAndRefresh(info); err != nil { - return cmdutil.AddSourceToErr("creating", info.Source, err) - } - - count++ - shortOutput := cmdutil.GetFlagString(cmd, "output") == "name" - if !shortOutput { - printObjectSpecificMessage(info.Object, out) - } - cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, "created") - return nil - }) - if err != nil { - return err - } - if count == 0 { - return fmt.Errorf("no objects passed to create") - } - return nil -} - -func printObjectSpecificMessage(obj runtime.Object, out io.Writer) { - switch obj := obj.(type) { - case *api.Service: - if obj.Spec.Type == api.ServiceTypeNodePort { - msg := fmt.Sprintf( - `You have exposed your service on an external port on all nodes in your -cluster. If you want to expose this service to the external internet, you may -need to set up firewall rules for the service port(s) (%s) to serve traffic. - -See http://releases.k8s.io/HEAD/docs/user-guide/services-firewalls.md for more details. -`, - makePortsString(obj.Spec.Ports, true)) - out.Write([]byte(msg)) - } - } -} - -func makePortsString(ports []api.ServicePort, useNodePort bool) string { - pieces := make([]string, len(ports)) - for ix := range ports { - var port int - if useNodePort { - port = ports[ix].NodePort - } else { - port = ports[ix].Port - } - pieces[ix] = fmt.Sprintf("%s:%d", strings.ToLower(string(ports[ix].Protocol)), port) - } - return strings.Join(pieces, ",") -} - -// createAndRefresh creates an object from input info and refreshes info with that object -func createAndRefresh(info *resource.Info) error { - obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) - if err != nil { - return err - } - info.Refresh(obj, true) - return nil -} - -// NameFromCommandArgs is a utility function for commands that assume the first argument is a resource name -func NameFromCommandArgs(cmd *cobra.Command, args []string) (string, error) { - if len(args) == 0 { - return "", cmdutil.UsageError(cmd, "NAME is required") - } - return args[0], nil -} - -// CreateSubcommandOptions is an options struct to support create subcommands -type CreateSubcommandOptions struct { - // Name of resource being created - Name string - // StructuredGenerator is the resource generator for the object being created - StructuredGenerator kubectl.StructuredGenerator - // DryRun is true if the command should be simulated but not run against the server - DryRun bool - // OutputFormat - OutputFormat string -} - -// RunCreateSubcommand executes a create subcommand using the specified options -func RunCreateSubcommand(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *CreateSubcommandOptions) error { - namespace, _, err := f.DefaultNamespace() - if err != nil { - return err - } - obj, err := options.StructuredGenerator.StructuredGenerate() - if err != nil { - return err - } - mapper, typer := f.Object() - gvk, err := typer.ObjectKind(obj) - mapping, err := mapper.RESTMapping(unversioned.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, gvk.Version) - if err != nil { - return err - } - client, err := f.ClientForMapping(mapping) - if err != nil { - return err - } - resourceMapper := &resource.Mapper{ - ObjectTyper: typer, - RESTMapper: mapper, - ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), - } - info, err := resourceMapper.InfoForObject(obj) - if err != nil { - return err - } - if err := kubectl.UpdateApplyAnnotation(info, f.JSONEncoder()); err != nil { - return err - } - if !options.DryRun { - obj, err = resource.NewHelper(client, mapping).Create(namespace, false, info.Object) - if err != nil { - return err - } - } - - if useShortOutput := options.OutputFormat == "name"; useShortOutput || len(options.OutputFormat) == 0 { - cmdutil.PrintSuccess(mapper, useShortOutput, out, mapping.Resource, options.Name, "created") - return nil - } - - return f.PrintObject(cmd, obj, out) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_configmap.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_configmap.go deleted file mode 100644 index 99a7dcc2c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_configmap.go +++ /dev/null @@ -1,96 +0,0 @@ -/* -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 cmd - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" -) - -const ( - configMapLong = `Create a configmap based on a file, directory, or specified literal value. - -A single configmap may package one or more key/value pairs. - -When creating a configmap based on a file, the key will default to the basename of the file, and the value will -default to the file content. If the basename is an invalid key, you may specify an alternate key. - -When creating a configmap based on a directory, each file whose basename is a valid key in the directory will be -packaged into the configmap. Any directory entries except regular files are ignored (e.g. subdirectories, -symlinks, devices, pipes, etc). -` - - configMapExample = ` # Create a new configmap named my-config with keys for each file in folder bar - $ kubectl create configmap generic my-config --from-file=path/to/bar - - # Create a new configmap named my-config with specified keys instead of names on disk - $ kubectl create configmap generic my-config --from-file=ssh-privatekey=~/.ssh/id_rsa --from-file=ssh-publickey=~/.ssh/id_rsa.pub - - # Create a new configMap named my-config with key1=config1 and key2=config2 - $ kubectl create configmap generic my-config --from-literal=key1=config1 --from-literal=key2=config2` -) - -// ConfigMap is a command to ease creating ConfigMaps. -func NewCmdCreateConfigMap(f *cmdutil.Factory, cmdOut io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "configmap NAME [--type=string] [--from-file=[key=]source] [--from-literal=key1=value1] [--dry-run]", - Short: "Create a configMap from a local file, directory or literal value.", - Long: configMapLong, - Example: configMapExample, - Run: func(cmd *cobra.Command, args []string) { - err := CreateConfigMap(f, cmdOut, cmd, args) - cmdutil.CheckErr(err) - }, - } - cmdutil.AddApplyAnnotationFlags(cmd) - cmdutil.AddValidateFlags(cmd) - cmdutil.AddPrinterFlags(cmd) - cmdutil.AddGeneratorFlags(cmd, cmdutil.ConfigMapV1GeneratorName) - cmd.Flags().StringSlice("from-file", []string{}, "Key files can be specified using their file path, in which case a default name will be given to them, or optionally with a name and file path, in which case the given name will be used. Specifying a directory will iterate each named file in the directory that is a valid configmap key.") - cmd.Flags().StringSlice("from-literal", []string{}, "Specify a key and literal value to insert in configmap (i.e. mykey=somevalue)") - return cmd -} - -// CreateConfigMap is the implementation of the create configmap generic command. -func CreateConfigMap(f *cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error { - name, err := NameFromCommandArgs(cmd, args) - if err != nil { - return err - } - var generator kubectl.StructuredGenerator - switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName { - case cmdutil.ConfigMapV1GeneratorName: - generator = &kubectl.ConfigMapGeneratorV1{ - Name: name, - FileSources: cmdutil.GetFlagStringSlice(cmd, "from-file"), - LiteralSources: cmdutil.GetFlagStringSlice(cmd, "from-literal"), - } - default: - return cmdutil.UsageError(cmd, fmt.Sprintf("Generator: %s not supported.", generatorName)) - } - return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{ - Name: name, - StructuredGenerator: generator, - DryRun: cmdutil.GetFlagBool(cmd, "dry-run"), - OutputFormat: cmdutil.GetFlagString(cmd, "output"), - }) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_configmap_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_configmap_test.go deleted file mode 100644 index 9c3971e36..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_configmap_test.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -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 cmd - -import ( - "bytes" - "net/http" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/unversioned/fake" -) - -func TestCreateConfigMap(t *testing.T) { - configMap := &api.ConfigMap{} - configMap.Name = "my-configmap" - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/configmaps" && m == "POST": - return &http.Response{StatusCode: 201, Body: objBody(codec, configMap)}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdCreateConfigMap(f, buf) - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{configMap.Name}) - expectedOutput := "configmap/" + configMap.Name + "\n" - if buf.String() != expectedOutput { - t.Errorf("expected output: %s, but got: %s", buf.String(), expectedOutput) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_namespace.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_namespace.go deleted file mode 100644 index a66055fb1..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_namespace.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2015 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 cmd - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" -) - -const ( - namespaceLong = ` -Create a namespace with the specified name.` - - namespaceExample = ` # Create a new namespace named my-namespace - $ kubectl create namespace my-namespace` -) - -// NewCmdCreateNamespace is a macro command to create a new namespace -func NewCmdCreateNamespace(f *cmdutil.Factory, cmdOut io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "namespace NAME [--dry-run]", - Aliases: []string{"ns"}, - Short: "Create a namespace with the specified name.", - Long: namespaceLong, - Example: namespaceExample, - Run: func(cmd *cobra.Command, args []string) { - err := CreateNamespace(f, cmdOut, cmd, args) - cmdutil.CheckErr(err) - }, - } - cmdutil.AddApplyAnnotationFlags(cmd) - cmdutil.AddValidateFlags(cmd) - cmdutil.AddPrinterFlags(cmd) - cmdutil.AddGeneratorFlags(cmd, cmdutil.NamespaceV1GeneratorName) - return cmd -} - -// CreateNamespace implements the behavior to run the create namespace command -func CreateNamespace(f *cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error { - name, err := NameFromCommandArgs(cmd, args) - if err != nil { - return err - } - var generator kubectl.StructuredGenerator - switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName { - case cmdutil.NamespaceV1GeneratorName: - generator = &kubectl.NamespaceGeneratorV1{Name: name} - default: - return cmdutil.UsageError(cmd, fmt.Sprintf("Generator: %s not supported.", generatorName)) - } - return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{ - Name: name, - StructuredGenerator: generator, - DryRun: cmdutil.GetFlagBool(cmd, "dry-run"), - OutputFormat: cmdutil.GetFlagString(cmd, "output"), - }) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_namespace_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_namespace_test.go deleted file mode 100644 index dd74eec4b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_namespace_test.go +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "net/http" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/unversioned/fake" -) - -func TestCreateNamespace(t *testing.T) { - namespaceObject := &api.Namespace{} - namespaceObject.Name = "my-namespace" - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces" && m == "POST": - return &http.Response{StatusCode: 201, Body: objBody(codec, namespaceObject)}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdCreateNamespace(f, buf) - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{namespaceObject.Name}) - expectedOutput := "namespace/" + namespaceObject.Name + "\n" - if buf.String() != expectedOutput { - t.Errorf("expected output: %s, but got: %s", buf.String(), expectedOutput) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_secret.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_secret.go deleted file mode 100644 index 6cba3ba07..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_secret.go +++ /dev/null @@ -1,192 +0,0 @@ -/* -Copyright 2015 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 cmd - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" -) - -// NewCmdCreateSecret groups subcommands to create various types of secrets -func NewCmdCreateSecret(f *cmdutil.Factory, cmdOut io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "secret", - Short: "Create a secret using specified subcommand.", - Long: "Create a secret using specified subcommand.", - Run: func(cmd *cobra.Command, args []string) { - cmd.Help() - }, - } - cmd.AddCommand(NewCmdCreateSecretDockerRegistry(f, cmdOut)) - cmd.AddCommand(NewCmdCreateSecretGeneric(f, cmdOut)) - return cmd -} - -const ( - secretLong = ` -Create a secret based on a file, directory, or specified literal value. - -A single secret may package one or more key/value pairs. - -When creating a secret based on a file, the key will default to the basename of the file, and the value will -default to the file content. If the basename is an invalid key, you may specify an alternate key. - -When creating a secret based on a directory, each file whose basename is a valid key in the directory will be -packaged into the secret. Any directory entries except regular files are ignored (e.g. subdirectories, -symlinks, devices, pipes, etc). -` - - secretExample = ` # Create a new secret named my-secret with keys for each file in folder bar - $ kubectl create secret generic my-secret --from-file=path/to/bar - - # Create a new secret named my-secret with specified keys instead of names on disk - $ kubectl create secret generic my-secret --from-file=ssh-privatekey=~/.ssh/id_rsa --from-file=ssh-publickey=~/.ssh/id_rsa.pub - - # Create a new secret named my-secret with key1=supersecret and key2=topsecret - $ kubectl create secret generic my-secret --from-literal=key1=supersecret --from-literal=key2=topsecret` -) - -// NewCmdCreateSecretGeneric is a command to create generic secrets from files, directories, or literal values -func NewCmdCreateSecretGeneric(f *cmdutil.Factory, cmdOut io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "generic NAME [--type=string] [--from-file=[key=]source] [--from-literal=key1=value1] [--dry-run]", - Short: "Create a secret from a local file, directory or literal value.", - Long: secretLong, - Example: secretExample, - Run: func(cmd *cobra.Command, args []string) { - err := CreateSecretGeneric(f, cmdOut, cmd, args) - cmdutil.CheckErr(err) - }, - } - cmdutil.AddApplyAnnotationFlags(cmd) - cmdutil.AddValidateFlags(cmd) - cmdutil.AddPrinterFlags(cmd) - cmdutil.AddGeneratorFlags(cmd, cmdutil.SecretV1GeneratorName) - cmd.Flags().StringSlice("from-file", []string{}, "Key files can be specified using their file path, in which case a default name will be given to them, or optionally with a name and file path, in which case the given name will be used. Specifying a directory will iterate each named file in the directory that is a valid secret key.") - cmd.Flags().StringSlice("from-literal", []string{}, "Specify a key and literal value to insert in secret (i.e. mykey=somevalue)") - cmd.Flags().String("type", "", "The type of secret to create") - return cmd -} - -// CreateSecretGeneric is the implementation of the create secret generic command -func CreateSecretGeneric(f *cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error { - name, err := NameFromCommandArgs(cmd, args) - if err != nil { - return err - } - var generator kubectl.StructuredGenerator - switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName { - case cmdutil.SecretV1GeneratorName: - generator = &kubectl.SecretGeneratorV1{ - Name: name, - Type: cmdutil.GetFlagString(cmd, "type"), - FileSources: cmdutil.GetFlagStringSlice(cmd, "from-file"), - LiteralSources: cmdutil.GetFlagStringSlice(cmd, "from-literal"), - } - default: - return cmdutil.UsageError(cmd, fmt.Sprintf("Generator: %s not supported.", generatorName)) - } - return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{ - Name: name, - StructuredGenerator: generator, - DryRun: cmdutil.GetFlagBool(cmd, "dry-run"), - OutputFormat: cmdutil.GetFlagString(cmd, "output"), - }) -} - -const ( - secretForDockerRegistryLong = ` -Create a new secret for use with Docker registries. - -Dockercfg secrets are used to authenticate against Docker registries. - -When using the Docker command line to push images, you can authenticate to a given registry by running - 'docker login DOCKER_REGISTRY_SERVER --username=DOCKER_USER --password=DOCKER_PASSWORD --email=DOCKER_EMAIL'. -That produces a ~/.dockercfg file that is used by subsequent 'docker push' and 'docker pull' commands to -authenticate to the registry. - -When creating applications, you may have a Docker registry that requires authentication. In order for the -nodes to pull images on your behalf, they have to have the credentials. You can provide this information -by creating a dockercfg secret and attaching it to your service account.` - - secretForDockerRegistryExample = ` # If you don't already have a .dockercfg file, you can create a dockercfg secret directly by using: - $ kubectl create secret docker-registry my-secret --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL` -) - -// NewCmdCreateSecretDockerRegistry is a macro command for creating secrets to work with Docker registries -func NewCmdCreateSecretDockerRegistry(f *cmdutil.Factory, cmdOut io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "docker-registry NAME --docker-username=user --docker-password=password --docker-email=email [--docker-server=string] [--from-literal=key1=value1] [--dry-run]", - Short: "Create a secret for use with a Docker registry.", - Long: secretForDockerRegistryLong, - Example: secretForDockerRegistryExample, - Run: func(cmd *cobra.Command, args []string) { - err := CreateSecretDockerRegistry(f, cmdOut, cmd, args) - cmdutil.CheckErr(err) - }, - } - cmdutil.AddApplyAnnotationFlags(cmd) - cmdutil.AddValidateFlags(cmd) - cmdutil.AddPrinterFlags(cmd) - cmdutil.AddGeneratorFlags(cmd, cmdutil.SecretForDockerRegistryV1GeneratorName) - cmd.Flags().String("docker-username", "", "Username for Docker registry authentication") - cmd.MarkFlagRequired("docker-username") - cmd.Flags().String("docker-password", "", "Password for Docker registry authentication") - cmd.MarkFlagRequired("docker-password") - cmd.Flags().String("docker-email", "", "Email for Docker registry") - cmd.MarkFlagRequired("docker-email") - cmd.Flags().String("docker-server", "https://index.docker.io/v1/", "Server location for Docker registry") - return cmd -} - -// CreateSecretDockerRegistry is the implementation of the create secret docker-registry command -func CreateSecretDockerRegistry(f *cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error { - name, err := NameFromCommandArgs(cmd, args) - if err != nil { - return err - } - requiredFlags := []string{"docker-username", "docker-password", "docker-email", "docker-server"} - for _, requiredFlag := range requiredFlags { - if value := cmdutil.GetFlagString(cmd, requiredFlag); len(value) == 0 { - return cmdutil.UsageError(cmd, "flag %s is required", requiredFlag) - } - } - var generator kubectl.StructuredGenerator - switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName { - case cmdutil.SecretForDockerRegistryV1GeneratorName: - generator = &kubectl.SecretForDockerRegistryGeneratorV1{ - Name: name, - Username: cmdutil.GetFlagString(cmd, "docker-username"), - Email: cmdutil.GetFlagString(cmd, "docker-email"), - Password: cmdutil.GetFlagString(cmd, "docker-password"), - Server: cmdutil.GetFlagString(cmd, "docker-server"), - } - default: - return cmdutil.UsageError(cmd, fmt.Sprintf("Generator: %s not supported.", generatorName)) - } - return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{ - Name: name, - StructuredGenerator: generator, - DryRun: cmdutil.GetFlagBool(cmd, "dry-run"), - OutputFormat: cmdutil.GetFlagString(cmd, "output"), - }) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_secret_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_secret_test.go deleted file mode 100644 index 8365513eb..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_secret_test.go +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "net/http" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/unversioned/fake" -) - -func TestCreateSecretGeneric(t *testing.T) { - secretObject := &api.Secret{} - secretObject.Name = "my-secret" - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/secrets" && m == "POST": - return &http.Response{StatusCode: 201, Body: objBody(codec, secretObject)}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdCreateSecretGeneric(f, buf) - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{secretObject.Name}) - expectedOutput := "secret/" + secretObject.Name + "\n" - if buf.String() != expectedOutput { - t.Errorf("expected output: %s, but got: %s", buf.String(), expectedOutput) - } -} - -func TestCreateSecretDockerRegistry(t *testing.T) { - secretObject := &api.Secret{} - secretObject.Name = "my-secret" - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/secrets" && m == "POST": - return &http.Response{StatusCode: 201, Body: objBody(codec, secretObject)}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdCreateSecretDockerRegistry(f, buf) - cmd.Flags().Set("docker-username", "test-user") - cmd.Flags().Set("docker-password", "test-pass") - cmd.Flags().Set("docker-email", "test-email") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{secretObject.Name}) - expectedOutput := "secret/" + secretObject.Name + "\n" - if buf.String() != expectedOutput { - t.Errorf("expected output: %s, but got: %s", buf.String(), expectedOutput) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_test.go deleted file mode 100644 index 9fdee69f1..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/create_test.go +++ /dev/null @@ -1,232 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "net/http" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/unversioned/fake" - "k8s.io/kubernetes/pkg/runtime" -) - -func TestExtraArgsFail(t *testing.T) { - initTestErrorHandler(t) - buf := bytes.NewBuffer([]byte{}) - - f, _, _ := NewAPIFactory() - c := NewCmdCreate(f, buf) - if ValidateArgs(c, []string{"rc"}) == nil { - t.Errorf("unexpected non-error") - } -} - -func TestCreateObject(t *testing.T) { - initTestErrorHandler(t) - _, _, rc := testData() - rc.Items[0].Name = "redis-master-controller" - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/replicationcontrollers" && m == "POST": - return &http.Response{StatusCode: 201, Body: objBody(codec, &rc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdCreate(f, buf) - cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - // uses the name from the file, not the response - if buf.String() != "replicationcontroller/redis-master-controller\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestCreateMultipleObject(t *testing.T) { - initTestErrorHandler(t) - _, svc, rc := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/services" && m == "POST": - return &http.Response{StatusCode: 201, Body: objBody(codec, &svc.Items[0])}, nil - case p == "/namespaces/test/replicationcontrollers" && m == "POST": - return &http.Response{StatusCode: 201, Body: objBody(codec, &rc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdCreate(f, buf) - cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") - cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - // Names should come from the REST response, NOT the files - if buf.String() != "replicationcontroller/rc1\nservice/baz\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestCreateDirectory(t *testing.T) { - initTestErrorHandler(t) - _, svc, rc := testData() - rc.Items[0].Name = "name" - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/services" && m == "POST": - return &http.Response{StatusCode: 201, Body: objBody(codec, &svc.Items[0])}, nil - case p == "/namespaces/test/replicationcontrollers" && m == "POST": - return &http.Response{StatusCode: 201, Body: objBody(codec, &rc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdCreate(f, buf) - cmd.Flags().Set("filename", "../../../examples/guestbook") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - if buf.String() != "replicationcontroller/name\nservice/baz\nreplicationcontroller/name\nservice/baz\nreplicationcontroller/name\nservice/baz\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestPrintObjectSpecificMessage(t *testing.T) { - initTestErrorHandler(t) - tests := []struct { - obj runtime.Object - expectOutput bool - }{ - { - obj: &api.Service{}, - expectOutput: false, - }, - { - obj: &api.Pod{}, - expectOutput: false, - }, - { - obj: &api.Service{Spec: api.ServiceSpec{Type: api.ServiceTypeLoadBalancer}}, - expectOutput: false, - }, - { - obj: &api.Service{Spec: api.ServiceSpec{Type: api.ServiceTypeNodePort}}, - expectOutput: true, - }, - } - for _, test := range tests { - buff := &bytes.Buffer{} - printObjectSpecificMessage(test.obj, buff) - if test.expectOutput && buff.Len() == 0 { - t.Errorf("Expected output, saw none for %v", test.obj) - } - if !test.expectOutput && buff.Len() > 0 { - t.Errorf("Expected no output, saw %s for %v", buff.String(), test.obj) - } - } -} - -func TestMakePortsString(t *testing.T) { - initTestErrorHandler(t) - tests := []struct { - ports []api.ServicePort - useNodePort bool - expectedOutput string - }{ - {ports: nil, expectedOutput: ""}, - {ports: []api.ServicePort{}, expectedOutput: ""}, - {ports: []api.ServicePort{ - { - Port: 80, - Protocol: "TCP", - }, - }, - expectedOutput: "tcp:80", - }, - {ports: []api.ServicePort{ - { - Port: 80, - Protocol: "TCP", - }, - { - Port: 8080, - Protocol: "UDP", - }, - { - Port: 9000, - Protocol: "TCP", - }, - }, - expectedOutput: "tcp:80,udp:8080,tcp:9000", - }, - {ports: []api.ServicePort{ - { - Port: 80, - NodePort: 9090, - Protocol: "TCP", - }, - { - Port: 8080, - NodePort: 80, - Protocol: "UDP", - }, - }, - useNodePort: true, - expectedOutput: "tcp:9090,udp:80", - }, - } - for _, test := range tests { - output := makePortsString(test.ports, test.useNodePort) - if output != test.expectedOutput { - t.Errorf("expected: %s, saw: %s.", test.expectedOutput, output) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/delete.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/delete.go deleted file mode 100644 index 7dffd4f99..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/delete.go +++ /dev/null @@ -1,210 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - "time" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" -) - -// DeleteOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of -// referencing the cmd.Flags() -type DeleteOptions struct { - Filenames []string -} - -const ( - delete_long = `Delete resources by filenames, stdin, resources and names, or by resources and label selector. - -JSON and YAML formats are accepted. - -Only one type of the arguments may be specified: filenames, resources and names, or resources and label selector - -Note that the delete command does NOT do resource version checks, so if someone -submits an update to a resource right when you submit a delete, their update -will be lost along with the rest of the resource.` - delete_example = `# Delete a pod using the type and name specified in pod.json. -$ kubectl delete -f ./pod.json - -# Delete a pod based on the type and name in the JSON passed into stdin. -$ cat pod.json | kubectl delete -f - - -# Delete pods and services with same names "baz" and "foo" -$ kubectl delete pod,service baz foo - -# Delete pods and services with label name=myLabel. -$ kubectl delete pods,services -l name=myLabel - -# Delete a pod with UID 1234-56-7890-234234-456456. -$ kubectl delete pod 1234-56-7890-234234-456456 - -# Delete all pods -$ kubectl delete pods --all` -) - -func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &DeleteOptions{} - - // retrieve a list of handled resources from printer as valid args - validArgs := []string{} - p, err := f.Printer(nil, false, false, false, false, false, false, []string{}) - cmdutil.CheckErr(err) - if p != nil { - validArgs = p.HandledResources() - } - - cmd := &cobra.Command{ - Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])", - Short: "Delete resources by filenames, stdin, resources and names, or by resources and label selector.", - Long: delete_long, - Example: delete_example, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) - err := RunDelete(f, out, cmd, args, options) - cmdutil.CheckErr(err) - }, - ValidArgs: validArgs, - } - usage := "Filename, directory, or URL to a file containing the resource to delete." - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) - cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on.") - cmd.Flags().Bool("all", false, "[-all] to select all the specified resources.") - cmd.Flags().Bool("ignore-not-found", false, "Treat \"resource not found\" as a successful delete. Defaults to \"true\" when --all is specified.") - cmd.Flags().Bool("cascade", true, "If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.") - cmd.Flags().Int("grace-period", -1, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative.") - cmd.Flags().Duration("timeout", 0, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object") - cmdutil.AddOutputFlagsForMutation(cmd) - return cmd -} - -func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *DeleteOptions) error { - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - deleteAll := cmdutil.GetFlagBool(cmd, "all") - mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - ContinueOnError(). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). - SelectorParam(cmdutil.GetFlagString(cmd, "selector")). - SelectAllParam(deleteAll). - ResourceTypeOrNameArgs(false, args...).RequireObject(false). - Flatten(). - Do() - err = r.Err() - if err != nil { - return err - } - - ignoreNotFound := cmdutil.GetFlagBool(cmd, "ignore-not-found") - if deleteAll { - f := cmd.Flags().Lookup("ignore-not-found") - // The flag should never be missing - if f == nil { - return fmt.Errorf("missing --ignore-not-found flag") - } - // If the user didn't explicitly set the option, default to ignoring NotFound errors when used with --all - if !f.Changed { - ignoreNotFound = true - } - } - - shortOutput := cmdutil.GetFlagString(cmd, "output") == "name" - // By default use a reaper to delete all related resources. - if cmdutil.GetFlagBool(cmd, "cascade") { - return ReapResult(r, f, out, cmdutil.GetFlagBool(cmd, "cascade"), ignoreNotFound, cmdutil.GetFlagDuration(cmd, "timeout"), cmdutil.GetFlagInt(cmd, "grace-period"), shortOutput, mapper) - } - return DeleteResult(r, out, ignoreNotFound, shortOutput, mapper) -} - -func ReapResult(r *resource.Result, f *cmdutil.Factory, out io.Writer, isDefaultDelete, ignoreNotFound bool, timeout time.Duration, gracePeriod int, shortOutput bool, mapper meta.RESTMapper) error { - found := 0 - if ignoreNotFound { - r = r.IgnoreErrors(errors.IsNotFound) - } - err := r.Visit(func(info *resource.Info, err error) error { - if err != nil { - return err - } - found++ - reaper, err := f.Reaper(info.Mapping) - if err != nil { - // If there is no reaper for this resources and the user didn't explicitly ask for stop. - if kubectl.IsNoSuchReaperError(err) && isDefaultDelete { - return deleteResource(info, out, shortOutput, mapper) - } - return cmdutil.AddSourceToErr("reaping", info.Source, err) - } - var options *api.DeleteOptions - if gracePeriod >= 0 { - options = api.NewDeleteOptions(int64(gracePeriod)) - } - if err := reaper.Stop(info.Namespace, info.Name, timeout, options); err != nil { - return cmdutil.AddSourceToErr("stopping", info.Source, err) - } - cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, "deleted") - return nil - }) - if err != nil { - return err - } - if found == 0 { - fmt.Fprintf(out, "No resources found\n") - } - return nil -} - -func DeleteResult(r *resource.Result, out io.Writer, ignoreNotFound bool, shortOutput bool, mapper meta.RESTMapper) error { - found := 0 - if ignoreNotFound { - r = r.IgnoreErrors(errors.IsNotFound) - } - err := r.Visit(func(info *resource.Info, err error) error { - if err != nil { - return err - } - found++ - return deleteResource(info, out, shortOutput, mapper) - }) - if err != nil { - return err - } - if found == 0 { - fmt.Fprintf(out, "No resources found\n") - } - return nil -} - -func deleteResource(info *resource.Info, out io.Writer, shortOutput bool, mapper meta.RESTMapper) error { - if err := resource.NewHelper(info.Client, info.Mapping).Delete(info.Namespace, info.Name); err != nil { - return cmdutil.AddSourceToErr("deleting", info.Source, err) - } - cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, "deleted") - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/delete_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/delete_test.go deleted file mode 100644 index 61da45f4f..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/delete_test.go +++ /dev/null @@ -1,451 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "net/http" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/fake" -) - -func TestDeleteObjectByTuple(t *testing.T) { - _, _, rc := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/replicationcontrollers/redis-master-controller" && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil - default: - // Ensures no GET is performed when deleting by name - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdDelete(f, buf) - cmd.Flags().Set("namespace", "test") - cmd.Flags().Set("cascade", "false") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{"replicationcontrollers/redis-master-controller"}) - - if buf.String() != "replicationcontroller/redis-master-controller\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestDeleteNamedObject(t *testing.T) { - _, _, rc := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/replicationcontrollers/redis-master-controller" && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil - default: - // Ensures no GET is performed when deleting by name - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdDelete(f, buf) - cmd.Flags().Set("namespace", "test") - cmd.Flags().Set("cascade", "false") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{"replicationcontrollers", "redis-master-controller"}) - - if buf.String() != "replicationcontroller/redis-master-controller\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestDeleteObject(t *testing.T) { - _, _, rc := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdDelete(f, buf) - cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") - cmd.Flags().Set("cascade", "false") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - // uses the name from the file, not the response - if buf.String() != "replicationcontroller/redis-master\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestDeleteObjectNotFound(t *testing.T) { - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE": - return &http.Response{StatusCode: 404, Body: stringBody("")}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdDelete(f, buf) - options := &DeleteOptions{ - Filenames: []string{"../../../examples/guestbook/redis-master-controller.yaml"}, - } - cmd.Flags().Set("cascade", "false") - cmd.Flags().Set("output", "name") - err := RunDelete(f, buf, cmd, []string{}, options) - if err == nil || !errors.IsNotFound(err) { - t.Errorf("unexpected error: expected NotFound, got %v", err) - } -} - -func TestDeleteObjectIgnoreNotFound(t *testing.T) { - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE": - return &http.Response{StatusCode: 404, Body: stringBody("")}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdDelete(f, buf) - cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") - cmd.Flags().Set("cascade", "false") - cmd.Flags().Set("ignore-not-found", "true") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - if buf.String() != "" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestDeleteAllNotFound(t *testing.T) { - _, svc, _ := testData() - - f, tf, codec := NewAPIFactory() - - // Add an item to the list which will result in a 404 on delete - svc.Items = append(svc.Items, api.Service{ObjectMeta: api.ObjectMeta{Name: "foo"}}) - notFoundError := &errors.NewNotFound(api.Resource("services"), "foo").(*errors.StatusError).ErrStatus - - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/services" && m == "GET": - return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil - case p == "/namespaces/test/services/foo" && m == "DELETE": - return &http.Response{StatusCode: 404, Body: objBody(codec, notFoundError)}, nil - case p == "/namespaces/test/services/baz" && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdDelete(f, buf) - cmd.Flags().Set("all", "true") - cmd.Flags().Set("cascade", "false") - // Make sure we can explicitly choose to fail on NotFound errors, even with --all - cmd.Flags().Set("ignore-not-found", "false") - cmd.Flags().Set("output", "name") - - err := RunDelete(f, buf, cmd, []string{"services"}, &DeleteOptions{}) - if err == nil || !errors.IsNotFound(err) { - t.Errorf("unexpected error: expected NotFound, got %v", err) - } -} - -func TestDeleteAllIgnoreNotFound(t *testing.T) { - _, svc, _ := testData() - - f, tf, codec := NewAPIFactory() - - // Add an item to the list which will result in a 404 on delete - svc.Items = append(svc.Items, api.Service{ObjectMeta: api.ObjectMeta{Name: "foo"}}) - notFoundError := &errors.NewNotFound(api.Resource("services"), "foo").(*errors.StatusError).ErrStatus - - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/services" && m == "GET": - return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil - case p == "/namespaces/test/services/foo" && m == "DELETE": - return &http.Response{StatusCode: 404, Body: objBody(codec, notFoundError)}, nil - case p == "/namespaces/test/services/baz" && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdDelete(f, buf) - cmd.Flags().Set("all", "true") - cmd.Flags().Set("cascade", "false") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{"services"}) - - if buf.String() != "service/baz\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestDeleteMultipleObject(t *testing.T) { - _, svc, rc := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil - case p == "/namespaces/test/services/frontend" && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdDelete(f, buf) - cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") - cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml") - cmd.Flags().Set("cascade", "false") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - if buf.String() != "replicationcontroller/redis-master\nservice/frontend\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) { - _, svc, _ := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE": - return &http.Response{StatusCode: 404, Body: stringBody("")}, nil - case p == "/namespaces/test/services/frontend" && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdDelete(f, buf) - options := &DeleteOptions{ - Filenames: []string{"../../../examples/guestbook/redis-master-controller.yaml", "../../../examples/guestbook/frontend-service.yaml"}, - } - cmd.Flags().Set("cascade", "false") - cmd.Flags().Set("output", "name") - err := RunDelete(f, buf, cmd, []string{}, options) - if err == nil || !errors.IsNotFound(err) { - t.Errorf("unexpected error: expected NotFound, got %v", err) - } - - if buf.String() != "service/frontend\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestDeleteMultipleResourcesWithTheSameName(t *testing.T) { - _, svc, rc := testData() - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/replicationcontrollers/baz" && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil - case p == "/namespaces/test/replicationcontrollers/foo" && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil - case p == "/namespaces/test/services/baz" && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil - case p == "/namespaces/test/services/foo" && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil - default: - // Ensures no GET is performed when deleting by name - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdDelete(f, buf) - cmd.Flags().Set("namespace", "test") - cmd.Flags().Set("cascade", "false") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{"replicationcontrollers,services", "baz", "foo"}) - if buf.String() != "replicationcontroller/baz\nreplicationcontroller/foo\nservice/baz\nservice/foo\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestDeleteDirectory(t *testing.T) { - _, svc, rc := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case strings.HasPrefix(p, "/namespaces/test/services/") && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil - case strings.HasPrefix(p, "/namespaces/test/replicationcontrollers/") && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdDelete(f, buf) - cmd.Flags().Set("filename", "../../../examples/guestbook") - cmd.Flags().Set("cascade", "false") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - if buf.String() != "replicationcontroller/frontend\nservice/frontend\nreplicationcontroller/redis-master\nservice/redis-master\nreplicationcontroller/redis-slave\nservice/redis-slave\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestDeleteMultipleSelector(t *testing.T) { - pods, svc, _ := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/pods" && m == "GET": - if req.URL.Query().Get(unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String())) != "a=b" { - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - } - return &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, nil - case p == "/namespaces/test/services" && m == "GET": - if req.URL.Query().Get(unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String())) != "a=b" { - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - } - return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil - case strings.HasPrefix(p, "/namespaces/test/pods/") && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil - case strings.HasPrefix(p, "/namespaces/test/services/") && m == "DELETE": - return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdDelete(f, buf) - cmd.Flags().Set("selector", "a=b") - cmd.Flags().Set("cascade", "false") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{"pods,services"}) - - if buf.String() != "pod/foo\npod/bar\nservice/baz\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/describe.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/describe.go deleted file mode 100644 index 3ea83ecdc..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/describe.go +++ /dev/null @@ -1,184 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - "strings" - - "github.com/spf13/cobra" - - apierrors "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/runtime" - utilerrors "k8s.io/kubernetes/pkg/util/errors" -) - -// DescribeOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of -// referencing the cmd.Flags() -type DescribeOptions struct { - Filenames []string -} - -const ( - describe_long = `Show details of a specific resource or group of resources. - -This command joins many API calls together to form a detailed description of a -given resource or group of resources. - -$ kubectl describe TYPE NAME_PREFIX - -will first check for an exact match on TYPE and NAME_PREFIX. If no such resource -exists, it will output details for every resource that has a name prefixed with NAME_PREFIX - -Possible resource types include (case insensitive): pods (po), services (svc), -replicationcontrollers (rc), nodes (no), events (ev), limitranges (limits), -persistentvolumes (pv), persistentvolumeclaims (pvc), resourcequotas (quota), -namespaces (ns), serviceaccounts, horizontalpodautoscalers (hpa), -endpoints (ep) or secrets.` - describe_example = `# Describe a node -$ kubectl describe nodes kubernetes-minion-emt8.c.myproject.internal - -# Describe a pod -$ kubectl describe pods/nginx - -# Describe a pod identified by type and name in "pod.json" -$ kubectl describe -f pod.json - -# Describe all pods -$ kubectl describe pods - -# Describe pods by label name=myLabel -$ kubectl describe po -l name=myLabel - -# Describe all pods managed by the 'frontend' replication controller (rc-created pods -# get the name of the rc as a prefix in the pod the name). -$ kubectl describe pods frontend` -) - -func NewCmdDescribe(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &DescribeOptions{} - - cmd := &cobra.Command{ - Use: "describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME)", - Short: "Show details of a specific resource or group of resources", - Long: describe_long, - Example: describe_example, - Run: func(cmd *cobra.Command, args []string) { - err := RunDescribe(f, out, cmd, args, options) - cmdutil.CheckErr(err) - }, - ValidArgs: kubectl.DescribableResources(), - } - usage := "Filename, directory, or URL to a file containing the resource to describe" - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) - cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") - return cmd -} - -func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *DescribeOptions) error { - selector := cmdutil.GetFlagString(cmd, "selector") - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - if len(args) == 0 && len(options.Filenames) == 0 { - fmt.Fprint(out, "You must specify the type of resource to describe. ", valid_resources) - return cmdutil.UsageError(cmd, "Required resource not specified.") - } - - mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - ContinueOnError(). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). - SelectorParam(selector). - ResourceTypeOrNameArgs(true, args...). - Flatten(). - Do() - err = r.Err() - if err != nil { - return err - } - - allErrs := []error{} - infos, err := r.Infos() - if err != nil { - if apierrors.IsNotFound(err) && len(args) == 2 { - return DescribeMatchingResources(mapper, typer, f, cmdNamespace, args[0], args[1], out, err) - } - allErrs = append(allErrs, err) - } - - for _, info := range infos { - mapping := info.ResourceMapping() - describer, err := f.Describer(mapping) - if err != nil { - allErrs = append(allErrs, err) - continue - } - s, err := describer.Describe(info.Namespace, info.Name) - if err != nil { - allErrs = append(allErrs, err) - continue - } - fmt.Fprintf(out, "%s\n\n", s) - } - - return utilerrors.NewAggregate(allErrs) -} - -func DescribeMatchingResources(mapper meta.RESTMapper, typer runtime.ObjectTyper, f *cmdutil.Factory, namespace, rsrc, prefix string, out io.Writer, originalError error) error { - r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - NamespaceParam(namespace).DefaultNamespace(). - ResourceTypeOrNameArgs(true, rsrc). - SingleResourceType(). - Flatten(). - Do() - mapping, err := r.ResourceMapping() - if err != nil { - return err - } - describer, err := f.Describer(mapping) - if err != nil { - return err - } - infos, err := r.Infos() - if err != nil { - return err - } - isFound := false - for ix := range infos { - info := infos[ix] - if strings.HasPrefix(info.Name, prefix) { - isFound = true - s, err := describer.Describe(info.Namespace, info.Name) - if err != nil { - return err - } - fmt.Fprintf(out, "%s\n", s) - } - } - if !isFound { - return originalError - } - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/describe_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/describe_test.go deleted file mode 100644 index 35a87f2e5..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/describe_test.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "fmt" - "net/http" - "testing" - - "k8s.io/kubernetes/pkg/client/unversioned/fake" -) - -// Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get. -func TestDescribeUnknownSchemaObject(t *testing.T) { - d := &testDescriber{Output: "test output"} - f, tf, codec := NewTestFactory() - tf.Describer = d - tf.Client = &fake.RESTClient{ - Codec: codec, - Resp: &http.Response{StatusCode: 200, Body: objBody(codec, &internalType{Name: "foo"})}, - } - tf.Namespace = "non-default" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdDescribe(f, buf) - cmd.Run(cmd, []string{"type", "foo"}) - - if d.Name != "foo" || d.Namespace != "non-default" { - t.Errorf("unexpected describer: %#v", d) - } - - if buf.String() != fmt.Sprintf("%s\n\n", d.Output) { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestDescribeObject(t *testing.T) { - _, _, rc := testData() - f, tf, codec := NewAPIFactory() - d := &testDescriber{Output: "test output"} - tf.Describer = d - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "GET": - return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdDescribe(f, buf) - cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") - cmd.Run(cmd, []string{}) - - if d.Name != "redis-master" || d.Namespace != "test" { - t.Errorf("unexpected describer: %#v", d) - } - - if buf.String() != fmt.Sprintf("%s\n\n", d.Output) { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestDescribeListObjects(t *testing.T) { - pods, _, _ := testData() - f, tf, codec := NewAPIFactory() - d := &testDescriber{Output: "test output"} - tf.Describer = d - tf.Client = &fake.RESTClient{ - Codec: codec, - Resp: &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, - } - - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdDescribe(f, buf) - cmd.Run(cmd, []string{"pods"}) - if buf.String() != fmt.Sprintf("%s\n\n%s\n\n", d.Output, d.Output) { - t.Errorf("unexpected output: %s", buf.String()) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/drain.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/drain.go deleted file mode 100644 index 89cba686d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/drain.go +++ /dev/null @@ -1,373 +0,0 @@ -/* -Copyright 2015 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 cmd - -import ( - "errors" - "fmt" - "io" - "reflect" - "strings" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/controller" - // "k8s.io/kubernetes/pkg/api/unversioned" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/fields" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/kubelet/types" - "k8s.io/kubernetes/pkg/runtime" -) - -type DrainOptions struct { - client *client.Client - factory *cmdutil.Factory - Force bool - GracePeriodSeconds int - IgnoreDaemonsets bool - mapper meta.RESTMapper - nodeInfo *resource.Info - out io.Writer - typer runtime.ObjectTyper -} - -const ( - cordon_long = `Mark node as unschedulable. -` - cordon_example = `# Mark node "foo" as unschedulable. -$ kubectl cordon foo -` -) - -func NewCmdCordon(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &DrainOptions{factory: f, out: out} - - return &cobra.Command{ - Use: "cordon NODE", - Short: "Mark node as unschedulable", - Long: cordon_long, - Example: cordon_example, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(options.SetupDrain(cmd, args)) - cmdutil.CheckErr(options.RunCordonOrUncordon(true)) - }, - } -} - -const ( - uncordon_long = `Mark node as schedulable. -` - uncordon_example = `# Mark node "foo" as schedulable. -$ kubectl uncordon foo -` -) - -func NewCmdUncordon(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &DrainOptions{factory: f, out: out} - - return &cobra.Command{ - Use: "uncordon NODE", - Short: "Mark node as schedulable", - Long: uncordon_long, - Example: uncordon_example, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(options.SetupDrain(cmd, args)) - cmdutil.CheckErr(options.RunCordonOrUncordon(false)) - }, - } -} - -const ( - drain_long = `Drain node in preparation for maintenance. - -The given node will be marked unschedulable to prevent new pods from arriving. -Then drain deletes all pods except mirror pods (which cannot be deleted through -the API server). If there are DaemonSet-managed pods, drain will not proceed -without --ignore-daemonsets, and regardless it will not delete any -DaemonSet-managed pods, because those pods would be immediately replaced by the -DaemonSet controller, which ignores unschedulable marknigs. If there are any -pods that are neither mirror pods nor managed--by ReplicationController, -DaemonSet or Job--, then drain will not delete any pods unless you use --force. - -When you are ready to put the node back into service, use kubectl uncordon, which -will make the node schedulable again. -` - drain_example = `# Drain node "foo", even if there are pods not managed by a ReplicationController, Job, or DaemonSet on it. -$ kubectl drain foo --force - -# As above, but abort if there are pods not managed by a ReplicationController, Job, or DaemonSet, and use a grace period of 15 minutes. -$ kubectl drain foo --grace-period=900 -` -) - -func NewCmdDrain(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &DrainOptions{factory: f, out: out} - - cmd := &cobra.Command{ - Use: "drain NODE", - Short: "Drain node in preparation for maintenance", - Long: drain_long, - Example: drain_example, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(options.SetupDrain(cmd, args)) - cmdutil.CheckErr(options.RunDrain()) - }, - } - cmd.Flags().BoolVar(&options.Force, "force", false, "Continue even if there are pods not managed by a ReplicationController, Job, or DaemonSet.") - cmd.Flags().BoolVar(&options.IgnoreDaemonsets, "ignore-daemonsets", false, "Ignore DaemonSet-managed pods.") - cmd.Flags().IntVar(&options.GracePeriodSeconds, "grace-period", -1, "Period of time in seconds given to each pod to terminate gracefully. If negative, the default value specified in the pod will be used.") - return cmd -} - -// SetupDrain populates some fields from the factory, grabs command line -// arguments and looks up the node using Builder -func (o *DrainOptions) SetupDrain(cmd *cobra.Command, args []string) error { - var err error - if len(args) != 1 { - return cmdutil.UsageError(cmd, fmt.Sprintf("USAGE: %s [flags]", cmd.Use)) - } - - if o.client, err = o.factory.Client(); err != nil { - return err - } - - o.mapper, o.typer = o.factory.Object() - - cmdNamespace, _, err := o.factory.DefaultNamespace() - if err != nil { - return err - } - - r := o.factory.NewBuilder(). - NamespaceParam(cmdNamespace).DefaultNamespace(). - ResourceNames("node", args[0]). - Do() - - if err = r.Err(); err != nil { - return err - } - - return r.Visit(func(info *resource.Info, err error) error { - if err != nil { - return err - } - o.nodeInfo = info - return nil - }) -} - -// RunDrain runs the 'drain' command -func (o *DrainOptions) RunDrain() error { - if err := o.RunCordonOrUncordon(true); err != nil { - return err - } - - pods, err := o.getPodsForDeletion() - if err != nil { - return err - } - - if err = o.deletePods(pods); err != nil { - return err - } - cmdutil.PrintSuccess(o.mapper, false, o.out, "node", o.nodeInfo.Name, "drained") - return nil -} - -// getPodsForDeletion returns all the pods we're going to delete. If there are -// any unmanaged pods and the user didn't pass --force, we return that list in -// an error. -func (o *DrainOptions) getPodsForDeletion() ([]api.Pod, error) { - pods := []api.Pod{} - podList, err := o.client.Pods(api.NamespaceAll).List(api.ListOptions{FieldSelector: fields.SelectorFromSet(fields.Set{"spec.nodeName": o.nodeInfo.Name})}) - if err != nil { - return pods, err - } - unreplicatedPodNames := []string{} - daemonSetPodNames := []string{} - - for _, pod := range podList.Items { - _, found := pod.ObjectMeta.Annotations[types.ConfigMirrorAnnotationKey] - if found { - // Skip mirror pod - continue - } - replicated := false - daemonset_pod := false - - creatorRef, found := pod.ObjectMeta.Annotations[controller.CreatedByAnnotation] - if found { - // Now verify that the specified creator actually exists. - var sr api.SerializedReference - if err := runtime.DecodeInto(o.factory.Decoder(true), []byte(creatorRef), &sr); err != nil { - return pods, err - } - if sr.Reference.Kind == "ReplicationController" { - rc, err := o.client.ReplicationControllers(sr.Reference.Namespace).Get(sr.Reference.Name) - // Assume the only reason for an error is because the RC is - // gone/missing, not for any other cause. TODO(mml): something more - // sophisticated than this - if err == nil && rc != nil { - replicated = true - } - } else if sr.Reference.Kind == "DaemonSet" { - ds, err := o.client.DaemonSets(sr.Reference.Namespace).Get(sr.Reference.Name) - - // Assume the only reason for an error is because the DaemonSet is - // gone/missing, not for any other cause. TODO(mml): something more - // sophisticated than this - if err == nil && ds != nil { - // Otherwise, treat daemonset-managed pods as unmanaged since - // DaemonSet Controller currently ignores the unschedulable bit. - // FIXME(mml): Add link to the issue concerning a proper way to drain - // daemonset pods, probably using taints. - daemonset_pod = true - } - } else if sr.Reference.Kind == "Job" { - job, err := o.client.Jobs(sr.Reference.Namespace).Get(sr.Reference.Name) - - // Assume the only reason for an error is because the Job is - // gone/missing, not for any other cause. TODO(mml): something more - // sophisticated than this - if err == nil && job != nil { - replicated = true - } - } - } - - switch { - case daemonset_pod: - daemonSetPodNames = append(daemonSetPodNames, pod.Name) - case !replicated: - unreplicatedPodNames = append(unreplicatedPodNames, pod.Name) - if o.Force { - pods = append(pods, pod) - } - default: - pods = append(pods, pod) - } - } - - daemonSetErrors := !o.IgnoreDaemonsets && len(daemonSetPodNames) > 0 - unreplicatedErrors := !o.Force && len(unreplicatedPodNames) > 0 - - switch { - case daemonSetErrors && unreplicatedErrors: - return []api.Pod{}, errors.New(unmanagedMsg(unreplicatedPodNames, daemonSetPodNames, true)) - case daemonSetErrors && !unreplicatedErrors: - return []api.Pod{}, errors.New(unmanagedMsg([]string{}, daemonSetPodNames, true)) - case unreplicatedErrors && !daemonSetErrors: - return []api.Pod{}, errors.New(unmanagedMsg(unreplicatedPodNames, []string{}, true)) - } - - if len(unreplicatedPodNames) > 0 { - fmt.Fprintf(o.out, "WARNING: About to delete these %s\n", unmanagedMsg(unreplicatedPodNames, []string{}, false)) - } - if len(daemonSetPodNames) > 0 { - fmt.Fprintf(o.out, "WARNING: Skipping %s\n", unmanagedMsg([]string{}, daemonSetPodNames, false)) - } - - return pods, nil -} - -// Helper for generating errors or warnings about unmanaged pods. -func unmanagedMsg(unreplicatedNames []string, daemonSetNames []string, include_guidance bool) string { - msgs := []string{} - if len(unreplicatedNames) > 0 { - msg := fmt.Sprintf("pods not managed by ReplicationController, Job, or DaemonSet: %s", strings.Join(unreplicatedNames, ",")) - if include_guidance { - msg += " (use --force to override)" - } - msgs = append(msgs, msg) - } - if len(daemonSetNames) > 0 { - msg := fmt.Sprintf("DaemonSet-managed pods: %s", strings.Join(daemonSetNames, ",")) - if include_guidance { - msg += " (use --ignore-daemonsets to ignore)" - } - msgs = append(msgs, msg) - } - - return strings.Join(msgs, " and ") -} - -// deletePods deletes the pods on the api server -func (o *DrainOptions) deletePods(pods []api.Pod) error { - deleteOptions := api.DeleteOptions{} - if o.GracePeriodSeconds >= 0 { - gracePeriodSeconds := int64(o.GracePeriodSeconds) - deleteOptions.GracePeriodSeconds = &gracePeriodSeconds - } - - for _, pod := range pods { - err := o.client.Pods(pod.Namespace).Delete(pod.Name, &deleteOptions) - if err != nil { - return err - } - cmdutil.PrintSuccess(o.mapper, false, o.out, "pod", pod.Name, "deleted") - } - - return nil -} - -// RunCordonOrUncordon runs either Cordon or Uncordon. The desired value for -// "Unschedulable" is passed as the first arg. -func (o *DrainOptions) RunCordonOrUncordon(desired bool) error { - cmdNamespace, _, err := o.factory.DefaultNamespace() - if err != nil { - return err - } - - if o.nodeInfo.Mapping.GroupVersionKind.Kind == "Node" { - unsched := reflect.ValueOf(o.nodeInfo.Object).Elem().FieldByName("Spec").FieldByName("Unschedulable") - if unsched.Bool() == desired { - cmdutil.PrintSuccess(o.mapper, false, o.out, o.nodeInfo.Mapping.Resource, o.nodeInfo.Name, already(desired)) - } else { - helper := resource.NewHelper(o.client, o.nodeInfo.Mapping) - unsched.SetBool(desired) - _, err := helper.Replace(cmdNamespace, o.nodeInfo.Name, true, o.nodeInfo.Object) - if err != nil { - return err - } - cmdutil.PrintSuccess(o.mapper, false, o.out, o.nodeInfo.Mapping.Resource, o.nodeInfo.Name, changed(desired)) - } - } else { - cmdutil.PrintSuccess(o.mapper, false, o.out, o.nodeInfo.Mapping.Resource, o.nodeInfo.Name, "skipped") - } - - return nil -} - -// already() and changed() return suitable strings for {un,}cordoning - -func already(desired bool) string { - if desired { - return "already cordoned" - } - return "already uncordoned" -} - -func changed(desired bool) string { - if desired { - return "cordoned" - } - return "uncordoned" -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/drain_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/drain_test.go deleted file mode 100644 index b0aa36649..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/drain_test.go +++ /dev/null @@ -1,494 +0,0 @@ -/* -Copyright 2015 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 cmd - -import ( - "bytes" - "io" - "io/ioutil" - "net/http" - "net/url" - "os" - "reflect" - "strings" - "testing" - "time" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/fake" - "k8s.io/kubernetes/pkg/controller" - "k8s.io/kubernetes/pkg/conversion" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/runtime" -) - -var node *api.Node -var cordoned_node *api.Node - -func TestMain(m *testing.M) { - // Create a node. - node = &api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "node", - CreationTimestamp: unversioned.Time{time.Now()}, - }, - Spec: api.NodeSpec{ - ExternalID: "node", - }, - Status: api.NodeStatus{}, - } - clone, _ := conversion.NewCloner().DeepCopy(node) - - // A copy of the same node, but cordoned. - cordoned_node = clone.(*api.Node) - cordoned_node.Spec.Unschedulable = true - os.Exit(m.Run()) -} - -func TestCordon(t *testing.T) { - tests := []struct { - description string - node *api.Node - expected *api.Node - cmd func(*cmdutil.Factory, io.Writer) *cobra.Command - arg string - expectFatal bool - }{ - { - description: "node/node syntax", - node: cordoned_node, - expected: node, - cmd: NewCmdUncordon, - arg: "node/node", - expectFatal: false, - }, - { - description: "uncordon for real", - node: cordoned_node, - expected: node, - cmd: NewCmdUncordon, - arg: "node", - expectFatal: false, - }, - { - description: "uncordon does nothing", - node: node, - expected: node, - cmd: NewCmdUncordon, - arg: "node", - expectFatal: false, - }, - { - description: "cordon does nothing", - node: cordoned_node, - expected: cordoned_node, - cmd: NewCmdCordon, - arg: "node", - expectFatal: false, - }, - { - description: "cordon for real", - node: node, - expected: cordoned_node, - cmd: NewCmdCordon, - arg: "node", - expectFatal: false, - }, - { - description: "cordon missing node", - node: node, - expected: node, - cmd: NewCmdCordon, - arg: "bar", - expectFatal: true, - }, - { - description: "uncordon missing node", - node: node, - expected: node, - cmd: NewCmdUncordon, - arg: "bar", - expectFatal: true, - }, - } - - for _, test := range tests { - f, tf, codec := NewAPIFactory() - new_node := &api.Node{} - updated := false - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - m := &MyReq{req} - switch { - case m.isFor("GET", "/nodes/node"): - return &http.Response{StatusCode: 200, Body: objBody(codec, test.node)}, nil - case m.isFor("GET", "/nodes/bar"): - return &http.Response{StatusCode: 404, Body: stringBody("nope")}, nil - case m.isFor("PUT", "/nodes/node"): - data, err := ioutil.ReadAll(req.Body) - if err != nil { - t.Fatalf("%s: unexpected error: %v", test.description, err) - } - defer req.Body.Close() - if err := runtime.DecodeInto(codec, data, new_node); err != nil { - t.Fatalf("%s: unexpected error: %v", test.description, err) - } - if !reflect.DeepEqual(test.expected.Spec, new_node.Spec) { - t.Fatalf("%s: expected:\n%v\nsaw:\n%v\n", test.description, test.expected.Spec, new_node.Spec) - } - updated = true - return &http.Response{StatusCode: 200, Body: objBody(codec, new_node)}, nil - default: - t.Fatalf("%s: unexpected request: %v %#v\n%#v", test.description, req.Method, req.URL, req) - return nil, nil - } - }), - } - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} - - buf := bytes.NewBuffer([]byte{}) - cmd := test.cmd(f, buf) - - saw_fatal := false - func() { - defer func() { - // Recover from the panic below. - _ = recover() - // Restore cmdutil behavior - cmdutil.DefaultBehaviorOnFatal() - }() - cmdutil.BehaviorOnFatal(func(e string) { saw_fatal = true; panic(e) }) - cmd.SetArgs([]string{test.arg}) - cmd.Execute() - }() - - if test.expectFatal { - if !saw_fatal { - t.Fatalf("%s: unexpected non-error", test.description) - } - if updated { - t.Fatalf("%s: unexpcted update", test.description) - } - } - - if !test.expectFatal && saw_fatal { - t.Fatalf("%s: unexpected error", test.description) - } - if !reflect.DeepEqual(test.expected.Spec, test.node.Spec) && !updated { - t.Fatalf("%s: node never updated", test.description) - } - } -} - -func TestDrain(t *testing.T) { - labels := make(map[string]string) - labels["my_key"] = "my_value" - - rc := api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "rc", - Namespace: "default", - CreationTimestamp: unversioned.Time{time.Now()}, - Labels: labels, - SelfLink: testapi.Default.SelfLink("replicationcontrollers", "rc"), - }, - Spec: api.ReplicationControllerSpec{ - Selector: labels, - }, - } - - rc_anno := make(map[string]string) - rc_anno[controller.CreatedByAnnotation] = refJson(t, &rc) - - replicated_pod := api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "bar", - Namespace: "default", - CreationTimestamp: unversioned.Time{time.Now()}, - Labels: labels, - Annotations: rc_anno, - }, - Spec: api.PodSpec{ - NodeName: "node", - }, - } - - ds := extensions.DaemonSet{ - ObjectMeta: api.ObjectMeta{ - Name: "ds", - Namespace: "default", - CreationTimestamp: unversioned.Time{time.Now()}, - SelfLink: "/apis/extensions/v1beta1/namespaces/default/daemonsets/ds", - }, - Spec: extensions.DaemonSetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: labels}, - }, - } - - ds_anno := make(map[string]string) - ds_anno[controller.CreatedByAnnotation] = refJson(t, &ds) - - ds_pod := api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "bar", - Namespace: "default", - CreationTimestamp: unversioned.Time{time.Now()}, - Labels: labels, - Annotations: ds_anno, - }, - Spec: api.PodSpec{ - NodeName: "node", - }, - } - - job := extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "job", - Namespace: "default", - CreationTimestamp: unversioned.Time{time.Now()}, - SelfLink: "/apis/extensions/v1beta1/namespaces/default/jobs/job", - }, - Spec: extensions.JobSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: labels}, - }, - } - - job_pod := api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "bar", - Namespace: "default", - CreationTimestamp: unversioned.Time{time.Now()}, - Labels: labels, - Annotations: map[string]string{controller.CreatedByAnnotation: refJson(t, &job)}, - }, - } - - naked_pod := api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "bar", - Namespace: "default", - CreationTimestamp: unversioned.Time{time.Now()}, - Labels: labels, - }, - Spec: api.PodSpec{ - NodeName: "node", - }, - } - - tests := []struct { - description string - node *api.Node - expected *api.Node - pods []api.Pod - rcs []api.ReplicationController - args []string - expectFatal bool - expectDelete bool - }{ - { - description: "RC-managed pod", - node: node, - expected: cordoned_node, - pods: []api.Pod{replicated_pod}, - rcs: []api.ReplicationController{rc}, - args: []string{"node"}, - expectFatal: false, - expectDelete: true, - }, - { - description: "DS-managed pod", - node: node, - expected: cordoned_node, - pods: []api.Pod{ds_pod}, - rcs: []api.ReplicationController{rc}, - args: []string{"node"}, - expectFatal: true, - expectDelete: false, - }, - { - description: "DS-managed pod with --ignore-daemonsets", - node: node, - expected: cordoned_node, - pods: []api.Pod{ds_pod}, - rcs: []api.ReplicationController{rc}, - args: []string{"node", "--ignore-daemonsets"}, - expectFatal: false, - expectDelete: false, - }, - { - description: "Job-managed pod", - node: node, - expected: cordoned_node, - pods: []api.Pod{job_pod}, - rcs: []api.ReplicationController{rc}, - args: []string{"node"}, - expectFatal: false, - expectDelete: true, - }, - { - description: "naked pod", - node: node, - expected: cordoned_node, - pods: []api.Pod{naked_pod}, - rcs: []api.ReplicationController{}, - args: []string{"node"}, - expectFatal: true, - expectDelete: false, - }, - { - description: "naked pod with --force", - node: node, - expected: cordoned_node, - pods: []api.Pod{naked_pod}, - rcs: []api.ReplicationController{}, - args: []string{"node", "--force"}, - expectFatal: false, - expectDelete: true, - }, - { - description: "empty node", - node: node, - expected: cordoned_node, - pods: []api.Pod{}, - rcs: []api.ReplicationController{rc}, - args: []string{"node"}, - expectFatal: false, - expectDelete: false, - }, - } - - for _, test := range tests { - new_node := &api.Node{} - deleted := false - f, tf, codec := NewAPIFactory() - - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - m := &MyReq{req} - switch { - case m.isFor("GET", "/nodes/node"): - return &http.Response{StatusCode: 200, Body: objBody(codec, test.node)}, nil - case m.isFor("GET", "/namespaces/default/replicationcontrollers/rc"): - return &http.Response{StatusCode: 200, Body: objBody(codec, &test.rcs[0])}, nil - case m.isFor("GET", "/namespaces/default/daemonsets/ds"): - return &http.Response{StatusCode: 200, Body: objBody(testapi.Extensions.Codec(), &ds)}, nil - case m.isFor("GET", "/namespaces/default/jobs/job"): - return &http.Response{StatusCode: 200, Body: objBody(testapi.Extensions.Codec(), &job)}, nil - case m.isFor("GET", "/pods"): - values, err := url.ParseQuery(req.URL.RawQuery) - if err != nil { - t.Fatalf("%s: unexpected error: %v", test.description, err) - } - get_params := make(url.Values) - get_params["fieldSelector"] = []string{"spec.nodeName=node"} - if !reflect.DeepEqual(get_params, values) { - t.Fatalf("%s: expected:\n%v\nsaw:\n%v\n", test.description, get_params, values) - } - return &http.Response{StatusCode: 200, Body: objBody(codec, &api.PodList{Items: test.pods})}, nil - case m.isFor("GET", "/replicationcontrollers"): - return &http.Response{StatusCode: 200, Body: objBody(codec, &api.ReplicationControllerList{Items: test.rcs})}, nil - case m.isFor("PUT", "/nodes/node"): - data, err := ioutil.ReadAll(req.Body) - if err != nil { - t.Fatalf("%s: unexpected error: %v", test.description, err) - } - defer req.Body.Close() - if err := runtime.DecodeInto(codec, data, new_node); err != nil { - t.Fatalf("%s: unexpected error: %v", test.description, err) - } - if !reflect.DeepEqual(test.expected.Spec, new_node.Spec) { - t.Fatalf("%s: expected:\n%v\nsaw:\n%v\n", test.description, test.expected.Spec, new_node.Spec) - } - return &http.Response{StatusCode: 200, Body: objBody(codec, new_node)}, nil - case m.isFor("DELETE", "/namespaces/default/pods/bar"): - deleted = true - return &http.Response{StatusCode: 204, Body: objBody(codec, &test.pods[0])}, nil - default: - t.Fatalf("%s: unexpected request: %v %#v\n%#v", test.description, req.Method, req.URL, req) - return nil, nil - } - }), - } - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} - - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdDrain(f, buf) - - saw_fatal := false - func() { - defer func() { - // Recover from the panic below. - _ = recover() - // Restore cmdutil behavior - cmdutil.DefaultBehaviorOnFatal() - }() - cmdutil.BehaviorOnFatal(func(e string) { saw_fatal = true; panic(e) }) - cmd.SetArgs(test.args) - cmd.Execute() - }() - - if test.expectFatal { - if !saw_fatal { - t.Fatalf("%s: unexpected non-error", test.description) - } - } - - if test.expectDelete { - if !deleted { - t.Fatalf("%s: pod never deleted", test.description) - } - } - if !test.expectDelete { - if deleted { - t.Fatalf("%s: unexpected delete", test.description) - } - } - } -} - -type MyReq struct { - Request *http.Request -} - -func (m *MyReq) isFor(method string, path string) bool { - req := m.Request - - return method == req.Method && (req.URL.Path == path || req.URL.Path == strings.Join([]string{"/api/v1", path}, "") || req.URL.Path == strings.Join([]string{"/apis/extensions/v1beta1", path}, "")) -} - -func refJson(t *testing.T, o runtime.Object) string { - ref, err := api.GetReference(o) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - _, _, codec := NewAPIFactory() - json, err := runtime.Encode(codec, &api.SerializedReference{Reference: *ref}) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - return string(json) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/edit.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/edit.go deleted file mode 100644 index 279cd9eca..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/edit.go +++ /dev/null @@ -1,465 +0,0 @@ -/* -Copyright 2015 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 cmd - -import ( - "bufio" - "bytes" - "fmt" - "io" - "os" - gruntime "runtime" - "strings" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/cmd/util/editor" - "k8s.io/kubernetes/pkg/kubectl/cmd/util/jsonmerge" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util" - "k8s.io/kubernetes/pkg/util/strategicpatch" - "k8s.io/kubernetes/pkg/util/yaml" - - "github.com/golang/glog" - "github.com/spf13/cobra" -) - -const ( - editLong = `Edit a resource from the default editor. - -The edit command allows you to directly edit any API resource you can retrieve via the -command line tools. It will open the editor defined by your KUBE_EDITOR, or EDITOR -environment variables, or fall back to 'vi' for Linux or 'notepad' for Windows. -You can edit multiple objects, although changes are applied one at a time. The command -accepts filenames as well as command line arguments, although the files you point to must -be previously saved versions of resources. - -The files to edit will be output in the default API version, or a version specified -by --output-version. The default format is YAML - if you would like to edit in JSON -pass -o json. The flag --windows-line-endings can be used to force Windows line endings, -otherwise the default for your operating system will be used. - -In the event an error occurs while updating, a temporary file will be created on disk -that contains your unapplied changes. The most common error when updating a resource -is another editor changing the resource on the server. When this occurs, you will have -to apply your changes to the newer version of the resource, or update your temporary -saved copy to include the latest resource version.` - - editExample = ` # Edit the service named 'docker-registry': - $ kubectl edit svc/docker-registry - - # Use an alternative editor - $ KUBE_EDITOR="nano" kubectl edit svc/docker-registry - - # Edit the service 'docker-registry' in JSON using the v1 API format: - $ kubectl edit svc/docker-registry --output-version=v1 -o json` -) - -var errExit = fmt.Errorf("exit directly") - -func NewCmdEdit(f *cmdutil.Factory, out io.Writer) *cobra.Command { - filenames := []string{} - cmd := &cobra.Command{ - Use: "edit (RESOURCE/NAME | -f FILENAME)", - Short: "Edit a resource on the server", - Long: editLong, - Example: fmt.Sprintf(editExample), - Run: func(cmd *cobra.Command, args []string) { - err := RunEdit(f, out, cmd, args, filenames) - if err == errExit { - os.Exit(1) - } - cmdutil.CheckErr(err) - }, - } - usage := "Filename, directory, or URL to file to use to edit the resource" - kubectl.AddJsonFilenameFlag(cmd, &filenames, usage) - cmd.Flags().StringP("output", "o", "yaml", "Output format. One of: yaml|json.") - cmd.Flags().String("output-version", "", "Output the formatted object with the given version (default api-version).") - cmd.Flags().Bool("windows-line-endings", gruntime.GOOS == "windows", "Use Windows line-endings (default Unix line-endings)") - cmdutil.AddApplyAnnotationFlags(cmd) - cmdutil.AddRecordFlag(cmd) - return cmd -} - -func RunEdit(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, filenames []string) error { - var printer kubectl.ResourcePrinter - var ext string - switch format := cmdutil.GetFlagString(cmd, "output"); format { - case "json": - printer = &kubectl.JSONPrinter{} - ext = ".json" - case "yaml": - printer = &kubectl.YAMLPrinter{} - ext = ".yaml" - default: - return cmdutil.UsageError(cmd, "The flag 'output' must be one of yaml|json") - } - - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - mapper, typer := f.Object() - resourceMapper := &resource.Mapper{ - ObjectTyper: typer, - RESTMapper: mapper, - ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), - Decoder: f.Decoder(true), - } - - r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, filenames...). - ResourceTypeOrNameArgs(true, args...). - Latest(). - Flatten(). - Do() - err = r.Err() - if err != nil { - return err - } - - infos, err := r.Infos() - if err != nil { - return err - } - - clientConfig, err := f.ClientConfig() - if err != nil { - return err - } - - encoder := f.JSONEncoder() - defaultVersion, err := cmdutil.OutputVersion(cmd, clientConfig.GroupVersion) - if err != nil { - return err - } - objs, err := resource.AsVersionedObjects(infos, defaultVersion.String(), encoder) - if err != nil { - return err - } - - var ( - windowsLineEndings = cmdutil.GetFlagBool(cmd, "windows-line-endings") - edit = editor.NewDefaultEditor(f.EditorEnvs()) - results = editResults{} - original = []byte{} - edited = []byte{} - file string - ) - -outter: - for i := range objs { - obj := objs[i] - // some bookkeeping - results.header.flush() - containsError := false - - for { - // generate the file to edit - buf := &bytes.Buffer{} - var w io.Writer = buf - if windowsLineEndings { - w = util.NewCRLFWriter(w) - } - if err := results.header.writeTo(w); err != nil { - return preservedFile(err, results.file, out) - } - if !containsError { - if err := printer.PrintObj(obj, w); err != nil { - return preservedFile(err, results.file, out) - } - original = buf.Bytes() - } else { - // In case of an error, preserve the edited file. - // Remove the comments (header) from it since we already - // have included the latest header in the buffer above. - buf.Write(manualStrip(edited)) - } - - // launch the editor - editedDiff := edited - edited, file, err = edit.LaunchTempFile(fmt.Sprintf("%s-edit-", os.Args[0]), ext, buf) - if err != nil { - return preservedFile(err, results.file, out) - } - if bytes.Equal(stripComments(editedDiff), stripComments(edited)) { - // Ugly hack right here. We will hit this either (1) when we try to - // save the same changes we tried to save in the previous iteration - // which means our changes are invalid or (2) when we exit the second - // time. The second case is more usual so we can probably live with it. - // TODO: A less hacky fix would be welcome :) - fmt.Fprintln(out, "Edit cancelled, no valid changes were saved.") - continue outter - } - - // cleanup any file from the previous pass - if len(results.file) > 0 { - os.Remove(results.file) - } - glog.V(4).Infof("User edited:\n%s", string(edited)) - - // Compare content without comments - if bytes.Equal(stripComments(original), stripComments(edited)) { - os.Remove(file) - fmt.Fprintln(out, "Edit cancelled, no changes made.") - continue outter - } - lines, err := hasLines(bytes.NewBuffer(edited)) - if err != nil { - return preservedFile(err, file, out) - } - if !lines { - os.Remove(file) - fmt.Fprintln(out, "Edit cancelled, saved file was empty.") - continue outter - } - - results = editResults{ - file: file, - } - - // parse the edited file - updates, err := resourceMapper.InfoForData(edited, "edited-file") - if err != nil { - // syntax error - containsError = true - results.header.reasons = append(results.header.reasons, editReason{head: fmt.Sprintf("The edited file had a syntax error: %v", err)}) - continue - } - // not a syntax error as it turns out... - containsError = false - - // put configuration annotation in "updates" - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), updates, encoder); err != nil { - return preservedFile(err, file, out) - } - if cmdutil.ShouldRecord(cmd, updates) { - err = cmdutil.RecordChangeCause(updates.Object, f.Command()) - if err != nil { - return err - } - } - editedCopy := edited - if editedCopy, err = runtime.Encode(encoder, updates.Object); err != nil { - return preservedFile(err, file, out) - } - - visitor := resource.NewFlattenListVisitor(updates, resourceMapper) - - // need to make sure the original namespace wasn't changed while editing - if err = visitor.Visit(resource.RequireNamespace(cmdNamespace)); err != nil { - return preservedFile(err, file, out) - } - - // use strategic merge to create a patch - originalJS, err := yaml.ToJSON(original) - if err != nil { - return preservedFile(err, file, out) - } - editedJS, err := yaml.ToJSON(editedCopy) - if err != nil { - return preservedFile(err, file, out) - } - patch, err := strategicpatch.CreateStrategicMergePatch(originalJS, editedJS, obj) - // TODO: change all jsonmerge to strategicpatch - // for checking preconditions - preconditions := []jsonmerge.PreconditionFunc{} - if err != nil { - glog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err) - return preservedFile(err, file, out) - } else { - preconditions = append(preconditions, jsonmerge.RequireKeyUnchanged("apiVersion")) - preconditions = append(preconditions, jsonmerge.RequireKeyUnchanged("kind")) - preconditions = append(preconditions, jsonmerge.RequireMetadataKeyUnchanged("name")) - results.version = defaultVersion - } - - if hold, msg := jsonmerge.TestPreconditionsHold(patch, preconditions); !hold { - fmt.Fprintf(out, "error: %s", msg) - return preservedFile(nil, file, out) - } - - err = visitor.Visit(func(info *resource.Info, err error) error { - patched, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch) - if err != nil { - glog.V(4).Infof(results.addError(err, info)) - return err - } - info.Refresh(patched, true) - cmdutil.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, "edited") - return nil - }) - if err == nil { - os.Remove(file) - continue outter - } - // Handle all possible errors - // - // 1. retryable: propose kubectl replace -f - // 2. notfound: indicate the location of the saved configuration of the deleted resource - // 3. invalid: retry those on the spot by looping ie. reloading the editor - if results.retryable > 0 { - fmt.Fprintf(out, "You can run `%s replace -f %s` to try this update again.\n", os.Args[0], file) - continue outter - } - if results.notfound > 0 { - fmt.Fprintf(out, "The edits you made on deleted resources have been saved to %q\n", file) - continue outter - } - // validation error - containsError = true - } - } - return nil -} - -// editReason preserves a message about the reason this file must be edited again -type editReason struct { - head string - other []string -} - -// editHeader includes a list of reasons the edit must be retried -type editHeader struct { - reasons []editReason -} - -// writeTo outputs the current header information into a stream -func (h *editHeader) writeTo(w io.Writer) error { - fmt.Fprint(w, `# Please edit the object below. Lines beginning with a '#' will be ignored, -# and an empty file will abort the edit. If an error occurs while saving this file will be -# reopened with the relevant failures. -# -`) - for _, r := range h.reasons { - if len(r.other) > 0 { - fmt.Fprintf(w, "# %s:\n", r.head) - } else { - fmt.Fprintf(w, "# %s\n", r.head) - } - for _, o := range r.other { - fmt.Fprintf(w, "# * %s\n", o) - } - fmt.Fprintln(w, "#") - } - return nil -} - -func (h *editHeader) flush() { - h.reasons = []editReason{} -} - -// editResults capture the result of an update -type editResults struct { - header editHeader - retryable int - notfound int - edit []*resource.Info - file string - - version unversioned.GroupVersion -} - -func (r *editResults) addError(err error, info *resource.Info) string { - switch { - case errors.IsInvalid(err): - r.edit = append(r.edit, info) - reason := editReason{ - head: fmt.Sprintf("%s %q was not valid", info.Mapping.Resource, info.Name), - } - if err, ok := err.(errors.APIStatus); ok { - if details := err.Status().Details; details != nil { - for _, cause := range details.Causes { - reason.other = append(reason.other, fmt.Sprintf("%s: %s", cause.Field, cause.Message)) - } - } - } - r.header.reasons = append(r.header.reasons, reason) - return fmt.Sprintf("Error: %s %q is invalid", info.Mapping.Resource, info.Name) - case errors.IsNotFound(err): - r.notfound++ - return fmt.Sprintf("Error: %s %q could not be found on the server", info.Mapping.Resource, info.Name) - default: - r.retryable++ - return fmt.Sprintf("Error: %s %q could not be patched: %v", info.Mapping.Resource, info.Name, err) - } -} - -// preservedFile writes out a message about the provided file if it exists to the -// provided output stream when an error happens. Used to notify the user where -// their updates were preserved. -func preservedFile(err error, path string, out io.Writer) error { - if len(path) > 0 { - if _, err := os.Stat(path); !os.IsNotExist(err) { - fmt.Fprintf(out, "A copy of your changes has been stored to %q\n", path) - } - } - return err -} - -// hasLines returns true if any line in the provided stream is non empty - has non-whitespace -// characters, or the first non-whitespace character is a '#' indicating a comment. Returns -// any errors encountered reading the stream. -func hasLines(r io.Reader) (bool, error) { - // TODO: if any files we read have > 64KB lines, we'll need to switch to bytes.ReadLine - // TODO: probably going to be secrets - s := bufio.NewScanner(r) - for s.Scan() { - if line := strings.TrimSpace(s.Text()); len(line) > 0 && line[0] != '#' { - return true, nil - } - } - if err := s.Err(); err != nil && err != io.EOF { - return false, err - } - return false, nil -} - -// stripComments will transform a YAML file into JSON, thus dropping any comments -// in it. Note that if the given file has a syntax error, the transformation will -// fail and we will manually drop all comments from the file. -func stripComments(file []byte) []byte { - stripped := file - stripped, err := yaml.ToJSON(stripped) - if err != nil { - stripped = manualStrip(file) - } - return stripped -} - -// manualStrip is used for dropping comments from a YAML file -func manualStrip(file []byte) []byte { - stripped := []byte{} - lines := bytes.Split(file, []byte("\n")) - for i, line := range lines { - if bytes.HasPrefix(bytes.TrimSpace(line), []byte("#")) { - continue - } - stripped = append(stripped, line...) - if i < len(lines)-1 { - stripped = append(stripped, '\n') - } - } - return stripped -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/exec.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/exec.go deleted file mode 100644 index eec6da62f..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/exec.go +++ /dev/null @@ -1,239 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - "net/url" - "os" - "os/signal" - "syscall" - - "github.com/docker/docker/pkg/term" - "github.com/golang/glog" - "github.com/spf13/cobra" - "k8s.io/kubernetes/pkg/api" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/remotecommand" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" -) - -const ( - exec_example = `# Get output from running 'date' from pod 123456-7890, using the first container by default -$ kubectl exec 123456-7890 date - -# Get output from running 'date' in ruby-container from pod 123456-7890 -$ kubectl exec 123456-7890 -c ruby-container date - -# Switch to raw terminal mode, sends stdin to 'bash' in ruby-container from pod 123456-7890 -# and sends stdout/stderr from 'bash' back to the client -$ kubectl exec 123456-7890 -c ruby-container -i -t -- bash -il` -) - -func NewCmdExec(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command { - options := &ExecOptions{ - In: cmdIn, - Out: cmdOut, - Err: cmdErr, - - Executor: &DefaultRemoteExecutor{}, - } - cmd := &cobra.Command{ - Use: "exec POD [-c CONTAINER] -- COMMAND [args...]", - Short: "Execute a command in a container.", - Long: "Execute a command in a container.", - Example: exec_example, - Run: func(cmd *cobra.Command, args []string) { - argsLenAtDash := cmd.ArgsLenAtDash() - cmdutil.CheckErr(options.Complete(f, cmd, args, argsLenAtDash)) - cmdutil.CheckErr(options.Validate()) - cmdutil.CheckErr(options.Run()) - }, - } - cmd.Flags().StringVarP(&options.PodName, "pod", "p", "", "Pod name") - // TODO support UID - cmd.Flags().StringVarP(&options.ContainerName, "container", "c", "", "Container name. If omitted, the first container in the pod will be chosen") - cmd.Flags().BoolVarP(&options.Stdin, "stdin", "i", false, "Pass stdin to the container") - cmd.Flags().BoolVarP(&options.TTY, "tty", "t", false, "Stdin is a TTY") - return cmd -} - -// RemoteExecutor defines the interface accepted by the Exec command - provided for test stubbing -type RemoteExecutor interface { - Execute(method string, url *url.URL, config *client.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool) error -} - -// DefaultRemoteExecutor is the standard implementation of remote command execution -type DefaultRemoteExecutor struct{} - -func (*DefaultRemoteExecutor) Execute(method string, url *url.URL, config *client.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool) error { - exec, err := remotecommand.NewExecutor(config, method, url) - if err != nil { - return err - } - return exec.Stream(stdin, stdout, stderr, tty) -} - -// ExecOptions declare the arguments accepted by the Exec command -type ExecOptions struct { - Namespace string - PodName string - ContainerName string - Stdin bool - TTY bool - Command []string - - In io.Reader - Out io.Writer - Err io.Writer - - Executor RemoteExecutor - Client *client.Client - Config *client.Config -} - -// Complete verifies command line arguments and loads data from the command environment -func (p *ExecOptions) Complete(f *cmdutil.Factory, cmd *cobra.Command, argsIn []string, argsLenAtDash int) error { - // Let kubectl exec follow rules for `--`, see #13004 issue - if len(p.PodName) == 0 && (len(argsIn) == 0 || argsLenAtDash == 0) { - return cmdutil.UsageError(cmd, "POD is required for exec") - } - if len(p.PodName) != 0 { - printDeprecationWarning("exec POD", "-p POD") - if len(argsIn) < 1 { - return cmdutil.UsageError(cmd, "COMMAND is required for exec") - } - p.Command = argsIn - } else { - p.PodName = argsIn[0] - p.Command = argsIn[1:] - if len(p.Command) < 1 { - return cmdutil.UsageError(cmd, "COMMAND is required for exec") - } - } - - namespace, _, err := f.DefaultNamespace() - if err != nil { - return err - } - p.Namespace = namespace - - config, err := f.ClientConfig() - if err != nil { - return err - } - p.Config = config - - client, err := f.Client() - if err != nil { - return err - } - p.Client = client - - return nil -} - -// Validate checks that the provided exec options are specified. -func (p *ExecOptions) Validate() error { - if len(p.PodName) == 0 { - return fmt.Errorf("pod name must be specified") - } - if len(p.Command) == 0 { - return fmt.Errorf("you must specify at least one command for the container") - } - if p.Out == nil || p.Err == nil { - return fmt.Errorf("both output and error output must be provided") - } - if p.Executor == nil || p.Client == nil || p.Config == nil { - return fmt.Errorf("client, client config, and executor must be provided") - } - return nil -} - -// Run executes a validated remote execution against a pod. -func (p *ExecOptions) Run() error { - pod, err := p.Client.Pods(p.Namespace).Get(p.PodName) - if err != nil { - return err - } - - if pod.Status.Phase != api.PodRunning { - return fmt.Errorf("pod %s is not running and cannot execute commands; current phase is %s", p.PodName, pod.Status.Phase) - } - - containerName := p.ContainerName - if len(containerName) == 0 { - glog.V(4).Infof("defaulting container name to %s", pod.Spec.Containers[0].Name) - containerName = pod.Spec.Containers[0].Name - } - - // TODO: refactor with terminal helpers from the edit utility once that is merged - var stdin io.Reader - tty := p.TTY - if p.Stdin { - stdin = p.In - if tty { - if file, ok := stdin.(*os.File); ok { - inFd := file.Fd() - if term.IsTerminal(inFd) { - oldState, err := term.SetRawTerminal(inFd) - if err != nil { - glog.Fatal(err) - } - // this handles a clean exit, where the command finished - defer term.RestoreTerminal(inFd, oldState) - - // SIGINT is handled by term.SetRawTerminal (it runs a goroutine that listens - // for SIGINT and restores the terminal before exiting) - - // this handles SIGTERM - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGTERM) - go func() { - <-sigChan - term.RestoreTerminal(inFd, oldState) - os.Exit(0) - }() - } else { - fmt.Fprintln(p.Err, "STDIN is not a terminal") - } - } else { - tty = false - fmt.Fprintln(p.Err, "Unable to use a TTY - input is not the right kind of file") - } - } - } - - // TODO: consider abstracting into a client invocation or client helper - req := p.Client.RESTClient.Post(). - Resource("pods"). - Name(pod.Name). - Namespace(pod.Namespace). - SubResource("exec"). - Param("container", containerName) - req.VersionedParams(&api.PodExecOptions{ - Container: containerName, - Command: p.Command, - Stdin: stdin != nil, - Stdout: p.Out != nil, - Stderr: p.Err != nil, - TTY: tty, - }, api.ParameterCodec) - - return p.Executor.Execute("POST", req.URL(), p.Config, stdin, p.Out, p.Err, tty) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/exec_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/exec_test.go deleted file mode 100644 index 8317db936..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/exec_test.go +++ /dev/null @@ -1,258 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "fmt" - "io" - "net/http" - "net/url" - "reflect" - "testing" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/fake" -) - -type fakeRemoteExecutor struct { - method string - url *url.URL - execErr error -} - -func (f *fakeRemoteExecutor) Execute(method string, url *url.URL, config *client.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool) error { - f.method = method - f.url = url - return f.execErr -} - -func TestPodAndContainer(t *testing.T) { - tests := []struct { - args []string - argsLenAtDash int - p *ExecOptions - name string - expectError bool - expectedPod string - expectedContainer string - expectedArgs []string - }{ - { - p: &ExecOptions{}, - argsLenAtDash: -1, - expectError: true, - name: "empty", - }, - { - p: &ExecOptions{PodName: "foo"}, - argsLenAtDash: -1, - expectError: true, - name: "no cmd", - }, - { - p: &ExecOptions{PodName: "foo", ContainerName: "bar"}, - argsLenAtDash: -1, - expectError: true, - name: "no cmd, w/ container", - }, - { - p: &ExecOptions{PodName: "foo"}, - args: []string{"cmd"}, - argsLenAtDash: -1, - expectedPod: "foo", - expectedArgs: []string{"cmd"}, - name: "pod in flags", - }, - { - p: &ExecOptions{}, - args: []string{"foo", "cmd"}, - argsLenAtDash: 0, - expectError: true, - name: "no pod, pod name is behind dash", - }, - { - p: &ExecOptions{}, - args: []string{"foo"}, - argsLenAtDash: -1, - expectError: true, - name: "no cmd, w/o flags", - }, - { - p: &ExecOptions{}, - args: []string{"foo", "cmd"}, - argsLenAtDash: -1, - expectedPod: "foo", - expectedArgs: []string{"cmd"}, - name: "cmd, w/o flags", - }, - { - p: &ExecOptions{}, - args: []string{"foo", "cmd"}, - argsLenAtDash: 1, - expectedPod: "foo", - expectedArgs: []string{"cmd"}, - name: "cmd, cmd is behind dash", - }, - { - p: &ExecOptions{ContainerName: "bar"}, - args: []string{"foo", "cmd"}, - argsLenAtDash: -1, - expectedPod: "foo", - expectedContainer: "bar", - expectedArgs: []string{"cmd"}, - name: "cmd, container in flag", - }, - } - for _, test := range tests { - f, tf, codec := NewAPIFactory() - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { return nil, nil }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{} - - cmd := &cobra.Command{} - options := test.p - err := options.Complete(f, cmd, test.args, test.argsLenAtDash) - if test.expectError && err == nil { - t.Errorf("unexpected non-error (%s)", test.name) - } - if !test.expectError && err != nil { - t.Errorf("unexpected error: %v (%s)", err, test.name) - } - if err != nil { - continue - } - if options.PodName != test.expectedPod { - t.Errorf("expected: %s, got: %s (%s)", test.expectedPod, options.PodName, test.name) - } - if options.ContainerName != test.expectedContainer { - t.Errorf("expected: %s, got: %s (%s)", test.expectedContainer, options.ContainerName, test.name) - } - if !reflect.DeepEqual(test.expectedArgs, options.Command) { - t.Errorf("expected: %v, got %v (%s)", test.expectedArgs, options.Command, test.name) - } - } -} - -func TestExec(t *testing.T) { - version := testapi.Default.GroupVersion().Version - tests := []struct { - name, version, podPath, execPath, container string - pod *api.Pod - execErr bool - }{ - { - name: "pod exec", - version: version, - podPath: "/api/" + version + "/namespaces/test/pods/foo", - execPath: "/api/" + version + "/namespaces/test/pods/foo/exec", - pod: execPod(), - }, - { - name: "pod exec error", - version: version, - podPath: "/api/" + version + "/namespaces/test/pods/foo", - execPath: "/api/" + version + "/namespaces/test/pods/foo/exec", - pod: execPod(), - execErr: true, - }, - } - for _, test := range tests { - f, tf, codec := NewAPIFactory() - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == test.podPath && m == "GET": - body := objBody(codec, test.pod) - return &http.Response{StatusCode: 200, Body: body}, nil - default: - // Ensures no GET is performed when deleting by name - t.Errorf("%s: unexpected request: %s %#v\n%#v", test.name, req.Method, req.URL, req) - return nil, fmt.Errorf("unexpected request") - } - }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: &unversioned.GroupVersion{Version: test.version}}} - bufOut := bytes.NewBuffer([]byte{}) - bufErr := bytes.NewBuffer([]byte{}) - bufIn := bytes.NewBuffer([]byte{}) - ex := &fakeRemoteExecutor{} - if test.execErr { - ex.execErr = fmt.Errorf("exec error") - } - params := &ExecOptions{ - PodName: "foo", - ContainerName: "bar", - In: bufIn, - Out: bufOut, - Err: bufErr, - Executor: ex, - } - cmd := &cobra.Command{} - args := []string{"test", "command"} - if err := params.Complete(f, cmd, args, -1); err != nil { - t.Fatal(err) - } - err := params.Run() - if test.execErr && err != ex.execErr { - t.Errorf("%s: Unexpected exec error: %v", test.name, err) - continue - } - if !test.execErr && err != nil { - t.Errorf("%s: Unexpected error: %v", test.name, err) - continue - } - if test.execErr { - continue - } - if ex.url.Path != test.execPath { - t.Errorf("%s: Did not get expected path for exec request", test.name) - continue - } - if ex.method != "POST" { - t.Errorf("%s: Did not get method for exec request: %s", test.name, ex.method) - } - } -} - -func execPod() *api.Pod { - return &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{ - { - Name: "bar", - }, - }, - }, - Status: api.PodStatus{ - Phase: api.PodRunning, - }, - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/explain.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/explain.go deleted file mode 100644 index 765091c03..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/explain.go +++ /dev/null @@ -1,107 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "io" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apimachinery/registered" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" -) - -const ( - explainExamples = `# Get the documentation of the resource and its fields -$ kubectl explain pods - -# Get the documentation of a specific field of a resource -$ kubectl explain pods.spec.containers` - - explainLong = `Documentation of resources. - -Possible resource types include: pods (po), services (svc), -replicationcontrollers (rc), nodes (no), events (ev), componentstatuses (cs), -limitranges (limits), persistentvolumes (pv), persistentvolumeclaims (pvc), -resourcequotas (quota), namespaces (ns), horizontalpodautoscalers (hpa) -or endpoints (ep).` -) - -// NewCmdExplain returns a cobra command for swagger docs -func NewCmdExplain(f *cmdutil.Factory, out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "explain RESOURCE", - Short: "Documentation of resources.", - Long: explainLong, - Example: explainExamples, - Run: func(cmd *cobra.Command, args []string) { - err := RunExplain(f, out, cmd, args) - cmdutil.CheckErr(err) - }, - } - cmd.Flags().Bool("recursive", false, "Print the fields of fields (Currently only 1 level deep)") - return cmd -} - -// RunExplain executes the appropriate steps to print a model's documentation -func RunExplain(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { - if len(args) != 1 { - return cmdutil.UsageError(cmd, "We accept only this format: explain RESOURCE") - } - - recursive := cmdutil.GetFlagBool(cmd, "recursive") - apiVersionString := cmdutil.GetFlagString(cmd, "api-version") - apiVersion := unversioned.GroupVersion{} - - mapper, _ := f.Object() - // TODO: After we figured out the new syntax to separate group and resource, allow - // the users to use it in explain (kubectl explain ). - // Refer to issue #16039 for why we do this. Refer to PR #15808 that used "/" syntax. - inModel, fieldsPath, err := kubectl.SplitAndParseResourceRequest(args[0], mapper) - if err != nil { - return err - } - - // TODO: We should deduce the group for a resource by discovering the supported resources at server. - gvk, err := mapper.KindFor(unversioned.GroupVersionResource{Resource: inModel}) - if err != nil { - return err - } - - if len(apiVersionString) == 0 { - groupMeta, err := registered.Group(gvk.Group) - if err != nil { - return err - } - apiVersion = groupMeta.GroupVersion - - } else { - apiVersion, err = unversioned.ParseGroupVersion(apiVersionString) - if err != nil { - return nil - } - } - - schema, err := f.SwaggerSchema(apiVersion.WithKind(gvk.Kind)) - if err != nil { - return err - } - - return kubectl.PrintModelDescription(inModel, fieldsPath, out, schema, recursive) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/expose.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/expose.go deleted file mode 100644 index 48b624436..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/expose.go +++ /dev/null @@ -1,244 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - "strings" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/validation" -) - -// ExposeOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of -// referencing the cmd.Flags() -type ExposeOptions struct { - Filenames []string -} - -const ( - expose_long = `Take a replication controller, service, replica set or pod and expose it as a new Kubernetes service. - -Looks up a replication controller, service, replica set or pod by name and uses the selector for that -resource as the selector for a new service on the specified port. A replica set will be exposed as a -service only if it's selector is convertible to a selector that service supports, i.e. when the -replica set selector contains only the matchLabels component. Note that if no port is specified -via --port and the exposed resource has multiple ports, all will be re-used by the new service. Also -if no labels are specified, the new service will re-use the labels from the resource it exposes.` - - expose_example = `# Create a service for a replicated nginx, which serves on port 80 and connects to the containers on port 8000. -$ kubectl expose rc nginx --port=80 --target-port=8000 - -# Create a service for a replication controller identified by type and name specified in "nginx-controller.yaml", which serves on port 80 and connects to the containers on port 8000. -$ kubectl expose -f nginx-controller.yaml --port=80 --target-port=8000 - -# Create a service for a pod valid-pod, which serves on port 444 with the name "frontend" -$ kubectl expose pod valid-pod --port=444 --name=frontend - -# Create a second service based on the above service, exposing the container port 8443 as port 443 with the name "nginx-https" -$ kubectl expose service nginx --port=443 --target-port=8443 --name=nginx-https - -# Create a service for a replicated streaming application on port 4100 balancing UDP traffic and named 'video-stream'. -$ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream - -# Create a service for a replicated nginx using replica set, which serves on port 80 and connects to the containers on port 8000. -$ kubectl expose rs nginx --port=80 --target-port=8000` -) - -func NewCmdExposeService(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &ExposeOptions{} - - cmd := &cobra.Command{ - Use: "expose (-f FILENAME | TYPE NAME) [--port=port] [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--external-ip=external-ip-of-service] [--type=type]", - Short: "Take a replication controller, service or pod and expose it as a new Kubernetes Service", - Long: expose_long, - Example: expose_example, - Run: func(cmd *cobra.Command, args []string) { - err := RunExpose(f, out, cmd, args, options) - cmdutil.CheckErr(err) - }, - } - cmdutil.AddPrinterFlags(cmd) - cmd.Flags().String("generator", "service/v2", "The name of the API generator to use. There are 2 generators: 'service/v1' and 'service/v2'. The only difference between them is that service port in v1 is named 'default', while it is left unnamed in v2. Default is 'service/v2'.") - cmd.Flags().String("protocol", "TCP", "The network protocol for the service to be created. Default is 'tcp'.") - cmd.Flags().String("port", "", "The port that the service should serve on. Copied from the resource being exposed, if unspecified") - cmd.Flags().String("type", "", "Type for this service: ClusterIP, NodePort, or LoadBalancer. Default is 'ClusterIP'.") - // TODO: remove create-external-load-balancer in code on or after Aug 25, 2016. - cmd.Flags().Bool("create-external-load-balancer", false, "If true, create an external load balancer for this service (trumped by --type). Implementation is cloud provider dependent. Default is 'false'.") - cmd.Flags().MarkDeprecated("create-external-load-balancer", "use --type=\"LoadBalancer\" instead") - cmd.Flags().String("load-balancer-ip", "", "IP to assign to to the Load Balancer. If empty, an ephemeral IP will be created and used (cloud-provider specific).") - cmd.Flags().String("selector", "", "A label selector to use for this service. Only equality-based selector requirements are supported. If empty (the default) infer the selector from the replication controller or replica set.") - cmd.Flags().StringP("labels", "l", "", "Labels to apply to the service created by this call.") - cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without creating it.") - cmd.Flags().String("container-port", "", "Synonym for --target-port") - cmd.Flags().String("target-port", "", "Name or number for the port on the container that the service should direct traffic to. Optional.") - cmd.Flags().String("external-ip", "", "Additional external IP address (not managed by Kubernetes) to accept for the service. If this IP is routed to a node, the service can be accessed by this IP in addition to its generated service IP.") - cmd.Flags().String("overrides", "", "An inline JSON override for the generated object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field.") - cmd.Flags().String("name", "", "The name for the newly created object.") - cmd.Flags().String("session-affinity", "", "If non-empty, set the session affinity for the service to this; legal values: 'None', 'ClientIP'") - - usage := "Filename, directory, or URL to a file identifying the resource to expose a service" - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) - cmdutil.AddApplyAnnotationFlags(cmd) - cmdutil.AddRecordFlag(cmd) - return cmd -} - -func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *ExposeOptions) error { - namespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - ContinueOnError(). - NamespaceParam(namespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). - ResourceTypeOrNameArgs(false, args...). - Flatten(). - Do() - infos, err := r.Infos() - if err != nil { - return err - } - if len(infos) > 1 { - return fmt.Errorf("multiple resources provided: %v", args) - } - info := infos[0] - mapping := info.ResourceMapping() - if err := f.CanBeExposed(mapping.GroupVersionKind.GroupKind()); err != nil { - return err - } - // Get the input object - inputObject, err := r.Object() - if err != nil { - return err - } - - // Get the generator, setup and validate all required parameters - generatorName := cmdutil.GetFlagString(cmd, "generator") - generators := f.Generators("expose") - generator, found := generators[generatorName] - if !found { - return cmdutil.UsageError(cmd, fmt.Sprintf("generator %q not found.", generatorName)) - } - names := generator.ParamNames() - params := kubectl.MakeParams(cmd, names) - name := info.Name - if len(name) > validation.DNS952LabelMaxLength { - name = name[:validation.DNS952LabelMaxLength] - } - params["default-name"] = name - - // For objects that need a pod selector, derive it from the exposed object in case a user - // didn't explicitly specify one via --selector - if s, found := params["selector"]; found && kubectl.IsZero(s) { - s, err := f.MapBasedSelectorForObject(inputObject) - if err != nil { - return cmdutil.UsageError(cmd, fmt.Sprintf("couldn't retrieve selectors via --selector flag or introspection: %s", err)) - } - params["selector"] = s - } - - // For objects that need a port, derive it from the exposed object in case a user - // didn't explicitly specify one via --port - if port, found := params["port"]; found && kubectl.IsZero(port) { - ports, err := f.PortsForObject(inputObject) - if err != nil { - return cmdutil.UsageError(cmd, fmt.Sprintf("couldn't find port via --port flag or introspection: %s", err)) - } - switch len(ports) { - case 0: - return cmdutil.UsageError(cmd, "couldn't find port via --port flag or introspection") - case 1: - params["port"] = ports[0] - default: - params["ports"] = strings.Join(ports, ",") - } - } - if kubectl.IsZero(params["labels"]) { - labels, err := f.LabelsForObject(inputObject) - if err != nil { - return err - } - params["labels"] = kubectl.MakeLabels(labels) - } - if err = kubectl.ValidateParams(names, params); err != nil { - return err - } - // Check for invalid flags used against the present generator. - if err := kubectl.EnsureFlagsValid(cmd, generators, generatorName); err != nil { - return err - } - - // Generate new object - object, err := generator.Generate(params) - if err != nil { - return err - } - - if inline := cmdutil.GetFlagString(cmd, "overrides"); len(inline) > 0 { - codec := runtime.NewCodec(f.JSONEncoder(), f.Decoder(true)) - object, err = cmdutil.Merge(codec, object, inline, mapping.GroupVersionKind.Kind) - if err != nil { - return err - } - } - - resourceMapper := &resource.Mapper{ - ObjectTyper: typer, - RESTMapper: mapper, - ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), - Decoder: f.Decoder(true), - } - info, err = resourceMapper.InfoForObject(object) - if err != nil { - return err - } - if cmdutil.ShouldRecord(cmd, info) { - if err := cmdutil.RecordChangeCause(object, f.Command()); err != nil { - return err - } - } - info.Refresh(object, true) - // TODO: extract this flag to a central location, when such a location exists. - if cmdutil.GetFlagBool(cmd, "dry-run") { - return f.PrintObject(cmd, object, out) - } - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { - return err - } - - // Serialize the object with the annotation applied. - object, err = resource.NewHelper(info.Client, info.Mapping).Create(namespace, false, object) - if err != nil { - return err - } - - if len(cmdutil.GetFlagString(cmd, "output")) > 0 { - return f.PrintObject(cmd, object, out) - } - cmdutil.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, "exposed") - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/expose_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/expose_test.go deleted file mode 100644 index 6bad24357..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/expose_test.go +++ /dev/null @@ -1,351 +0,0 @@ -/* -Copyright 2015 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 cmd - -import ( - "bytes" - "net/http" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/unversioned/fake" - "k8s.io/kubernetes/pkg/kubectl" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/intstr" -) - -func TestRunExposeService(t *testing.T) { - tests := []struct { - name string - args []string - ns string - calls map[string]string - input runtime.Object - flags map[string]string - output runtime.Object - expected string - status int - }{ - { - name: "expose-service-from-service-no-selector-defined", - args: []string{"service", "baz"}, - ns: "test", - calls: map[string]string{ - "GET": "/namespaces/test/services/baz", - "POST": "/namespaces/test/services", - }, - input: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, - Spec: api.ServiceSpec{ - Selector: map[string]string{"app": "go"}, - }, - }, - flags: map[string]string{"protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test"}, - output: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}}, - Spec: api.ServiceSpec{ - Ports: []api.ServicePort{ - { - Protocol: api.ProtocolUDP, - Port: 14, - TargetPort: intstr.FromInt(14), - }, - }, - Selector: map[string]string{"app": "go"}, - }, - }, - expected: "service \"foo\" exposed", - status: 200, - }, - { - name: "expose-service-from-service", - args: []string{"service", "baz"}, - ns: "test", - calls: map[string]string{ - "GET": "/namespaces/test/services/baz", - "POST": "/namespaces/test/services", - }, - input: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, - Spec: api.ServiceSpec{ - Selector: map[string]string{"app": "go"}, - }, - }, - flags: map[string]string{"selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test"}, - output: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}}, - Spec: api.ServiceSpec{ - Ports: []api.ServicePort{ - { - Protocol: api.ProtocolUDP, - Port: 14, - TargetPort: intstr.FromInt(14), - }, - }, - Selector: map[string]string{"func": "stream"}, - }, - }, - expected: "service \"foo\" exposed", - status: 200, - }, - { - name: "no-name-passed-from-the-cli", - args: []string{"service", "mayor"}, - ns: "default", - calls: map[string]string{ - "GET": "/namespaces/default/services/mayor", - "POST": "/namespaces/default/services", - }, - input: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "mayor", Namespace: "default", ResourceVersion: "12"}, - Spec: api.ServiceSpec{ - Selector: map[string]string{"run": "this"}, - }, - }, - // No --name flag specified below. Service will use the rc's name passed via the 'default-name' parameter - flags: map[string]string{"selector": "run=this", "port": "80", "labels": "runas=amayor"}, - output: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "mayor", Namespace: "", Labels: map[string]string{"runas": "amayor"}}, - Spec: api.ServiceSpec{ - Ports: []api.ServicePort{ - { - Protocol: api.ProtocolTCP, - Port: 80, - TargetPort: intstr.FromInt(80), - }, - }, - Selector: map[string]string{"run": "this"}, - }, - }, - expected: "service \"mayor\" exposed", - status: 200, - }, - { - name: "expose-service", - args: []string{"service", "baz"}, - ns: "test", - calls: map[string]string{ - "GET": "/namespaces/test/services/baz", - "POST": "/namespaces/test/services", - }, - input: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, - Spec: api.ServiceSpec{ - Selector: map[string]string{"app": "go"}, - }, - }, - flags: map[string]string{"selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test", "type": "LoadBalancer", "dry-run": "true"}, - output: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}}, - Spec: api.ServiceSpec{ - Ports: []api.ServicePort{ - { - Protocol: api.ProtocolUDP, - Port: 14, - TargetPort: intstr.FromInt(14), - }, - }, - Selector: map[string]string{"func": "stream"}, - Type: api.ServiceTypeLoadBalancer, - }, - }, - status: 200, - }, - { - name: "expose-affinity-service", - args: []string{"service", "baz"}, - ns: "test", - calls: map[string]string{ - "GET": "/namespaces/test/services/baz", - "POST": "/namespaces/test/services", - }, - input: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, - Spec: api.ServiceSpec{ - Selector: map[string]string{"app": "go"}, - }, - }, - flags: map[string]string{"selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test", "type": "LoadBalancer", "session-affinity": "ClientIP", "dry-run": "true"}, - output: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}}, - Spec: api.ServiceSpec{ - Ports: []api.ServicePort{ - { - Protocol: api.ProtocolUDP, - Port: 14, - TargetPort: intstr.FromInt(14), - }, - }, - Selector: map[string]string{"func": "stream"}, - Type: api.ServiceTypeLoadBalancer, - SessionAffinity: api.ServiceAffinityClientIP, - }, - }, - status: 200, - }, - { - name: "expose-from-file", - args: []string{}, - ns: "test", - calls: map[string]string{ - "GET": "/namespaces/test/services/redis-master", - "POST": "/namespaces/test/services", - }, - input: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "redis-master", Namespace: "test", ResourceVersion: "12"}, - Spec: api.ServiceSpec{ - Selector: map[string]string{"app": "go"}, - }, - }, - flags: map[string]string{"filename": "../../../examples/guestbook/redis-master-service.yaml", "selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test", "dry-run": "true"}, - output: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "foo", Labels: map[string]string{"svc": "test"}}, - Spec: api.ServiceSpec{ - Ports: []api.ServicePort{ - { - Protocol: api.ProtocolUDP, - Port: 14, - TargetPort: intstr.FromInt(14), - }, - }, - Selector: map[string]string{"func": "stream"}, - }, - }, - status: 200, - }, - { - name: "truncate-name", - args: []string{"pod", "a-name-that-is-toooo-big-for-a-service"}, - ns: "test", - calls: map[string]string{ - "GET": "/namespaces/test/pods/a-name-that-is-toooo-big-for-a-service", - "POST": "/namespaces/test/services", - }, - input: &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, - }, - flags: map[string]string{"selector": "svc=frompod", "port": "90", "labels": "svc=frompod", "generator": "service/v2"}, - output: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "a-name-that-is-toooo-big", Namespace: "", Labels: map[string]string{"svc": "frompod"}}, - Spec: api.ServiceSpec{ - Ports: []api.ServicePort{ - { - Protocol: api.ProtocolTCP, - Port: 90, - TargetPort: intstr.FromInt(90), - }, - }, - Selector: map[string]string{"svc": "frompod"}, - }, - }, - expected: "service \"a-name-that-is-toooo-big\" exposed", - status: 200, - }, - { - name: "expose-multiport-object", - args: []string{"service", "foo"}, - ns: "test", - calls: map[string]string{ - "GET": "/namespaces/test/services/foo", - "POST": "/namespaces/test/services", - }, - input: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "multiport"}}, - Spec: api.ServiceSpec{ - Ports: []api.ServicePort{ - { - Protocol: api.ProtocolTCP, - Port: 80, - TargetPort: intstr.FromInt(80), - }, - { - Protocol: api.ProtocolTCP, - Port: 443, - TargetPort: intstr.FromInt(443), - }, - }, - }, - }, - flags: map[string]string{"selector": "svc=fromfoo", "generator": "service/v2", "name": "fromfoo", "dry-run": "true"}, - output: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "fromfoo", Namespace: "", Labels: map[string]string{"svc": "multiport"}}, - Spec: api.ServiceSpec{ - Ports: []api.ServicePort{ - { - Name: "port-1", - Protocol: api.ProtocolTCP, - Port: 80, - TargetPort: intstr.FromInt(80), - }, - { - Name: "port-2", - Protocol: api.ProtocolTCP, - Port: 443, - TargetPort: intstr.FromInt(443), - }, - }, - Selector: map[string]string{"svc": "fromfoo"}, - }, - }, - status: 200, - }, - } - - for _, test := range tests { - f, tf, codec := NewAPIFactory() - tf.Printer = &kubectl.JSONPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == test.calls[m] && m == "GET": - return &http.Response{StatusCode: test.status, Body: objBody(codec, test.input)}, nil - case p == test.calls[m] && m == "POST": - return &http.Response{StatusCode: test.status, Body: objBody(codec, test.output)}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = test.ns - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdExposeService(f, buf) - cmd.SetOutput(buf) - for flag, value := range test.flags { - cmd.Flags().Set(flag, value) - } - cmd.Run(cmd, test.args) - - out := buf.String() - if _, ok := test.flags["dry-run"]; ok { - buf.Reset() - if err := tf.Printer.PrintObj(test.output, buf); err != nil { - t.Errorf("%s: Unexpected error: %v", test.name, err) - continue - } - - test.expected = buf.String() - } - - if !strings.Contains(out, test.expected) { - t.Errorf("%s: Unexpected output! Expected\n%s\ngot\n%s", test.name, test.expected, out) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/get.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/get.go deleted file mode 100644 index 21706e60d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/get.go +++ /dev/null @@ -1,288 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/watch" -) - -// GetOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of -// referencing the cmd.Flags() -type GetOptions struct { - Filenames []string -} - -const ( - get_long = `Display one or many resources. - -Possible resource types include (case insensitive): pods (po), services (svc), -replicationcontrollers (rc), nodes (no), events (ev), componentstatuses (cs), -limitranges (limits), persistentvolumes (pv), persistentvolumeclaims (pvc), -resourcequotas (quota), namespaces (ns), endpoints (ep), -horizontalpodautoscalers (hpa), serviceaccounts or secrets. - -By specifying the output as 'template' and providing a Go template as the value -of the --template flag, you can filter the attributes of the fetched resource(s).` - get_example = `# List all pods in ps output format. -$ kubectl get pods - -# List all pods in ps output format with more information (such as node name). -$ kubectl get pods -o wide - -# List a single replication controller with specified NAME in ps output format. -$ kubectl get replicationcontroller web - -# List a single pod in JSON output format. -$ kubectl get -o json pod web-pod-13je7 - -# List a pod identified by type and name specified in "pod.yaml" in JSON output format. -$ kubectl get -f pod.yaml -o json - -# Return only the phase value of the specified pod. -$ kubectl get -o template pod/web-pod-13je7 --template={{.status.phase}} --api-version=v1 - -# List all replication controllers and services together in ps output format. -$ kubectl get rc,services - -# List one or more resources by their type and names. -$ kubectl get rc/web service/frontend pods/web-pod-13je7` -) - -// NewCmdGet creates a command object for the generic "get" action, which -// retrieves one or more resources from a server. -func NewCmdGet(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &GetOptions{} - - // retrieve a list of handled resources from printer as valid args - validArgs := []string{} - p, err := f.Printer(nil, false, false, false, false, false, false, []string{}) - cmdutil.CheckErr(err) - if p != nil { - validArgs = p.HandledResources() - } - - cmd := &cobra.Command{ - Use: "get [(-o|--output=)json|yaml|wide|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=...] (TYPE [NAME | -l label] | TYPE/NAME ...) [flags]", - Short: "Display one or many resources", - Long: get_long, - Example: get_example, - Run: func(cmd *cobra.Command, args []string) { - err := RunGet(f, out, cmd, args, options) - cmdutil.CheckErr(err) - }, - ValidArgs: validArgs, - } - cmdutil.AddPrinterFlags(cmd) - cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") - cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes.") - cmd.Flags().Bool("watch-only", false, "Watch for changes to the requested object(s), without listing/getting first.") - cmd.Flags().Bool("all-namespaces", false, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") - cmd.Flags().StringSliceP("label-columns", "L", []string{}, "Accepts a comma separated list of labels that are going to be presented as columns. Names are case-sensitive. You can also use multiple flag statements like -L label1 -L label2...") - cmd.Flags().Bool("export", false, "If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.") - usage := "Filename, directory, or URL to a file identifying the resource to get from a server." - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) - return cmd -} - -// RunGet implements the generic Get command -// TODO: convert all direct flag accessors to a struct and pass that instead of cmd -func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *GetOptions) error { - selector := cmdutil.GetFlagString(cmd, "selector") - allNamespaces := cmdutil.GetFlagBool(cmd, "all-namespaces") - mapper, typer := f.Object() - - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - if allNamespaces { - enforceNamespace = false - } - - if len(args) == 0 && len(options.Filenames) == 0 { - fmt.Fprint(out, "You must specify the type of resource to get. ", valid_resources) - return cmdutil.UsageError(cmd, "Required resource not specified.") - } - - // always show resources when getting by name or filename - argsHasNames, err := resource.HasNames(args) - if err != nil { - return err - } - if len(options.Filenames) > 0 || argsHasNames { - cmd.Flag("show-all").Value.Set("true") - } - export := cmdutil.GetFlagBool(cmd, "export") - - // handle watch separately since we cannot watch multiple resource types - isWatch, isWatchOnly := cmdutil.GetFlagBool(cmd, "watch"), cmdutil.GetFlagBool(cmd, "watch-only") - if isWatch || isWatchOnly { - r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). - FilenameParam(enforceNamespace, options.Filenames...). - SelectorParam(selector). - ExportParam(export). - ResourceTypeOrNameArgs(true, args...). - SingleResourceType(). - Latest(). - Do() - err := r.Err() - if err != nil { - return err - } - infos, err := r.Infos() - if err != nil { - return err - } - if len(infos) != 1 { - return fmt.Errorf("watch is only supported on individual resources and resource collections - %d resources were found", len(infos)) - } - info := infos[0] - mapping := info.ResourceMapping() - printer, err := f.PrinterForMapping(cmd, mapping, allNamespaces) - if err != nil { - return err - } - - obj, err := r.Object() - if err != nil { - return err - } - rv, err := mapping.MetadataAccessor.ResourceVersion(obj) - if err != nil { - return err - } - - // print the current object - if !isWatchOnly { - if err := printer.PrintObj(obj, out); err != nil { - return fmt.Errorf("unable to output the provided object: %v", err) - } - } - - // print watched changes - w, err := r.Watch(rv) - if err != nil { - return err - } - - kubectl.WatchLoop(w, func(e watch.Event) error { - return printer.PrintObj(e.Object, out) - }) - return nil - } - - b := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). - FilenameParam(enforceNamespace, options.Filenames...). - SelectorParam(selector). - ExportParam(export). - ResourceTypeOrNameArgs(true, args...). - ContinueOnError(). - Latest() - printer, generic, err := cmdutil.PrinterForCommand(cmd) - if err != nil { - return err - } - - if generic { - clientConfig, err := f.ClientConfig() - if err != nil { - return err - } - - singular := false - r := b.Flatten().Do() - infos, err := r.IntoSingular(&singular).Infos() - if err != nil { - return err - } - - // the outermost object will be converted to the output-version, but inner - // objects can use their mappings - version, err := cmdutil.OutputVersion(cmd, clientConfig.GroupVersion) - if err != nil { - return err - } - obj, err := resource.AsVersionedObject(infos, !singular, version.String(), f.JSONEncoder()) - if err != nil { - return err - } - - return printer.PrintObj(obj, out) - } - - infos, err := b.Flatten().Do().Infos() - if err != nil { - return err - } - objs := make([]runtime.Object, len(infos)) - for ix := range infos { - objs[ix] = infos[ix].Object - } - - sorting, err := cmd.Flags().GetString("sort-by") - var sorter *kubectl.RuntimeSort - if err == nil && len(sorting) > 0 && len(objs) > 1 { - // TODO: questionable - if sorter, err = kubectl.SortObjects(f.Decoder(true), objs, sorting); err != nil { - return err - } - } - - // use the default printer for each object - printer = nil - var lastMapping *meta.RESTMapping - w := kubectl.GetNewTabWriter(out) - defer w.Flush() - - for ix := range objs { - var mapping *meta.RESTMapping - if sorter != nil { - mapping = infos[sorter.OriginalPosition(ix)].Mapping - } else { - mapping = infos[ix].Mapping - } - if printer == nil || lastMapping == nil || mapping == nil || mapping.Resource != lastMapping.Resource { - printer, err = f.PrinterForMapping(cmd, mapping, allNamespaces) - if err != nil { - return err - } - lastMapping = mapping - } - if _, found := printer.(*kubectl.HumanReadablePrinter); found { - if err := printer.PrintObj(objs[ix], w); err != nil { - return err - } - continue - } - if err := printer.PrintObj(objs[ix], w); err != nil { - return err - } - } - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/get_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/get_test.go deleted file mode 100644 index 4d4ac5e18..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/get_test.go +++ /dev/null @@ -1,817 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - encjson "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" - "reflect" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/testapi" - apitesting "k8s.io/kubernetes/pkg/api/testing" - "k8s.io/kubernetes/pkg/api/unversioned" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/fake" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util" - "k8s.io/kubernetes/pkg/watch" - "k8s.io/kubernetes/pkg/watch/json" -) - -func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList) { - pods := &api.PodList{ - ListMeta: unversioned.ListMeta{ - ResourceVersion: "15", - }, - Items: []api.Pod{ - { - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"}, - Spec: apitesting.DeepEqualSafePodSpec(), - }, - { - ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"}, - Spec: apitesting.DeepEqualSafePodSpec(), - }, - }, - } - svc := &api.ServiceList{ - ListMeta: unversioned.ListMeta{ - ResourceVersion: "16", - }, - Items: []api.Service{ - { - ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, - Spec: api.ServiceSpec{ - SessionAffinity: "None", - Type: api.ServiceTypeClusterIP, - }, - }, - }, - } - rc := &api.ReplicationControllerList{ - ListMeta: unversioned.ListMeta{ - ResourceVersion: "17", - }, - Items: []api.ReplicationController{ - { - ObjectMeta: api.ObjectMeta{Name: "rc1", Namespace: "test", ResourceVersion: "18"}, - Spec: api.ReplicationControllerSpec{ - Replicas: 1, - }, - }, - }, - } - return pods, svc, rc -} - -func testComponentStatusData() *api.ComponentStatusList { - good := api.ComponentStatus{ - Conditions: []api.ComponentCondition{ - {Type: api.ComponentHealthy, Status: api.ConditionTrue, Message: "ok"}, - }, - ObjectMeta: api.ObjectMeta{Name: "servergood"}, - } - - bad := api.ComponentStatus{ - Conditions: []api.ComponentCondition{ - {Type: api.ComponentHealthy, Status: api.ConditionFalse, Message: "", Error: "bad status: 500"}, - }, - ObjectMeta: api.ObjectMeta{Name: "serverbad"}, - } - - unknown := api.ComponentStatus{ - Conditions: []api.ComponentCondition{ - {Type: api.ComponentHealthy, Status: api.ConditionUnknown, Message: "", Error: "fizzbuzz error"}, - }, - ObjectMeta: api.ObjectMeta{Name: "serverunknown"}, - } - - return &api.ComponentStatusList{ - Items: []api.ComponentStatus{good, bad, unknown}, - } -} - -// Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get. -func TestGetUnknownSchemaObject(t *testing.T) { - f, tf, codec := NewTestFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Resp: &http.Response{StatusCode: 200, Body: objBody(codec, &internalType{Name: "foo"})}, - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - cmd.Run(cmd, []string{"type", "foo"}) - - expected := &internalType{Name: "foo"} - actual := tf.Printer.(*testPrinter).Objects[0] - if !reflect.DeepEqual(expected, actual) { - t.Errorf("unexpected object: %#v", actual) - } - if buf.String() != fmt.Sprintf("%#v", expected) { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -// Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get. -// Because api.List is part of the Kube API, resource.Builder has to perform a conversion on -// api.Scheme, which may not have access to all objects, and not all objects are at the same -// internal versioning scheme. This test verifies that two isolated schemes (Test, and api.Scheme) -// can be conjoined into a single output object. -// -// The expected behavior of the `kubectl get` command is: -// 1. objects using unrecognized schemes will always be returned using that scheme/version, "unlikelyversion" in this test; -// 2. if the specified output-version is a recognized, valid Scheme, then the list should use that scheme, and otherwise it will default to the client version, testapi.Default.GroupVersion().String() in this test; -// 3a. if the specified output-version is a recognized, valid Scheme, in which the requested object (replicationcontroller) can be represented, then the object should be returned using that version; -// 3b. otherwise if the specified output-version is unrecognized, but the requested object (replicationcontroller) is recognized by the client's codec, then it will be converted to the client version, testapi.Default.GroupVersion().String() in this test. -func TestGetUnknownSchemaObjectListGeneric(t *testing.T) { - testCases := map[string]struct { - outputVersion string - listVersion string - testtypeVersion string - rcVersion string - }{ - "handles specific version": { - outputVersion: testapi.Default.GroupVersion().String(), - listVersion: testapi.Default.GroupVersion().String(), - testtypeVersion: unlikelyGV.String(), - rcVersion: testapi.Default.GroupVersion().String(), - }, - "handles second specific version": { - outputVersion: "unlikely.group/unlikelyversion", - listVersion: testapi.Default.GroupVersion().String(), - testtypeVersion: unlikelyGV.String(), - rcVersion: testapi.Default.GroupVersion().String(), // see expected behavior 3b - }, - "handles common version": { - outputVersion: testapi.Default.GroupVersion().String(), - listVersion: testapi.Default.GroupVersion().String(), - testtypeVersion: unlikelyGV.String(), - rcVersion: testapi.Default.GroupVersion().String(), - }, - } - for k, test := range testCases { - apiCodec := testapi.Default.Codec() - regularClient := &fake.RESTClient{ - Codec: apiCodec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - return &http.Response{StatusCode: 200, Body: objBody(apiCodec, &api.ReplicationController{ObjectMeta: api.ObjectMeta{Name: "foo"}})}, nil - }), - } - - f, tf, codec := NewMixedFactory(regularClient) - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - return &http.Response{StatusCode: 200, Body: objBody(codec, &internalType{Name: "foo"})}, nil - }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - cmd.Flags().Set("output", "json") - - cmd.Flags().Set("output-version", test.outputVersion) - err := RunGet(f, buf, cmd, []string{"type/foo", "replicationcontrollers/foo"}, &GetOptions{}) - if err != nil { - t.Errorf("%s: unexpected error: %v", k, err) - continue - } - out := make(map[string]interface{}) - if err := encjson.Unmarshal(buf.Bytes(), &out); err != nil { - t.Errorf("%s: unexpected error: %v\n%s", k, err, buf.String()) - continue - } - if out["apiVersion"] != test.listVersion { - t.Errorf("%s: unexpected list: %#v", k, out) - } - arr := out["items"].([]interface{}) - if arr[0].(map[string]interface{})["apiVersion"] != test.testtypeVersion { - t.Errorf("%s: unexpected list: %#v", k, out) - } - if arr[1].(map[string]interface{})["apiVersion"] != test.rcVersion { - t.Errorf("%s: unexpected list: %#v", k, out) - } - } -} - -// Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get. -func TestGetSchemaObject(t *testing.T) { - f, tf, _ := NewTestFactory() - tf.Mapper = testapi.Default.RESTMapper() - tf.Typer = api.Scheme - codec := testapi.Default.Codec() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Resp: &http.Response{StatusCode: 200, Body: objBody(codec, &api.ReplicationController{ObjectMeta: api.ObjectMeta{Name: "foo"}})}, - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: &unversioned.GroupVersion{Version: "v1"}}} - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdGet(f, buf) - cmd.Run(cmd, []string{"replicationcontrollers", "foo"}) - - if !strings.Contains(buf.String(), "\"foo\"") { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestGetObjects(t *testing.T) { - pods, _, _ := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Resp: &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - cmd.Run(cmd, []string{"pods", "foo"}) - - expected := []runtime.Object{&pods.Items[0]} - actual := tf.Printer.(*testPrinter).Objects - if !reflect.DeepEqual(expected, actual) { - t.Errorf("unexpected object: %#v", actual) - } - if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") - } -} - -func TestGetObjectsIdentifiedByFile(t *testing.T) { - pods, _, _ := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Resp: &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - cmd.Flags().Set("filename", "../../../examples/cassandra/cassandra.yaml") - cmd.Run(cmd, []string{}) - - expected := []runtime.Object{&pods.Items[0]} - actual := tf.Printer.(*testPrinter).Objects - if !reflect.DeepEqual(expected, actual) { - t.Errorf("unexpected object: %#v", actual) - } - if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") - } -} - -func TestGetListObjects(t *testing.T) { - pods, _, _ := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Resp: &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - cmd.Run(cmd, []string{"pods"}) - - expected, err := extractResourceList([]runtime.Object{pods}) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - actual := tf.Printer.(*testPrinter).Objects - if !reflect.DeepEqual(expected, actual) { - t.Errorf("unexpected object: expected %#v, got %#v", expected, actual) - } - if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") - } -} - -func extractResourceList(objs []runtime.Object) ([]runtime.Object, error) { - finalObjs := []runtime.Object{} - for _, obj := range objs { - items, err := meta.ExtractList(obj) - if err != nil { - return nil, err - } - for _, item := range items { - finalObjs = append(finalObjs, item) - } - } - return finalObjs, nil -} - -func TestGetAllListObjects(t *testing.T) { - pods, _, _ := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Resp: &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - cmd.Flags().Set("show-all", "true") - cmd.Run(cmd, []string{"pods"}) - - expected, err := extractResourceList([]runtime.Object{pods}) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - actual := tf.Printer.(*testPrinter).Objects - if !reflect.DeepEqual(expected, actual) { - t.Errorf("unexpected object: %#v %#v", expected, actual) - } - if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") - } -} - -func TestGetListComponentStatus(t *testing.T) { - statuses := testComponentStatusData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Resp: &http.Response{StatusCode: 200, Body: objBody(codec, statuses)}, - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - cmd.Run(cmd, []string{"componentstatuses"}) - - expected, err := extractResourceList([]runtime.Object{statuses}) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - actual := tf.Printer.(*testPrinter).Objects - if !reflect.DeepEqual(expected, actual) { - t.Errorf("unexpected object: expected %#v, got %#v", expected, actual) - } - if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") - } -} - -func TestGetMultipleTypeObjects(t *testing.T) { - pods, svc, _ := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch req.URL.Path { - case "/namespaces/test/pods": - return &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, nil - case "/namespaces/test/services": - return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - cmd.Run(cmd, []string{"pods,services"}) - - expected, err := extractResourceList([]runtime.Object{pods, svc}) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - actual := tf.Printer.(*testPrinter).Objects - if !reflect.DeepEqual(expected, actual) { - t.Errorf("unexpected object: %#v", actual) - } - if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") - } -} - -func TestGetMultipleTypeObjectsAsList(t *testing.T) { - pods, svc, _ := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch req.URL.Path { - case "/namespaces/test/pods": - return &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, nil - case "/namespaces/test/services": - return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - - cmd.Flags().Set("output", "json") - cmd.Run(cmd, []string{"pods,services"}) - - if tf.Printer.(*testPrinter).Objects != nil { - t.Errorf("unexpected print to default printer") - } - - out, err := runtime.Decode(codec, buf.Bytes()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - list, err := meta.ExtractList(out) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if errs := runtime.DecodeList(list, codec); len(errs) > 0 { - t.Fatalf("unexpected error: %v", errs) - } - if err := meta.SetList(out, list); err != nil { - t.Fatalf("unexpected error: %v", err) - } - - expected := &api.List{ - Items: []runtime.Object{ - &pods.Items[0], - &pods.Items[1], - &svc.Items[0], - }, - } - if !reflect.DeepEqual(expected, out) { - t.Errorf("unexpected output: %#v", out) - } -} - -func TestGetMultipleTypeObjectsWithSelector(t *testing.T) { - pods, svc, _ := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - if req.URL.Query().Get(unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String())) != "a=b" { - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - } - switch req.URL.Path { - case "/namespaces/test/pods": - return &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, nil - case "/namespaces/test/services": - return &http.Response{StatusCode: 200, Body: objBody(codec, svc)}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - - cmd.Flags().Set("selector", "a=b") - cmd.Run(cmd, []string{"pods,services"}) - - expected, err := extractResourceList([]runtime.Object{pods, svc}) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - actual := tf.Printer.(*testPrinter).Objects - if !reflect.DeepEqual(expected, actual) { - t.Errorf("unexpected object: %#v", actual) - } - if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") - } -} - -func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) { - _, svc, _ := testData() - node := &api.Node{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.NodeSpec{ - ExternalID: "ext", - }, - } - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch req.URL.Path { - case "/nodes/foo": - return &http.Response{StatusCode: 200, Body: objBody(codec, node)}, nil - case "/namespaces/test/services/bar": - return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - - cmd.Run(cmd, []string{"services/bar", "node/foo"}) - - expected := []runtime.Object{&svc.Items[0], node} - actual := tf.Printer.(*testPrinter).Objects - if !api.Semantic.DeepEqual(expected, actual) { - t.Errorf("unexpected object: %s", util.ObjectDiff(expected, actual)) - } - if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") - } -} - -func TestGetByNameForcesFlag(t *testing.T) { - pods, _, _ := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Resp: &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - cmd.Run(cmd, []string{"pods", "foo"}) - - showAllFlag, _ := cmd.Flags().GetBool("show-all") - if !showAllFlag { - t.Errorf("expected showAll to be true when getting resource by name") - } -} - -func watchTestData() ([]api.Pod, []watch.Event) { - pods := []api.Pod{ - { - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "test", - ResourceVersion: "10", - }, - Spec: apitesting.DeepEqualSafePodSpec(), - }, - } - events := []watch.Event{ - { - Type: watch.Modified, - Object: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "test", - ResourceVersion: "11", - }, - Spec: apitesting.DeepEqualSafePodSpec(), - }, - }, - { - Type: watch.Deleted, - Object: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: "test", - ResourceVersion: "12", - }, - Spec: apitesting.DeepEqualSafePodSpec(), - }, - }, - } - return pods, events -} - -func TestWatchSelector(t *testing.T) { - pods, events := watchTestData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - if req.URL.Query().Get(unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String())) != "a=b" { - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - } - switch req.URL.Path { - case "/namespaces/test/pods": - return &http.Response{StatusCode: 200, Body: objBody(codec, &api.PodList{Items: pods})}, nil - case "/watch/namespaces/test/pods": - return &http.Response{StatusCode: 200, Body: watchBody(codec, events)}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - - cmd.Flags().Set("watch", "true") - cmd.Flags().Set("selector", "a=b") - cmd.Run(cmd, []string{"pods"}) - - expected := []runtime.Object{&api.PodList{Items: pods}, events[0].Object, events[1].Object} - actual := tf.Printer.(*testPrinter).Objects - if !reflect.DeepEqual(expected, actual) { - t.Errorf("unexpected object:\nExpected: %#v\n\nGot: %#v\n\n", expected[0], actual[0]) - } - if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") - } -} - -func TestWatchResource(t *testing.T) { - pods, events := watchTestData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch req.URL.Path { - case "/namespaces/test/pods/foo": - return &http.Response{StatusCode: 200, Body: objBody(codec, &pods[0])}, nil - case "/watch/namespaces/test/pods/foo": - return &http.Response{StatusCode: 200, Body: watchBody(codec, events)}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - - cmd.Flags().Set("watch", "true") - cmd.Run(cmd, []string{"pods", "foo"}) - - expected := []runtime.Object{&pods[0], events[0].Object, events[1].Object} - actual := tf.Printer.(*testPrinter).Objects - if !reflect.DeepEqual(expected, actual) { - t.Errorf("unexpected object:\nExpected: %#v\n\nGot: %#v\n\n", expected, actual) - } - if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") - } -} - -func TestWatchResourceIdentifiedByFile(t *testing.T) { - pods, events := watchTestData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch req.URL.Path { - case "/namespaces/test/pods/cassandra": - return &http.Response{StatusCode: 200, Body: objBody(codec, &pods[0])}, nil - case "/watch/namespaces/test/pods/cassandra": - return &http.Response{StatusCode: 200, Body: watchBody(codec, events)}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - - cmd.Flags().Set("watch", "true") - cmd.Flags().Set("filename", "../../../examples/cassandra/cassandra.yaml") - cmd.Run(cmd, []string{}) - - expected := []runtime.Object{&pods[0], events[0].Object, events[1].Object} - actual := tf.Printer.(*testPrinter).Objects - if !reflect.DeepEqual(expected, actual) { - t.Errorf("expected object: %#v unexpected object: %#v", expected, actual) - } - - if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") - } -} - -func TestWatchOnlyResource(t *testing.T) { - pods, events := watchTestData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch req.URL.Path { - case "/namespaces/test/pods/foo": - return &http.Response{StatusCode: 200, Body: objBody(codec, &pods[0])}, nil - case "/watch/namespaces/test/pods/foo": - return &http.Response{StatusCode: 200, Body: watchBody(codec, events)}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdGet(f, buf) - cmd.SetOutput(buf) - - cmd.Flags().Set("watch-only", "true") - cmd.Run(cmd, []string{"pods", "foo"}) - - expected := []runtime.Object{events[0].Object, events[1].Object} - actual := tf.Printer.(*testPrinter).Objects - if !reflect.DeepEqual(expected, actual) { - t.Errorf("unexpected object: %#v", actual) - } - if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") - } -} - -func watchBody(codec runtime.Codec, events []watch.Event) io.ReadCloser { - buf := bytes.NewBuffer([]byte{}) - enc := json.NewEncoder(buf, codec) - for i := range events { - enc.Encode(&events[i]) - } - return ioutil.NopCloser(buf) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/label.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/label.go deleted file mode 100644 index 39c401d52..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/label.go +++ /dev/null @@ -1,297 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "encoding/json" - "fmt" - "io" - "reflect" - "strings" - - "github.com/golang/glog" - "github.com/spf13/cobra" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/runtime" - utilerrors "k8s.io/kubernetes/pkg/util/errors" - "k8s.io/kubernetes/pkg/util/strategicpatch" - "k8s.io/kubernetes/pkg/util/validation" -) - -// LabelOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of -// referencing the cmd.Flags() -type LabelOptions struct { - Filenames []string -} - -const ( - label_long = `Update the labels on a resource. - -A label must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to %[1]d characters. -If --overwrite is true, then existing labels can be overwritten, otherwise attempting to overwrite a label will result in an error. -If --resource-version is specified, then updates will use this resource version, otherwise the existing resource-version will be used.` - label_example = `# Update pod 'foo' with the label 'unhealthy' and the value 'true'. -$ kubectl label pods foo unhealthy=true - -# Update pod 'foo' with the label 'status' and the value 'unhealthy', overwriting any existing value. -$ kubectl label --overwrite pods foo status=unhealthy - -# Update all pods in the namespace -$ kubectl label pods --all status=unhealthy - -# Update a pod identified by the type and name in "pod.json" -$ kubectl label -f pod.json status=unhealthy - -# Update pod 'foo' only if the resource is unchanged from version 1. -$ kubectl label pods foo status=unhealthy --resource-version=1 - -# Update pod 'foo' by removing a label named 'bar' if it exists. -# Does not require the --overwrite flag. -$ kubectl label pods foo bar-` -) - -func NewCmdLabel(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &LabelOptions{} - - // retrieve a list of handled resources from printer as valid args - validArgs := []string{} - p, err := f.Printer(nil, false, false, false, false, false, false, []string{}) - cmdutil.CheckErr(err) - if p != nil { - validArgs = p.HandledResources() - } - - cmd := &cobra.Command{ - Use: "label [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]", - Short: "Update the labels on a resource", - Long: fmt.Sprintf(label_long, validation.LabelValueMaxLength), - Example: label_example, - Run: func(cmd *cobra.Command, args []string) { - err := RunLabel(f, out, cmd, args, options) - cmdutil.CheckErr(err) - }, - ValidArgs: validArgs, - } - cmdutil.AddPrinterFlags(cmd) - cmd.Flags().Bool("overwrite", false, "If true, allow labels to be overwritten, otherwise reject label updates that overwrite existing labels.") - cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") - cmd.Flags().Bool("all", false, "select all resources in the namespace of the specified resource types") - cmd.Flags().String("resource-version", "", "If non-empty, the labels update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource.") - usage := "Filename, directory, or URL to a file identifying the resource to update the labels" - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) - cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it.") - cmdutil.AddRecordFlag(cmd) - - return cmd -} - -func validateNoOverwrites(meta *api.ObjectMeta, labels map[string]string) error { - allErrs := []error{} - for key := range labels { - if value, found := meta.Labels[key]; found { - allErrs = append(allErrs, fmt.Errorf("'%s' already has a value (%s), and --overwrite is false", key, value)) - } - } - return utilerrors.NewAggregate(allErrs) -} - -func parseLabels(spec []string) (map[string]string, []string, error) { - labels := map[string]string{} - var remove []string - for _, labelSpec := range spec { - if strings.Index(labelSpec, "=") != -1 { - parts := strings.Split(labelSpec, "=") - if len(parts) != 2 || len(parts[1]) == 0 || !validation.IsValidLabelValue(parts[1]) { - return nil, nil, fmt.Errorf("invalid label spec: %v", labelSpec) - } - labels[parts[0]] = parts[1] - } else if strings.HasSuffix(labelSpec, "-") { - remove = append(remove, labelSpec[:len(labelSpec)-1]) - } else { - return nil, nil, fmt.Errorf("unknown label spec: %v", labelSpec) - } - } - for _, removeLabel := range remove { - if _, found := labels[removeLabel]; found { - return nil, nil, fmt.Errorf("can not both modify and remove a label in the same command") - } - } - return labels, remove, nil -} - -func labelFunc(obj runtime.Object, overwrite bool, resourceVersion string, labels map[string]string, remove []string) error { - meta, err := api.ObjectMetaFor(obj) - if err != nil { - return err - } - if !overwrite { - if err := validateNoOverwrites(meta, labels); err != nil { - return err - } - } - - if meta.Labels == nil { - meta.Labels = make(map[string]string) - } - - for key, value := range labels { - meta.Labels[key] = value - } - for _, label := range remove { - delete(meta.Labels, label) - } - - if len(resourceVersion) != 0 { - meta.ResourceVersion = resourceVersion - } - return nil -} - -func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *LabelOptions) error { - resources, labelArgs := []string{}, []string{} - first := true - for _, s := range args { - isLabel := strings.Contains(s, "=") || strings.HasSuffix(s, "-") - switch { - case first && isLabel: - first = false - fallthrough - case !first && isLabel: - labelArgs = append(labelArgs, s) - case first && !isLabel: - resources = append(resources, s) - case !first && !isLabel: - return cmdutil.UsageError(cmd, "all resources must be specified before label changes: %s", s) - } - } - if len(resources) < 1 && len(options.Filenames) == 0 { - return cmdutil.UsageError(cmd, "one or more resources must be specified as or /") - } - if len(labelArgs) < 1 { - return cmdutil.UsageError(cmd, "at least one label update is required") - } - - selector := cmdutil.GetFlagString(cmd, "selector") - all := cmdutil.GetFlagBool(cmd, "all") - overwrite := cmdutil.GetFlagBool(cmd, "overwrite") - resourceVersion := cmdutil.GetFlagString(cmd, "resource-version") - - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - lbls, remove, err := parseLabels(labelArgs) - if err != nil { - return cmdutil.UsageError(cmd, err.Error()) - } - mapper, typer := f.Object() - b := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - ContinueOnError(). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). - SelectorParam(selector). - ResourceTypeOrNameArgs(all, resources...). - Flatten(). - Latest() - - one := false - r := b.Do().IntoSingular(&one) - if err := r.Err(); err != nil { - return err - } - - // only apply resource version locking on a single resource - if !one && len(resourceVersion) > 0 { - return cmdutil.UsageError(cmd, "--resource-version may only be used with a single resource") - } - - // TODO: support bulk generic output a la Get - return r.Visit(func(info *resource.Info, err error) error { - if err != nil { - return err - } - - var outputObj runtime.Object - dataChangeMsg := "not labeled" - if cmdutil.GetFlagBool(cmd, "dry-run") { - err = labelFunc(info.Object, overwrite, resourceVersion, lbls, remove) - if err != nil { - return err - } - outputObj = info.Object - } else { - name, namespace, obj := info.Name, info.Namespace, info.Object - oldData, err := json.Marshal(obj) - if err != nil { - return err - } - meta, err := api.ObjectMetaFor(obj) - for _, label := range remove { - if _, ok := meta.Labels[label]; !ok { - fmt.Fprintf(out, "label %q not found.\n", label) - } - } - - if err := labelFunc(obj, overwrite, resourceVersion, lbls, remove); err != nil { - return err - } - if cmdutil.ShouldRecord(cmd, info) { - if err := cmdutil.RecordChangeCause(obj, f.Command()); err != nil { - return err - } - } - newData, err := json.Marshal(obj) - if err != nil { - return err - } - if !reflect.DeepEqual(oldData, newData) { - dataChangeMsg = "labeled" - } - patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, obj) - createdPatch := err == nil - if err != nil { - glog.V(2).Infof("couldn't compute patch: %v", err) - } - - mapping := info.ResourceMapping() - client, err := f.ClientForMapping(mapping) - if err != nil { - return err - } - helper := resource.NewHelper(client, mapping) - - if createdPatch { - outputObj, err = helper.Patch(namespace, name, api.StrategicMergePatchType, patchBytes) - } else { - outputObj, err = helper.Replace(namespace, name, false, obj) - } - if err != nil { - return err - } - } - outputFormat := cmdutil.GetFlagString(cmd, "output") - if outputFormat != "" { - return f.PrintObject(cmd, outputObj, out) - } - cmdutil.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, dataChangeMsg) - return nil - }) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/label_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/label_test.go deleted file mode 100644 index 5267bc3cc..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/label_test.go +++ /dev/null @@ -1,418 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "net/http" - "reflect" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/fake" - "k8s.io/kubernetes/pkg/runtime" -) - -func TestValidateLabels(t *testing.T) { - tests := []struct { - meta *api.ObjectMeta - labels map[string]string - expectErr bool - test string - }{ - { - meta: &api.ObjectMeta{ - Labels: map[string]string{ - "a": "b", - "c": "d", - }, - }, - labels: map[string]string{ - "a": "c", - "d": "b", - }, - test: "one shared", - expectErr: true, - }, - { - meta: &api.ObjectMeta{ - Labels: map[string]string{ - "a": "b", - "c": "d", - }, - }, - labels: map[string]string{ - "b": "d", - "c": "a", - }, - test: "second shared", - expectErr: true, - }, - { - meta: &api.ObjectMeta{ - Labels: map[string]string{ - "a": "b", - "c": "d", - }, - }, - labels: map[string]string{ - "b": "a", - "d": "c", - }, - test: "no overlap", - }, - { - meta: &api.ObjectMeta{}, - labels: map[string]string{ - "b": "a", - "d": "c", - }, - test: "no labels", - }, - } - for _, test := range tests { - err := validateNoOverwrites(test.meta, test.labels) - if test.expectErr && err == nil { - t.Errorf("%s: unexpected non-error", test.test) - } - if !test.expectErr && err != nil { - t.Errorf("%s: unexpected error: %v", test.test, err) - } - } -} - -func TestParseLabels(t *testing.T) { - tests := []struct { - labels []string - expected map[string]string - expectedRemove []string - expectErr bool - }{ - { - labels: []string{"a=b", "c=d"}, - expected: map[string]string{"a": "b", "c": "d"}, - }, - { - labels: []string{}, - expected: map[string]string{}, - }, - { - labels: []string{"a=b", "c=d", "e-"}, - expected: map[string]string{"a": "b", "c": "d"}, - expectedRemove: []string{"e"}, - }, - { - labels: []string{"ab", "c=d"}, - expectErr: true, - }, - { - labels: []string{"a=b", "c=d", "a-"}, - expectErr: true, - }, - { - labels: []string{"a="}, - expectErr: true, - }, - { - labels: []string{"a=%^$"}, - expectErr: true, - }, - } - for _, test := range tests { - labels, remove, err := parseLabels(test.labels) - if test.expectErr && err == nil { - t.Errorf("unexpected non-error: %v", test) - } - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v %v", err, test) - } - if !reflect.DeepEqual(labels, test.expected) { - t.Errorf("expected: %v, got %v", test.expected, labels) - } - if !reflect.DeepEqual(remove, test.expectedRemove) { - t.Errorf("expected: %v, got %v", test.expectedRemove, remove) - } - } -} - -func TestLabelFunc(t *testing.T) { - tests := []struct { - obj runtime.Object - overwrite bool - version string - labels map[string]string - remove []string - expected runtime.Object - expectErr bool - }{ - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"a": "b"}, - }, - }, - labels: map[string]string{"a": "b"}, - expectErr: true, - }, - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"a": "b"}, - }, - }, - labels: map[string]string{"a": "c"}, - overwrite: true, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"a": "c"}, - }, - }, - }, - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"a": "b"}, - }, - }, - labels: map[string]string{"c": "d"}, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"a": "b", "c": "d"}, - }, - }, - }, - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"a": "b"}, - }, - }, - labels: map[string]string{"c": "d"}, - version: "2", - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"a": "b", "c": "d"}, - ResourceVersion: "2", - }, - }, - }, - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"a": "b"}, - }, - }, - labels: map[string]string{}, - remove: []string{"a"}, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{}, - }, - }, - }, - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"a": "b", "c": "d"}, - }, - }, - labels: map[string]string{"e": "f"}, - remove: []string{"a"}, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "c": "d", - "e": "f", - }, - }, - }, - }, - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{}, - }, - labels: map[string]string{"a": "b"}, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"a": "b"}, - }, - }, - }, - } - for _, test := range tests { - err := labelFunc(test.obj, test.overwrite, test.version, test.labels, test.remove) - if test.expectErr { - if err == nil { - t.Errorf("unexpected non-error: %v", test) - } - continue - } - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v %v", err, test) - } - if !reflect.DeepEqual(test.obj, test.expected) { - t.Errorf("expected: %v, got %v", test.expected, test.obj) - } - } -} - -func TestLabelErrors(t *testing.T) { - testCases := map[string]struct { - args []string - flags map[string]string - errFn func(error) bool - }{ - "no args": { - args: []string{}, - errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") }, - }, - "not enough labels": { - args: []string{"pods"}, - errFn: func(err error) bool { return strings.Contains(err.Error(), "at least one label update is required") }, - }, - "no resources": { - args: []string{"pods-"}, - errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") }, - }, - "no resources 2": { - args: []string{"pods=bar"}, - errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") }, - }, - } - - for k, testCase := range testCases { - f, tf, _ := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} - - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdLabel(f, buf) - cmd.SetOutput(buf) - - for k, v := range testCase.flags { - cmd.Flags().Set(k, v) - } - err := RunLabel(f, buf, cmd, testCase.args, &LabelOptions{}) - if !testCase.errFn(err) { - t.Errorf("%s: unexpected error: %v", k, err) - continue - } - if tf.Printer.(*testPrinter).Objects != nil { - t.Errorf("unexpected print to default printer") - } - if buf.Len() > 0 { - t.Errorf("buffer should be empty: %s", string(buf.Bytes())) - } - } -} - -func TestLabelForResourceFromFile(t *testing.T) { - pods, _, _ := testData() - f, tf, codec := NewAPIFactory() - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch req.Method { - case "GET": - switch req.URL.Path { - case "/namespaces/test/pods/cassandra": - return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - case "PATCH": - switch req.URL.Path { - case "/namespaces/test/pods/cassandra": - return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - default: - t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} - - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdLabel(f, buf) - options := &LabelOptions{ - Filenames: []string{"../../../examples/cassandra/cassandra.yaml"}, - } - - err := RunLabel(f, buf, cmd, []string{"a=b"}, options) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !strings.Contains(buf.String(), "labeled") { - t.Errorf("did not set labels: %s", buf.String()) - } -} - -func TestLabelMultipleObjects(t *testing.T) { - pods, _, _ := testData() - f, tf, codec := NewAPIFactory() - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch req.Method { - case "GET": - switch req.URL.Path { - case "/namespaces/test/pods": - return &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - case "PATCH": - switch req.URL.Path { - case "/namespaces/test/pods/foo": - return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil - case "/namespaces/test/pods/bar": - return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[1])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - default: - t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} - - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdLabel(f, buf) - cmd.Flags().Set("all", "true") - - if err := RunLabel(f, buf, cmd, []string{"pods", "a=b"}, &LabelOptions{}); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if strings.Count(buf.String(), "labeled") != len(pods.Items) { - t.Errorf("not all labels are set: %s", buf.String()) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/logs.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/logs.go deleted file mode 100644 index 369d41d8a..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/logs.go +++ /dev/null @@ -1,206 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "errors" - "io" - "math" - "os" - "time" - - "github.com/spf13/cobra" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/validation" - client "k8s.io/kubernetes/pkg/client/unversioned" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/runtime" -) - -const ( - logs_example = `# Return snapshot logs from pod nginx with only one container -$ kubectl logs nginx - -# Return snapshot of previous terminated ruby container logs from pod web-1 -$ kubectl logs -p -c ruby web-1 - -# Begin streaming the logs of the ruby container in pod web-1 -$ kubectl logs -f -c ruby web-1 - -# Display only the most recent 20 lines of output in pod nginx -$ kubectl logs --tail=20 nginx - -# Show all logs from pod nginx written in the last hour -$ kubectl logs --since=1h nginx` -) - -type LogsOptions struct { - Namespace string - ResourceArg string - Options runtime.Object - - Mapper meta.RESTMapper - Typer runtime.ObjectTyper - ClientMapper resource.ClientMapper - Decoder runtime.Decoder - - LogsForObject func(object, options runtime.Object) (*client.Request, error) - - Out io.Writer -} - -// NewCmdLog creates a new pod logs command -func NewCmdLogs(f *cmdutil.Factory, out io.Writer) *cobra.Command { - o := &LogsOptions{} - cmd := &cobra.Command{ - Use: "logs [-f] [-p] POD [-c CONTAINER]", - Short: "Print the logs for a container in a pod.", - Long: "Print the logs for a container in a pod. If the pod has only one container, the container name is optional.", - Example: logs_example, - PreRun: func(cmd *cobra.Command, args []string) { - if len(os.Args) > 1 && os.Args[1] == "log" { - printDeprecationWarning("logs", "log") - } - }, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(o.Complete(f, out, cmd, args)) - if err := o.Validate(); err != nil { - cmdutil.CheckErr(cmdutil.UsageError(cmd, err.Error())) - } - _, err := o.RunLogs() - cmdutil.CheckErr(err) - }, - Aliases: []string{"log"}, - } - cmd.Flags().BoolP("follow", "f", false, "Specify if the logs should be streamed.") - cmd.Flags().Bool("timestamps", false, "Include timestamps on each line in the log output") - cmd.Flags().Int64("limit-bytes", 0, "Maximum bytes of logs to return. Defaults to no limit.") - cmd.Flags().BoolP("previous", "p", false, "If true, print the logs for the previous instance of the container in a pod if it exists.") - cmd.Flags().Int64("tail", -1, "Lines of recent log file to display. Defaults to -1, showing all log lines.") - cmd.Flags().String("since-time", "", "Only return logs after a specific date (RFC3339). Defaults to all logs. Only one of since-time / since may be used.") - cmd.Flags().Duration("since", 0, "Only return logs newer than a relative duration like 5s, 2m, or 3h. Defaults to all logs. Only one of since-time / since may be used.") - cmd.Flags().StringP("container", "c", "", "Print the logs of this container") - - cmd.Flags().Bool("interactive", false, "If true, prompt the user for input when required.") - cmd.Flags().MarkDeprecated("interactive", "This flag is no longer respected and there is no replacement.") - return cmd -} - -func (o *LogsOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { - containerName := cmdutil.GetFlagString(cmd, "container") - switch len(args) { - case 0: - return cmdutil.UsageError(cmd, "POD is required for logs") - case 1: - o.ResourceArg = args[0] - case 2: - if cmd.Flag("container").Changed { - return cmdutil.UsageError(cmd, "only one of -c, [CONTAINER] arg is allowed") - } - o.ResourceArg = args[0] - containerName = args[1] - default: - return cmdutil.UsageError(cmd, "logs POD [-c CONTAINER]") - } - var err error - o.Namespace, _, err = f.DefaultNamespace() - if err != nil { - return err - } - - logOptions := &api.PodLogOptions{ - Container: containerName, - Follow: cmdutil.GetFlagBool(cmd, "follow"), - Previous: cmdutil.GetFlagBool(cmd, "previous"), - Timestamps: cmdutil.GetFlagBool(cmd, "timestamps"), - } - if sinceTime := cmdutil.GetFlagString(cmd, "since-time"); len(sinceTime) > 0 { - t, err := api.ParseRFC3339(sinceTime, unversioned.Now) - if err != nil { - return err - } - logOptions.SinceTime = &t - } - if limit := cmdutil.GetFlagInt64(cmd, "limit-bytes"); limit != 0 { - logOptions.LimitBytes = &limit - } - if tail := cmdutil.GetFlagInt64(cmd, "tail"); tail != -1 { - logOptions.TailLines = &tail - } - if sinceSeconds := cmdutil.GetFlagDuration(cmd, "since"); sinceSeconds != 0 { - // round up to the nearest second - sec := int64(math.Ceil(float64(sinceSeconds) / float64(time.Second))) - logOptions.SinceSeconds = &sec - } - o.Options = logOptions - - o.Mapper, o.Typer = f.Object() - o.Decoder = f.Decoder(true) - o.ClientMapper = resource.ClientMapperFunc(f.ClientForMapping) - o.LogsForObject = f.LogsForObject - - o.Out = out - - return nil -} - -func (o LogsOptions) Validate() error { - if len(o.ResourceArg) == 0 { - return errors.New("a pod must be specified") - } - logsOptions, ok := o.Options.(*api.PodLogOptions) - if !ok { - return errors.New("unexpected logs options object") - } - if errs := validation.ValidatePodLogOptions(logsOptions); len(errs) > 0 { - return errs.ToAggregate() - } - - return nil -} - -// RunLogs retrieves a pod log -func (o LogsOptions) RunLogs() (int64, error) { - infos, err := resource.NewBuilder(o.Mapper, o.Typer, o.ClientMapper, o.Decoder). - NamespaceParam(o.Namespace).DefaultNamespace(). - ResourceNames("pods", o.ResourceArg). - SingleResourceType(). - Do().Infos() - if err != nil { - return 0, err - } - if len(infos) != 1 { - return 0, errors.New("expected a resource") - } - info := infos[0] - - req, err := o.LogsForObject(info.Object, o.Options) - if err != nil { - return 0, err - } - - readCloser, err := req.Stream() - if err != nil { - return 0, err - } - defer readCloser.Close() - - return io.Copy(o.Out, readCloser) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/logs_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/logs_test.go deleted file mode 100644 index caa867031..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/logs_test.go +++ /dev/null @@ -1,141 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "io/ioutil" - "net/http" - "os" - "strings" - "testing" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/fake" -) - -func TestLog(t *testing.T) { - tests := []struct { - name, version, podPath, logPath, container string - pod *api.Pod - }{ - { - name: "v1 - pod log", - version: "v1", - podPath: "/namespaces/test/pods/foo", - logPath: "/api/v1/namespaces/test/pods/foo/log", - pod: testPod(), - }, - } - for _, test := range tests { - logContent := "test log content" - f, tf, codec := NewAPIFactory() - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == test.podPath && m == "GET": - body := objBody(codec, test.pod) - return &http.Response{StatusCode: 200, Body: body}, nil - case p == test.logPath && m == "GET": - body := ioutil.NopCloser(bytes.NewBufferString(logContent)) - return &http.Response{StatusCode: 200, Body: body}, nil - default: - // Ensures no GET is performed when deleting by name - t.Errorf("%s: unexpected request: %#v\n%#v", test.name, req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: &unversioned.GroupVersion{Version: test.version}}} - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdLogs(f, buf) - cmd.Flags().Set("namespace", "test") - cmd.Run(cmd, []string{"foo"}) - - if buf.String() != logContent { - t.Errorf("%s: did not get expected log content. Got: %s", test.name, buf.String()) - } - } -} - -func testPod() *api.Pod { - return &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"}, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - Containers: []api.Container{ - { - Name: "bar", - }, - }, - }, - } -} - -func TestValidateLogFlags(t *testing.T) { - f, _, _ := NewAPIFactory() - - tests := []struct { - name string - flags map[string]string - expected string - }{ - { - name: "since & since-time", - flags: map[string]string{"since": "1h", "since-time": "2006-01-02T15:04:05Z"}, - expected: "at most one of `sinceTime` or `sinceSeconds` may be specified", - }, - { - name: "negative limit-bytes", - flags: map[string]string{"limit-bytes": "-100"}, - expected: "must be greater than 0", - }, - { - name: "negative tail", - flags: map[string]string{"tail": "-100"}, - expected: "must be greater than or equal to 0", - }, - } - for _, test := range tests { - cmd := NewCmdLogs(f, bytes.NewBuffer([]byte{})) - out := "" - for flag, value := range test.flags { - cmd.Flags().Set(flag, value) - } - // checkErr breaks tests in case of errors, plus we just - // need to check errors returned by the command validation - o := &LogsOptions{} - cmd.Run = func(cmd *cobra.Command, args []string) { - o.Complete(f, os.Stdout, cmd, args) - out = o.Validate().Error() - o.RunLogs() - } - cmd.Run(cmd, []string{"foo"}) - - if !strings.Contains(out, test.expected) { - t.Errorf("%s: expected to find:\n\t%s\nfound:\n\t%s\n", test.name, test.expected, out) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/namespace.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/namespace.go deleted file mode 100644 index e50e4c078..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/namespace.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - "k8s.io/kubernetes/pkg/kubectl/cmd/util" -) - -// TODO remove once people have been given enough time to notice -func NewCmdNamespace(out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "namespace [namespace]", - Short: "SUPERSEDED: Set and view the current Kubernetes namespace", - Long: `SUPERSEDED: Set and view the current Kubernetes namespace scope for command line requests. - -namespace has been superseded by the context.namespace field of .kubeconfig files. See 'kubectl config set-context --help' for more details. -`, - Run: func(cmd *cobra.Command, args []string) { - util.CheckErr(fmt.Errorf("namespace has been superseded by the context.namespace field of .kubeconfig files. See 'kubectl config set-context --help' for more details.")) - }, - } - return cmd -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/patch.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/patch.go deleted file mode 100644 index 5b0b6b8d3..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/patch.go +++ /dev/null @@ -1,157 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - "strings" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/util/sets" - "k8s.io/kubernetes/pkg/util/yaml" -) - -var patchTypes = map[string]api.PatchType{"json": api.JSONPatchType, "merge": api.MergePatchType, "strategic": api.StrategicMergePatchType} - -// PatchOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of -// referencing the cmd.Flags() -type PatchOptions struct { - Filenames []string -} - -const ( - patch_long = `Update field(s) of a resource using strategic merge patch - -JSON and YAML formats are accepted. - -Please refer to the models in https://htmlpreview.github.io/?https://github.com/kubernetes/kubernetes/blob/HEAD/docs/api-reference/v1/definitions.html to find if a field is mutable.` - patch_example = ` -# Partially update a node using strategic merge patch -kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}' - -# Partially update a node identified by the type and name specified in "node.json" using strategic merge patch -kubectl patch -f node.json -p '{"spec":{"unschedulable":true}}' - -# Update a container's image; spec.containers[*].name is required because it's a merge key -kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve-hostname","image":"new image"}]}}' - -# Update a container's image using a json patch with positional arrays -kubectl patch pod valid-pod -type='json' -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"new image"}]'` -) - -func NewCmdPatch(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &PatchOptions{} - - cmd := &cobra.Command{ - Use: "patch (-f FILENAME | TYPE NAME) -p PATCH", - Short: "Update field(s) of a resource using strategic merge patch.", - Long: patch_long, - Example: patch_example, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) - shortOutput := cmdutil.GetFlagString(cmd, "output") == "name" - err := RunPatch(f, out, cmd, args, shortOutput, options) - cmdutil.CheckErr(err) - }, - } - cmd.Flags().StringP("patch", "p", "", "The patch to be applied to the resource JSON file.") - cmd.MarkFlagRequired("patch") - cmd.Flags().String("type", "strategic", fmt.Sprintf("The type of patch being provided; one of %v", sets.StringKeySet(patchTypes).List())) - cmdutil.AddOutputFlagsForMutation(cmd) - cmdutil.AddRecordFlag(cmd) - - usage := "Filename, directory, or URL to a file identifying the resource to update" - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) - return cmd -} - -func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, shortOutput bool, options *PatchOptions) error { - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - patchType := api.StrategicMergePatchType - patchTypeString := strings.ToLower(cmdutil.GetFlagString(cmd, "type")) - if len(patchTypeString) != 0 { - ok := false - patchType, ok = patchTypes[patchTypeString] - if !ok { - return cmdutil.UsageError(cmd, fmt.Sprintf("--type must be one of %v, not %q", sets.StringKeySet(patchTypes).List(), patchTypeString)) - } - } - - patch := cmdutil.GetFlagString(cmd, "patch") - if len(patch) == 0 { - return cmdutil.UsageError(cmd, "Must specify -p to patch") - } - patchBytes, err := yaml.ToJSON([]byte(patch)) - if err != nil { - return fmt.Errorf("unable to parse %q: %v", patch, err) - } - - mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - ContinueOnError(). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). - ResourceTypeOrNameArgs(false, args...). - Flatten(). - Do() - err = r.Err() - if err != nil { - return err - } - - infos, err := r.Infos() - if err != nil { - return err - } - if len(infos) > 1 { - return fmt.Errorf("multiple resources provided") - } - info := infos[0] - name, namespace := info.Name, info.Namespace - mapping := info.ResourceMapping() - client, err := f.ClientForMapping(mapping) - if err != nil { - return err - } - - helper := resource.NewHelper(client, mapping) - patchedObject, err := helper.Patch(namespace, name, patchType, patchBytes) - if err != nil { - return err - } - if cmdutil.ShouldRecord(cmd, info) { - if err := cmdutil.RecordChangeCause(patchedObject, f.Command()); err == nil { - // don't return an error on failure. The patch itself succeeded, its only the hint for that change that failed - // don't bother checking for failures of this replace, because a failure to indicate the hint doesn't fail the command - // also, don't force the replacement. If the replacement fails on a resourceVersion conflict, then it means this - // record hint is likely to be invalid anyway, so avoid the bad hint - resource.NewHelper(client, mapping).Replace(namespace, name, false, patchedObject) - } - } - cmdutil.PrintSuccess(mapper, shortOutput, out, "", name, "patched") - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/patch_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/patch_test.go deleted file mode 100644 index fd22dcaf4..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/patch_test.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright 2015 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 cmd - -import ( - "bytes" - "net/http" - "testing" - - "k8s.io/kubernetes/pkg/client/unversioned/fake" -) - -func TestPatchObject(t *testing.T) { - _, svc, _ := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/services/frontend" && (m == "PATCH" || m == "GET"): - return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdPatch(f, buf) - cmd.Flags().Set("namespace", "test") - cmd.Flags().Set("patch", `{"spec":{"type":"NodePort"}}`) - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{"services/frontend"}) - - // uses the name from the file, not the response - if buf.String() != "frontend\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestPatchObjectFromFile(t *testing.T) { - _, svc, _ := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/services/frontend" && (m == "PATCH" || m == "GET"): - return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdPatch(f, buf) - cmd.Flags().Set("namespace", "test") - cmd.Flags().Set("patch", `{"spec":{"type":"NodePort"}}`) - cmd.Flags().Set("output", "name") - cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml") - cmd.Run(cmd, []string{}) - - // uses the name from the file, not the response - if buf.String() != "frontend\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/portforward.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/portforward.go deleted file mode 100644 index 942a1c500..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/portforward.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "net/url" - "os" - "os/signal" - - "github.com/golang/glog" - "github.com/spf13/cobra" - "k8s.io/kubernetes/pkg/api" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/portforward" - "k8s.io/kubernetes/pkg/client/unversioned/remotecommand" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" -) - -const ( - portforward_example = ` -# Listen on ports 5000 and 6000 locally, forwarding data to/from ports 5000 and 6000 in the pod -$ kubectl port-forward mypod 5000 6000 - -# Listen on port 8888 locally, forwarding to 5000 in the pod -$ kubectl port-forward mypod 8888:5000 - -# Listen on a random port locally, forwarding to 5000 in the pod -$ kubectl port-forward mypod :5000 - -# Listen on a random port locally, forwarding to 5000 in the pod -$ kubectl port-forward mypod 0:5000` -) - -func NewCmdPortForward(f *cmdutil.Factory) *cobra.Command { - cmd := &cobra.Command{ - Use: "port-forward POD [LOCAL_PORT:]REMOTE_PORT [...[LOCAL_PORT_N:]REMOTE_PORT_N]", - Short: "Forward one or more local ports to a pod.", - Long: "Forward one or more local ports to a pod.", - Example: portforward_example, - Run: func(cmd *cobra.Command, args []string) { - err := RunPortForward(f, cmd, args, &defaultPortForwarder{}) - cmdutil.CheckErr(err) - }, - } - cmd.Flags().StringP("pod", "p", "", "Pod name") - // TODO support UID - return cmd -} - -type portForwarder interface { - ForwardPorts(method string, url *url.URL, config *client.Config, ports []string, stopChan <-chan struct{}) error -} - -type defaultPortForwarder struct{} - -func (*defaultPortForwarder) ForwardPorts(method string, url *url.URL, config *client.Config, ports []string, stopChan <-chan struct{}) error { - dialer, err := remotecommand.NewExecutor(config, method, url) - if err != nil { - return err - } - fw, err := portforward.New(dialer, ports, stopChan) - if err != nil { - return err - } - return fw.ForwardPorts() -} - -func RunPortForward(f *cmdutil.Factory, cmd *cobra.Command, args []string, fw portForwarder) error { - podName := cmdutil.GetFlagString(cmd, "pod") - if len(podName) == 0 && len(args) == 0 { - return cmdutil.UsageError(cmd, "POD is required for port-forward") - } - - if len(podName) != 0 { - printDeprecationWarning("port-forward POD", "-p POD") - } else { - podName = args[0] - args = args[1:] - } - - if len(args) < 1 { - return cmdutil.UsageError(cmd, "at least 1 PORT is required for port-forward") - } - - namespace, _, err := f.DefaultNamespace() - if err != nil { - return err - } - - client, err := f.Client() - if err != nil { - return err - } - - pod, err := client.Pods(namespace).Get(podName) - if err != nil { - return err - } - - if pod.Status.Phase != api.PodRunning { - glog.Fatalf("Unable to execute command because pod is not running. Current status=%v", pod.Status.Phase) - } - - config, err := f.ClientConfig() - if err != nil { - return err - } - - signals := make(chan os.Signal, 1) - signal.Notify(signals, os.Interrupt) - defer signal.Stop(signals) - - stopCh := make(chan struct{}, 1) - go func() { - <-signals - close(stopCh) - }() - - req := client.RESTClient.Post(). - Resource("pods"). - Namespace(namespace). - Name(pod.Name). - SubResource("portforward") - - return fw.ForwardPorts("POST", req.URL(), config, args, stopCh) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/portforward_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/portforward_test.go deleted file mode 100644 index eebda28b8..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/portforward_test.go +++ /dev/null @@ -1,176 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "net/http" - "net/url" - "testing" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/fake" -) - -type fakePortForwarder struct { - method string - url *url.URL - pfErr error -} - -func (f *fakePortForwarder) ForwardPorts(method string, url *url.URL, config *client.Config, ports []string, stopChan <-chan struct{}) error { - f.method = method - f.url = url - return f.pfErr -} - -func TestPortForward(t *testing.T) { - version := testapi.Default.GroupVersion().Version - - tests := []struct { - name, version, podPath, pfPath, container string - pod *api.Pod - pfErr bool - }{ - { - name: "pod portforward", - version: version, - podPath: "/api/" + version + "/namespaces/test/pods/foo", - pfPath: "/api/" + version + "/namespaces/test/pods/foo/portforward", - pod: execPod(), - }, - { - name: "pod portforward error", - version: version, - podPath: "/api/" + version + "/namespaces/test/pods/foo", - pfPath: "/api/" + version + "/namespaces/test/pods/foo/portforward", - pod: execPod(), - pfErr: true, - }, - } - for _, test := range tests { - f, tf, codec := NewAPIFactory() - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == test.podPath && m == "GET": - body := objBody(codec, test.pod) - return &http.Response{StatusCode: 200, Body: body}, nil - default: - // Ensures no GET is performed when deleting by name - t.Errorf("%s: unexpected request: %#v\n%#v", test.name, req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: &unversioned.GroupVersion{Version: test.version}}} - ff := &fakePortForwarder{} - if test.pfErr { - ff.pfErr = fmt.Errorf("pf error") - } - cmd := &cobra.Command{} - cmd.Flags().StringP("pod", "p", "", "Pod name") - err := RunPortForward(f, cmd, []string{"foo", ":5000", ":1000"}, ff) - - if test.pfErr && err != ff.pfErr { - t.Errorf("%s: Unexpected exec error: %v", test.name, err) - } - if !test.pfErr && err != nil { - t.Errorf("%s: Unexpected error: %v", test.name, err) - } - if test.pfErr { - continue - } - - if ff.url.Path != test.pfPath { - t.Errorf("%s: Did not get expected path for portforward request", test.name) - } - if ff.method != "POST" { - t.Errorf("%s: Did not get method for attach request: %s", test.name, ff.method) - } - - } -} - -func TestPortForwardWithPFlag(t *testing.T) { - version := testapi.Default.GroupVersion().Version - - tests := []struct { - name, version, podPath, pfPath, container string - pod *api.Pod - pfErr bool - }{ - { - name: "pod portforward", - version: version, - podPath: "/api/" + version + "/namespaces/test/pods/foo", - pfPath: "/api/" + version + "/namespaces/test/pods/foo/portforward", - pod: execPod(), - }, - { - name: "pod portforward error", - version: version, - podPath: "/api/" + version + "/namespaces/test/pods/foo", - pfPath: "/api/" + version + "/namespaces/test/pods/foo/portforward", - pod: execPod(), - pfErr: true, - }, - } - for _, test := range tests { - f, tf, codec := NewAPIFactory() - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == test.podPath && m == "GET": - body := objBody(codec, test.pod) - return &http.Response{StatusCode: 200, Body: body}, nil - default: - // Ensures no GET is performed when deleting by name - t.Errorf("%s: unexpected request: %#v\n%#v", test.name, req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: &unversioned.GroupVersion{Version: test.version}}} - ff := &fakePortForwarder{} - if test.pfErr { - ff.pfErr = fmt.Errorf("pf error") - } - cmd := &cobra.Command{} - podPtr := cmd.Flags().StringP("pod", "p", "", "Pod name") - *podPtr = "foo" - err := RunPortForward(f, cmd, []string{":5000", ":1000"}, ff) - if test.pfErr && err != ff.pfErr { - t.Errorf("%s: Unexpected exec error: %v", test.name, err) - } - if !test.pfErr && ff.url.Path != test.pfPath { - t.Errorf("%s: Did not get expected path for portforward request", test.name) - } - if !test.pfErr && err != nil { - t.Errorf("%s: Unexpected error: %v", test.name, err) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/proxy.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/proxy.go deleted file mode 100644 index 130e2ae59..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/proxy.go +++ /dev/null @@ -1,137 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "errors" - "fmt" - "io" - "net" - "strings" - - "github.com/golang/glog" - "github.com/spf13/cobra" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" -) - -const ( - default_port = 8001 - proxy_example = `# Run a proxy to kubernetes apiserver on port 8011, serving static content from ./local/www/ -$ kubectl proxy --port=8011 --www=./local/www/ - -# Run a proxy to kubernetes apiserver on an arbitrary local port. -# The chosen port for the server will be output to stdout. -$ kubectl proxy --port=0 - -# Run a proxy to kubernetes apiserver, changing the api prefix to k8s-api -# This makes e.g. the pods api available at localhost:8011/k8s-api/v1/pods/ -$ kubectl proxy --api-prefix=/k8s-api` -) - -func NewCmdProxy(f *cmdutil.Factory, out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "proxy [--port=PORT] [--www=static-dir] [--www-prefix=prefix] [--api-prefix=prefix]", - Short: "Run a proxy to the Kubernetes API server", - Long: `To proxy all of the kubernetes api and nothing else, use: - -kubectl proxy --api-prefix=/ - -To proxy only part of the kubernetes api and also some static files: - -kubectl proxy --www=/my/files --www-prefix=/static/ --api-prefix=/api/ - -The above lets you 'curl localhost:8001/api/v1/pods'. - -To proxy the entire kubernetes api at a different root, use: - -kubectl proxy --api-prefix=/custom/ - -The above lets you 'curl localhost:8001/custom/api/v1/pods' -`, - Example: proxy_example, - Run: func(cmd *cobra.Command, args []string) { - err := RunProxy(f, out, cmd) - cmdutil.CheckErr(err) - }, - } - cmd.Flags().StringP("www", "w", "", "Also serve static files from the given directory under the specified prefix.") - cmd.Flags().StringP("www-prefix", "P", "/static/", "Prefix to serve static files under, if static file directory is specified.") - cmd.Flags().StringP("api-prefix", "", "/", "Prefix to serve the proxied API under.") - cmd.Flags().String("accept-paths", kubectl.DefaultPathAcceptRE, "Regular expression for paths that the proxy should accept.") - cmd.Flags().String("reject-paths", kubectl.DefaultPathRejectRE, "Regular expression for paths that the proxy should reject.") - cmd.Flags().String("accept-hosts", kubectl.DefaultHostAcceptRE, "Regular expression for hosts that the proxy should accept.") - cmd.Flags().String("reject-methods", kubectl.DefaultMethodRejectRE, "Regular expression for HTTP methods that the proxy should reject.") - cmd.Flags().IntP("port", "p", default_port, "The port on which to run the proxy. Set to 0 to pick a random port.") - cmd.Flags().StringP("address", "", "127.0.0.1", "The IP address on which to serve on.") - cmd.Flags().Bool("disable-filter", false, "If true, disable request filtering in the proxy. This is dangerous, and can leave you vulnerable to XSRF attacks, when used with an accessible port.") - cmd.Flags().StringP("unix-socket", "u", "", "Unix socket on which to run the proxy.") - return cmd -} - -func RunProxy(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command) error { - path := cmdutil.GetFlagString(cmd, "unix-socket") - port := cmdutil.GetFlagInt(cmd, "port") - address := cmdutil.GetFlagString(cmd, "address") - - if port != default_port && path != "" { - return errors.New("Don't specify both --unix-socket and --port") - } - - clientConfig, err := f.ClientConfig() - if err != nil { - return err - } - - staticPrefix := cmdutil.GetFlagString(cmd, "www-prefix") - if !strings.HasSuffix(staticPrefix, "/") { - staticPrefix += "/" - } - - apiProxyPrefix := cmdutil.GetFlagString(cmd, "api-prefix") - if !strings.HasSuffix(apiProxyPrefix, "/") { - apiProxyPrefix += "/" - } - filter := &kubectl.FilterServer{ - AcceptPaths: kubectl.MakeRegexpArrayOrDie(cmdutil.GetFlagString(cmd, "accept-paths")), - RejectPaths: kubectl.MakeRegexpArrayOrDie(cmdutil.GetFlagString(cmd, "reject-paths")), - AcceptHosts: kubectl.MakeRegexpArrayOrDie(cmdutil.GetFlagString(cmd, "accept-hosts")), - } - if cmdutil.GetFlagBool(cmd, "disable-filter") { - if path == "" { - glog.Warning("Request filter disabled, your proxy is vulnerable to XSRF attacks, please be cautious") - } - filter = nil - } - - server, err := kubectl.NewProxyServer(cmdutil.GetFlagString(cmd, "www"), apiProxyPrefix, staticPrefix, filter, clientConfig) - - // Separate listening from serving so we can report the bound port - // when it is chosen by os (eg: port == 0) - var l net.Listener - if path == "" { - l, err = server.Listen(address, port) - } else { - l, err = server.ListenUnix(path) - } - if err != nil { - glog.Fatal(err) - } - fmt.Fprintf(out, "Starting to serve on %s", l.Addr().String()) - glog.Fatal(server.ServeOnListener(l)) - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/replace.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/replace.go deleted file mode 100644 index 81eacecbd..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/replace.go +++ /dev/null @@ -1,254 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - - "github.com/spf13/cobra" - - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" -) - -// ReplaceOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of -// referencing the cmd.Flags() -type ReplaceOptions struct { - Filenames []string -} - -const ( - replace_long = `Replace a resource by filename or stdin. - -JSON and YAML formats are accepted. If replacing an existing resource, the -complete resource spec must be provided. This can be obtained by -$ kubectl get TYPE NAME -o yaml - -Please refer to the models in https://htmlpreview.github.io/?https://github.com/kubernetes/kubernetes/blob/HEAD/docs/api-reference/v1/definitions.html to find if a field is mutable.` - replace_example = `# Replace a pod using the data in pod.json. -$ kubectl replace -f ./pod.json - -# Replace a pod based on the JSON passed into stdin. -$ cat pod.json | kubectl replace -f - - -# Update a single-container pod's image version (tag) to v4 -kubectl get pod mypod -o yaml | sed 's/\(image: myimage\):.*$/\1:v4/' | kubectl replace -f - - -# Force replace, delete and then re-create the resource -kubectl replace --force -f ./pod.json` -) - -func NewCmdReplace(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &ReplaceOptions{} - - cmd := &cobra.Command{ - Use: "replace -f FILENAME", - // update is deprecated. - Aliases: []string{"update"}, - Short: "Replace a resource by filename or stdin.", - Long: replace_long, - Example: replace_example, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) - err := RunReplace(f, out, cmd, args, options) - cmdutil.CheckErr(err) - }, - } - usage := "Filename, directory, or URL to file to use to replace the resource." - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) - cmd.MarkFlagRequired("filename") - cmd.Flags().Bool("force", false, "Delete and re-create the specified resource") - cmd.Flags().Bool("cascade", false, "Only relevant during a force replace. If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController).") - cmd.Flags().Int("grace-period", -1, "Only relevant during a force replace. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative.") - cmd.Flags().Duration("timeout", 0, "Only relevant during a force replace. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object") - cmdutil.AddValidateFlags(cmd) - cmdutil.AddOutputFlagsForMutation(cmd) - cmdutil.AddApplyAnnotationFlags(cmd) - cmdutil.AddRecordFlag(cmd) - return cmd -} - -func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *ReplaceOptions) error { - if len(os.Args) > 1 && os.Args[1] == "update" { - printDeprecationWarning("replace", "update") - } - schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir")) - if err != nil { - return err - } - - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - force := cmdutil.GetFlagBool(cmd, "force") - if len(options.Filenames) == 0 { - return cmdutil.UsageError(cmd, "Must specify --filename to replace") - } - - shortOutput := cmdutil.GetFlagString(cmd, "output") == "name" - if force { - return forceReplace(f, out, cmd, args, shortOutput, options) - } - - mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - Schema(schema). - ContinueOnError(). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). - Flatten(). - Do() - err = r.Err() - if err != nil { - return err - } - - return r.Visit(func(info *resource.Info, err error) error { - if err != nil { - return err - } - - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { - return cmdutil.AddSourceToErr("replacing", info.Source, err) - } - - if cmdutil.ShouldRecord(cmd, info) { - if err := cmdutil.RecordChangeCause(info.Object, f.Command()); err != nil { - return cmdutil.AddSourceToErr("replacing", info.Source, err) - } - } - - // Serialize the object with the annotation applied. - obj, err := resource.NewHelper(info.Client, info.Mapping).Replace(info.Namespace, info.Name, true, info.Object) - if err != nil { - return cmdutil.AddSourceToErr("replacing", info.Source, err) - } - - info.Refresh(obj, true) - printObjectSpecificMessage(obj, out) - cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, "replaced") - return nil - }) -} - -func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, shortOutput bool, options *ReplaceOptions) error { - schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir")) - if err != nil { - return err - } - - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - for i, filename := range options.Filenames { - if filename == "-" { - tempDir, err := ioutil.TempDir("", "kubectl_replace_") - if err != nil { - return err - } - defer os.RemoveAll(tempDir) - tempFilename := filepath.Join(tempDir, "resource.stdin") - err = cmdutil.DumpReaderToFile(os.Stdin, tempFilename) - if err != nil { - return err - } - options.Filenames[i] = tempFilename - } - } - - mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - ContinueOnError(). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). - ResourceTypeOrNameArgs(false, args...).RequireObject(false). - Flatten(). - Do() - err = r.Err() - if err != nil { - return err - } - //Replace will create a resource if it doesn't exist already, so ignore not found error - ignoreNotFound := true - // By default use a reaper to delete all related resources. - if cmdutil.GetFlagBool(cmd, "cascade") { - glog.Warningf("\"cascade\" is set, kubectl will delete and re-create all resources managed by this resource (e.g. Pods created by a ReplicationController). Consider using \"kubectl rolling-update\" if you want to update a ReplicationController together with its Pods.") - err = ReapResult(r, f, out, cmdutil.GetFlagBool(cmd, "cascade"), ignoreNotFound, cmdutil.GetFlagDuration(cmd, "timeout"), cmdutil.GetFlagInt(cmd, "grace-period"), shortOutput, mapper) - } else { - err = DeleteResult(r, out, ignoreNotFound, shortOutput, mapper) - } - if err != nil { - return err - } - - r = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - Schema(schema). - ContinueOnError(). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). - Flatten(). - Do() - err = r.Err() - if err != nil { - return err - } - - count := 0 - err = r.Visit(func(info *resource.Info, err error) error { - if err != nil { - return err - } - - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { - return err - } - - if cmdutil.ShouldRecord(cmd, info) { - if err := cmdutil.RecordChangeCause(info.Object, f.Command()); err != nil { - return cmdutil.AddSourceToErr("replacing", info.Source, err) - } - } - - obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) - if err != nil { - return err - } - - count++ - info.Refresh(obj, true) - printObjectSpecificMessage(obj, out) - cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, "replaced") - return nil - }) - if err != nil { - return err - } - if count == 0 { - return fmt.Errorf("no objects passed to replace") - } - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/replace_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/replace_test.go deleted file mode 100644 index 5ca2b2ec8..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/replace_test.go +++ /dev/null @@ -1,197 +0,0 @@ -/* -Copyright 2015 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 cmd - -import ( - "bytes" - "net/http" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/client/unversioned/fake" -) - -func TestReplaceObject(t *testing.T) { - _, _, rc := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/replicationcontrollers/redis-master" && (m == "GET" || m == "PUT" || m == "DELETE"): - return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil - case p == "/namespaces/test/replicationcontrollers" && m == "POST": - return &http.Response{StatusCode: 201, Body: objBody(codec, &rc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdReplace(f, buf) - cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - // uses the name from the file, not the response - if buf.String() != "replicationcontroller/rc1\n" { - t.Errorf("unexpected output: %s", buf.String()) - } - - buf.Reset() - cmd.Flags().Set("force", "true") - cmd.Flags().Set("cascade", "false") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - if buf.String() != "replicationcontroller/redis-master\nreplicationcontroller/rc1\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestReplaceMultipleObject(t *testing.T) { - _, svc, rc := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/replicationcontrollers/redis-master" && (m == "GET" || m == "PUT" || m == "DELETE"): - return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil - case p == "/namespaces/test/replicationcontrollers" && m == "POST": - return &http.Response{StatusCode: 201, Body: objBody(codec, &rc.Items[0])}, nil - case p == "/namespaces/test/services/frontend" && (m == "GET" || m == "PUT" || m == "DELETE"): - return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil - case p == "/namespaces/test/services" && m == "POST": - return &http.Response{StatusCode: 201, Body: objBody(codec, &svc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdReplace(f, buf) - cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") - cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - if buf.String() != "replicationcontroller/rc1\nservice/baz\n" { - t.Errorf("unexpected output: %s", buf.String()) - } - - buf.Reset() - cmd.Flags().Set("force", "true") - cmd.Flags().Set("cascade", "false") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - if buf.String() != "replicationcontroller/redis-master\nservice/frontend\nreplicationcontroller/rc1\nservice/baz\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestReplaceDirectory(t *testing.T) { - _, svc, rc := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case strings.HasPrefix(p, "/namespaces/test/services/") && (m == "GET" || m == "PUT" || m == "DELETE"): - return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil - case strings.HasPrefix(p, "/namespaces/test/replicationcontrollers/") && (m == "GET" || m == "PUT" || m == "DELETE"): - return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.Items[0])}, nil - case strings.HasPrefix(p, "/namespaces/test/services") && m == "POST": - return &http.Response{StatusCode: 201, Body: objBody(codec, &svc.Items[0])}, nil - case strings.HasPrefix(p, "/namespaces/test/replicationcontrollers") && m == "POST": - return &http.Response{StatusCode: 201, Body: objBody(codec, &rc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdReplace(f, buf) - cmd.Flags().Set("filename", "../../../examples/guestbook") - cmd.Flags().Set("namespace", "test") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - if buf.String() != "replicationcontroller/rc1\nservice/baz\nreplicationcontroller/rc1\nservice/baz\nreplicationcontroller/rc1\nservice/baz\n" { - t.Errorf("unexpected output: %s", buf.String()) - } - - buf.Reset() - cmd.Flags().Set("force", "true") - cmd.Flags().Set("cascade", "false") - cmd.Run(cmd, []string{}) - - if buf.String() != "replicationcontroller/frontend\nservice/frontend\nreplicationcontroller/redis-master\nservice/redis-master\nreplicationcontroller/redis-slave\nservice/redis-slave\n"+ - "replicationcontroller/rc1\nservice/baz\nreplicationcontroller/rc1\nservice/baz\nreplicationcontroller/rc1\nservice/baz\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} - -func TestForceReplaceObjectNotFound(t *testing.T) { - _, _, rc := testData() - - f, tf, codec := NewAPIFactory() - tf.Printer = &testPrinter{} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE": - return &http.Response{StatusCode: 404, Body: stringBody("")}, nil - case p == "/namespaces/test/replicationcontrollers" && m == "POST": - return &http.Response{StatusCode: 201, Body: objBody(codec, &rc.Items[0])}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - - cmd := NewCmdReplace(f, buf) - cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") - cmd.Flags().Set("force", "true") - cmd.Flags().Set("cascade", "false") - cmd.Flags().Set("output", "name") - cmd.Run(cmd, []string{}) - - if buf.String() != "replicationcontroller/rc1\n" { - t.Errorf("unexpected output: %s", buf.String()) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollingupdate.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollingupdate.go deleted file mode 100644 index c3e796371..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollingupdate.go +++ /dev/null @@ -1,394 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "fmt" - "io" - "os" - "time" - - "github.com/golang/glog" - - "github.com/spf13/cobra" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/v1" - "k8s.io/kubernetes/pkg/apimachinery/registered" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/util/intstr" -) - -// RollingUpdateOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of -// referencing the cmd.Flags() -type RollingUpdateOptions struct { - Filenames []string -} - -const ( - rollingUpdate_long = `Perform a rolling update of the given ReplicationController. - -Replaces the specified replication controller with a new replication controller by updating one pod at a time to use the -new PodTemplate. The new-controller.json must specify the same namespace as the -existing replication controller and overwrite at least one (common) label in its replicaSelector.` - rollingUpdate_example = `# Update pods of frontend-v1 using new replication controller data in frontend-v2.json. -$ kubectl rolling-update frontend-v1 -f frontend-v2.json - -# Update pods of frontend-v1 using JSON data passed into stdin. -$ cat frontend-v2.json | kubectl rolling-update frontend-v1 -f - - -# Update the pods of frontend-v1 to frontend-v2 by just changing the image, and switching the -# name of the replication controller. -$ kubectl rolling-update frontend-v1 frontend-v2 --image=image:v2 - -# Update the pods of frontend by just changing the image, and keeping the old name. -$ kubectl rolling-update frontend --image=image:v2 - -# Abort and reverse an existing rollout in progress (from frontend-v1 to frontend-v2). -$ kubectl rolling-update frontend-v1 frontend-v2 --rollback -` -) - -var ( - updatePeriod, _ = time.ParseDuration("1m0s") - timeout, _ = time.ParseDuration("5m0s") - pollInterval, _ = time.ParseDuration("3s") -) - -func NewCmdRollingUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &RollingUpdateOptions{} - - cmd := &cobra.Command{ - Use: "rolling-update OLD_CONTROLLER_NAME ([NEW_CONTROLLER_NAME] --image=NEW_CONTAINER_IMAGE | -f NEW_CONTROLLER_SPEC)", - // rollingupdate is deprecated. - Aliases: []string{"rollingupdate"}, - Short: "Perform a rolling update of the given ReplicationController.", - Long: rollingUpdate_long, - Example: rollingUpdate_example, - Run: func(cmd *cobra.Command, args []string) { - err := RunRollingUpdate(f, out, cmd, args, options) - cmdutil.CheckErr(err) - }, - } - cmd.Flags().Duration("update-period", updatePeriod, `Time to wait between updating pods. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`) - cmd.Flags().Duration("poll-interval", pollInterval, `Time delay between polling for replication controller status after the update. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`) - cmd.Flags().Duration("timeout", timeout, `Max time to wait for a replication controller to update before giving up. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`) - usage := "Filename or URL to file to use to create the new replication controller." - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) - cmd.MarkFlagRequired("filename") - cmd.Flags().String("image", "", "Image to use for upgrading the replication controller. Must be distinct from the existing image (either new image or new image tag). Can not be used with --filename/-f") - cmd.MarkFlagRequired("image") - cmd.Flags().String("deployment-label-key", "deployment", "The key to use to differentiate between two different controllers, default 'deployment'. Only relevant when --image is specified, ignored otherwise") - cmd.Flags().String("container", "", "Container name which will have its image upgraded. Only relevant when --image is specified, ignored otherwise. Required when using --image on a multi-container pod") - cmd.Flags().Bool("dry-run", false, "If true, print out the changes that would be made, but don't actually make them.") - cmd.Flags().Bool("rollback", false, "If true, this is a request to abort an existing rollout that is partially rolled out. It effectively reverses current and next and runs a rollout") - cmdutil.AddValidateFlags(cmd) - cmdutil.AddPrinterFlags(cmd) - return cmd -} - -func validateArguments(cmd *cobra.Command, filenames, args []string) error { - deploymentKey := cmdutil.GetFlagString(cmd, "deployment-label-key") - image := cmdutil.GetFlagString(cmd, "image") - rollback := cmdutil.GetFlagBool(cmd, "rollback") - - if len(deploymentKey) == 0 { - return cmdutil.UsageError(cmd, "--deployment-label-key can not be empty") - } - if len(filenames) > 1 { - return cmdutil.UsageError(cmd, "May only specify a single filename for new controller") - } - - if !rollback { - if len(filenames) == 0 && len(image) == 0 { - return cmdutil.UsageError(cmd, "Must specify --filename or --image for new controller") - } - if len(filenames) != 0 && len(image) != 0 { - return cmdutil.UsageError(cmd, "--filename and --image can not both be specified") - } - } else { - if len(filenames) != 0 || len(image) != 0 { - return cmdutil.UsageError(cmd, "Don't specify --filename or --image on rollback") - } - } - - if len(args) < 1 { - return cmdutil.UsageError(cmd, "Must specify the controller to update") - } - - return nil -} - -func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *RollingUpdateOptions) error { - if len(os.Args) > 1 && os.Args[1] == "rollingupdate" { - printDeprecationWarning("rolling-update", "rollingupdate") - } - err := validateArguments(cmd, options.Filenames, args) - if err != nil { - return err - } - - deploymentKey := cmdutil.GetFlagString(cmd, "deployment-label-key") - filename := "" - image := cmdutil.GetFlagString(cmd, "image") - oldName := args[0] - rollback := cmdutil.GetFlagBool(cmd, "rollback") - period := cmdutil.GetFlagDuration(cmd, "update-period") - interval := cmdutil.GetFlagDuration(cmd, "poll-interval") - timeout := cmdutil.GetFlagDuration(cmd, "timeout") - dryrun := cmdutil.GetFlagBool(cmd, "dry-run") - outputFormat := cmdutil.GetFlagString(cmd, "output") - container := cmdutil.GetFlagString(cmd, "container") - - if len(options.Filenames) > 0 { - filename = options.Filenames[0] - } - - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - client, err := f.Client() - if err != nil { - return err - } - - var newRc *api.ReplicationController - // fetch rc - oldRc, err := client.ReplicationControllers(cmdNamespace).Get(oldName) - if err != nil { - if !errors.IsNotFound(err) || len(image) == 0 || len(args) > 1 { - return err - } - // We're in the middle of a rename, look for an RC with a source annotation of oldName - newRc, err := kubectl.FindSourceController(client, cmdNamespace, oldName) - if err != nil { - return err - } - return kubectl.Rename(client, newRc, oldName) - } - - var keepOldName bool - var replicasDefaulted bool - - mapper, typer := f.Object() - - if len(filename) != 0 { - schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir")) - if err != nil { - return err - } - - request := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - Schema(schema). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, filename). - Do() - obj, err := request.Object() - if err != nil { - return err - } - var ok bool - // Handle filename input from stdin. The resource builder always returns an api.List - // when creating resource(s) from a stream. - if list, ok := obj.(*api.List); ok { - if len(list.Items) > 1 { - return cmdutil.UsageError(cmd, "%s specifies multiple items", filename) - } - obj = list.Items[0] - } - newRc, ok = obj.(*api.ReplicationController) - if !ok { - if gvk, err := typer.ObjectKind(obj); err == nil { - return cmdutil.UsageError(cmd, "%s contains a %v not a ReplicationController", filename, gvk) - } - glog.V(4).Infof("Object %#v is not a ReplicationController", obj) - return cmdutil.UsageError(cmd, "%s does not specify a valid ReplicationController", filename) - } - infos, err := request.Infos() - if err != nil || len(infos) != 1 { - glog.V(2).Infof("was not able to recover adequate information to discover if .spec.replicas was defaulted") - } else { - replicasDefaulted = isReplicasDefaulted(infos[0]) - } - } - // If the --image option is specified, we need to create a new rc with at least one different selector - // than the old rc. This selector is the hash of the rc, which will differ because the new rc has a - // different image. - if len(image) != 0 { - codec := registered.GroupOrDie(client.APIVersion().Group).Codec - keepOldName = len(args) == 1 - newName := findNewName(args, oldRc) - if newRc, err = kubectl.LoadExistingNextReplicationController(client, cmdNamespace, newName); err != nil { - return err - } - if newRc != nil { - if inProgressImage := newRc.Spec.Template.Spec.Containers[0].Image; inProgressImage != image { - return cmdutil.UsageError(cmd, "Found existing in-progress update to image (%s).\nEither continue in-progress update with --image=%s or rollback with --rollback", inProgressImage, inProgressImage) - } - fmt.Fprintf(out, "Found existing update in progress (%s), resuming.\n", newRc.Name) - } else { - if oldRc.Spec.Template.Spec.Containers[0].Image == image { - return cmdutil.UsageError(cmd, "Specified --image must be distinct from existing container image") - } - newRc, err = kubectl.CreateNewControllerFromCurrentController(client, codec, cmdNamespace, oldName, newName, image, container, deploymentKey) - if err != nil { - return err - } - } - // Update the existing replication controller with pointers to the 'next' controller - // and adding the label if necessary to distinguish it from the 'next' controller. - oldHash, err := api.HashObject(oldRc, codec) - if err != nil { - return err - } - oldRc, err = kubectl.UpdateExistingReplicationController(client, oldRc, cmdNamespace, newRc.Name, deploymentKey, oldHash, out) - if err != nil { - return err - } - } - - if rollback { - keepOldName = len(args) == 1 - newName := findNewName(args, oldRc) - if newRc, err = kubectl.LoadExistingNextReplicationController(client, cmdNamespace, newName); err != nil { - return err - } - - if newRc == nil { - return cmdutil.UsageError(cmd, "Could not find %s to rollback.\n", newName) - } - } - - if oldName == newRc.Name { - return cmdutil.UsageError(cmd, "%s cannot have the same name as the existing ReplicationController %s", - filename, oldName) - } - - updater := kubectl.NewRollingUpdater(newRc.Namespace, client) - - // To successfully pull off a rolling update the new and old rc have to differ - // by at least one selector. Every new pod should have the selector and every - // old pod should not have the selector. - var hasLabel bool - for key, oldValue := range oldRc.Spec.Selector { - if newValue, ok := newRc.Spec.Selector[key]; ok && newValue != oldValue { - hasLabel = true - break - } - } - if !hasLabel { - return cmdutil.UsageError(cmd, "%s must specify a matching key with non-equal value in Selector for %s", - filename, oldName) - } - // TODO: handle scales during rolling update - if replicasDefaulted { - newRc.Spec.Replicas = oldRc.Spec.Replicas - } - if dryrun { - oldRcData := &bytes.Buffer{} - newRcData := &bytes.Buffer{} - if outputFormat == "" { - oldRcData.WriteString(oldRc.Name) - newRcData.WriteString(newRc.Name) - } else { - if err := f.PrintObject(cmd, oldRc, oldRcData); err != nil { - return err - } - if err := f.PrintObject(cmd, newRc, newRcData); err != nil { - return err - } - } - fmt.Fprintf(out, "Rolling from:\n%s\nTo:\n%s\n", string(oldRcData.Bytes()), string(newRcData.Bytes())) - return nil - } - updateCleanupPolicy := kubectl.DeleteRollingUpdateCleanupPolicy - if keepOldName { - updateCleanupPolicy = kubectl.RenameRollingUpdateCleanupPolicy - } - config := &kubectl.RollingUpdaterConfig{ - Out: out, - OldRc: oldRc, - NewRc: newRc, - UpdatePeriod: period, - Interval: interval, - Timeout: timeout, - CleanupPolicy: updateCleanupPolicy, - MaxUnavailable: intstr.FromInt(1), - MaxSurge: intstr.FromInt(1), - } - if rollback { - err = kubectl.AbortRollingUpdate(config) - if err != nil { - return err - } - client.ReplicationControllers(config.NewRc.Namespace).Update(config.NewRc) - } - err = updater.Update(config) - if err != nil { - return err - } - - message := "rolling updated" - if keepOldName { - newRc.Name = oldName - } else { - message = fmt.Sprintf("rolling updated to %q", newRc.Name) - } - newRc, err = client.ReplicationControllers(cmdNamespace).Get(newRc.Name) - if err != nil { - return err - } - if outputFormat != "" { - return f.PrintObject(cmd, newRc, out) - } - kind, err := api.Scheme.ObjectKind(newRc) - if err != nil { - return err - } - _, res := meta.KindToResource(kind) - cmdutil.PrintSuccess(mapper, false, out, res.Resource, oldName, message) - return nil -} - -func findNewName(args []string, oldRc *api.ReplicationController) string { - if len(args) >= 2 { - return args[1] - } - if oldRc != nil { - newName, _ := kubectl.GetNextControllerAnnotation(oldRc) - return newName - } - return "" -} - -func isReplicasDefaulted(info *resource.Info) bool { - if info == nil || info.VersionedObject == nil { - // was unable to recover versioned info - return false - } - switch t := info.VersionedObject.(type) { - case *v1.ReplicationController: - return t.Spec.Replicas == nil - } - return false -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollingupdate_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollingupdate_test.go deleted file mode 100644 index bf2204b27..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollingupdate_test.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "testing" -) - -func TestValidateArgs(t *testing.T) { - f, _, _ := NewAPIFactory() - - tests := []struct { - flags map[string]string - filenames []string - args []string - expectErr bool - testName string - }{ - { - expectErr: true, - testName: "nothing", - }, - { - flags: map[string]string{}, - args: []string{"foo"}, - expectErr: true, - testName: "no file, no image", - }, - { - filenames: []string{"bar.yaml"}, - args: []string{"foo"}, - testName: "valid file example", - }, - { - flags: map[string]string{ - "image": "foo:v2", - }, - args: []string{"foo"}, - testName: "missing second image name", - }, - { - flags: map[string]string{ - "image": "foo:v2", - }, - args: []string{"foo", "foo-v2"}, - testName: "valid image example", - }, - { - flags: map[string]string{ - "image": "foo:v2", - }, - filenames: []string{"bar.yaml"}, - args: []string{"foo", "foo-v2"}, - expectErr: true, - testName: "both filename and image example", - }, - } - for _, test := range tests { - out := &bytes.Buffer{} - cmd := NewCmdRollingUpdate(f, out) - - if test.flags != nil { - for key, val := range test.flags { - cmd.Flags().Set(key, val) - } - } - err := validateArguments(cmd, test.filenames, test.args) - if err != nil && !test.expectErr { - t.Errorf("unexpected error: %v (%s)", err, test.testName) - } - if err == nil && test.expectErr { - t.Errorf("unexpected non-error (%s)", test.testName) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout.go deleted file mode 100644 index 8fbb53595..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -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 rollout - -import ( - "io" - - "github.com/spf13/cobra" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" -) - -const ( - rollout_long = `rollout manages a deployment using subcommands like "kubectl rollout undo deployment/abc"` - rollout_example = `# Rollback to the previous deployment -$ kubectl rollout undo deployment/abc` - rollout_valid_resources = `Valid resource types include: - * deployments -` -) - -func NewCmdRollout(f *cmdutil.Factory, out io.Writer) *cobra.Command { - - cmd := &cobra.Command{ - Use: "rollout SUBCOMMAND", - Short: "rollout manages a deployment", - Long: rollout_long, - Example: rollout_example, - Run: func(cmd *cobra.Command, args []string) { - cmd.Help() - }, - } - - // subcommands - cmd.AddCommand(NewCmdRolloutHistory(f, out)) - cmd.AddCommand(NewCmdRolloutPause(f, out)) - cmd.AddCommand(NewCmdRolloutResume(f, out)) - cmd.AddCommand(NewCmdRolloutUndo(f, out)) - - return cmd -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_history.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_history.go deleted file mode 100644 index f3eeb4a71..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_history.go +++ /dev/null @@ -1,122 +0,0 @@ -/* -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 rollout - -import ( - "fmt" - "io" - - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/util/errors" - - "github.com/spf13/cobra" -) - -// HistoryOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of -// referencing the cmd.Flags() -type HistoryOptions struct { - Filenames []string -} - -const ( - history_long = `view previous rollout revisions and configurations.` - history_example = `# View the rollout history of a deployment -$ kubectl rollout history deployment/abc` -) - -func NewCmdRolloutHistory(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &HistoryOptions{} - - cmd := &cobra.Command{ - Use: "history (TYPE NAME | TYPE/NAME) [flags]", - Short: "view rollout history", - Long: history_long, - Example: history_example, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(RunHistory(f, cmd, out, args, options)) - }, - } - - cmd.Flags().Int64("revision", 0, "See the details, including podTemplate of the revision specified") - usage := "Filename, directory, or URL to a file identifying the resource to get from a server." - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) - return cmd -} - -func RunHistory(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []string, options *HistoryOptions) error { - if len(args) == 0 && len(options.Filenames) == 0 { - return cmdutil.UsageError(cmd, "Required resource not specified.") - } - revisionDetail := cmdutil.GetFlagInt64(cmd, "revision") - - mapper, typer := f.Object() - - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - infos, err := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). - ResourceTypeOrNameArgs(true, args...). - Latest(). - Flatten(). - Do(). - Infos() - if err != nil { - return err - } - - errs := []error{} - for _, info := range infos { - mapping := info.ResourceMapping() - historyViewer, err := f.HistoryViewer(mapping) - if err != nil { - errs = append(errs, err) - continue - } - historyInfo, err := historyViewer.History(info.Namespace, info.Name) - if err != nil { - errs = append(errs, err) - continue - } - - formattedOutput := "" - if revisionDetail > 0 { - // Print details of a specific revision - template, ok := historyInfo.RevisionToTemplate[revisionDetail] - if !ok { - return fmt.Errorf("unable to find revision %d of %s %q", revisionDetail, mapping.Resource, info.Name) - } - fmt.Fprintf(out, "%s %q revision %d\n", mapping.Resource, info.Name, revisionDetail) - formattedOutput, err = kubectl.DescribePodTemplate(template) - } else { - // Print all revisions - formattedOutput, err = kubectl.PrintRolloutHistory(historyInfo, mapping.Resource, info.Name) - } - if err != nil { - errs = append(errs, err) - continue - } - fmt.Fprintf(out, "%s\n", formattedOutput) - } - - return errors.NewAggregate(errs) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_pause.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_pause.go deleted file mode 100644 index ae07dcefd..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_pause.go +++ /dev/null @@ -1,118 +0,0 @@ -/* -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 rollout - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/runtime" -) - -// PauseConfig is the start of the data required to perform the operation. As new fields are added, add them here instead of -// referencing the cmd.Flags() -type PauseConfig struct { - PauseObject func(object runtime.Object) (bool, error) - Mapper meta.RESTMapper - Typer runtime.ObjectTyper - Info *resource.Info - - Out io.Writer - Filenames []string -} - -const ( - pause_long = `Mark the provided resource as paused - -Paused resources will not be reconciled by a controller. -Use \"kubectl rollout resume\" to resume a paused resource. -Currently only deployments support being paused.` - - pause_example = `# Mark the nginx deployment as paused. Any current state of -# the deployment will continue its function, new updates to the deployment will not -# have an effect as long as the deployment is paused. -$ kubectl rollout pause deployment/nginx` -) - -func NewCmdRolloutPause(f *cmdutil.Factory, out io.Writer) *cobra.Command { - opts := &PauseConfig{} - - cmd := &cobra.Command{ - Use: "pause RESOURCE", - Short: "Mark the provided resource as paused", - Long: pause_long, - Example: pause_example, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(opts.CompletePause(f, cmd, out, args)) - cmdutil.CheckErr(opts.RunPause()) - }, - } - - usage := "Filename, directory, or URL to a file identifying the resource to get from a server." - kubectl.AddJsonFilenameFlag(cmd, &opts.Filenames, usage) - return cmd -} - -func (o *PauseConfig) CompletePause(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []string) error { - if len(args) == 0 && len(o.Filenames) == 0 { - return cmdutil.UsageError(cmd, cmd.Use) - } - - o.Mapper, o.Typer = f.Object() - o.PauseObject = f.PauseObject - o.Out = out - - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - infos, err := resource.NewBuilder(o.Mapper, o.Typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, o.Filenames...). - ResourceTypeOrNameArgs(true, args...). - SingleResourceType(). - Latest(). - Do().Infos() - if err != nil { - return err - } - if len(infos) != 1 { - return fmt.Errorf("rollout pause is only supported on individual resources - %d resources were found", len(infos)) - } - o.Info = infos[0] - return nil -} - -func (o PauseConfig) RunPause() error { - isAlreadyPaused, err := o.PauseObject(o.Info.Object) - if err != nil { - return err - } - if isAlreadyPaused { - cmdutil.PrintSuccess(o.Mapper, false, o.Out, o.Info.Mapping.Resource, o.Info.Name, "already paused") - return nil - } - cmdutil.PrintSuccess(o.Mapper, false, o.Out, o.Info.Mapping.Resource, o.Info.Name, "paused") - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_resume.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_resume.go deleted file mode 100644 index a38147e75..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_resume.go +++ /dev/null @@ -1,116 +0,0 @@ -/* -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 rollout - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/runtime" -) - -// ResumeConfig is the start of the data required to perform the operation. As new fields are added, add them here instead of -// referencing the cmd.Flags() -type ResumeConfig struct { - ResumeObject func(object runtime.Object) (bool, error) - Mapper meta.RESTMapper - Typer runtime.ObjectTyper - Info *resource.Info - - Out io.Writer - Filenames []string -} - -const ( - resume_long = `Resume a paused resource - -Paused resources will not be reconciled by a controller. By resuming a -resource, we allow it to be reconciled again. -Currently only deployments support being resumed.` - - resume_example = `# Resume an already paused deployment -$ kubectl rollout resume deployment/nginx` -) - -func NewCmdRolloutResume(f *cmdutil.Factory, out io.Writer) *cobra.Command { - opts := &ResumeConfig{} - - cmd := &cobra.Command{ - Use: "resume RESOURCE", - Short: "Resume a paused resource", - Long: resume_long, - Example: resume_example, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(opts.CompleteResume(f, cmd, out, args)) - cmdutil.CheckErr(opts.RunResume()) - }, - } - - usage := "Filename, directory, or URL to a file identifying the resource to get from a server." - kubectl.AddJsonFilenameFlag(cmd, &opts.Filenames, usage) - return cmd -} - -func (o *ResumeConfig) CompleteResume(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []string) error { - if len(args) == 0 && len(o.Filenames) == 0 { - return cmdutil.UsageError(cmd, cmd.Use) - } - - o.Mapper, o.Typer = f.Object() - o.ResumeObject = f.ResumeObject - o.Out = out - - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - infos, err := resource.NewBuilder(o.Mapper, o.Typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, o.Filenames...). - ResourceTypeOrNameArgs(true, args...). - SingleResourceType(). - Latest(). - Do().Infos() - if err != nil { - return err - } - if len(infos) != 1 { - return fmt.Errorf("rollout resume is only supported on individual resources - %d resources were found", len(infos)) - } - o.Info = infos[0] - return nil -} - -func (o ResumeConfig) RunResume() error { - isAlreadyResumed, err := o.ResumeObject(o.Info.Object) - if err != nil { - return err - } - if isAlreadyResumed { - cmdutil.PrintSuccess(o.Mapper, false, o.Out, o.Info.Mapping.Resource, o.Info.Name, "already resumed") - return nil - } - cmdutil.PrintSuccess(o.Mapper, false, o.Out, o.Info.Mapping.Resource, o.Info.Name, "resumed") - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_undo.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_undo.go deleted file mode 100644 index ccaf429ee..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/rollout/rollout_undo.go +++ /dev/null @@ -1,111 +0,0 @@ -/* -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 rollout - -import ( - "fmt" - "io" - - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/runtime" - - "github.com/spf13/cobra" -) - -// UndoOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of -// referencing the cmd.Flags() -type UndoOptions struct { - Rollbacker kubectl.Rollbacker - Mapper meta.RESTMapper - Typer runtime.ObjectTyper - Info *resource.Info - ToRevision int64 - Out io.Writer - Filenames []string -} - -const ( - undo_long = `undo rolls back to a previous rollout.` - undo_example = `# Rollback to the previous deployment -$ kubectl rollout undo deployment/abc` -) - -func NewCmdRolloutUndo(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &UndoOptions{} - - cmd := &cobra.Command{ - Use: "undo (TYPE NAME | TYPE/NAME) [flags]", - Short: "undoes a previous rollout", - Long: undo_long, - Example: undo_example, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(options.CompleteUndo(f, cmd, out, args)) - cmdutil.CheckErr(options.RunUndo()) - }, - } - - cmd.Flags().Int64("to-revision", 0, "The revision to rollback to. Default to 0 (last revision).") - usage := "Filename, directory, or URL to a file identifying the resource to get from a server." - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) - return cmd -} - -func (o *UndoOptions) CompleteUndo(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []string) error { - if len(args) == 0 && len(o.Filenames) == 0 { - return cmdutil.UsageError(cmd, "Required resource not specified.") - } - - o.ToRevision = cmdutil.GetFlagInt64(cmd, "to-revision") - o.Mapper, o.Typer = f.Object() - o.Out = out - - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - infos, err := resource.NewBuilder(o.Mapper, o.Typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, o.Filenames...). - ResourceTypeOrNameArgs(true, args...). - Latest(). - Flatten(). - Do(). - Infos() - if err != nil { - return err - } - - if len(infos) != 1 { - return fmt.Errorf("rollout undo is only supported on individual resources - %d resources were found", len(infos)) - } - o.Info = infos[0] - o.Rollbacker, err = f.Rollbacker(o.Info.ResourceMapping()) - return err -} - -func (o *UndoOptions) RunUndo() error { - result, err := o.Rollbacker.Rollback(o.Info.Namespace, o.Info.Name, nil, o.ToRevision, o.Info.Object) - if err != nil { - return err - } - cmdutil.PrintSuccess(o.Mapper, false, o.Out, o.Info.Mapping.Resource, o.Info.Name, result) - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/run.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/run.go deleted file mode 100644 index f95759a58..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/run.go +++ /dev/null @@ -1,459 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - "os" - "time" - - "github.com/spf13/cobra" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/meta" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/runtime" -) - -const ( - run_long = `Create and run a particular image, possibly replicated. -Creates a deployment or job to manage the created container(s).` - run_example = `# Start a single instance of nginx. -$ kubectl run nginx --image=nginx - -# Start a single instance of hazelcast and let the container expose port 5701 . -$ kubectl run hazelcast --image=hazelcast --port=5701 - -# Start a single instance of hazelcast and set environment variables "DNS_DOMAIN=cluster" and "POD_NAMESPACE=default" in the container. -$ kubectl run hazelcast --image=hazelcast --env="DNS_DOMAIN=cluster" --env="POD_NAMESPACE=default" - -# Start a replicated instance of nginx. -$ kubectl run nginx --image=nginx --replicas=5 - -# Dry run. Print the corresponding API objects without creating them. -$ kubectl run nginx --image=nginx --dry-run - -# Start a single instance of nginx, but overload the spec of the deployment with a partial set of values parsed from JSON. -$ kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { ... } }' - -# Start a single instance of busybox and keep it in the foreground, don't restart it if it exits. -$ kubectl run -i --tty busybox --image=busybox --restart=Never - -# Start the nginx container using the default command, but use custom arguments (arg1 .. argN) for that command. -$ kubectl run nginx --image=nginx -- ... - -# Start the nginx container using a different command and custom arguments. -$ kubectl run nginx --image=nginx --command -- ... - -# Start the perl container to compute π to 2000 places and print it out. -$ kubectl run pi --image=perl --restart=OnFailure -- perl -Mbignum=bpi -wle 'print bpi(2000)'` -) - -func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "run NAME --image=image [--env=\"key=value\"] [--port=port] [--replicas=replicas] [--dry-run=bool] [--overrides=inline-json] [--command] -- [COMMAND] [args...]", - // run-container is deprecated - Aliases: []string{"run-container"}, - Short: "Run a particular image on the cluster.", - Long: run_long, - Example: run_example, - Run: func(cmd *cobra.Command, args []string) { - argsLenAtDash := cmd.ArgsLenAtDash() - err := Run(f, cmdIn, cmdOut, cmdErr, cmd, args, argsLenAtDash) - cmdutil.CheckErr(err) - }, - } - cmdutil.AddPrinterFlags(cmd) - addRunFlags(cmd) - cmdutil.AddApplyAnnotationFlags(cmd) - cmdutil.AddRecordFlag(cmd) - return cmd -} - -func addRunFlags(cmd *cobra.Command) { - cmd.Flags().String("generator", "", "The name of the API generator to use. Default is 'deployment/v1beta1' if --restart=Always, otherwise the default is 'job/v1beta1'.") - cmd.Flags().String("image", "", "The image for the container to run.") - cmd.MarkFlagRequired("image") - cmd.Flags().IntP("replicas", "r", 1, "Number of replicas to create for this container. Default is 1.") - cmd.Flags().Bool("rm", false, "If true, delete resources created in this command for attached containers.") - cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it.") - cmd.Flags().String("overrides", "", "An inline JSON override for the generated object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field.") - cmd.Flags().StringSlice("env", []string{}, "Environment variables to set in the container") - cmd.Flags().Int("port", -1, "The port that this container exposes. If --expose is true, this is also the port used by the service that is created.") - cmd.Flags().Int("hostport", -1, "The host port mapping for the container port. To demonstrate a single-machine container.") - cmd.Flags().StringP("labels", "l", "", "Labels to apply to the pod(s).") - cmd.Flags().BoolP("stdin", "i", false, "Keep stdin open on the container(s) in the pod, even if nothing is attached.") - cmd.Flags().Bool("tty", false, "Allocated a TTY for each container in the pod. Because -t is currently shorthand for --template, -t is not supported for --tty. This shorthand is deprecated and we expect to adopt -t for --tty soon.") - cmd.Flags().Bool("attach", false, "If true, wait for the Pod to start running, and then attach to the Pod as if 'kubectl attach ...' were called. Default false, unless '-i/--interactive' is set, in which case the default is true.") - cmd.Flags().Bool("leave-stdin-open", false, "If the pod is started in interactive mode or with stdin, leave stdin open after the first attach completes. By default, stdin will be closed after the first attach completes.") - cmd.Flags().String("restart", "Always", "The restart policy for this Pod. Legal values [Always, OnFailure, Never]. If set to 'Always' a deployment is created for this pod, if set to OnFailure or Never, a job is created for this pod and --replicas must be 1. Default 'Always'") - cmd.Flags().Bool("command", false, "If true and extra arguments are present, use them as the 'command' field in the container, rather than the 'args' field which is the default.") - cmd.Flags().String("requests", "", "The resource requirement requests for this container. For example, 'cpu=100m,memory=256Mi'") - cmd.Flags().String("limits", "", "The resource requirement limits for this container. For example, 'cpu=200m,memory=512Mi'") - cmd.Flags().Bool("expose", false, "If true, a public, external service is created for the container(s) which are run") - cmd.Flags().String("service-generator", "service/v2", "The name of the generator to use for creating a service. Only used if --expose is true") - cmd.Flags().String("service-overrides", "", "An inline JSON override for the generated service object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field. Only used if --expose is true.") -} - -func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobra.Command, args []string, argsLenAtDash int) error { - if len(os.Args) > 1 && os.Args[1] == "run-container" { - printDeprecationWarning("run", "run-container") - } - - // Let kubectl run follow rules for `--`, see #13004 issue - if len(args) == 0 || argsLenAtDash == 0 { - return cmdutil.UsageError(cmd, "NAME is required for run") - } - - interactive := cmdutil.GetFlagBool(cmd, "stdin") - tty := cmdutil.GetFlagBool(cmd, "tty") - if tty && !interactive { - return cmdutil.UsageError(cmd, "-i/--stdin is required for containers with --tty=true") - } - replicas := cmdutil.GetFlagInt(cmd, "replicas") - if interactive && replicas != 1 { - return cmdutil.UsageError(cmd, fmt.Sprintf("-i/--stdin requires that replicas is 1, found %d", replicas)) - } - - namespace, _, err := f.DefaultNamespace() - if err != nil { - return err - } - restartPolicy, err := getRestartPolicy(cmd, interactive) - if err != nil { - return err - } - if restartPolicy != api.RestartPolicyAlways && replicas != 1 { - return cmdutil.UsageError(cmd, fmt.Sprintf("--restart=%s requires that --replicas=1, found %d", restartPolicy, replicas)) - } - - generatorName := cmdutil.GetFlagString(cmd, "generator") - if len(generatorName) == 0 { - if restartPolicy == api.RestartPolicyAlways { - generatorName = "deployment/v1beta1" - } else { - generatorName = "job/v1beta1" - } - } - generators := f.Generators("run") - generator, found := generators[generatorName] - if !found { - return cmdutil.UsageError(cmd, fmt.Sprintf("generator %q not found.", generatorName)) - } - names := generator.ParamNames() - params := kubectl.MakeParams(cmd, names) - params["name"] = args[0] - if len(args) > 1 { - params["args"] = args[1:] - } - - params["env"] = cmdutil.GetFlagStringSlice(cmd, "env") - - obj, _, mapper, mapping, err := createGeneratedObject(f, cmd, generator, names, params, cmdutil.GetFlagString(cmd, "overrides"), namespace) - if err != nil { - return err - } - - if cmdutil.GetFlagBool(cmd, "expose") { - serviceGenerator := cmdutil.GetFlagString(cmd, "service-generator") - if len(serviceGenerator) == 0 { - return cmdutil.UsageError(cmd, fmt.Sprintf("No service generator specified")) - } - if err := generateService(f, cmd, args, serviceGenerator, params, namespace, cmdOut); err != nil { - return err - } - } - - attachFlag := cmd.Flags().Lookup("attach") - attach := cmdutil.GetFlagBool(cmd, "attach") - - if !attachFlag.Changed && interactive { - attach = true - } - - remove := cmdutil.GetFlagBool(cmd, "rm") - if !attach && remove { - return cmdutil.UsageError(cmd, "--rm should only be used for attached containers") - } - - if attach { - opts := &AttachOptions{ - In: cmdIn, - Out: cmdOut, - Err: cmdErr, - Stdin: interactive, - TTY: tty, - - Attach: &DefaultRemoteAttach{}, - } - config, err := f.ClientConfig() - if err != nil { - return err - } - opts.Config = config - - client, err := f.Client() - if err != nil { - return err - } - opts.Client = client - - attachablePod, err := f.AttachablePodForObject(obj) - if err != nil { - return err - } - err = handleAttachPod(f, client, attachablePod, opts) - if err != nil { - return err - } - - if remove { - namespace, err = mapping.MetadataAccessor.Namespace(obj) - if err != nil { - return err - } - var name string - name, err = mapping.MetadataAccessor.Name(obj) - if err != nil { - return err - } - _, typer := f.Object() - r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - ContinueOnError(). - NamespaceParam(namespace).DefaultNamespace(). - ResourceNames(mapping.Resource, name). - Flatten(). - Do() - return ReapResult(r, f, cmdOut, true, true, 0, -1, false, mapper) - } - return nil - } - - outputFormat := cmdutil.GetFlagString(cmd, "output") - if outputFormat != "" { - return f.PrintObject(cmd, obj, cmdOut) - } - cmdutil.PrintSuccess(mapper, false, cmdOut, mapping.Resource, args[0], "created") - return nil -} - -func waitForPodRunning(c *client.Client, pod *api.Pod, out io.Writer) (status api.PodPhase, err error) { - for { - pod, err := c.Pods(pod.Namespace).Get(pod.Name) - if err != nil { - return api.PodUnknown, err - } - ready := false - if pod.Status.Phase == api.PodRunning { - ready = true - for _, status := range pod.Status.ContainerStatuses { - if !status.Ready { - ready = false - break - } - } - if ready { - return api.PodRunning, nil - } - } - if pod.Status.Phase == api.PodSucceeded || pod.Status.Phase == api.PodFailed { - return pod.Status.Phase, nil - } - fmt.Fprintf(out, "Waiting for pod %s/%s to be running, status is %s, pod ready: %v\n", pod.Namespace, pod.Name, pod.Status.Phase, ready) - time.Sleep(2 * time.Second) - continue - } -} - -func handleAttachPod(f *cmdutil.Factory, c *client.Client, pod *api.Pod, opts *AttachOptions) error { - status, err := waitForPodRunning(c, pod, opts.Out) - if err != nil { - return err - } - if status == api.PodSucceeded || status == api.PodFailed { - req, err := f.LogsForObject(pod, &api.PodLogOptions{Container: opts.GetContainerName(pod)}) - if err != nil { - return err - } - readCloser, err := req.Stream() - if err != nil { - return err - } - defer readCloser.Close() - _, err = io.Copy(opts.Out, readCloser) - return err - } - opts.Client = c - opts.PodName = pod.Name - opts.Namespace = pod.Namespace - if err := opts.Run(); err != nil { - fmt.Fprintf(opts.Out, "Error attaching, falling back to logs: %v\n", err) - req, err := f.LogsForObject(pod, &api.PodLogOptions{Container: opts.GetContainerName(pod)}) - if err != nil { - return err - } - readCloser, err := req.Stream() - if err != nil { - return err - } - defer readCloser.Close() - _, err = io.Copy(opts.Out, readCloser) - return err - } - return nil -} - -func getRestartPolicy(cmd *cobra.Command, interactive bool) (api.RestartPolicy, error) { - restart := cmdutil.GetFlagString(cmd, "restart") - if len(restart) == 0 { - if interactive { - return api.RestartPolicyOnFailure, nil - } else { - return api.RestartPolicyAlways, nil - } - } - switch api.RestartPolicy(restart) { - case api.RestartPolicyAlways: - return api.RestartPolicyAlways, nil - case api.RestartPolicyOnFailure: - return api.RestartPolicyOnFailure, nil - case api.RestartPolicyNever: - return api.RestartPolicyNever, nil - default: - return "", cmdutil.UsageError(cmd, fmt.Sprintf("invalid restart policy: %s", restart)) - } -} - -func generateService(f *cmdutil.Factory, cmd *cobra.Command, args []string, serviceGenerator string, paramsIn map[string]interface{}, namespace string, out io.Writer) error { - generators := f.Generators("expose") - generator, found := generators[serviceGenerator] - if !found { - return fmt.Errorf("missing service generator: %s", serviceGenerator) - } - names := generator.ParamNames() - - port := cmdutil.GetFlagInt(cmd, "port") - if port < 1 { - return fmt.Errorf("--port must be a positive integer when exposing a service") - } - - params := map[string]interface{}{} - for key, value := range paramsIn { - _, isString := value.(string) - if isString { - params[key] = value - } - } - - name, found := params["name"] - if !found || len(name.(string)) == 0 { - return fmt.Errorf("name is a required parameter") - } - selector, found := params["labels"] - if !found || len(selector.(string)) == 0 { - selector = fmt.Sprintf("run=%s", name.(string)) - } - params["selector"] = selector - - if defaultName, found := params["default-name"]; !found || len(defaultName.(string)) == 0 { - params["default-name"] = name - } - - obj, _, mapper, mapping, err := createGeneratedObject(f, cmd, generator, names, params, cmdutil.GetFlagString(cmd, "service-overrides"), namespace) - if err != nil { - return err - } - - if cmdutil.GetFlagString(cmd, "output") != "" { - return f.PrintObject(cmd, obj, out) - } - cmdutil.PrintSuccess(mapper, false, out, mapping.Resource, args[0], "created") - - return nil -} - -func createGeneratedObject(f *cmdutil.Factory, cmd *cobra.Command, generator kubectl.Generator, names []kubectl.GeneratorParam, params map[string]interface{}, overrides, namespace string) (runtime.Object, string, meta.RESTMapper, *meta.RESTMapping, error) { - err := kubectl.ValidateParams(names, params) - if err != nil { - return nil, "", nil, nil, err - } - - // TODO: Validate flag usage against selected generator. More tricky since --expose was added. - obj, err := generator.Generate(params) - if err != nil { - return nil, "", nil, nil, err - } - - mapper, typer := f.Object() - groupVersionKind, err := typer.ObjectKind(obj) - if err != nil { - return nil, "", nil, nil, err - } - - if len(overrides) > 0 { - codec := runtime.NewCodec(f.JSONEncoder(), f.Decoder(true)) - obj, err = cmdutil.Merge(codec, obj, overrides, groupVersionKind.Kind) - if err != nil { - return nil, "", nil, nil, err - } - } - - mapping, err := mapper.RESTMapping(groupVersionKind.GroupKind(), groupVersionKind.Version) - if err != nil { - return nil, "", nil, nil, err - } - client, err := f.ClientForMapping(mapping) - if err != nil { - return nil, "", nil, nil, err - } - - annotations, err := mapping.MetadataAccessor.Annotations(obj) - if err != nil { - return nil, "", nil, nil, err - } - if cmdutil.GetRecordFlag(cmd) || len(annotations[kubectl.ChangeCauseAnnotation]) > 0 { - if err := cmdutil.RecordChangeCause(obj, f.Command()); err != nil { - return nil, "", nil, nil, err - } - } - // TODO: extract this flag to a central location, when such a location exists. - if !cmdutil.GetFlagBool(cmd, "dry-run") { - resourceMapper := &resource.Mapper{ - ObjectTyper: typer, - RESTMapper: mapper, - ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), - Decoder: f.Decoder(true), - } - info, err := resourceMapper.InfoForObject(obj) - if err != nil { - return nil, "", nil, nil, err - } - - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { - return nil, "", nil, nil, err - } - - obj, err = resource.NewHelper(client, mapping).Create(namespace, false, info.Object) - if err != nil { - return nil, "", nil, nil, err - } - } - return obj, groupVersionKind.Kind, mapper, mapping, err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/run_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/run_test.go deleted file mode 100644 index a728a810e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/run_test.go +++ /dev/null @@ -1,331 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "bytes" - "fmt" - "io/ioutil" - "net/http" - "os" - "reflect" - "testing" - - "github.com/spf13/cobra" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/fake" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/intstr" -) - -func TestGetRestartPolicy(t *testing.T) { - tests := []struct { - input string - interactive bool - expected api.RestartPolicy - expectErr bool - }{ - { - input: "", - expected: api.RestartPolicyAlways, - }, - { - input: "", - interactive: true, - expected: api.RestartPolicyOnFailure, - }, - { - input: string(api.RestartPolicyAlways), - interactive: true, - expected: api.RestartPolicyAlways, - }, - { - input: string(api.RestartPolicyNever), - interactive: true, - expected: api.RestartPolicyNever, - }, - { - input: string(api.RestartPolicyAlways), - expected: api.RestartPolicyAlways, - }, - { - input: string(api.RestartPolicyNever), - expected: api.RestartPolicyNever, - }, - { - input: "foo", - expectErr: true, - }, - } - for _, test := range tests { - cmd := &cobra.Command{} - cmd.Flags().String("restart", "", "dummy restart flag") - cmd.Flags().Lookup("restart").Value.Set(test.input) - policy, err := getRestartPolicy(cmd, test.interactive) - if test.expectErr && err == nil { - t.Error("unexpected non-error") - } - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v", err) - } - if !test.expectErr && policy != test.expected { - t.Errorf("expected: %s, saw: %s (%s:%v)", test.expected, policy, test.input, test.interactive) - } - } -} - -func TestGetEnv(t *testing.T) { - test := struct { - input []string - expected []string - }{ - input: []string{"a=b", "c=d"}, - expected: []string{"a=b", "c=d"}, - } - cmd := &cobra.Command{} - cmd.Flags().StringSlice("env", test.input, "") - - envStrings := cmdutil.GetFlagStringSlice(cmd, "env") - if len(envStrings) != 2 || !reflect.DeepEqual(envStrings, test.expected) { - t.Errorf("expected: %s, saw: %s", test.expected, envStrings) - } -} - -func TestRunArgsFollowDashRules(t *testing.T) { - _, _, rc := testData() - - tests := []struct { - args []string - argsLenAtDash int - expectError bool - name string - }{ - { - args: []string{}, - argsLenAtDash: -1, - expectError: true, - name: "empty", - }, - { - args: []string{"foo"}, - argsLenAtDash: -1, - expectError: false, - name: "no cmd", - }, - { - args: []string{"foo", "sleep"}, - argsLenAtDash: -1, - expectError: false, - name: "cmd no dash", - }, - { - args: []string{"foo", "sleep"}, - argsLenAtDash: 1, - expectError: false, - name: "cmd has dash", - }, - { - args: []string{"foo", "sleep"}, - argsLenAtDash: 0, - expectError: true, - name: "no name", - }, - } - for _, test := range tests { - f, tf, codec := NewAPIFactory() - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - return &http.Response{StatusCode: 201, Body: objBody(codec, &rc.Items[0])}, nil - }), - } - tf.Namespace = "test" - tf.ClientConfig = &client.Config{} - cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr) - cmd.Flags().Set("image", "nginx") - cmd.Flags().Set("generator", "run/v1") - err := Run(f, os.Stdin, os.Stdout, os.Stderr, cmd, test.args, test.argsLenAtDash) - if test.expectError && err == nil { - t.Errorf("unexpected non-error (%s)", test.name) - } - if !test.expectError && err != nil { - t.Errorf("unexpected error: %v (%s)", err, test.name) - } - } -} - -func TestGenerateService(t *testing.T) { - - tests := []struct { - port string - args []string - serviceGenerator string - params map[string]interface{} - expectErr bool - name string - service api.Service - expectPOST bool - }{ - { - port: "80", - args: []string{"foo"}, - serviceGenerator: "service/v2", - params: map[string]interface{}{ - "name": "foo", - }, - expectErr: false, - name: "basic", - service: api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.ServiceSpec{ - Ports: []api.ServicePort{ - { - Port: 80, - Protocol: "TCP", - TargetPort: intstr.FromInt(80), - }, - }, - Selector: map[string]string{ - "run": "foo", - }, - Type: api.ServiceTypeClusterIP, - SessionAffinity: api.ServiceAffinityNone, - }, - }, - expectPOST: true, - }, - { - port: "80", - args: []string{"foo"}, - serviceGenerator: "service/v2", - params: map[string]interface{}{ - "name": "foo", - "labels": "app=bar", - }, - expectErr: false, - name: "custom labels", - service: api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"app": "bar"}, - }, - Spec: api.ServiceSpec{ - Ports: []api.ServicePort{ - { - Port: 80, - Protocol: "TCP", - TargetPort: intstr.FromInt(80), - }, - }, - Selector: map[string]string{ - "app": "bar", - }, - Type: api.ServiceTypeClusterIP, - SessionAffinity: api.ServiceAffinityNone, - }, - }, - expectPOST: true, - }, - { - expectErr: true, - name: "missing port", - expectPOST: false, - }, - { - port: "80", - args: []string{"foo"}, - serviceGenerator: "service/v2", - params: map[string]interface{}{ - "name": "foo", - }, - expectErr: false, - name: "dry-run", - expectPOST: false, - }, - } - for _, test := range tests { - sawPOST := false - f, tf, codec := NewAPIFactory() - tf.ClientConfig = &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} - tf.Client = &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case test.expectPOST && m == "POST" && p == "/namespaces/namespace/services": - sawPOST = true - body := objBody(codec, &test.service) - data, err := ioutil.ReadAll(req.Body) - if err != nil { - t.Errorf("unexpected error: %v", err) - t.FailNow() - } - defer req.Body.Close() - svc := &api.Service{} - if err := runtime.DecodeInto(codec, data, svc); err != nil { - t.Errorf("unexpected error: %v", err) - t.FailNow() - } - // Copy things that are defaulted by the system - test.service.Annotations = svc.Annotations - - if !reflect.DeepEqual(&test.service, svc) { - t.Errorf("expected:\n%v\nsaw:\n%v\n", &test.service, svc) - } - return &http.Response{StatusCode: 200, Body: body}, nil - default: - // Ensures no GET is performed when deleting by name - t.Errorf("%s: unexpected request: %s %#v\n%#v", test.name, req.Method, req.URL, req) - return nil, fmt.Errorf("unexpected request") - } - }), - } - cmd := &cobra.Command{} - cmd.Flags().String("output", "", "") - cmd.Flags().Bool(cmdutil.ApplyAnnotationsFlag, false, "") - cmd.Flags().Bool("record", false, "Record current kubectl command in the resource annotation.") - addRunFlags(cmd) - - if !test.expectPOST { - cmd.Flags().Set("dry-run", "true") - } - - if len(test.port) > 0 { - cmd.Flags().Set("port", test.port) - test.params["port"] = test.port - } - - buff := &bytes.Buffer{} - err := generateService(f, cmd, test.args, test.serviceGenerator, test.params, "namespace", buff) - if test.expectErr { - if err == nil { - t.Error("unexpected non-error") - } - continue - } - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if test.expectPOST != sawPOST { - t.Error("expectPost: %v, sawPost: %v", test.expectPOST, sawPOST) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/scale.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/scale.go deleted file mode 100644 index 6ffd43566..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/scale.go +++ /dev/null @@ -1,176 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - "os" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - utilerrors "k8s.io/kubernetes/pkg/util/errors" -) - -// ScaleOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of -// referencing the cmd.Flags() -type ScaleOptions struct { - Filenames []string -} - -const ( - scale_long = `Set a new size for a Replication Controller, Job, or Deployment. - -Scale also allows users to specify one or more preconditions for the scale action. -If --current-replicas or --resource-version is specified, it is validated before the -scale is attempted, and it is guaranteed that the precondition holds true when the -scale is sent to the server.` - scale_example = `# Scale replication controller named 'foo' to 3. -$ kubectl scale --replicas=3 rc/foo - -# Scale a resource identified by type and name specified in "foo.yaml" to 3. -$ kubectl scale --replicas=3 -f foo.yaml - -# If the deployment named mysql's current size is 2, scale mysql to 3. -$ kubectl scale --current-replicas=2 --replicas=3 deployment/mysql - -# Scale multiple replication controllers. -$ kubectl scale --replicas=5 rc/foo rc/bar rc/baz - -# Scale job named 'cron' to 3. -$ kubectl scale --replicas=3 job/cron` -) - -// NewCmdScale returns a cobra command with the appropriate configuration and flags to run scale -func NewCmdScale(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &ScaleOptions{} - - cmd := &cobra.Command{ - Use: "scale [--resource-version=version] [--current-replicas=count] --replicas=COUNT (-f FILENAME | TYPE NAME)", - // resize is deprecated - Aliases: []string{"resize"}, - Short: "Set a new size for a Replication Controller, Job, or Deployment.", - Long: scale_long, - Example: scale_example, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) - shortOutput := cmdutil.GetFlagString(cmd, "output") == "name" - err := RunScale(f, out, cmd, args, shortOutput, options) - cmdutil.CheckErr(err) - }, - } - cmd.Flags().String("resource-version", "", "Precondition for resource version. Requires that the current resource version match this value in order to scale.") - cmd.Flags().Int("current-replicas", -1, "Precondition for current size. Requires that the current size of the resource match this value in order to scale.") - cmd.Flags().Int("replicas", -1, "The new desired number of replicas. Required.") - cmd.MarkFlagRequired("replicas") - cmd.Flags().Duration("timeout", 0, "The length of time to wait before giving up on a scale operation, zero means don't wait.") - cmdutil.AddOutputFlagsForMutation(cmd) - cmdutil.AddRecordFlag(cmd) - - usage := "Filename, directory, or URL to a file identifying the resource to set a new size" - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) - return cmd -} - -// RunScale executes the scaling -func RunScale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, shortOutput bool, options *ScaleOptions) error { - if len(os.Args) > 1 && os.Args[1] == "resize" { - printDeprecationWarning("scale", "resize") - } - - count := cmdutil.GetFlagInt(cmd, "replicas") - if count < 0 { - return cmdutil.UsageError(cmd, "--replicas=COUNT is required, and COUNT must be greater than or equal to 0") - } - - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - ContinueOnError(). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options.Filenames...). - ResourceTypeOrNameArgs(false, args...). - Flatten(). - Do() - err = r.Err() - if err != nil { - return err - } - - infos, err := r.Infos() - if err != nil { - return err - } - info := infos[0] - mapping := info.ResourceMapping() - scaler, err := f.Scaler(mapping) - if err != nil { - return err - } - - resourceVersion := cmdutil.GetFlagString(cmd, "resource-version") - if len(resourceVersion) != 0 && len(infos) > 1 { - return fmt.Errorf("cannot use --resource-version with multiple resources") - } - currentSize := cmdutil.GetFlagInt(cmd, "current-replicas") - if currentSize != -1 && len(infos) > 1 { - return fmt.Errorf("cannot use --current-replicas with multiple resources") - } - precondition := &kubectl.ScalePrecondition{Size: currentSize, ResourceVersion: resourceVersion} - retry := kubectl.NewRetryParams(kubectl.Interval, kubectl.Timeout) - var waitForReplicas *kubectl.RetryParams - if timeout := cmdutil.GetFlagDuration(cmd, "timeout"); timeout != 0 { - waitForReplicas = kubectl.NewRetryParams(kubectl.Interval, timeout) - } - - errs := []error{} - for _, info := range infos { - if err := scaler.Scale(info.Namespace, info.Name, uint(count), precondition, retry, waitForReplicas); err != nil { - errs = append(errs, err) - continue - } - if cmdutil.ShouldRecord(cmd, info) { - patchBytes, err := cmdutil.ChangeResourcePatch(info, f.Command()) - if err != nil { - errs = append(errs, err) - continue - } - mapping := info.ResourceMapping() - client, err := f.ClientForMapping(mapping) - if err != nil { - return err - } - helper := resource.NewHelper(client, mapping) - _, err = helper.Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patchBytes) - if err != nil { - errs = append(errs, err) - continue - } - } - cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, "scaled") - } - - return utilerrors.NewAggregate(errs) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/stop.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/stop.go deleted file mode 100644 index 606986617..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/stop.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" -) - -// StopOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of -// referencing the cmd.Flags() -type StopOptions struct { - Filenames []string -} - -const ( - stop_long = `Deprecated: Gracefully shut down a resource by name or filename. - -The stop command is deprecated, all its functionalities are covered by delete command. -See 'kubectl delete --help' for more details. - -Attempts to shut down and delete a resource that supports graceful termination. -If the resource is scalable it will be scaled to 0 before deletion.` - stop_example = `# Shut down foo. -$ kubectl stop replicationcontroller foo - -# Stop pods and services with label name=myLabel. -$ kubectl stop pods,services -l name=myLabel - -# Shut down the service defined in service.json -$ kubectl stop -f service.json - -# Shut down all resources in the path/to/resources directory -$ kubectl stop -f path/to/resources` -) - -func NewCmdStop(f *cmdutil.Factory, out io.Writer) *cobra.Command { - options := &StopOptions{} - - cmd := &cobra.Command{ - Use: "stop (-f FILENAME | TYPE (NAME | -l label | --all))", - Short: "Deprecated: Gracefully shut down a resource by name or filename.", - Long: stop_long, - Example: stop_example, - Deprecated: fmt.Sprintf("use %q instead.", "delete"), - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) - cmdutil.CheckErr(RunStop(f, cmd, args, out, options)) - }, - } - usage := "Filename, directory, or URL to file of resource(s) to be stopped." - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) - cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on.") - cmd.Flags().Bool("all", false, "[-all] to select all the specified resources.") - cmd.Flags().Bool("ignore-not-found", false, "Treat \"resource not found\" as a successful stop.") - cmd.Flags().Int("grace-period", -1, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative.") - cmd.Flags().Duration("timeout", 0, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object") - cmdutil.AddOutputFlagsForMutation(cmd) - return cmd -} - -func RunStop(f *cmdutil.Factory, cmd *cobra.Command, args []string, out io.Writer, options *StopOptions) error { - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). - ContinueOnError(). - NamespaceParam(cmdNamespace).DefaultNamespace(). - ResourceTypeOrNameArgs(false, args...). - FilenameParam(enforceNamespace, options.Filenames...). - SelectorParam(cmdutil.GetFlagString(cmd, "selector")). - SelectAllParam(cmdutil.GetFlagBool(cmd, "all")). - Flatten(). - Do() - if r.Err() != nil { - return r.Err() - } - shortOutput := cmdutil.GetFlagString(cmd, "output") == "name" - return ReapResult(r, f, out, false, cmdutil.GetFlagBool(cmd, "ignore-not-found"), cmdutil.GetFlagDuration(cmd, "timeout"), cmdutil.GetFlagInt(cmd, "grace-period"), shortOutput, mapper) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/clientcache.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/clientcache.go index 523c57750..5e6551cde 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/clientcache.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/clientcache.go @@ -19,6 +19,7 @@ package util import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/client/restclient" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" ) @@ -26,7 +27,7 @@ import ( func NewClientCache(loader clientcmd.ClientConfig) *ClientCache { return &ClientCache{ clients: make(map[unversioned.GroupVersion]*client.Client), - configs: make(map[unversioned.GroupVersion]*client.Config), + configs: make(map[unversioned.GroupVersion]*restclient.Config), loader: loader, } } @@ -36,14 +37,14 @@ func NewClientCache(loader clientcmd.ClientConfig) *ClientCache { type ClientCache struct { loader clientcmd.ClientConfig clients map[unversioned.GroupVersion]*client.Client - configs map[unversioned.GroupVersion]*client.Config - defaultConfig *client.Config + configs map[unversioned.GroupVersion]*restclient.Config + defaultConfig *restclient.Config defaultClient *client.Client matchVersion bool } // ClientConfigForVersion returns the correct config for a server -func (c *ClientCache) ClientConfigForVersion(version *unversioned.GroupVersion) (*client.Config, error) { +func (c *ClientCache) ClientConfigForVersion(version *unversioned.GroupVersion) (*restclient.Config, error) { if c.defaultConfig == nil { config, err := c.loader.ClientConfig() if err != nil { diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/editor.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/editor.go deleted file mode 100644 index f77f8e137..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/editor.go +++ /dev/null @@ -1,226 +0,0 @@ -/* -Copyright 2015 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 editor - -import ( - "fmt" - "io" - "io/ioutil" - "math/rand" - "os" - "os/exec" - "os/signal" - "path/filepath" - "runtime" - "strings" - - "github.com/docker/docker/pkg/term" - "github.com/golang/glog" -) - -const ( - // sorry, blame Git - // TODO: on Windows rely on 'start' to launch the editor associated - // with the given file type. If we can't because of the need of - // blocking, use a script with 'ftype' and 'assoc' to detect it. - defaultEditor = "vi" - defaultShell = "/bin/bash" - windowsEditor = "notepad" - windowsShell = "cmd" -) - -type Editor struct { - Args []string - Shell bool -} - -// NewDefaultEditor creates a struct Editor that uses the OS environment to -// locate the editor program, looking at EDITOR environment variable to find -// the proper command line. If the provided editor has no spaces, or no quotes, -// it is treated as a bare command to be loaded. Otherwise, the string will -// be passed to the user's shell for execution. -func NewDefaultEditor(envs []string) Editor { - args, shell := defaultEnvEditor(envs) - return Editor{ - Args: args, - Shell: shell, - } -} - -func defaultEnvShell() []string { - shell := os.Getenv("SHELL") - if len(shell) == 0 { - shell = platformize(defaultShell, windowsShell) - } - flag := "-c" - if shell == windowsShell { - flag = "/C" - } - return []string{shell, flag} -} - -func defaultEnvEditor(envs []string) ([]string, bool) { - var editor string - for _, env := range envs { - if len(env) > 0 { - editor = os.Getenv(env) - } - if len(editor) > 0 { - break - } - } - if len(editor) == 0 { - editor = platformize(defaultEditor, windowsEditor) - } - if !strings.Contains(editor, " ") { - return []string{editor}, false - } - if !strings.ContainsAny(editor, "\"'\\") { - return strings.Split(editor, " "), false - } - // rather than parse the shell arguments ourselves, punt to the shell - shell := defaultEnvShell() - return append(shell, editor), true -} - -func (e Editor) args(path string) []string { - args := make([]string, len(e.Args)) - copy(args, e.Args) - if e.Shell { - last := args[len(args)-1] - args[len(args)-1] = fmt.Sprintf("%s %q", last, path) - } else { - args = append(args, path) - } - return args -} - -// Launch opens the described or returns an error. The TTY will be protected, and -// SIGQUIT, SIGTERM, and SIGINT will all be trapped. -func (e Editor) Launch(path string) error { - if len(e.Args) == 0 { - return fmt.Errorf("no editor defined, can't open %s", path) - } - abs, err := filepath.Abs(path) - if err != nil { - return err - } - args := e.args(abs) - cmd := exec.Command(args[0], args[1:]...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Stdin = os.Stdin - glog.V(5).Infof("Opening file with editor %v", args) - if err := withSafeTTYAndInterrupts(cmd.Run); err != nil { - if err, ok := err.(*exec.Error); ok { - if err.Err == exec.ErrNotFound { - return fmt.Errorf("unable to launch the editor %q", strings.Join(e.Args, " ")) - } - } - return fmt.Errorf("there was a problem with the editor %q", strings.Join(e.Args, " ")) - } - return nil -} - -// LaunchTempFile reads the provided stream into a temporary file in the given directory -// and file prefix, and then invokes Launch with the path of that file. It will return -// the contents of the file after launch, any errors that occur, and the path of the -// temporary file so the caller can clean it up as needed. -func (e Editor) LaunchTempFile(prefix, suffix string, r io.Reader) ([]byte, string, error) { - f, err := tempFile(prefix, suffix) - if err != nil { - return nil, "", err - } - defer f.Close() - path := f.Name() - if _, err := io.Copy(f, r); err != nil { - os.Remove(path) - return nil, path, err - } - // This file descriptor needs to close so the next process (Launch) can claim it. - f.Close() - if err := e.Launch(path); err != nil { - return nil, path, err - } - bytes, err := ioutil.ReadFile(path) - return bytes, path, err -} - -// withSafeTTYAndInterrupts invokes the provided function after the terminal -// state has been stored, and then on any error or termination attempts to -// restore the terminal state to its prior behavior. It also eats signals -// for the duration of the function. -func withSafeTTYAndInterrupts(fn func() error) error { - ch := make(chan os.Signal, 1) - signal.Notify(ch, childSignals...) - defer signal.Stop(ch) - - inFd := os.Stdin.Fd() - if !term.IsTerminal(inFd) { - if f, err := os.Open("/dev/tty"); err == nil { - defer f.Close() - inFd = f.Fd() - } - } - - if term.IsTerminal(inFd) { - state, err := term.SaveState(inFd) - if err != nil { - return err - } - go func() { - if _, ok := <-ch; !ok { - return - } - term.RestoreTerminal(inFd, state) - }() - defer term.RestoreTerminal(inFd, state) - return fn() - } - return fn() -} - -func tempFile(prefix, suffix string) (f *os.File, err error) { - dir := os.TempDir() - - for i := 0; i < 10000; i++ { - name := filepath.Join(dir, prefix+randSeq(5)+suffix) - f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) - if os.IsExist(err) { - continue - } - break - } - return -} - -var letters = []rune("abcdefghijklmnopqrstuvwxyz0123456789") - -func randSeq(n int) string { - b := make([]rune, n) - for i := range b { - b[i] = letters[rand.Intn(len(letters))] - } - return string(b) -} - -func platformize(linux, windows string) string { - if runtime.GOOS == "windows" { - return windows - } - return linux -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/editor_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/editor_test.go deleted file mode 100644 index 9be83a042..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/editor_test.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2015 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 editor - -import ( - "bytes" - "io/ioutil" - "os" - "reflect" - "strings" - "testing" -) - -func TestArgs(t *testing.T) { - if e, a := []string{"/bin/bash", "-c \"test\""}, (Editor{Args: []string{"/bin/bash", "-c"}, Shell: true}).args("test"); !reflect.DeepEqual(e, a) { - t.Errorf("unexpected args: %v", a) - } - if e, a := []string{"/bin/bash", "-c", "test"}, (Editor{Args: []string{"/bin/bash", "-c"}, Shell: false}).args("test"); !reflect.DeepEqual(e, a) { - t.Errorf("unexpected args: %v", a) - } - if e, a := []string{"/bin/bash", "-i -c \"test\""}, (Editor{Args: []string{"/bin/bash", "-i -c"}, Shell: true}).args("test"); !reflect.DeepEqual(e, a) { - t.Errorf("unexpected args: %v", a) - } - if e, a := []string{"/test", "test"}, (Editor{Args: []string{"/test"}}).args("test"); !reflect.DeepEqual(e, a) { - t.Errorf("unexpected args: %v", a) - } -} - -func TestEditor(t *testing.T) { - edit := Editor{Args: []string{"cat"}} - testStr := "test something\n" - contents, path, err := edit.LaunchTempFile("", "someprefix", bytes.NewBufferString(testStr)) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if _, err := os.Stat(path); err != nil { - t.Fatalf("no temp file: %s", path) - } - defer os.Remove(path) - if disk, err := ioutil.ReadFile(path); err != nil || !bytes.Equal(contents, disk) { - t.Errorf("unexpected file on disk: %v %s", err, string(disk)) - } - if !bytes.Equal(contents, []byte(testStr)) { - t.Errorf("unexpected contents: %s", string(contents)) - } - if !strings.Contains(path, "someprefix") { - t.Errorf("path not expected: %s", path) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/term_unsupported.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/term_unsupported.go deleted file mode 100644 index 4c0b78816..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/term_unsupported.go +++ /dev/null @@ -1,26 +0,0 @@ -// +build windows - -/* -Copyright 2015 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 editor - -import ( - "os" -) - -// childSignals are the allowed signals that can be sent to children in Windows to terminate -var childSignals = []os.Signal{os.Interrupt} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory.go index ca6413b75..78b2503b4 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory.go @@ -26,6 +26,7 @@ import ( "os" "os/user" "path" + "path/filepath" "strconv" "strings" "time" @@ -40,8 +41,11 @@ import ( "k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/autoscaling" + "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/metrics" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + "k8s.io/kubernetes/pkg/client/restclient" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" "k8s.io/kubernetes/pkg/kubectl" @@ -64,7 +68,6 @@ const ( type Factory struct { clients *ClientCache flags *pflag.FlagSet - cmd string // Returns interfaces for dealing with arbitrary runtime.Objects. Object func() (meta.RESTMapper, runtime.ObjectTyper) @@ -77,7 +80,7 @@ type Factory struct { // Returns a client for accessing Kubernetes resources or an error. Client func() (*client.Client, error) // Returns a client.Config for accessing the Kubernetes server. - ClientConfig func() (*client.Config, error) + ClientConfig func() (*restclient.Config, error) // Returns a RESTClient for working with the specified RESTMapping or an error. This is intended // for working with arbitrary resources and is not guaranteed to point to a Kubernetes APIServer. ClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error) @@ -104,7 +107,7 @@ type Factory struct { // LabelsForObject returns the labels associated with the provided object LabelsForObject func(object runtime.Object) (map[string]string, error) // LogsForObject returns a request for the logs associated with the provided object - LogsForObject func(object, options runtime.Object) (*client.Request, error) + LogsForObject func(object, options runtime.Object) (*restclient.Request, error) // PauseObject marks the provided object as paused ie. it will not be reconciled by its controller. PauseObject func(object runtime.Object) (bool, error) // ResumeObject resumes a paused object ie. it will be reconciled by its controller. @@ -136,9 +139,11 @@ const ( RunPodV1GeneratorName = "run-pod/v1" ServiceV1GeneratorName = "service/v1" ServiceV2GeneratorName = "service/v2" + ServiceAccountV1GeneratorName = "serviceaccount/v1" HorizontalPodAutoscalerV1Beta1GeneratorName = "horizontalpodautoscaler/v1beta1" DeploymentV1Beta1GeneratorName = "deployment/v1beta1" JobV1Beta1GeneratorName = "job/v1beta1" + JobV1GeneratorName = "job/v1" NamespaceV1GeneratorName = "namespace/v1" SecretV1GeneratorName = "secret/v1" SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1" @@ -157,6 +162,7 @@ func DefaultGenerators(cmdName string) map[string]kubectl.Generator { RunPodV1GeneratorName: kubectl.BasicPod{}, DeploymentV1Beta1GeneratorName: kubectl.DeploymentV1Beta1{}, JobV1Beta1GeneratorName: kubectl.JobV1Beta1{}, + JobV1GeneratorName: kubectl.JobV1{}, } generators["autoscale"] = map[string]kubectl.Generator{ HorizontalPodAutoscalerV1Beta1GeneratorName: kubectl.HorizontalPodAutoscalerV1Beta1{}, @@ -192,7 +198,6 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { return &Factory{ clients: clients, flags: flags, - cmd: recordCommand(os.Args), Object: func() (meta.RESTMapper, runtime.ObjectTyper) { cfg, err := clientConfig.ClientConfig() @@ -202,12 +207,29 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { cmdApiVersion = *cfg.GroupVersion } - return kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}}, api.Scheme + outputRESTMapper := kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}} + + // eventually this should allow me choose a group priority based on the order of the discovery doc, for now hardcode a given order + priorityRESTMapper := meta.PriorityRESTMapper{ + Delegate: outputRESTMapper, + ResourcePriority: []unversioned.GroupVersionResource{ + {Group: api.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, + {Group: extensions.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, + {Group: metrics.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, + }, + KindPriority: []unversioned.GroupVersionKind{ + {Group: api.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, + {Group: extensions.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, + {Group: metrics.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, + }, + } + + return priorityRESTMapper, api.Scheme }, Client: func() (*client.Client, error) { return clients.ClientForVersion(nil) }, - ClientConfig: func() (*client.Config, error) { + ClientConfig: func() (*restclient.Config, error) { return clients.ClientConfigForVersion(nil) }, ClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { @@ -221,6 +243,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { return client.RESTClient, nil case autoscaling.GroupName: return client.AutoscalingClient.RESTClient, nil + case batch.GroupName: + return client.BatchClient.RESTClient, nil case extensions.GroupName: return client.ExtensionsClient.RESTClient, nil } @@ -345,7 +369,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { LabelsForObject: func(object runtime.Object) (map[string]string, error) { return meta.NewAccessor().Labels(object) }, - LogsForObject: func(object, options runtime.Object) (*client.Request, error) { + LogsForObject: func(object, options runtime.Object) (*restclient.Request, error) { c, err := clients.ClientForVersion(nil) if err != nil { return nil, err @@ -358,6 +382,42 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { return nil, errors.New("provided options object is not a PodLogOptions") } return c.Pods(t.Namespace).GetLogs(t.Name, opts), nil + + case *api.ReplicationController: + opts, ok := options.(*api.PodLogOptions) + if !ok { + return nil, errors.New("provided options object is not a PodLogOptions") + } + selector := labels.SelectorFromSet(t.Spec.Selector) + pod, numPods, err := GetFirstPod(c, t.Namespace, selector) + if err != nil { + return nil, err + } + if numPods > 1 { + fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name) + } + + return c.Pods(pod.Namespace).GetLogs(pod.Name, opts), nil + + case *extensions.ReplicaSet: + opts, ok := options.(*api.PodLogOptions) + if !ok { + return nil, errors.New("provided options object is not a PodLogOptions") + } + selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) + if err != nil { + return nil, fmt.Errorf("invalid label selector: %v", err) + } + pod, numPods, err := GetFirstPod(c, t.Namespace, selector) + if err != nil { + return nil, err + } + if numPods > 1 { + fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name) + } + + return c.Pods(pod.Namespace).GetLogs(pod.Name, opts), nil + default: gvk, err := api.Scheme.ObjectKind(object) if err != nil { @@ -490,7 +550,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { }, CanBeAutoscaled: func(kind unversioned.GroupKind) error { switch kind { - case api.Kind("ReplicationController"), extensions.Kind("Deployment"): + case api.Kind("ReplicationController"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"): // nothing to do here default: return fmt.Errorf("cannot autoscale a %v", kind) @@ -505,19 +565,22 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { switch t := object.(type) { case *api.ReplicationController: selector := labels.SelectorFromSet(t.Spec.Selector) - return GetFirstPod(client, t.Namespace, selector) + pod, _, err := GetFirstPod(client, t.Namespace, selector) + return pod, err case *extensions.Deployment: selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } - return GetFirstPod(client, t.Namespace, selector) + pod, _, err := GetFirstPod(client, t.Namespace, selector) + return pod, err case *extensions.Job: selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector) if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } - return GetFirstPod(client, t.Namespace, selector) + pod, _, err := GetFirstPod(client, t.Namespace, selector) + return pod, err case *api.Pod: return t, nil default: @@ -534,39 +597,37 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { } } -// GetFirstPod returns the first pod of an object from its namespace and selector -func GetFirstPod(client *client.Client, namespace string, selector labels.Selector) (*api.Pod, error) { +// GetFirstPod returns the first pod of an object from its namespace and selector and the number of matching pods +func GetFirstPod(client *client.Client, namespace string, selector labels.Selector) (*api.Pod, int, error) { var pods *api.PodList for pods == nil || len(pods.Items) == 0 { var err error options := api.ListOptions{LabelSelector: selector} if pods, err = client.Pods(namespace).List(options); err != nil { - return nil, err + return nil, 0, err } if len(pods.Items) == 0 { time.Sleep(2 * time.Second) } } pod := &pods.Items[0] - return pod, nil -} - -func recordCommand(args []string) string { - if len(args) > 0 { - args[0] = "kubectl" - } - return strings.Join(args, " ") + return pod, len(pods.Items), nil } +// Command will stringify and return all environment arguments ie. a command run by a client +// using the factory. +// TODO: We need to filter out stuff like secrets. func (f *Factory) Command() string { - return f.cmd + if len(os.Args) == 0 { + return "" + } + base := filepath.Base(os.Args[0]) + args := append([]string{base}, os.Args[1:]...) + return strings.Join(args, " ") } // BindFlags adds any flags that are common to all kubectl sub commands. func (f *Factory) BindFlags(flags *pflag.FlagSet) { - // any flags defined by external projects (not part of pflags) - flags.AddGoFlagSet(flag.CommandLine) - // Merge factory's flags flags.AddFlagSet(f.flags) @@ -581,6 +642,12 @@ func (f *Factory) BindFlags(flags *pflag.FlagSet) { flags.SetNormalizeFunc(util.WordSepNormalizeFunc) } +// BindCommonFlags adds any flags defined by external projects (not part of pflags) +func (f *Factory) BindExternalFlags(flags *pflag.FlagSet) { + // any flags defined by external projects (not part of pflags) + flags.AddGoFlagSet(flag.CommandLine) +} + func getPorts(spec api.PodSpec) []string { result := []string{} for _, container := range spec.Containers { @@ -609,7 +676,7 @@ type clientSwaggerSchema struct { const schemaFileName = "schema.json" type schemaClient interface { - Get() *client.Request + Get() *restclient.Request } func recursiveSplit(dir string) []string { @@ -664,6 +731,7 @@ func writeSchemaFile(schemaData []byte, cacheDir, cacheFile, prefix, groupVersio func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cacheDir string) (err error) { var schemaData []byte + var firstSeen bool fullDir, err := substituteUserHome(cacheDir) if err != nil { return err @@ -676,24 +744,50 @@ func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cac } } if schemaData == nil { - schemaData, err = c.Get(). - AbsPath("/swaggerapi", prefix, groupVersion). - Do(). - Raw() + firstSeen = true + schemaData, err = downloadSchemaAndStore(c, cacheDir, fullDir, cacheFile, prefix, groupVersion) if err != nil { return err } - if len(cacheDir) != 0 { - if err := writeSchemaFile(schemaData, fullDir, cacheFile, prefix, groupVersion); err != nil { - return err - } - } } schema, err := validation.NewSwaggerSchemaFromBytes(schemaData) if err != nil { return err } - return schema.ValidateBytes(data) + err = schema.ValidateBytes(data) + if _, ok := err.(validation.TypeNotFoundError); ok && !firstSeen { + // As a temporay hack, kubectl would re-get the schema if validation + // fails for type not found reason. + // TODO: runtime-config settings needs to make into the file's name + schemaData, err = downloadSchemaAndStore(c, cacheDir, fullDir, cacheFile, prefix, groupVersion) + if err != nil { + return err + } + schema, err := validation.NewSwaggerSchemaFromBytes(schemaData) + if err != nil { + return err + } + return schema.ValidateBytes(data) + } + + return err +} + +// Download swagger schema from apiserver and store it to file. +func downloadSchemaAndStore(c schemaClient, cacheDir, fullDir, cacheFile, prefix, groupVersion string) (schemaData []byte, err error) { + schemaData, err = c.Get(). + AbsPath("/swaggerapi", prefix, groupVersion). + Do(). + Raw() + if err != nil { + return + } + if len(cacheDir) != 0 { + if err = writeSchemaFile(schemaData, fullDir, cacheFile, prefix, groupVersion); err != nil { + return + } + } + return } func (c *clientSwaggerSchema) ValidateBytes(data []byte) error { @@ -710,6 +804,12 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error { } return getSchemaAndValidate(c.c.AutoscalingClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir) } + if gvk.Group == batch.GroupName { + if c.c.BatchClient == nil { + return errors.New("unable to validate: no batch client") + } + return getSchemaAndValidate(c.c.BatchClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir) + } if gvk.Group == extensions.GroupName { if c.c.ExtensionsClient == nil { return errors.New("unable to validate: no experimental client") diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory_test.go deleted file mode 100644 index be9484fae..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/factory_test.go +++ /dev/null @@ -1,338 +0,0 @@ -/* -Copyright 2014 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 util - -import ( - "bytes" - "encoding/json" - "io/ioutil" - "net/http" - "os" - "os/user" - "path" - "sort" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/validation" - "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" - "k8s.io/kubernetes/pkg/client/unversioned/fake" - "k8s.io/kubernetes/pkg/kubectl" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util" -) - -func TestNewFactoryDefaultFlagBindings(t *testing.T) { - factory := NewFactory(nil) - - if !factory.flags.HasFlags() { - t.Errorf("Expected flags, but didn't get any") - } -} - -func TestNewFactoryNoFlagBindings(t *testing.T) { - clientConfig := clientcmd.NewDefaultClientConfig(*clientcmdapi.NewConfig(), &clientcmd.ConfigOverrides{}) - factory := NewFactory(clientConfig) - - if factory.flags.HasFlags() { - t.Errorf("Expected zero flags, but got %v", factory.flags) - } -} - -func TestPodSelectorForObject(t *testing.T) { - f := NewFactory(nil) - - svc := &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test"}, - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - }, - } - - expected := "foo=bar" - got, err := f.PodSelectorForObject(svc) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if expected != got { - t.Fatalf("Selector mismatch! Expected %s, got %s", expected, got) - } -} - -func TestPortsForObject(t *testing.T) { - f := NewFactory(nil) - - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Ports: []api.ContainerPort{ - { - ContainerPort: 101, - }, - }, - }, - }, - }, - } - - expected := []string{"101"} - got, err := f.PortsForObject(pod) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if len(expected) != len(got) { - t.Fatalf("Ports size mismatch! Expected %d, got %d", len(expected), len(got)) - } - - sort.Strings(expected) - sort.Strings(got) - - for i, port := range got { - if port != expected[i] { - t.Fatalf("Port mismatch! Expected %s, got %s", expected[i], port) - } - } -} - -func TestLabelsForObject(t *testing.T) { - f := NewFactory(nil) - - tests := []struct { - name string - object runtime.Object - expected string - err error - }{ - { - name: "successful re-use of labels", - object: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", Labels: map[string]string{"svc": "test"}}, - TypeMeta: unversioned.TypeMeta{Kind: "Service", APIVersion: "v1"}, - }, - expected: "svc=test", - err: nil, - }, - { - name: "empty labels", - object: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", Labels: map[string]string{}}, - TypeMeta: unversioned.TypeMeta{Kind: "Service", APIVersion: "v1"}, - }, - expected: "", - err: nil, - }, - { - name: "nil labels", - object: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "zen", Namespace: "test", Labels: nil}, - TypeMeta: unversioned.TypeMeta{Kind: "Service", APIVersion: "v1"}, - }, - expected: "", - err: nil, - }, - } - - for _, test := range tests { - gotLabels, err := f.LabelsForObject(test.object) - if err != test.err { - t.Fatalf("%s: Error mismatch: Expected %v, got %v", test.name, test.err, err) - } - got := kubectl.MakeLabels(gotLabels) - if test.expected != got { - t.Fatalf("%s: Labels mismatch! Expected %s, got %s", test.name, test.expected, got) - } - - } -} - -func TestCanBeExposed(t *testing.T) { - factory := NewFactory(nil) - tests := []struct { - kind unversioned.GroupKind - expectErr bool - }{ - { - kind: api.Kind("ReplicationController"), - expectErr: false, - }, - { - kind: api.Kind("Node"), - expectErr: true, - }, - } - - for _, test := range tests { - err := factory.CanBeExposed(test.kind) - if test.expectErr && err == nil { - t.Error("unexpected non-error") - } - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v", err) - } - } -} - -func TestFlagUnderscoreRenaming(t *testing.T) { - factory := NewFactory(nil) - - factory.flags.SetNormalizeFunc(util.WordSepNormalizeFunc) - factory.flags.Bool("valid_flag", false, "bool value") - - // In case of failure of this test check this PR: spf13/pflag#23 - if factory.flags.Lookup("valid_flag").Name != "valid-flag" { - t.Fatalf("Expected flag name to be valid-flag, got %s", factory.flags.Lookup("valid_flag").Name) - } -} - -func loadSchemaForTest() (validation.Schema, error) { - pathToSwaggerSpec := "../../../../api/swagger-spec/" + testapi.Default.GroupVersion().Version + ".json" - data, err := ioutil.ReadFile(pathToSwaggerSpec) - if err != nil { - return nil, err - } - return validation.NewSwaggerSchemaFromBytes(data) -} - -func TestValidateCachesSchema(t *testing.T) { - schema, err := loadSchemaForTest() - if err != nil { - t.Errorf("Error loading schema: %v", err) - t.FailNow() - } - output, err := json.Marshal(schema) - if err != nil { - t.Errorf("Error serializing schema: %v", err) - t.FailNow() - } - requests := map[string]int{} - - c := &fake.RESTClient{ - Codec: testapi.Default.Codec(), - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case strings.HasPrefix(p, "/swaggerapi") && m == "GET": - requests[p] = requests[p] + 1 - return &http.Response{StatusCode: 200, Body: ioutil.NopCloser(bytes.NewBuffer(output))}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - dir := os.TempDir() + "/schemaCache" - os.RemoveAll(dir) - - obj := &api.Pod{} - data, err := runtime.Encode(testapi.Default.Codec(), obj) - if err != nil { - t.Errorf("unexpected error: %v", err) - t.FailNow() - } - - // Initial request, should use HTTP and write - if getSchemaAndValidate(c, data, "foo", "bar", dir); err != nil { - t.Errorf("unexpected error validating: %v", err) - } - if _, err := os.Stat(path.Join(dir, "foo", "bar", schemaFileName)); err != nil { - t.Errorf("unexpected missing cache file: %v", err) - } - if requests["/swaggerapi/foo/bar"] != 1 { - t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo/bar"]) - } - - // Same version and group, should skip HTTP - if getSchemaAndValidate(c, data, "foo", "bar", dir); err != nil { - t.Errorf("unexpected error validating: %v", err) - } - if requests["/swaggerapi/foo/bar"] != 1 { - t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo/bar"]) - } - - // Different API group, should go to HTTP and write - if getSchemaAndValidate(c, data, "foo", "baz", dir); err != nil { - t.Errorf("unexpected error validating: %v", err) - } - if _, err := os.Stat(path.Join(dir, "foo", "baz", schemaFileName)); err != nil { - t.Errorf("unexpected missing cache file: %v", err) - } - if requests["/swaggerapi/foo/baz"] != 1 { - t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo/baz"]) - } - - // Different version, should go to HTTP and write - if getSchemaAndValidate(c, data, "foo2", "bar", dir); err != nil { - t.Errorf("unexpected error validating: %v", err) - } - if _, err := os.Stat(path.Join(dir, "foo2", "bar", schemaFileName)); err != nil { - t.Errorf("unexpected missing cache file: %v", err) - } - if requests["/swaggerapi/foo2/bar"] != 1 { - t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo2/bar"]) - } - - // No cache dir, should go straight to HTTP and not write - if getSchemaAndValidate(c, data, "foo", "blah", ""); err != nil { - t.Errorf("unexpected error validating: %v", err) - } - if requests["/swaggerapi/foo/blah"] != 1 { - t.Errorf("expected 1 schema request, saw: %d", requests["/swaggerapi/foo/blah"]) - } - if _, err := os.Stat(path.Join(dir, "foo", "blah", schemaFileName)); err == nil || !os.IsNotExist(err) { - t.Errorf("unexpected cache file error: %v", err) - } -} - -func TestSubstitueUser(t *testing.T) { - usr, err := user.Current() - if err != nil { - t.Logf("SKIPPING TEST: unexpected error: %v", err) - return - } - tests := []struct { - input string - expected string - expectErr bool - }{ - {input: "~/foo", expected: path.Join(os.Getenv("HOME"), "foo")}, - {input: "~" + usr.Username + "/bar", expected: usr.HomeDir + "/bar"}, - {input: "/foo/bar", expected: "/foo/bar"}, - {input: "~doesntexit/bar", expectErr: true}, - } - for _, test := range tests { - output, err := substituteUserHome(test.input) - if test.expectErr { - if err == nil { - t.Error("unexpected non-error") - } - continue - } - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if output != test.expected { - t.Errorf("expected: %s, saw: %s", test.expected, output) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/helpers_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/helpers_test.go deleted file mode 100644 index 10422e676..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/helpers_test.go +++ /dev/null @@ -1,364 +0,0 @@ -/* -Copyright 2014 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 util - -import ( - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - "reflect" - "strings" - "syscall" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/testapi" - apitesting "k8s.io/kubernetes/pkg/api/testing" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/validation/field" -) - -func TestMerge(t *testing.T) { - grace := int64(30) - tests := []struct { - obj runtime.Object - fragment string - expected runtime.Object - expectErr bool - kind string - }{ - { - kind: "Pod", - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - }, - fragment: fmt.Sprintf(`{ "apiVersion": "%s" }`, testapi.Default.GroupVersion().String()), - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: apitesting.DeepEqualSafePodSpec(), - }, - }, - /* TODO: uncomment this test once Merge is updated to use - strategic-merge-patch. See #8449. - { - kind: "Pod", - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - api.Container{ - Name: "c1", - Image: "red-image", - }, - api.Container{ - Name: "c2", - Image: "blue-image", - }, - }, - }, - }, - fragment: fmt.Sprintf(`{ "apiVersion": "%s", "spec": { "containers": [ { "name": "c1", "image": "green-image" } ] } }`, testapi.Default.GroupVersion().String()), - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - api.Container{ - Name: "c1", - Image: "green-image", - }, - api.Container{ - Name: "c2", - Image: "blue-image", - }, - }, - }, - }, - }, */ - { - kind: "Pod", - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - }, - fragment: fmt.Sprintf(`{ "apiVersion": "%s", "spec": { "volumes": [ {"name": "v1"}, {"name": "v2"} ] } }`, testapi.Default.GroupVersion().String()), - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.PodSpec{ - Volumes: []api.Volume{ - { - Name: "v1", - VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}, - }, - { - Name: "v2", - VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}, - }, - }, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &grace, - SecurityContext: &api.PodSecurityContext{}, - }, - }, - }, - { - kind: "Pod", - obj: &api.Pod{}, - fragment: "invalid json", - expected: &api.Pod{}, - expectErr: true, - }, - { - kind: "Service", - obj: &api.Service{}, - fragment: `{ "apiVersion": "badVersion" }`, - expectErr: true, - }, - { - kind: "Service", - obj: &api.Service{ - Spec: api.ServiceSpec{}, - }, - fragment: fmt.Sprintf(`{ "apiVersion": "%s", "spec": { "ports": [ { "port": 0 } ] } }`, testapi.Default.GroupVersion().String()), - expected: &api.Service{ - Spec: api.ServiceSpec{ - SessionAffinity: "None", - Type: api.ServiceTypeClusterIP, - Ports: []api.ServicePort{ - { - Protocol: api.ProtocolTCP, - Port: 0, - }, - }, - }, - }, - }, - { - kind: "Service", - obj: &api.Service{ - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "version": "v1", - }, - }, - }, - fragment: fmt.Sprintf(`{ "apiVersion": "%s", "spec": { "selector": { "version": "v2" } } }`, testapi.Default.GroupVersion().String()), - expected: &api.Service{ - Spec: api.ServiceSpec{ - SessionAffinity: "None", - Type: api.ServiceTypeClusterIP, - Selector: map[string]string{ - "version": "v2", - }, - }, - }, - }, - } - - for i, test := range tests { - out, err := Merge(testapi.Default.Codec(), test.obj, test.fragment, test.kind) - if !test.expectErr { - if err != nil { - t.Errorf("testcase[%d], unexpected error: %v", i, err) - } else if !reflect.DeepEqual(out, test.expected) { - t.Errorf("\n\ntestcase[%d]\nexpected:\n%+v\nsaw:\n%+v", i, test.expected, out) - } - } - if test.expectErr && err == nil { - t.Errorf("testcase[%d], unexpected non-error", i) - } - } -} - -type fileHandler struct { - data []byte -} - -func (f *fileHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) { - if req.URL.Path == "/error" { - res.WriteHeader(http.StatusNotFound) - return - } - res.WriteHeader(http.StatusOK) - res.Write(f.data) -} - -func TestReadConfigData(t *testing.T) { - httpData := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - // TODO: Close() this server when fix #19254 - server := httptest.NewServer(&fileHandler{data: httpData}) - - fileData := []byte{11, 12, 13, 14, 15, 16, 17, 18, 19} - f, err := ioutil.TempFile("", "config") - if err != nil { - t.Errorf("unexpected error setting up config file") - t.Fail() - } - defer syscall.Unlink(f.Name()) - ioutil.WriteFile(f.Name(), fileData, 0644) - // TODO: test TLS here, requires making it possible to inject the HTTP client. - - tests := []struct { - config string - data []byte - expectErr bool - }{ - { - config: server.URL, - data: httpData, - }, - { - config: server.URL + "/error", - expectErr: true, - }, - { - config: "http://some.non.existent.foobar", - expectErr: true, - }, - { - config: f.Name(), - data: fileData, - }, - { - config: "some-non-existent-file", - expectErr: true, - }, - { - config: "", - expectErr: true, - }, - } - for _, test := range tests { - dataOut, err := ReadConfigData(test.config) - if err != nil && !test.expectErr { - t.Errorf("unexpected err: %v for %s", err, test.config) - } - if err == nil && test.expectErr { - t.Errorf("unexpected non-error for %s", test.config) - } - if !test.expectErr && !reflect.DeepEqual(test.data, dataOut) { - t.Errorf("unexpected data: %v, expected %v", dataOut, test.data) - } - } -} - -func TestCheckInvalidErr(t *testing.T) { - tests := []struct { - err error - expected string - }{ - { - errors.NewInvalid(api.Kind("Invalid1"), "invalidation", field.ErrorList{field.Invalid(field.NewPath("field"), "single", "details")}), - `Error from server: Invalid1 "invalidation" is invalid: field: Invalid value: "single": details`, - }, - { - errors.NewInvalid(api.Kind("Invalid2"), "invalidation", field.ErrorList{field.Invalid(field.NewPath("field1"), "multi1", "details"), field.Invalid(field.NewPath("field2"), "multi2", "details")}), - `Error from server: Invalid2 "invalidation" is invalid: [field1: Invalid value: "multi1": details, field2: Invalid value: "multi2": details]`, - }, - { - errors.NewInvalid(api.Kind("Invalid3"), "invalidation", field.ErrorList{}), - `Error from server: Invalid3 "invalidation" is invalid: `, - }, - } - - var errReturned string - errHandle := func(err string) { - errReturned = err - } - - for _, test := range tests { - checkErr(test.err, errHandle) - - if errReturned != test.expected { - t.Fatalf("Got: %s, expected: %s", errReturned, test.expected) - } - } -} - -func TestCheckNoResourceMatchError(t *testing.T) { - tests := []struct { - err error - expected string - }{ - { - &meta.NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Resource: "foo"}}, - `the server doesn't have a resource type "foo"`, - }, - { - &meta.NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Version: "theversion", Resource: "foo"}}, - `the server doesn't have a resource type "foo" in version "theversion"`, - }, - { - &meta.NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Group: "thegroup", Version: "theversion", Resource: "foo"}}, - `the server doesn't have a resource type "foo" in group "thegroup" and version "theversion"`, - }, - { - &meta.NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Group: "thegroup", Resource: "foo"}}, - `the server doesn't have a resource type "foo" in group "thegroup"`, - }, - } - - var errReturned string - errHandle := func(err string) { - errReturned = err - } - - for _, test := range tests { - checkErr(test.err, errHandle) - - if errReturned != test.expected { - t.Fatalf("Got: %s, expected: %s", errReturned, test.expected) - } - } -} - -func TestDumpReaderToFile(t *testing.T) { - testString := "TEST STRING" - tempFile, err := ioutil.TempFile("", "hlpers_test_dump_") - if err != nil { - t.Errorf("unexpected error setting up a temporary file %v", err) - } - defer syscall.Unlink(tempFile.Name()) - defer tempFile.Close() - err = DumpReaderToFile(strings.NewReader(testString), tempFile.Name()) - if err != nil { - t.Errorf("error in DumpReaderToFile: %v", err) - } - data, err := ioutil.ReadFile(tempFile.Name()) - if err != nil { - t.Errorf("error when reading %s: %v", tempFile.Name(), err) - } - stringData := string(data) - if stringData != testString { - t.Fatalf("Wrong file content %s != %s", testString, stringData) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/jsonmerge/jsonmerge.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/jsonmerge/jsonmerge.go deleted file mode 100644 index 84a76da95..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/jsonmerge/jsonmerge.go +++ /dev/null @@ -1,193 +0,0 @@ -/* -Copyright 2015 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 jsonmerge - -import ( - "encoding/json" - "fmt" - - "github.com/evanphx/json-patch" - "github.com/golang/glog" - - "k8s.io/kubernetes/pkg/util/strategicpatch" - "k8s.io/kubernetes/pkg/util/yaml" -) - -// Delta represents a change between two JSON documents. -type Delta struct { - original []byte - edit []byte - - preconditions []PreconditionFunc -} - -// PreconditionFunc is a test to verify that an incompatible change -// has occurred before an Apply can be successful. -type PreconditionFunc func(interface{}) (hold bool, message string) - -// AddPreconditions adds precondition checks to a change which must -// be satisfied before an Apply is considered successful. If a -// precondition returns false, the Apply is failed with -// ErrPreconditionFailed. -func (d *Delta) AddPreconditions(fns ...PreconditionFunc) { - d.preconditions = append(d.preconditions, fns...) -} - -// RequireKeyUnchanged creates a precondition function that fails -// if the provided key is present in the diff (indicating its value -// has changed). -func RequireKeyUnchanged(key string) PreconditionFunc { - return func(diff interface{}) (bool, string) { - m, ok := diff.(map[string]interface{}) - if !ok { - return true, "" - } - // the presence of key in a diff means that its value has been changed, therefore - // we should fail the precondition. - _, ok = m[key] - if ok { - return false, key + " should not be changed\n" - } else { - return true, "" - } - } -} - -// RequireKeyUnchanged creates a precondition function that fails -// if the metadata.key is present in the diff (indicating its value -// has changed). -func RequireMetadataKeyUnchanged(key string) PreconditionFunc { - return func(diff interface{}) (bool, string) { - m, ok := diff.(map[string]interface{}) - if !ok { - return true, "" - } - m1, ok := m["metadata"] - if !ok { - return true, "" - } - m2, ok := m1.(map[string]interface{}) - if !ok { - return true, "" - } - _, ok = m2[key] - if ok { - return false, "metadata." + key + " should not be changed\n" - } else { - return true, "" - } - } -} - -// TestPreconditions test if preconditions hold given the edit -func TestPreconditionsHold(edit []byte, preconditions []PreconditionFunc) (bool, string) { - diff := make(map[string]interface{}) - if err := json.Unmarshal(edit, &diff); err != nil { - return false, err.Error() - } - for _, fn := range preconditions { - if hold, msg := fn(diff); !hold { - return false, msg - } - } - return true, "" -} - -// NewDelta accepts two JSON or YAML documents and calculates the difference -// between them. It returns a Delta object which can be used to resolve -// conflicts against a third version with a common parent, or an error -// if either document is in error. -func NewDelta(from, to []byte) (*Delta, error) { - d := &Delta{} - before, err := yaml.ToJSON(from) - if err != nil { - return nil, err - } - after, err := yaml.ToJSON(to) - if err != nil { - return nil, err - } - diff, err := jsonpatch.CreateMergePatch(before, after) - if err != nil { - return nil, err - } - glog.V(6).Infof("Patch created from:\n%s\n%s\n%s", string(before), string(after), string(diff)) - d.original = before - d.edit = diff - return d, nil -} - -// Apply attempts to apply the changes described by Delta onto latest, -// returning an error if the changes cannot be applied cleanly. -// IsConflicting will be true if the changes overlap, otherwise a -// generic error will be returned. -func (d *Delta) Apply(latest []byte) ([]byte, error) { - base, err := yaml.ToJSON(latest) - if err != nil { - return nil, err - } - changes, err := jsonpatch.CreateMergePatch(d.original, base) - if err != nil { - return nil, err - } - diff1 := make(map[string]interface{}) - if err := json.Unmarshal(d.edit, &diff1); err != nil { - return nil, err - } - diff2 := make(map[string]interface{}) - if err := json.Unmarshal(changes, &diff2); err != nil { - return nil, err - } - for _, fn := range d.preconditions { - hold1, _ := fn(diff1) - hold2, _ := fn(diff2) - if !hold1 || !hold2 { - return nil, ErrPreconditionFailed - } - } - - glog.V(6).Infof("Testing for conflict between:\n%s\n%s", string(d.edit), string(changes)) - hasConflicts, err := strategicpatch.HasConflicts(diff1, diff2) - if err != nil { - return nil, err - } - if hasConflicts { - return nil, ErrConflict - } - - return jsonpatch.MergePatch(base, d.edit) -} - -// IsConflicting returns true if the provided error indicates a -// conflict exists between the original changes and the applied -// changes. -func IsConflicting(err error) bool { - return err == ErrConflict -} - -// IsPreconditionFailed returns true if the provided error indicates -// a Delta precondition did not succeed. -func IsPreconditionFailed(err error) bool { - return err == ErrPreconditionFailed -} - -var ErrPreconditionFailed = fmt.Errorf("a precondition failed") -var ErrConflict = fmt.Errorf("changes are in conflict") - -func (d *Delta) Edit() []byte { - return d.edit -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/printing.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/printing.go index 88af6fa22..c95337253 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/printing.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/util/printing.go @@ -30,8 +30,8 @@ import ( // AddPrinterFlags adds printing related flags to a command (e.g. output format, no headers, template path) func AddPrinterFlags(cmd *cobra.Command) { - cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml|wide|name|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://releases.k8s.io/HEAD/docs/user-guide/jsonpath.md].") - cmd.Flags().String("output-version", "", "Output the formatted object with the given version (default api-version).") + cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml|wide|name|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://releases.k8s.io/release-1.2/docs/user-guide/jsonpath.md].") + cmd.Flags().String("output-version", "", "Output the formatted object with the given group version (for ex: 'extensions/v1beta1').") cmd.Flags().Bool("no-headers", false, "When using the default output, don't print headers.") cmd.Flags().Bool("show-labels", false, "When printing, show all labels as the last column (default hide labels column)") // template shorthand -t is deprecated to support -t for --tty diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/version.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/version.go deleted file mode 100644 index beaeda7bb..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/cmd/version.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2014 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 cmd - -import ( - "io" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/kubectl" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" -) - -func NewCmdVersion(f *cmdutil.Factory, out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "version", - Short: "Print the client and server version information.", - Run: func(cmd *cobra.Command, args []string) { - err := RunVersion(f, out, cmd) - cmdutil.CheckErr(err) - }, - } - cmd.Flags().BoolP("client", "c", false, "Client version only (no server required).") - cmd.Flags().MarkShorthandDeprecated("client", "please use --client instead.") - return cmd -} - -func RunVersion(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command) error { - kubectl.GetClientVersion(out) - if cmdutil.GetFlagBool(cmd, "client") { - return nil - } - - client, err := f.Client() - if err != nil { - return err - } - - kubectl.GetServerVersion(out, client) - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/configmap_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/configmap_test.go deleted file mode 100644 index 2fc936c9e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/configmap_test.go +++ /dev/null @@ -1,108 +0,0 @@ -/* -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 kubectl - -import ( - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api" -) - -func TestConfigMapGenerate(t *testing.T) { - tests := []struct { - params map[string]interface{} - expected *api.ConfigMap - expectErr bool - }{ - { - params: map[string]interface{}{ - "name": "foo", - }, - expected: &api.ConfigMap{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Data: map[string]string{}, - }, - expectErr: false, - }, - { - params: map[string]interface{}{ - "name": "foo", - "type": "my-type", - }, - expected: &api.ConfigMap{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Data: map[string]string{}, - }, - expectErr: false, - }, - { - params: map[string]interface{}{ - "name": "foo", - "from-literal": []string{"key1=value1", "key2=value2"}, - }, - expected: &api.ConfigMap{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Data: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - }, - expectErr: false, - }, - { - params: map[string]interface{}{ - "name": "foo", - "from-literal": []string{"key1value1"}, - }, - expectErr: true, - }, - { - params: map[string]interface{}{ - "name": "foo", - "from-file": []string{"key1=/file=2"}, - }, - expectErr: true, - }, - { - params: map[string]interface{}{ - "name": "foo", - "from-file": []string{"key1==value"}, - }, - expectErr: true, - }, - } - generator := ConfigMapGeneratorV1{} - for _, test := range tests { - obj, err := generator.Generate(test.params) - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v", err) - } - if test.expectErr && err != nil { - continue - } - if !reflect.DeepEqual(obj.(*api.ConfigMap), test.expected) { - t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*api.ConfigMap)) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/custom_column_printer_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/custom_column_printer_test.go deleted file mode 100644 index 531881a73..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/custom_column_printer_test.go +++ /dev/null @@ -1,276 +0,0 @@ -/* -Copyright 2014 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 kubectl - -import ( - "bytes" - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/v1" - "k8s.io/kubernetes/pkg/runtime" -) - -func TestMassageJSONPath(t *testing.T) { - tests := []struct { - input string - expectedOutput string - expectErr bool - }{ - {input: "foo.bar", expectedOutput: "{.foo.bar}"}, - {input: "{foo.bar}", expectedOutput: "{.foo.bar}"}, - {input: ".foo.bar", expectedOutput: "{.foo.bar}"}, - {input: "{.foo.bar}", expectedOutput: "{.foo.bar}"}, - {input: "", expectedOutput: ""}, - {input: "{foo.bar", expectErr: true}, - {input: "foo.bar}", expectErr: true}, - {input: "{foo.bar}}", expectErr: true}, - {input: "{{foo.bar}", expectErr: true}, - } - for _, test := range tests { - output, err := massageJSONPath(test.input) - if err != nil && !test.expectErr { - t.Errorf("unexpected error: %v", err) - continue - } - if test.expectErr { - if err == nil { - t.Error("unexpected non-error") - } - continue - } - if output != test.expectedOutput { - t.Errorf("input: %s, expected: %s, saw: %s", test.input, test.expectedOutput, output) - } - } -} - -func TestNewColumnPrinterFromSpec(t *testing.T) { - tests := []struct { - spec string - expectedColumns []Column - expectErr bool - name string - }{ - { - spec: "", - expectErr: true, - name: "empty", - }, - { - spec: "invalid", - expectErr: true, - name: "invalid1", - }, - { - spec: "invalid=foobar", - expectErr: true, - name: "invalid2", - }, - { - spec: "invalid,foobar:blah", - expectErr: true, - name: "invalid3", - }, - { - spec: "NAME:metadata.name,API_VERSION:apiVersion", - name: "ok", - expectedColumns: []Column{ - { - Header: "NAME", - FieldSpec: "{.metadata.name}", - }, - { - Header: "API_VERSION", - FieldSpec: "{.apiVersion}", - }, - }, - }, - } - for _, test := range tests { - printer, err := NewCustomColumnsPrinterFromSpec(test.spec, api.Codecs.UniversalDecoder()) - if test.expectErr { - if err == nil { - t.Errorf("[%s] unexpected non-error", test.name) - } - continue - } - if !test.expectErr && err != nil { - t.Errorf("[%s] unexpected error: %v", test.name, err) - continue - } - - if !reflect.DeepEqual(test.expectedColumns, printer.Columns) { - t.Errorf("[%s]\nexpected:\n%v\nsaw:\n%v\n", test.name, test.expectedColumns, printer.Columns) - } - - } -} - -const exampleTemplateOne = `NAME API_VERSION -{metadata.name} {apiVersion}` - -const exampleTemplateTwo = `NAME API_VERSION - {metadata.name} {apiVersion}` - -func TestNewColumnPrinterFromTemplate(t *testing.T) { - tests := []struct { - spec string - expectedColumns []Column - expectErr bool - name string - }{ - { - spec: "", - expectErr: true, - name: "empty", - }, - { - spec: "invalid", - expectErr: true, - name: "invalid1", - }, - { - spec: "invalid=foobar", - expectErr: true, - name: "invalid2", - }, - { - spec: "invalid,foobar:blah", - expectErr: true, - name: "invalid3", - }, - { - spec: exampleTemplateOne, - name: "ok", - expectedColumns: []Column{ - { - Header: "NAME", - FieldSpec: "{.metadata.name}", - }, - { - Header: "API_VERSION", - FieldSpec: "{.apiVersion}", - }, - }, - }, - { - spec: exampleTemplateTwo, - name: "ok-2", - expectedColumns: []Column{ - { - Header: "NAME", - FieldSpec: "{.metadata.name}", - }, - { - Header: "API_VERSION", - FieldSpec: "{.apiVersion}", - }, - }, - }, - } - for _, test := range tests { - reader := bytes.NewBufferString(test.spec) - printer, err := NewCustomColumnsPrinterFromTemplate(reader, api.Codecs.UniversalDecoder()) - if test.expectErr { - if err == nil { - t.Errorf("[%s] unexpected non-error", test.name) - } - continue - } - if !test.expectErr && err != nil { - t.Errorf("[%s] unexpected error: %v", test.name, err) - continue - } - - if !reflect.DeepEqual(test.expectedColumns, printer.Columns) { - t.Errorf("[%s]\nexpected:\n%v\nsaw:\n%v\n", test.name, test.expectedColumns, printer.Columns) - } - - } -} - -func TestColumnPrint(t *testing.T) { - tests := []struct { - columns []Column - obj runtime.Object - expectedOutput string - }{ - { - columns: []Column{ - { - Header: "NAME", - FieldSpec: "{.metadata.name}", - }, - }, - obj: &v1.Pod{ObjectMeta: v1.ObjectMeta{Name: "foo"}}, - expectedOutput: `NAME -foo -`, - }, - { - columns: []Column{ - { - Header: "NAME", - FieldSpec: "{.metadata.name}", - }, - }, - obj: &v1.PodList{ - Items: []v1.Pod{ - {ObjectMeta: v1.ObjectMeta{Name: "foo"}}, - {ObjectMeta: v1.ObjectMeta{Name: "bar"}}, - }, - }, - expectedOutput: `NAME -foo -bar -`, - }, - { - columns: []Column{ - { - Header: "NAME", - FieldSpec: "{.metadata.name}", - }, - { - Header: "API_VERSION", - FieldSpec: "{.apiVersion}", - }, - }, - obj: &v1.Pod{ObjectMeta: v1.ObjectMeta{Name: "foo"}, TypeMeta: unversioned.TypeMeta{APIVersion: "baz"}}, - expectedOutput: `NAME API_VERSION -foo baz -`, - }, - } - - for _, test := range tests { - printer := &CustomColumnsPrinter{ - Columns: test.columns, - Decoder: api.Codecs.UniversalDecoder(), - } - buffer := &bytes.Buffer{} - if err := printer.PrintObj(test.obj, buffer); err != nil { - t.Errorf("unexpected error: %v", err) - } - if buffer.String() != test.expectedOutput { - t.Errorf("\nexpected:\n'%s'\nsaw\n'%s'\n", test.expectedOutput, buffer.String()) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/describe.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/describe.go index 6d2dc66ee..0a09b3a06 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/describe.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/describe.go @@ -33,6 +33,8 @@ import ( "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apis/autoscaling" + "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/extensions" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" client "k8s.io/kubernetes/pkg/client/unversioned" @@ -89,12 +91,14 @@ func describerMap(c *client.Client) map[unversioned.GroupKind]Describer { api.Kind("Endpoints"): &EndpointsDescriber{c}, api.Kind("ConfigMap"): &ConfigMapDescriber{c}, - extensions.Kind("ReplicaSet"): &ReplicaSetDescriber{c}, - extensions.Kind("HorizontalPodAutoscaler"): &HorizontalPodAutoscalerDescriber{c}, - extensions.Kind("DaemonSet"): &DaemonSetDescriber{c}, - extensions.Kind("Deployment"): &DeploymentDescriber{clientset.FromUnversionedClient(c)}, - extensions.Kind("Job"): &JobDescriber{c}, - extensions.Kind("Ingress"): &IngressDescriber{c}, + extensions.Kind("ReplicaSet"): &ReplicaSetDescriber{c}, + extensions.Kind("HorizontalPodAutoscaler"): &HorizontalPodAutoscalerDescriber{c}, + autoscaling.Kind("HorizontalPodAutoscaler"): &HorizontalPodAutoscalerDescriber{c}, + extensions.Kind("DaemonSet"): &DaemonSetDescriber{c}, + extensions.Kind("Deployment"): &DeploymentDescriber{clientset.FromUnversionedClient(c)}, + extensions.Kind("Job"): &JobDescriber{c}, + batch.Kind("Job"): &JobDescriber{c}, + extensions.Kind("Ingress"): &IngressDescriber{c}, } return m @@ -258,36 +262,41 @@ func DescribeResourceQuotas(quotas *api.ResourceQuotaList, w io.Writer) { fmt.Fprint(w, "No resource quota.\n") return } - resources := []api.ResourceName{} - hard := map[api.ResourceName]resource.Quantity{} - used := map[api.ResourceName]resource.Quantity{} + sort.Sort(SortableResourceQuotas(quotas.Items)) + + fmt.Fprint(w, "Resource Quotas") for _, q := range quotas.Items { + fmt.Fprintf(w, "\n Name:\t%s\n", q.Name) + if len(q.Spec.Scopes) > 0 { + scopes := []string{} + for _, scope := range q.Spec.Scopes { + scopes = append(scopes, string(scope)) + } + sort.Strings(scopes) + fmt.Fprintf(w, " Scopes:\t%s\n", strings.Join(scopes, ", ")) + for _, scope := range scopes { + helpText := helpTextForResourceQuotaScope(api.ResourceQuotaScope(scope)) + if len(helpText) > 0 { + fmt.Fprintf(w, " * %s\n", helpText) + } + } + } + + fmt.Fprintf(w, " Resource\tUsed\tHard\n") + fmt.Fprint(w, " --------\t---\t---\n") + + resources := []api.ResourceName{} for resource := range q.Status.Hard { resources = append(resources, resource) + } + sort.Sort(SortableResourceNames(resources)) + + for _, resource := range resources { hardQuantity := q.Status.Hard[resource] usedQuantity := q.Status.Used[resource] - - // if for some reason there are multiple quota documents, we take least permissive - prevQuantity, ok := hard[resource] - if ok { - if hardQuantity.Value() < prevQuantity.Value() { - hard[resource] = hardQuantity - } - } else { - hard[resource] = hardQuantity - } - used[resource] = usedQuantity + fmt.Fprintf(w, " %s\t%s\t%s\n", string(resource), usedQuantity.String(), hardQuantity.String()) } } - - sort.Sort(SortableResourceNames(resources)) - fmt.Fprint(w, "Resource Quotas\n Resource\tUsed\tHard\n") - fmt.Fprint(w, " ---\t---\t---\n") - for _, resource := range resources { - hardQuantity := hard[resource] - usedQuantity := used[resource] - fmt.Fprintf(w, " %s\t%s\t%s\n", string(resource), usedQuantity.String(), hardQuantity.String()) - } } // LimitRangeDescriber generates information about a limit range @@ -393,10 +402,38 @@ func (d *ResourceQuotaDescriber) Describe(namespace, name string) (string, error return describeQuota(resourceQuota) } +func helpTextForResourceQuotaScope(scope api.ResourceQuotaScope) string { + switch scope { + case api.ResourceQuotaScopeTerminating: + return "Matches all pods that have an active deadline." + case api.ResourceQuotaScopeNotTerminating: + return "Matches all pods that do not have an active deadline." + case api.ResourceQuotaScopeBestEffort: + return "Matches all pods that have best effort quality of service." + case api.ResourceQuotaScopeNotBestEffort: + return "Matches all pods that do not have best effort quality of service." + default: + return "" + } +} func describeQuota(resourceQuota *api.ResourceQuota) (string, error) { return tabbedString(func(out io.Writer) error { fmt.Fprintf(out, "Name:\t%s\n", resourceQuota.Name) fmt.Fprintf(out, "Namespace:\t%s\n", resourceQuota.Namespace) + if len(resourceQuota.Spec.Scopes) > 0 { + scopes := []string{} + for _, scope := range resourceQuota.Spec.Scopes { + scopes = append(scopes, string(scope)) + } + sort.Strings(scopes) + fmt.Fprintf(out, "Scopes:\t%s\n", strings.Join(scopes, ", ")) + for _, scope := range scopes { + helpText := helpTextForResourceQuotaScope(api.ResourceQuotaScope(scope)) + if len(helpText) > 0 { + fmt.Fprintf(out, " * %s\n", helpText) + } + } + } fmt.Fprintf(out, "Resource\tUsed\tHard\n") fmt.Fprintf(out, "--------\t----\t----\n") @@ -525,6 +562,8 @@ func describeVolumes(volumes []api.Volume, out io.Writer) { printGitRepoVolumeSource(volume.VolumeSource.GitRepo, out) case volume.VolumeSource.Secret != nil: printSecretVolumeSource(volume.VolumeSource.Secret, out) + case volume.VolumeSource.ConfigMap != nil: + printConfigMapVolumeSource(volume.VolumeSource.ConfigMap, out) case volume.VolumeSource.NFS != nil: printNFSVolumeSource(volume.VolumeSource.NFS, out) case volume.VolumeSource.ISCSI != nil: @@ -535,8 +574,10 @@ func describeVolumes(volumes []api.Volume, out io.Writer) { printPersistentVolumeClaimVolumeSource(volume.VolumeSource.PersistentVolumeClaim, out) case volume.VolumeSource.RBD != nil: printRBDVolumeSource(volume.VolumeSource.RBD, out) + case volume.VolumeSource.DownwardAPI != nil: + printDownwardAPIVolumeSource(volume.VolumeSource.DownwardAPI, out) default: - fmt.Fprintf(out, " \n") + fmt.Fprintf(out, " \n") } } } @@ -577,10 +618,15 @@ func printGitRepoVolumeSource(git *api.GitRepoVolumeSource, out io.Writer) { } func printSecretVolumeSource(secret *api.SecretVolumeSource, out io.Writer) { - fmt.Fprintf(out, " Type:\tSecret (a secret that should populate this volume)\n"+ + fmt.Fprintf(out, " Type:\tSecret (a volume populated by a Secret)\n"+ " SecretName:\t%v\n", secret.SecretName) } +func printConfigMapVolumeSource(configMap *api.ConfigMapVolumeSource, out io.Writer) { + fmt.Fprintf(out, " Type:\tConfigMap (a volume populated by a ConfigMap)\n"+ + " Name:\t%v\n", configMap.Name) +} + func printNFSVolumeSource(nfs *api.NFSVolumeSource, out io.Writer) { fmt.Fprintf(out, " Type:\tNFS (an NFS mount that lasts the lifetime of a pod)\n"+ " Server:\t%v\n"+ @@ -628,6 +674,13 @@ func printRBDVolumeSource(rbd *api.RBDVolumeSource, out io.Writer) { rbd.CephMonitors, rbd.RBDImage, rbd.FSType, rbd.RBDPool, rbd.RadosUser, rbd.Keyring, rbd.SecretRef, rbd.ReadOnly) } +func printDownwardAPIVolumeSource(d *api.DownwardAPIVolumeSource, out io.Writer) { + fmt.Fprintf(out, " Type:\tDownwardAPI (a volume populated by information about the pod)\n Items:\n") + for _, mapping := range d.Items { + fmt.Fprintf(out, " %v -> %v\n", mapping.FieldRef.FieldPath, mapping.Path) + } +} + type PersistentVolumeDescriber struct { client.Interface } @@ -720,13 +773,22 @@ func DescribeContainers(containers []api.Container, containerStatuses []api.Cont } for _, container := range containers { - status := statuses[container.Name] - state := status.State + status, ok := statuses[container.Name] fmt.Fprintf(out, " %v:\n", container.Name) - fmt.Fprintf(out, " Container ID:\t%s\n", status.ContainerID) + if ok { + fmt.Fprintf(out, " Container ID:\t%s\n", status.ContainerID) + } fmt.Fprintf(out, " Image:\t%s\n", container.Image) - fmt.Fprintf(out, " Image ID:\t%s\n", status.ImageID) + if ok { + fmt.Fprintf(out, " Image ID:\t%s\n", status.ImageID) + } + portString := describeContainerPorts(container.Ports) + if strings.Contains(portString, ",") { + fmt.Fprintf(out, " Ports:\t%s\n", portString) + } else { + fmt.Fprintf(out, " Port:\t%s\n", portString) + } if len(container.Command) > 0 { fmt.Fprintf(out, " Command:\n") @@ -763,12 +825,14 @@ func DescribeContainers(containers []api.Container, containerStatuses []api.Cont fmt.Fprintf(out, " %s:\t%s\n", name, quantity.String()) } - describeStatus("State", state, out) - if status.LastTerminationState.Terminated != nil { - describeStatus("Last State", status.LastTerminationState, out) + if ok { + describeStatus("State", status.State, out) + if status.LastTerminationState.Terminated != nil { + describeStatus("Last State", status.LastTerminationState, out) + } + fmt.Fprintf(out, " Ready:\t%v\n", printBool(status.Ready)) + fmt.Fprintf(out, " Restart Count:\t%d\n", status.RestartCount) } - fmt.Fprintf(out, " Ready:\t%v\n", printBool(status.Ready)) - fmt.Fprintf(out, " Restart Count:\t%d\n", status.RestartCount) if container.LivenessProbe != nil { probe := DescribeProbe(container.LivenessProbe) @@ -793,6 +857,14 @@ func DescribeContainers(containers []api.Container, containerStatuses []api.Cont } } +func describeContainerPorts(cPorts []api.ContainerPort) string { + ports := []string{} + for _, cPort := range cPorts { + ports = append(ports, fmt.Sprintf("%d/%s", cPort.ContainerPort, cPort.Protocol)) + } + return strings.Join(ports, ", ") +} + // DescribeProbe is exported for consumers in other API groups that have probes func DescribeProbe(probe *api.Probe) string { attrs := fmt.Sprintf("delay=%ds timeout=%ds period=%ds #success=%d #failure=%d", probe.InitialDelaySeconds, probe.TimeoutSeconds, probe.PeriodSeconds, probe.SuccessThreshold, probe.FailureThreshold) @@ -903,7 +975,7 @@ func describeReplicationController(controller *api.ReplicationController, events if controller.Spec.Template != nil { fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(&controller.Spec.Template.Spec)) } else { - fmt.Fprintf(out, "Image(s):\t%s\n", "") + fmt.Fprintf(out, "Image(s):\t%s\n", "") } fmt.Fprintf(out, "Selector:\t%s\n", labels.FormatLabels(controller.Spec.Selector)) fmt.Fprintf(out, "Labels:\t%s\n", labels.FormatLabels(controller.Labels)) @@ -922,7 +994,7 @@ func describeReplicationController(controller *api.ReplicationController, events func DescribePodTemplate(template *api.PodTemplateSpec) (string, error) { return tabbedString(func(out io.Writer) error { if template == nil { - fmt.Fprintf(out, "") + fmt.Fprintf(out, "") return nil } fmt.Fprintf(out, "Labels:\t%s\n", labels.FormatLabels(template.Labels)) @@ -966,18 +1038,12 @@ func describeReplicaSet(rs *extensions.ReplicaSet, events *api.EventList, runnin return tabbedString(func(out io.Writer) error { fmt.Fprintf(out, "Name:\t%s\n", rs.Name) fmt.Fprintf(out, "Namespace:\t%s\n", rs.Namespace) - if rs.Spec.Template != nil { - fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(&rs.Spec.Template.Spec)) - } else { - fmt.Fprintf(out, "Image(s):\t%s\n", "") - } + fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(&rs.Spec.Template.Spec)) fmt.Fprintf(out, "Selector:\t%s\n", unversioned.FormatLabelSelector(rs.Spec.Selector)) fmt.Fprintf(out, "Labels:\t%s\n", labels.FormatLabels(rs.Labels)) fmt.Fprintf(out, "Replicas:\t%d current / %d desired\n", rs.Status.Replicas, rs.Spec.Replicas) fmt.Fprintf(out, "Pods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed) - if rs.Spec.Template != nil { - describeVolumes(rs.Spec.Template.Spec.Volumes, out) - } + describeVolumes(rs.Spec.Template.Spec.Volumes, out) if events != nil { DescribeEvents(events, out) } @@ -1012,7 +1078,7 @@ func describeJob(job *extensions.Job, events *api.EventList) (string, error) { if job.Spec.Completions != nil { fmt.Fprintf(out, "Completions:\t%d\n", *job.Spec.Completions) } else { - fmt.Fprintf(out, "Completions:\tNot Set\n") + fmt.Fprintf(out, "Completions:\t\n") } if job.Status.StartTime != nil { fmt.Fprintf(out, "Start Time:\t%s\n", job.Status.StartTime.Time.Format(time.RFC1123Z)) @@ -1271,7 +1337,7 @@ func describeService(service *api.Service, endpoints *api.Endpoints, events *api name := sp.Name if name == "" { - name = "" + name = "" } fmt.Fprintf(out, "Port:\t%s\t%d/%s\n", name, sp.Port, sp.Protocol) if sp.NodePort != 0 { @@ -1342,7 +1408,7 @@ func describeEndpoints(ep *api.Endpoints, events *api.EventList) (string, error) for _, port := range subset.Ports { name := port.Name if len(name) == 0 { - name = "" + name = "" } fmt.Fprintf(out, " %s\t%d\t%s\n", name, port.Port, port.Protocol) } @@ -1372,7 +1438,7 @@ func (d *ServiceAccountDescriber) Describe(namespace, name string) (string, erro tokens := []api.Secret{} - tokenSelector := fields.SelectorFromSet(map[string]string{client.SecretType: string(api.SecretTypeServiceAccountToken)}) + tokenSelector := fields.SelectorFromSet(map[string]string{api.SecretTypeField: string(api.SecretTypeServiceAccountToken)}) options := api.ListOptions{FieldSelector: tokenSelector} secrets, err := d.Secrets(namespace).List(options) if err == nil { @@ -1552,6 +1618,7 @@ func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string) (str fmt.Fprintf(out, "Name:\t%s\n", hpa.Name) fmt.Fprintf(out, "Namespace:\t%s\n", hpa.Namespace) fmt.Fprintf(out, "Labels:\t%s\n", labels.FormatLabels(hpa.Labels)) + fmt.Fprintf(out, "Annotations:\t%s\n", labels.FormatLabels(hpa.Annotations)) fmt.Fprintf(out, "CreationTimestamp:\t%s\n", hpa.CreationTimestamp.Time.Format(time.RFC1123Z)) fmt.Fprintf(out, "Reference:\t%s/%s/%s\n", hpa.Spec.ScaleRef.Kind, @@ -1563,7 +1630,7 @@ func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string) (str if hpa.Status.CurrentCPUUtilizationPercentage != nil { fmt.Fprintf(out, "%d%%\n", *hpa.Status.CurrentCPUUtilizationPercentage) } else { - fmt.Fprintf(out, "\n") + fmt.Fprintf(out, "\n") } } minReplicas := "" @@ -1583,6 +1650,11 @@ func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string) (str fmt.Fprintf(out, "failed to check Replication Controller\n") } } + + events, _ := d.client.Events(namespace).Search(hpa) + if events != nil { + DescribeEvents(events, out) + } return nil }) } @@ -1710,11 +1782,11 @@ func (dd *DeploymentDescriber) Describe(namespace, name string) (string, error) ru := d.Spec.Strategy.RollingUpdate fmt.Fprintf(out, "RollingUpdateStrategy:\t%s max unavailable, %s max surge\n", ru.MaxUnavailable.String(), ru.MaxSurge.String()) } - oldRSs, _, err := deploymentutil.GetOldReplicaSets(*d, dd) + oldRSs, _, err := deploymentutil.GetOldReplicaSets(d, dd) if err == nil { fmt.Fprintf(out, "OldReplicaSets:\t%s\n", printReplicaSetsByLabels(oldRSs)) } - newRS, err := deploymentutil.GetNewReplicaSet(*d, dd) + newRS, err := deploymentutil.GetNewReplicaSet(d, dd) if err == nil { var newRSs []*extensions.ReplicaSet if newRS != nil { diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/describe_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/describe_test.go deleted file mode 100644 index a7d1c24e1..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/describe_test.go +++ /dev/null @@ -1,523 +0,0 @@ -/* -Copyright 2014 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 kubectl - -import ( - "bytes" - "fmt" - "reflect" - "strings" - "testing" - "time" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient" -) - -type describeClient struct { - T *testing.T - Namespace string - Err error - client.Interface -} - -func TestDescribePod(t *testing.T) { - fake := testclient.NewSimpleFake(&api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "bar", - Namespace: "foo", - }, - }) - c := &describeClient{T: t, Namespace: "foo", Interface: fake} - d := PodDescriber{c} - out, err := d.Describe("foo", "bar") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !strings.Contains(out, "bar") || !strings.Contains(out, "Status:") { - t.Errorf("unexpected out: %s", out) - } -} - -func TestDescribeService(t *testing.T) { - fake := testclient.NewSimpleFake(&api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "bar", - Namespace: "foo", - }, - }) - c := &describeClient{T: t, Namespace: "foo", Interface: fake} - d := ServiceDescriber{c} - out, err := d.Describe("foo", "bar") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !strings.Contains(out, "Labels:") || !strings.Contains(out, "bar") { - t.Errorf("unexpected out: %s", out) - } -} - -func TestPodDescribeResultsSorted(t *testing.T) { - // Arrange - fake := testclient.NewSimpleFake(&api.EventList{ - Items: []api.Event{ - { - Source: api.EventSource{Component: "kubelet"}, - Message: "Item 1", - FirstTimestamp: unversioned.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), - LastTimestamp: unversioned.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), - Count: 1, - Type: api.EventTypeNormal, - }, - { - Source: api.EventSource{Component: "scheduler"}, - Message: "Item 2", - FirstTimestamp: unversioned.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)), - LastTimestamp: unversioned.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)), - Count: 1, - Type: api.EventTypeNormal, - }, - { - Source: api.EventSource{Component: "kubelet"}, - Message: "Item 3", - FirstTimestamp: unversioned.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)), - LastTimestamp: unversioned.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)), - Count: 1, - Type: api.EventTypeNormal, - }, - }, - }) - c := &describeClient{T: t, Namespace: "foo", Interface: fake} - d := PodDescriber{c} - - // Act - out, err := d.Describe("foo", "bar") - - // Assert - if err != nil { - t.Errorf("unexpected error: %v", err) - } - VerifyDatesInOrder(out, "\n" /* rowDelimiter */, "\t" /* columnDelimiter */, t) -} - -func TestDescribeContainers(t *testing.T) { - testCases := []struct { - container api.Container - status api.ContainerStatus - expectedElements []string - }{ - // Running state. - { - container: api.Container{Name: "test", Image: "image"}, - status: api.ContainerStatus{ - Name: "test", - State: api.ContainerState{ - Running: &api.ContainerStateRunning{ - StartedAt: unversioned.NewTime(time.Now()), - }, - }, - Ready: true, - RestartCount: 7, - }, - expectedElements: []string{"test", "State", "Running", "Ready", "True", "Restart Count", "7", "Image", "image", "Started"}, - }, - // Waiting state. - { - container: api.Container{Name: "test", Image: "image"}, - status: api.ContainerStatus{ - Name: "test", - State: api.ContainerState{ - Waiting: &api.ContainerStateWaiting{ - Reason: "potato", - }, - }, - Ready: true, - RestartCount: 7, - }, - expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "Reason", "potato"}, - }, - // Terminated state. - { - container: api.Container{Name: "test", Image: "image"}, - status: api.ContainerStatus{ - Name: "test", - State: api.ContainerState{ - Terminated: &api.ContainerStateTerminated{ - StartedAt: unversioned.NewTime(time.Now()), - FinishedAt: unversioned.NewTime(time.Now()), - Reason: "potato", - ExitCode: 2, - }, - }, - Ready: true, - RestartCount: 7, - }, - expectedElements: []string{"test", "State", "Terminated", "Ready", "True", "Restart Count", "7", "Image", "image", "Reason", "potato", "Started", "Finished", "Exit Code", "2"}, - }, - // Last Terminated - { - container: api.Container{Name: "test", Image: "image"}, - status: api.ContainerStatus{ - Name: "test", - State: api.ContainerState{ - Running: &api.ContainerStateRunning{ - StartedAt: unversioned.NewTime(time.Now()), - }, - }, - LastTerminationState: api.ContainerState{ - Terminated: &api.ContainerStateTerminated{ - StartedAt: unversioned.NewTime(time.Now().Add(time.Second * 3)), - FinishedAt: unversioned.NewTime(time.Now()), - Reason: "crashing", - ExitCode: 3, - }, - }, - Ready: true, - RestartCount: 7, - }, - expectedElements: []string{"test", "State", "Terminated", "Ready", "True", "Restart Count", "7", "Image", "image", "Started", "Finished", "Exit Code", "2", "crashing", "3"}, - }, - // No state defaults to waiting. - { - container: api.Container{Name: "test", Image: "image"}, - status: api.ContainerStatus{ - Name: "test", - Ready: true, - RestartCount: 7, - }, - expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image"}, - }, - // Env - { - container: api.Container{Name: "test", Image: "image", Env: []api.EnvVar{{Name: "envname", Value: "xyz"}}}, - status: api.ContainerStatus{ - Name: "test", - Ready: true, - RestartCount: 7, - }, - expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz"}, - }, - // Command - { - container: api.Container{Name: "test", Image: "image", Command: []string{"sleep", "1000"}}, - status: api.ContainerStatus{ - Name: "test", - Ready: true, - RestartCount: 7, - }, - expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "sleep", "1000"}, - }, - // Args - { - container: api.Container{Name: "test", Image: "image", Args: []string{"time", "1000"}}, - status: api.ContainerStatus{ - Name: "test", - Ready: true, - RestartCount: 7, - }, - expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "time", "1000"}, - }, - // Using limits. - { - container: api.Container{ - Name: "test", - Image: "image", - Resources: api.ResourceRequirements{ - Limits: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("1000"), - api.ResourceName(api.ResourceMemory): resource.MustParse("4G"), - api.ResourceName(api.ResourceStorage): resource.MustParse("20G"), - }, - }, - }, - status: api.ContainerStatus{ - Name: "test", - Ready: true, - RestartCount: 7, - }, - expectedElements: []string{"cpu", "1k", "memory", "4G", "storage", "20G"}, - }, - } - - for i, testCase := range testCases { - out := new(bytes.Buffer) - pod := api.Pod{ - Spec: api.PodSpec{ - Containers: []api.Container{testCase.container}, - }, - Status: api.PodStatus{ - ContainerStatuses: []api.ContainerStatus{testCase.status}, - }, - } - DescribeContainers(pod.Spec.Containers, pod.Status.ContainerStatuses, EnvValueRetriever(&pod), out) - output := out.String() - for _, expected := range testCase.expectedElements { - if !strings.Contains(output, expected) { - t.Errorf("Test case %d: expected to find %q in output: %q", i, expected, output) - } - } - } -} - -func TestDescribers(t *testing.T) { - first := &api.Event{} - second := &api.Pod{} - var third *api.Pod - testErr := fmt.Errorf("test") - d := Describers{} - d.Add( - func(e *api.Event, p *api.Pod) (string, error) { - if e != first { - t.Errorf("first argument not equal: %#v", e) - } - if p != second { - t.Errorf("second argument not equal: %#v", p) - } - return "test", testErr - }, - ) - if out, err := d.DescribeObject(first, second); out != "test" || err != testErr { - t.Errorf("unexpected result: %s %v", out, err) - } - - if out, err := d.DescribeObject(first, second, third); out != "" || err == nil { - t.Errorf("unexpected result: %s %v", out, err) - } else { - if noDescriber, ok := err.(ErrNoDescriber); ok { - if !reflect.DeepEqual(noDescriber.Types, []string{"*api.Event", "*api.Pod", "*api.Pod"}) { - t.Errorf("unexpected describer: %v", err) - } - } else { - t.Errorf("unexpected error type: %v", err) - } - } - - d.Add( - func(e *api.Event) (string, error) { - if e != first { - t.Errorf("first argument not equal: %#v", e) - } - return "simpler", testErr - }, - ) - if out, err := d.DescribeObject(first); out != "simpler" || err != testErr { - t.Errorf("unexpected result: %s %v", out, err) - } -} - -func TestDefaultDescribers(t *testing.T) { - out, err := DefaultObjectDescriber.DescribeObject(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !strings.Contains(out, "foo") { - t.Errorf("unexpected output: %s", out) - } - - out, err = DefaultObjectDescriber.DescribeObject(&api.Service{ObjectMeta: api.ObjectMeta{Name: "foo"}}) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !strings.Contains(out, "foo") { - t.Errorf("unexpected output: %s", out) - } - - out, err = DefaultObjectDescriber.DescribeObject(&api.ReplicationController{ObjectMeta: api.ObjectMeta{Name: "foo"}}) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !strings.Contains(out, "foo") { - t.Errorf("unexpected output: %s", out) - } - - out, err = DefaultObjectDescriber.DescribeObject(&api.Node{ObjectMeta: api.ObjectMeta{Name: "foo"}}) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !strings.Contains(out, "foo") { - t.Errorf("unexpected output: %s", out) - } -} - -func TestGetPodsTotalRequests(t *testing.T) { - testCases := []struct { - pods *api.PodList - expectedReqs, expectedLimits map[api.ResourceName]resource.Quantity - }{ - { - pods: &api.PodList{ - Items: []api.Pod{ - { - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("1"), - api.ResourceName(api.ResourceMemory): resource.MustParse("300Mi"), - api.ResourceName(api.ResourceStorage): resource.MustParse("1G"), - }, - }, - }, - { - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("90m"), - api.ResourceName(api.ResourceMemory): resource.MustParse("120Mi"), - api.ResourceName(api.ResourceStorage): resource.MustParse("200M"), - }, - }, - }, - }, - }, - }, - { - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("60m"), - api.ResourceName(api.ResourceMemory): resource.MustParse("43Mi"), - api.ResourceName(api.ResourceStorage): resource.MustParse("500M"), - }, - }, - }, - { - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("34m"), - api.ResourceName(api.ResourceMemory): resource.MustParse("83Mi"), - api.ResourceName(api.ResourceStorage): resource.MustParse("700M"), - }, - }, - }, - }, - }, - }, - }, - }, - expectedReqs: map[api.ResourceName]resource.Quantity{ - api.ResourceName(api.ResourceCPU): resource.MustParse("1.184"), - api.ResourceName(api.ResourceMemory): resource.MustParse("546Mi"), - api.ResourceName(api.ResourceStorage): resource.MustParse("2.4G"), - }, - }, - } - - for _, testCase := range testCases { - reqs, _, err := getPodsTotalRequestsAndLimits(testCase.pods) - if err != nil { - t.Errorf("Unexpected error %v", err) - } - if !reflect.DeepEqual(reqs, testCase.expectedReqs) { - t.Errorf("Expected %v, got %v", testCase.expectedReqs, reqs) - } - } -} - -func TestPersistentVolumeDescriber(t *testing.T) { - tests := map[string]*api.PersistentVolume{ - - "hostpath": { - Spec: api.PersistentVolumeSpec{ - PersistentVolumeSource: api.PersistentVolumeSource{ - HostPath: &api.HostPathVolumeSource{}, - }, - }, - }, - "gce": { - Spec: api.PersistentVolumeSpec{ - PersistentVolumeSource: api.PersistentVolumeSource{ - GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, - }, - }, - }, - "ebs": { - Spec: api.PersistentVolumeSpec{ - PersistentVolumeSource: api.PersistentVolumeSource{ - AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{}, - }, - }, - }, - "nfs": { - Spec: api.PersistentVolumeSpec{ - PersistentVolumeSource: api.PersistentVolumeSource{ - NFS: &api.NFSVolumeSource{}, - }, - }, - }, - "iscsi": { - Spec: api.PersistentVolumeSpec{ - PersistentVolumeSource: api.PersistentVolumeSource{ - ISCSI: &api.ISCSIVolumeSource{}, - }, - }, - }, - "gluster": { - Spec: api.PersistentVolumeSpec{ - PersistentVolumeSource: api.PersistentVolumeSource{ - Glusterfs: &api.GlusterfsVolumeSource{}, - }, - }, - }, - "rbd": { - Spec: api.PersistentVolumeSpec{ - PersistentVolumeSource: api.PersistentVolumeSource{ - RBD: &api.RBDVolumeSource{}, - }, - }, - }, - } - - for name, pv := range tests { - fake := testclient.NewSimpleFake(pv) - c := PersistentVolumeDescriber{fake} - str, err := c.Describe("foo", "bar") - if err != nil { - t.Errorf("Unexpected error for test %s: %v", name, err) - } - if str == "" { - t.Errorf("Unexpected empty string for test %s. Expected PV Describer output", name) - } - } -} - -func TestDescribeDeployment(t *testing.T) { - fake := fake.NewSimpleClientset(&extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - Name: "bar", - Namespace: "foo", - }, - Spec: extensions.DeploymentSpec{ - Template: api.PodTemplateSpec{}, - }, - }) - d := DeploymentDescriber{fake} - out, err := d.Describe("foo", "bar") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !strings.Contains(out, "bar") || !strings.Contains(out, "foo") { - t.Errorf("unexpected out: %s", out) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/generate_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/generate_test.go deleted file mode 100644 index 62823f9dc..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/generate_test.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright 2014 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 kubectl - -import ( - "reflect" - "testing" - - "github.com/spf13/cobra" -) - -type TestStruct struct { - val int -} - -func TestIsZero(t *testing.T) { - tests := []struct { - val interface{} - expectZero bool - }{ - {"", true}, - {nil, true}, - {0, true}, - {TestStruct{}, true}, - {"foo", false}, - {1, false}, - {TestStruct{val: 2}, false}, - } - - for _, test := range tests { - output := IsZero(test.val) - if output != test.expectZero { - t.Errorf("expected: %v, saw %v", test.expectZero, output) - } - } -} - -func TestValidateParams(t *testing.T) { - tests := []struct { - paramSpec []GeneratorParam - params map[string]interface{} - valid bool - }{ - { - paramSpec: []GeneratorParam{}, - params: map[string]interface{}{}, - valid: true, - }, - { - paramSpec: []GeneratorParam{ - {Name: "foo"}, - }, - params: map[string]interface{}{}, - valid: true, - }, - { - paramSpec: []GeneratorParam{ - {Name: "foo", Required: true}, - }, - params: map[string]interface{}{ - "foo": "bar", - }, - valid: true, - }, - { - paramSpec: []GeneratorParam{ - {Name: "foo", Required: true}, - }, - params: map[string]interface{}{ - "baz": "blah", - "foo": "bar", - }, - valid: true, - }, - { - paramSpec: []GeneratorParam{ - {Name: "foo", Required: true}, - {Name: "baz", Required: true}, - }, - params: map[string]interface{}{ - "baz": "blah", - "foo": "bar", - }, - valid: true, - }, - { - paramSpec: []GeneratorParam{ - {Name: "foo", Required: true}, - {Name: "baz", Required: true}, - }, - params: map[string]interface{}{ - "foo": "bar", - }, - valid: false, - }, - } - for _, test := range tests { - err := ValidateParams(test.paramSpec, test.params) - if test.valid && err != nil { - t.Errorf("unexpected error: %v", err) - } - if !test.valid && err == nil { - t.Errorf("unexpected non-error") - } - } -} - -func TestMakeParams(t *testing.T) { - cmd := &cobra.Command{} - cmd.Flags().String("foo", "bar", "") - cmd.Flags().String("baz", "", "") - cmd.Flags().Set("baz", "blah") - - paramSpec := []GeneratorParam{ - {Name: "foo", Required: true}, - {Name: "baz", Required: true}, - } - expected := map[string]interface{}{ - "foo": "bar", - "baz": "blah", - } - params := MakeParams(cmd, paramSpec) - if !reflect.DeepEqual(params, expected) { - t.Errorf("\nexpected:\n%v\nsaw:\n%v", expected, params) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/history.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/history.go index 01995f376..37cb9e0b2 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/history.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/history.go @@ -67,11 +67,11 @@ func (h *DeploymentHistoryViewer) History(namespace, name string) (HistoryInfo, if err != nil { return historyInfo, fmt.Errorf("failed to retrieve deployment %s: %v", name, err) } - _, allOldRSs, err := deploymentutil.GetOldReplicaSets(*deployment, h.c) + _, allOldRSs, err := deploymentutil.GetOldReplicaSets(deployment, h.c) if err != nil { return historyInfo, fmt.Errorf("failed to retrieve old replica sets from deployment %s: %v", name, err) } - newRS, err := deploymentutil.GetNewReplicaSet(*deployment, h.c) + newRS, err := deploymentutil.GetNewReplicaSet(deployment, h.c) if err != nil { return historyInfo, fmt.Errorf("failed to retrieve new replica set from deployment %s: %v", name, err) } @@ -81,7 +81,7 @@ func (h *DeploymentHistoryViewer) History(namespace, name string) (HistoryInfo, if err != nil { continue } - historyInfo.RevisionToTemplate[v] = rs.Spec.Template + historyInfo.RevisionToTemplate[v] = &rs.Spec.Template changeCause := getChangeCause(rs) if historyInfo.RevisionToTemplate[v].Annotations == nil { historyInfo.RevisionToTemplate[v].Annotations = make(map[string]string) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/interfaces.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/interfaces.go index 890947b5c..8f1e6f197 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/interfaces.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/interfaces.go @@ -18,7 +18,7 @@ package kubectl import ( "k8s.io/kubernetes/pkg/api" - client "k8s.io/kubernetes/pkg/client/unversioned" + client "k8s.io/kubernetes/pkg/client/restclient" ) // RESTClient is a client helper for dealing with RESTful resources diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/kubectl.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/kubectl.go index 60b280174..844a53781 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/kubectl.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/kubectl.go @@ -26,7 +26,6 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" ) const kubectlAnnotationPrefix = "kubectl.kubernetes.io/" @@ -75,51 +74,72 @@ func (m OutputVersionMapper) RESTMapping(gk unversioned.GroupKind, versions ...s } // ShortcutExpander is a RESTMapper that can be used for Kubernetes -// resources. +// resources. It expands the resource first, then invokes the wrapped RESTMapper type ShortcutExpander struct { - meta.RESTMapper + RESTMapper meta.RESTMapper } var _ meta.RESTMapper = &ShortcutExpander{} -// KindFor implements meta.RESTMapper. It expands the resource first, then invokes the wrapped -// mapper. func (e ShortcutExpander) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) { - resource = expandResourceShortcut(resource) - return e.RESTMapper.KindFor(resource) + return e.RESTMapper.KindFor(expandResourceShortcut(resource)) +} + +func (e ShortcutExpander) KindsFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error) { + return e.RESTMapper.KindsFor(expandResourceShortcut(resource)) +} + +func (e ShortcutExpander) ResourcesFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) { + return e.RESTMapper.ResourcesFor(expandResourceShortcut(resource)) +} + +func (e ShortcutExpander) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) { + return e.RESTMapper.ResourceFor(expandResourceShortcut(resource)) } -// ResourceSingularizer expands the named resource and then singularizes it. func (e ShortcutExpander) ResourceSingularizer(resource string) (string, error) { return e.RESTMapper.ResourceSingularizer(expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource) } +func (e ShortcutExpander) RESTMapping(gk unversioned.GroupKind, versions ...string) (*meta.RESTMapping, error) { + return e.RESTMapper.RESTMapping(gk, versions...) +} + +func (e ShortcutExpander) AliasesForResource(resource string) ([]string, bool) { + return e.RESTMapper.AliasesForResource(expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource) +} + +// shortForms is the list of short names to their expanded names +var shortForms = map[string]string{ + // Please keep this alphabetized + // If you add an entry here, please also take a look at pkg/kubectl/cmd/cmd.go + // and add an entry to valid_resources when appropriate. + "cs": "componentstatuses", + "ds": "daemonsets", + "ep": "endpoints", + "ev": "events", + "hpa": "horizontalpodautoscalers", + "ing": "ingresses", + "limits": "limitranges", + "no": "nodes", + "ns": "namespaces", + "po": "pods", + "psp": "podSecurityPolicies", + "pvc": "persistentvolumeclaims", + "pv": "persistentvolumes", + "quota": "resourcequotas", + "rc": "replicationcontrollers", + "rs": "replicasets", + "svc": "services", +} + // expandResourceShortcut will return the expanded version of resource // (something that a pkg/api/meta.RESTMapper can understand), if it is // indeed a shortcut. Otherwise, will return resource unmodified. func expandResourceShortcut(resource unversioned.GroupVersionResource) unversioned.GroupVersionResource { - shortForms := map[string]unversioned.GroupVersionResource{ - // Please keep this alphabetized - "cs": api.SchemeGroupVersion.WithResource("componentstatuses"), - "ds": extensions.SchemeGroupVersion.WithResource("daemonsets"), - "ep": api.SchemeGroupVersion.WithResource("endpoints"), - "ev": api.SchemeGroupVersion.WithResource("events"), - "hpa": extensions.SchemeGroupVersion.WithResource("horizontalpodautoscalers"), - "ing": extensions.SchemeGroupVersion.WithResource("ingresses"), - "limits": api.SchemeGroupVersion.WithResource("limitranges"), - "no": api.SchemeGroupVersion.WithResource("nodes"), - "ns": api.SchemeGroupVersion.WithResource("namespaces"), - "po": api.SchemeGroupVersion.WithResource("pods"), - "psp": api.SchemeGroupVersion.WithResource("podSecurityPolicies"), - "pvc": api.SchemeGroupVersion.WithResource("persistentvolumeclaims"), - "pv": api.SchemeGroupVersion.WithResource("persistentvolumes"), - "quota": api.SchemeGroupVersion.WithResource("resourcequotas"), - "rc": api.SchemeGroupVersion.WithResource("replicationcontrollers"), - "rs": extensions.SchemeGroupVersion.WithResource("replicasets"), - "svc": api.SchemeGroupVersion.WithResource("services"), - } if expanded, ok := shortForms[resource.Resource]; ok { - return expanded + // don't change the group or version that's already been specified + resource.Resource = expanded } return resource } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/kubectl.test b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/kubectl.test deleted file mode 100644 index 152a3526f28a6debcd636872ffc360cc588a8235..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12585712 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 z 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�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 z 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

    0z(efI( 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ℜWO;(mRu9>m%t0W8L)B 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®%-6Lz3 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≥7 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

    Wj=RxFSEmV5NnsJdEnY?=w<$V-Qufg(?6vm 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� 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%0Dq25b5HJCsQUwH-p-3RS ztW%(9hcEnFycLWch9_%XR9YTb^Er+=>l`TB=?m|t1^P7XXU@7sW#ga6z)AyaHUdvN zg(8oc1>>Upjl`m&6~3Bx@$n#1?RsQzlS!`?p~xp`#>Vfz60_zm79JmS=(qnMPmWnI5gp%R5)P zjb{03W-!+_nx&1RIeY<}jUuC?Mg!4jrlC=lNvByYxc&@j8P%GWd)k%(-dFU66<~aP z3SOGYr@~8jD4f7%`3Su9BObO9ZiZKGe11uB1J33%&_c%-@!2jq4k)d2`^fpImI{}3 zwi9CdZ##MgAA=yn%fP$61DTuO?tz{NNOK@zwl$FJ>ml zBD@K3z;{REU0g1Xj9SB>j3?UV=ZrjgfHlOP5m}m~&pN-IL>WgLA8i9_gM#%#cqc4H z!VghZ60-*JN+(*VPaDSGI`?VA{Lh;Qw!o%C^IXu#XRJR9kUtU=AMIgKa)NIhdmIczI|F!xE zxQN{xEf$DQXh5k5=&QKO=o7?kwQ-(Yk}fms2mv`j#Q4+dT|d_ z5q+f*V-7{{k9nTtDoO>a=qYG14x*N|8z2P1I>MK#_~36i&z=-69R!DL) zA@XIW^b=Cc_rQ+`-Bu_kWkh%xA$>8p93FDq0bbDw<=82yN$N@x`IT6YS6 zBx}r&F8nf$;+Yn9SrB>-&9%a19>A3s{tPX}90G`;JtQbH6!c*!&%vdR@VtJd-ru6 zT?bsvDx$A!0eCGFvWfz7dQpR+g?hEfx;cE3uM^%#ol0GV>gapX#hMV;KR2*5OhG836agUSnnk=LkQkzuLn2YwDJO>aOo4MnF&L(wVHPV^aF*{X#DD8m;^r6-~M z3U;dmH?rG)bbdvUC)!_Po+2oP&Xb#|@9J6hw1<}qEr7#v{x0A%deItuWb?$R8aaZ4 ziu;>^);B|`OCbzr3%fuT#%0AcqBYm^HgL!WtKl$qqDj#bu52;#_{cR8} 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

    ^cd>e#Bctkd?b`w;xlACHHWAH)4kS6MUKl^hse3 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 z zE0Ln+C|cs z8C1nVRI^G`qPozJN?J{WswjwRpI=fIaA!ZN5;l_tRXB+1k*8Cl`cPKg$Aw|6g{mNk z>Y&tA&G4ftvry#+QT>|QSRLv|RbipZ4WfFucS^Qr`BBwas4{}6?p~0RS5JK86NYIP zs&=`zTD@`EDXE&^N7ZbhS{p<)ZGK9sw(+ByW1(__s6Nh0N!7nU^cAqhLbWD{>Mt*( zq^ik}YK4VrRS?xVsau>9KPsg}oef26vHjH9DXIGY1787^)rx9)kg6wtO^ND0KPr_a zMWuYRsCwz{l&DVhqf%ut-c*pPJAO)us;?iFS_4I;f?-irmAZh>zVG9enma``D@fHp zW~HR+5ugi-oEzh-$~wyn5?hUjbKGs7iyV7Nn+Xnjh5~3)R>ls@+pl zb$}n$S_@Sqh^jHQ;{5!MuYl6^Gd@E}5Y;oOt>{C3RQVE|Q*|B|L^ZuRrCgorM-{eE z6$eq}ewPx}7JgL47OJ8ks+$i>*$%$+wvSh`*P`oR1ySv~Iwe(gepF*EstSUrE+3PU zs$qUqWfrRZAgW*ANJ-TfZ}|#XVWG+mqI!5;N>sP|QPo(eGJ>dKQq{+gs@X!NofHdIMQR;vmG$)TW@3(oNN-EFwH7J~vDsAJaAQhT z2m4WFASCAYaayFCX;JmpuTr8~x5`&QZr9?XlF*(_)hTzRL^aosDr}*e6QpWhYF?e; zM^$X0l8~ZJ)kb%wq-twFsuBxTbC9Zqsi|7|hL2ZcEmRW9w5fXH-jq}|_)(QvsHO#} z>hpa{RQvc*RamGbglbcD(tRmWeZ#fVZrl>_D+^UkkgCm7Q+1D^3N>8l4IpU?%}JWb zqFtJ6GtD(abO8h1&YVgN5;d8*qv9PPfdOqyg(Uz0)n&k*btWy(@qRheb@SzUMkOX8 znNLjHB(K4WT<7U#6f!Df<-W-${jkRqCa2ae=J35cgCo|0({@pG`{x_~FX8kcGyXrv zjQ@+z5FK*-+@XxYpTLV$LNs)&xi81_qs)C7?hiNjrMN%T+>gcmXmcOI{Q>5_1otD% zJr`r|WA2MNOxO%UG3DU4=Q(O<=IC+iY5f&skC`lLW@lmsugaP`W7^$Z3hqiY`|%sN zdy3iG2J!9Lg*Cv670rJ4s*d3v z@xzt5`Jwuw}z<8sC$N)%EWV5V0S;9AXb!P()#mh8}I!QF7^ezR)E`YH2^mx0GzD=qYQw=|DvI<57`nbf#u-k2>^dqfXkODN%@*X#%(5t zJre+a-%85xfdR->fS1SVTm)ym1c0RqFx>!TD8TP`njBs?Q;9q-U8w*y2B4iTD9*?G z$}AqgGXdae1vtb2tW|(tr<)umCID=$09zXXM*(&^Z-8=X{{(3+{+93hp5(Rj5v9h&60T#FbKRg;N^nnU+n*pd%fStcKWmu8`u(1ME z7=S7T*yk2Q(zOWy|L!k^-p>G3D8LJ@)i@;q0H`S5`39g|0ron;SdCp108Uqc_ZMlQ z%M{>Dm#vuz06QzdYy(iL0K>mAgT#7`bs`^=1so$W}Loxax+q2UzqE?$r3-)v>x ztnl7EwTWO`y{VRGx%XzgzFB78w0Lhe(>K?dH}kwV3-Xn3=FJ@M%~|^9Ve@8|_olzT zx!k;I_TDVY6Ks>rn;G7lbM(!l=FK$k%~tXzbep_!*OutjNY?@^0f78zerCI1_!%_@ zM_XR~Df&f7aJZ8~40OJ}Ti?7x$OK(w^X}=51oIx|T{+*Cn|EjGyKT(7GQKM_@A~r{ zeD6{;z--GFKzE}vB}u=(l;2`X$?;ekf=MY6IOD7P00MJSN{bra|cf;;)za zSW)6qEY4U`ifNC!fOv)SKlJ zlOWXBAz~WSE+CHDi0w`b`uH8gOJr3-Y2{J2{z+{-(}y{2opTNnFyvckgM(jjg(Y+a!HG(T>o zNKM|n4sk05otnJ${kXN36!)ACakFvl0^JWR^6^9^S8>nk5O+)0a3Ai+t*WfJn>)nK z2EGe)f4|U2cUbT#?in58R!QxY-#7Si7hAZeb%>izMi=Pb$B(VJ5@}MWV<# z;uu8qqMT#W5mjM~KvmM%iAobR646%8Fy z?d^`SuD24M?`(gu7PEjpep&1wF0Jep>&NkoXsGHdb2J&^gBJ9Yap+I0ebD=3(Iues zdFj19c#sj>>RZ9|WqTacqKiB_-iTwm*^lXwZv^beALC$CEwCAJu+wd@^4N(Amhp8Q z>?jLti#XUWHdsZhkAki5F>7lJ?9E;-v)V7Ridz+X68l$yOV|7m&+XHHdSz>igSC2K z+C`0>qL4Gbk3&9SLEamOyxfMQWsc=2dMRXum*)}-@{Bm- ztu`cE{MbW0p&au2aXil^3-VK*?~eTQozrYc`XRA#3R(S89P)4r@~$}KPBtXHqgakY zPW~hg+24Y!i$i{Wp;gWnLO#lK&7+|PkJ@*tJPwVBLq1_cE+^!P3b}DCp64AF{g!@(J!c6%F0!Dc3F*)ViP2ziP^mVFS< z^ZN^|a>ny~xys^@L&!XZ{KS)?r!2^G;*i(bkZTF~6wYe~wV!_$&$HHo91@2dXG69V za=b$B>ec5#7UZIJG|rTvZEVO4nDy9Z3OUTH&y6g|+BoE=7g*)YCFFuOq4p9_vsa$) zap=c56Kto4Z&Qb>~~Zqmysdf7=Y-+nCbp3+O5UJlXAW_o!G$47vqJM?mvUUt*VFCWUgMS5w{ z%Mp6n3Ks}>#v=Z)e*b^kVg_#iU*m^2{;m664!)B6Q{X&2IZ&vCg=363A~$GcizK8a zJD6binQD0Db?Y)J{}GAq!fi3(#DB>x9Gs8MNRIV+3AgBg*X_a#jzqCtvkf$dK#WZL zbFITZ)r~oAE8Gn^oL(JfpS=19_!3NykVvq99isnSxEidiVX}0eGd`NmDBuR#m!Lk+ zK;5A8i%AYzrOYK?cYVY5I3+;i%y|oY>0p1MFjuCcU&GlO*ior6k5hNbxHMIFm!xwB zx{m1R+?lpd8*^gg*#$7(`$1l%2J3TblPP~O2`U8{QvNczK|Uo``CsHq6dn5?vFJ}Q z89fK{VJt!2dk&izZl}{!S}OaOo{gQDFk*t~S;!!lC3be$k0&L@w+8ZMQ@Mw3J%iGQ z&1&5ECPD8#qyi8iQ+5^AqP8t0b5J+f>~-@<^epUha{!BiLLHFHYuf_s_WB9&82ua})qlg5fBvT2@xQ!e`NQ!ai1Q!qNnQ zXBuG90|Q(uzLu6ALKe1@$O&x_Qy3QROK@9y=0g550H-7K1Yk35iFL%B5;L>e?t5_{t^c?N-ew8Er8`F^!E>h39f(LAT=Xeur2lmvIfquxHPV>PAyOAK z)Rzno8fx3pEg%f&9!uesKag;qmV!U|&aUxH+`T+)JyCTsdZH{2O}itYCzr|tZWauT zq9e?9>5 zSMJU$)Uj0}xJS_TF$ z4=Xl}om5aXH2JQ0OyHOexzd0QSTr;e;2Ljv_;f9BLE95Jk*CR3rGqWC%BEVeE>5uP zqG5JoUDVI$xURig`70p3x{l4Dp0HNx+Sm7Iv7}_%1VP@WOIb7l(#m2FFAMDKi?eAk zC)tzQcUuxm@<0D#Ni3<#t6xemSF-K{jFkZoYGWA?L^uhzLH+*x(sb(ggZm}2WyeIe zP|AC%-#LE$zV^OEPEEVM<2v{B`#3;){EF-M`hNX>ZQsO_6$^f=CMrEix)a}fSX1ct zQ`Z~){##>L^n1GwH1t$64wX{Bclojd>o8EDgZkZEmrTEJceRGl)FYK=t{K^8QULpKj!hiNsl2I#a@Tmg%*7+?H`S; z1STa_cF*GXwTqi^K^Ms;^#C8K3#GR6>diWk`LO`GZ9Alu+$xvcimsB|AWo9YS(3XD zNXX&wd{f&xgC#}$dIkx#82ySoNE@TAj%XJd-9lf?m5JJgDl8)h*;ENBOT}`t^}pxse3eNedlw3oxVDhmrujzZ8E{(4FQN}|fX3GmTz)eR zl`V4D@e_{|9A42XoWwg+p`Zv|8Wg^rLunK~(?b_#=;rIG_+gX3T#9Z)(5=XY&^%V= zoqUPd0_!vyI{x~A6docE(o>mqCOeirIQS%#KgPpW=q+el+>Bd9BjkieY*ar~oab0Q zdqr8XQ?+xno7G>+<~_Ezr>~V28!j$a>>4^oS?<>NuVAx#q~`LH8SAU5UvLtG$<@7h z{*z^2?>WEQ_VpK^(IVeJ;1kThkQUxj`!~O$I;Dr<=n8b-zV4qu>L5O;YYl_kncD{C zV4ubpq`I!P3Y1Tt9-#bOd5|_nci+A~?YFO#a#cwQGu5_= z!Noe?CGH5S3HQxQFU$X+ef?ZaUvFbi8Er~tT+75VR(m5)XU*uuxQZ>63N*%5tS#Ed z{(<`2B%N$Xn{gF8D&$M`nOdxC#j@hix@mzTd?^poBZ56Ou7}gAcEPyjs&SPtQ&wzm zHLlmHajlfjXKaO<(%oaS4$p8r@bRT_<&aCA9y00gvgxbP*afLY)&jiwX+Y#v{~qPcIh*Z(XG{kGwJ< zySvJRG%z=4e>$i66SCQ*+4X1GrY_}+xJO<5L4eKg^O}^+ZF{HVu%x;N5hv`H$(BC|2YBqGo?Yzs~jP z*P~t4ubF3xe${=BAywl;pyyaaWd{|_xbo-Ic&Yt+qi(l`k5fLzD{W8v>L1Q_~~ zJmA2Z+kl}d3pQX%;p-ERa`rD4!fM46&h)6le}*P<-4Giz`R(jL+>6ixP}jtXS!b zXd=UzT5$Bbgb~7kXlTXMctrEAZ}NeNy|ifPYJh7*bKRj@;Lb)Y;>@d`Dtun6d-M#V_c;%^Yv!zswBh@dP~}ZP3p}@+p-3sk3&2h*LoFKc2#JTD{=cMMG^H#!Ds>l!&J3tOC^S!*8f2K#{~doxqXq35DU#XKlob7_V5e zn^9Bs;#jZ_>p&)U#Qx5THG|bM5SPCA{o*@_q{#f%u9 zC%*7Q4&m)79M{WOj_sd}5c_T_#4WTNatlcYt?hy)6q_8E5H0^Id60(k2V+08m|;Ts zZL+IHIbAt&djco&X|0?lkNO%Oq_-$t^g?@lk>1YfeTF6SdIB{}ifrA&yV_Qb?S?L^ z4X}8UJm@KlseT;`7Br9r;E&o!q2tdLJb3uysBu}by;SjTR1f50UNU2Es|PZVmz>z; zycl&oiT9bYh1%u59T!C`e}nq^>q*_#*W+z{6=sg)liJn>QXdUH`A8rhRrIPFGBiy+ z>t_E!eVt+SRp_fTktj`AO4}mTTal>ur_g5G(jQP-(NAH(Hmb0SQ$jhrUmB38?c_mE zB?@g;50$odA(yJC5*#mg68)^dBtY*(d5{Krd#tpgqqfr4sGPlWae(9&dC*gm)2p;M z`;>N7he|8qvdq}yshI87o);JEP1L!xXrvzh>lGy zYz8fAx$EULZMBIcn2n8e%xV|291jvSgxVMl;_%J2> zDL$=h#eA~R&GQ1HGF2X=jqnl*g*5WI_g7arzKNf3G!zt-?=}>|*$Ew06X5vDDSVJ7 z!r4hRu1R@UNghk$JYJV6$-61ZWgvNzO9CWk)TfbTcfx^6_pELRNh8)&ScGFlj=Y}0 zr#g~W`ci=K@mXnA%bK`!V~5#P?8d-}ktZB=zdq?}siHkYoFTOG&Mi>?*nRV}Q`pQ) z*OFUpMMFO=3Zr~FYZc?KudruN11H-@>}1XkpITTY_KSTsNKuU>;N3Wf3?;hC!Z0U! z7Bw7ScpxFsa|4R_bZeQ$KbRC@KLQ}oofGbIzQnTnn(D#>!OwSc@m43lx`u>oUckq- zt-^w6=kj7W%j0|(lDFaclsTM*%!+kFT-pK`QTr} z7PH@d*<&_VWOT~xcXtUe`yevAED5tO%mGDyX4hghKbUehTK1oFXE%z+w~2Q|F;2YCBMuP_0mp6-S(=MnAs3B2w%IfIZpmvd{Nsf zv4m*o)Xf6qPv0u7CTa^4y9tyZEugilpwf<8J*9zN|`ws5A%Kg8y-{`5XP%Gr` zs~t+I)o5skx&Ze#mj`Jhf2_s*NH@5DHQ_w+DR=4&oXZ!rtuzA=p7O;3^82@^Rd|Xm z@{77b{$i5nkuTH=_wVG3+E&&i$ba^c0Qv1QteGC}$9tP|ScqVEsH1+J=`JD4bCiaN zxSj~92JLN@Yl!G6?QPzq@t`lYYxxo{nXzp&m8q#CbqdDryky2M$4m@_oyPN$8T+4~ zBy}S%nXxb0(FKi$uDi)J=(CSw3(Pe&%fKxZVOdG8x#sg4yAJ>uD5&8jBlhW!g5@u` z0Lx)6meV`K^5YMJ~@ z?mYs>(wH@E#idJ=!X18CyO?-5*0Ar5hOI{(OST?Kw!#t+@&1Ob`wLq|Z225xbtM42 zYgpXC(Ibs!ANe|+6|>X|F-Q*S;SY$gmVC|;4BC00#ruTLJBRU1$0njiO&SEqX$!yi zC|^7;3~+JSu*&Zu482uzAfuAhRbxFVHWk2s(a^6;To2D2FyT6RQM1qq#LKR1JkaCP zpP$D9e8e-57k|8OkvlJMlXNduyu_=o{)hgD>g&oGroQevrhE0(FIZo9uKeGvueocx zR$pTlccZ>uZtqEb)fX1SiWwC<^$-)^lC}x;UIqKeA7y~OExAX0i@uspsTyUXF6znA zgWQQvq9Ny=xN_(K${X^aUiTk&<6CU_5(m#KwOz6T89@a+xHVs{bLHM7m z0!`-j@*r&-&m~U{;TgE^D&aqx(1jt~#JAqm&;{dLPu~>~mD%zjO@#XfoMm9Bb6)@1 zPqK+@ZJ{KyZ@t090I!ez2Op%3;fZ5Ao%8w&iCh?7*DkD) zlGFCR$QMd=LvcW;pLlHZ^ht<2TSR+5&D~)4c7EC$;Q9k8`@R!-EfN`Up!dhy9u{66K^!MN09uFGSfba{}bCTXgK7hR=Y(GBw5aEl?IpxQQ>aEp`&`{x9kr+ef<+Q=`kY)*bRY>pdl zG32j`lW)Q;%lQEFFFpwfk0Sw$w2?o|BEPsBvt^^Uq3#(PJ2;6ER3Y0+>?vxZxKVAv!p_HSM?V}mu-M^ojDrDVkp(U8h*jEQ8$ z>NPa7MnfZuG&J%jO=W4SR6`?sX`tk44UJT4XyhdgjXZ`6ss}%>KdtL=Uf2iyvd<9u zsKHD2#b~kk<2}`%KmV+mTN%rQJ2;JR#8zL%Cv~mG5Y|4Y1Zw-Haz5zkBo6=FPI0wK zXzIyr4h1_-=)wXe_IVNaR=U~?P7Dyc@#$$Iw$yFM%_{fKi5=o6wpfX!IRR(sWbgWL zfY=cqr-|5Lk03d*foYSK=WuHo%z~e<)6Ti=Bt}~U+xh*YjB%8?Mpk~$s2m!J?u=dL z4=BVw=w_I-BcZEZT!SjctmL-mvBTt@W;$G30~9BwanTnvF1oX(Hqq2XjglUR3&4Z& z{`OzGE$`Jnd0%L1MizWUL-S7y)XIisX{r_U4Am7ZQ45#6R_y6)OZ*~ax|Xw4gbE-+ zy9AZ>#7291?vfwK#cCiX`5_#@3tlRQD-Hfq$_WPeY zxYj`CV}ABM0g;|(mT>$p&G)oLdbMBoP;>N7=ksZ8>sqCE_1yt_oj21YDBYLpt^D-n zE4{x{*5k_*2aOBRJ1<=(;|~co&S(_!dh0ZKOTP2!cjcStIYmAhzd;6f0?k2Q{m-n8 zq_gN(OFf-~cwDu&F8qk(SZ*^|R`BYhrAD!!-L|l7lnhJdVWeYC00TCLjh=%R%7T8G zqcYvl)>)XDeId2g%*;N5_c^h!_2_5nZBA?%FXpS;RF!|xcIjPSGGqH_Dy*qXb@c9Z zTtKz`3$|YacJGRQV| z@A|Ti#tPc4x;;(w_E6vL%_x4M=IE@})zZjX^cEeHCVG3K?`X`ho1fR1<<;*Y%Kp$C zh@VeO?me15TylK}mSKJ@ZwvTCEkP`&B*!B94lKTgHLt!-v4|D0H8zwC3-lcru&bn3 zno1a(ht>pXc<-n#>HA{0b*8?bZz;FY_mK6-==`aUTA7-z;~y$FOphj+kw3TAvzzB5L!S)M@Dadns|22re;AdKtTB=uUBKaQq4T zhLfie4d*$c^2Iku7%TG)fXIqFi9}(PtXW7{T!A67IC*D~81%<#9S_5h_ZD>h9Vo?lh;KEx%lKlH+ow8)ouT$X%gix1gTe)xO+9dM>2`C<#lL0VYh zFj|e3-fCMN>Y02~I;spL_CUTwszgzZd`TxS8v0~KK)(8kcJ!2=>5{K|yDnc7b~5tC zmisGVIp$~|_-8gLoDS18f$PC{q(P&(;m>F2WIhZQ>B7RXLf7zpG;~?4H`L9Nw|;Ui znKwOV3^g%m&%}lqdsah4N$i<5Jx1hBZ6GG&^832Q<*;Wmods4=4&4~-xxAbk)3mRg zzk$v6FDTPSl&5cuKTO-5q$MO$#5Z1a8=VKCz*|6%ks*Ew{@;!kKX1K}Vgi2tu)D?2 zs#N^s#$lTXWu0JwC8^nfvKI(|GL&l0T*AL!^zBOD4I!_ zi009J5)ExA&Zs4t$H{W%`obE1sjY*VVv-=BbL5lM^S6RN-#mc{%-_O18WmWQ`gm`f zRJYl^fls5M>gxlR=Kk0CAZ_FZy8xY&`xAR5an9QNXPPlJ;Ziho`vHMLsjKBdk0_t3 zYyp3k}|cLragdny@3AJoE#xVR^~ zIKvL1HrrLW*f@3qRZY#oH?-!rc7kx zP5J>ju{(G%k1DPE!RjWM4dl%3oU&oE`S?5S2Cd*FK7~0$Q)N1Zd8AHZzC))lUm<(S z84~5Ogwz`95mT z#hNtUr)E8F&-)sCE|1~3=i>9tp8-Kpf#{2BMyWf5Pkg@O06ehGaBXMQlvmiB#I&;h>hS*@07#OF4>s{6qXWs7{G{<^4dk`AR69gs#?BnAO8 zl8HvP85fyGE&?O&<4R*Nl~mVX7cV{gwZzjJaoPaxObM(BGnjqb9m zZ!nY6XW!qK2iosX1EYg2Q|FZbRqVREJ=8*}kvezU{*Fp~3#$q94;un(A2mO%Y!7+? zofBVYS`U$uT9yh}S`gBlisy;KPw=EI>zVCXyE;Ve9@ojX4%AgSS14 zOc%esP7S+Mjctkh?W^sDl-}zJL~Oh6L$re;lJ9LqFYJ>P(dVQ9-I2At`Vc+OC(*?5 zjXFVOo17a3HZERTC>#%U35J5jT0}#?U(U(T;wWfPGP5J5( zKarOlJ-fzO>s)>Gs%7D4^B&_M)}yh!&y1PL3FoWre_2iY{c7lYYwANa?c&8}#Rh8X zGBx!7prKD{-}mg_ZTk)@9c4Ngd%(VD%7g!+eJ}Of_m(*2!_>B~kO#8! z(4@x$QZj0Cj}_4l9(CWoNB#C)iFX^PGx)N$)f9ko(>G|}PZ^fR0`$7Gfkea-rZkyp#y8{?opY{t^Bi16|=9(|prU+)0lp_ou zTJt)4BnZ!;9Y-i0JM%d=ucjw>BwalQ^Dw}RNstNgh^HtzI31yZ_%Q(^hNC%lK+e(@)7`XLr0Ecldln+r zvkPk^yOqol8oz{OS4D;|&#McOp0mCPM>5+YjjQ7X3m%zvjp-9jN5t_caoRNf@VtW&OYWLPh>geFXN6<&b4i zQ+0F~>{~(Dc<__FSqIh`j!k_DveaSi8z|?)@)y?SZHQBk*WqjDbvGrZ+XOR)$sbP?7FXbDs5*|I*eHx6C>r~?GaPQB0li!bzGW&e01F#>BltHS6vI)-D{L zF}wF?+(pv3!#m+Zz{9B0tamnDg(l{*XNUh7qss$-}n;yCDyo z2j_>Mb}sq-H- z7twrozyI)OACYb>WD6xU=j;HXe@+*n5qCCPmlw@mDyB8wf1n$u*2o-!BF_sw7rbpl;kSJ(Ll4ndg{C)pf=(#!|Z(1;+(W79%%PrDD})R^w2#?%~w z@=vv#Yqa~YNV^Y%G?j04AFS~rwWoc4d#Sto7j&pUbAncIzjXBxd#XQ-^~fPY+W*c8 z68bnMtI=HdSQvY(KVE0n=#MB=G*o#F8U3qOhe^f3Oud2r9V(*ixnKNc@W%VP;-2a#jAlvV2Urd(e(eSAOw z7v7~hQx^9q(a8oKbPcbDwL=0zy}3-T3+zfTNWi)~L!N0s$V zzC3Fzck#{l54(f5HCZ;O}}+zWrpqofSa7@)Yeg{og6ym~Tz_$}||zQGCQy;j$6A9jq=1}2EbYNN9Xg642$Iy-^DR#suN&N%~Jh`mL1jg-QB3T{DIcY`+ z$&NUste-w>P5l(m$drh3;3lG4f)k6xd(m}LeWXubT^QCV;t!%Z{wU(peHk_KgDvED zb-l4J6cI=Rdyl}LgQcQY2(l&#@OcI}Yylsmz+oTo$Rxnq8elBKi|3r9z}Rcf%6Yvc zz^~Fyfb0?r_z6CXo>Srjes;6OGEX+ZXjM$km1hHRtPgNn0$^oHeC?_*s&X)Z%6vfM z2$Wb=b&CtQ3V4OPYV`>uu5{o6RGcw3!o=Sv=;^WWLUc z{muGnSAEpWdgNFg>;(P)BCYau!lsG5?y`J^lK^*FzCPR}G3PGJ*WF2gyDVR4BmwS{ zd=)1Ec0s;=$^U;!zQ(i}`RdC2uNpHIwMMj)4)}OJiH5E}HPE_z{4YNEU+y2QH1kcB zJU8a~IG>{ZKQ=(({^=so3NCfj|D0JUG;u6kn}U8a0~S*rX-#3`(1Yf{(vK3KeMncO zTK4tsjj22dSacsYY$W8za*JY-r?!WT!qXx*dj*g4jtk@{#O z>(OTnA?L*A@?zS=tM$=1>(MZMlx017PbasI)TyomHFb?nZoLo}&?`T$y~O;2c5~Rt z1)spEaX$(1`{3#^qR1aZEA#XgJU4SPq#F~>W-Yk;jL@`I@<}wb)%1YfxbMU+m^P_D zsymdnnHi^!Tgjv_346kKCV`loLE2?yn#90_$TyAs6|oTEX?52CL__CY6CiY(Bhy5v z6$4B`s5zay^(QcyU`<7E z_TsDPy`^$Fo`%$2DGgitgwUB#p}&Y=_STcDJ>zVb{1twZ)SR9oIL*whWi>Q+_#cI~ zwXjKYSg7)i$;$tXWQ!(Y{9=kvx*J=uNltSf&0;53up>>ht)Ob2ng&<%8Z+7^BRX`p zTD~I782&{2mKl4-GIRgJy~Xz>hEc-ZIHQE`cL;;-)#f3{_x*B`$baRTtinM1qq@-Ex;kWA%m*F=%!ds0&ine5Q(=2Ia8fOtKCPRN2ZSSMQ^O&*GZBdl(XsgJ( z;C^UJ>;@X>%-9iXuD7(z^;a~|Ik6{sF?N2sK03jAw5L9jL-Nd5Z_%V@#)he7|C6Ss z=(2?gxOnzm#~Wv^G5%^k-GgB$j`#^Ba?^BtVOU2CaN3!B+^uuNlCQuCoFE**F~OCC zBGI!E58C6y-K`z46EM{+&gaJ0D-*^$2XvA@3?%l0I)4$fAG z;0qvlvBTAo)wgbf&qDD1&I#98JVW>zBR4kCpflrLX6{FU?8x|i0CiO;+> z9wQ1-jQ8N`s&;a9rX+bhV_y9tX_^)yBh+cDTpE#~6!Y4`7P(F}`wfL2dr1%eBMQwC zQFG_gg{%n9QAEk0&@MRqD#RYE_y=6AFRT${;D_^cCaN9hWB-t&VgYpOOb}gha&&cz zz~zono=KEVa+)IjIT?Q-wyYPD5?8kaR+P_#*XB^7O`pf|E=6dzGhV|_)T6(vw?p^YRU>#}OC zZLvj*Rx4Fhs+RgdO$d*OFBBgLKJb0lh@w^n74!do&zbw!-Mb-Z@b~-uKV2m=cV^Da znKNh3Idf+2Tu#7$cM|ha+y>&Gc>YzxN91rOIt@i8eU=Cb4n743ls4rEKlvH|`g_uS zMC;Dw6vtR09z_j|N1O*ARk9Rz+~=yQF#%Qg{XqOhs`$L*!BBKKB$Q`O-35(AV<04D zjGfqTx8cP(Q7k+n(b!V=An32)v6ty6GZAwTlak-AFK*__1vbUMbX16rFGw1iD(2!Z6P93slMQyymCJ`=6B*ndp56 z)a;wH>%_XyZkkk5nA1@0TsjSONVswjg6EQJNtmG6o7xZhIID0rGw@}tYlI#5XP=t+$8>vTRwS0g;Sq54}zcyP(o2kl<9a!Q!VxSjP%1i<` zQE1)j#Mv?OxXzHXDfIzZD}Xvp4*Gm(V44u<oSonqWaA~v*p=n-bB&PFS+Vu6e@!5&Nq7DfSz_ScUe+@O7CJA?~4g9pDHiIS1l~kEQ4X9#7;W zSeB34t1;X|pR3+IU{ynlq6%4@`z%G)U|l*holkIJgjte=ESp7S(mE?8tEivLkB}AZ z%ng+_s6s)}6{pV>c7K+dPce*Oeg@-CJ&IB`+r+zk5$9zagz-_Fv++@6FjushD-Mtd zc17ju|4qRna({tnPS9lt_UTBb0#t*?{rHH+_{bkC6T2fZOVUV`c1fpxc3%BVi6Tj< zL}`<(qW>K03*c@N<)cOjuy<6&$Oj1eBu@$~mKH(w2G<8rGw%1Y|Fi>Ylrv&CWEr`#U$uQ)@9$wysH|?yEhBGJPo7X$o0F_TZ0lrA1=$1i zT=8_z%Y(oDK8=>o#(I=fw}3o|T$hG&=aoH*@)nQ>8?Q}6`Td@yau3RbOVb(R?4G6W z7LW(8rK9{oyyqC=f15l||KX&^Aqse(O9id=I*K7i>Lnr;l9<2GiqzlQjvoOlHA??b ze_7PRk)SKvVK;q0V`ba&CafTspRhrx&sDwC<;p6reuzzhDC2)%ryUl|M5Jj^MH|ZY z84qieel2Zt`RvR#2fx8K(VV-4h6ez4K*RP}eHZ_nF3zXT!n@;7vD}WNbcBnqhP`zC zLZ9zW-qMqck5fN{-6aRS`gFYGu3n!7I{n9=5k%+gBX`dr!40oxkibI>bO2&#YrnoL z_6#7F{F1Uhx8of&k#9{s4SFEh%&J#1dwsAp6X>kH+NQIGWuf7~)UlQ^?8i9<8|oAbw3sr*qE(q@173rA4RihSRgc&rP=zaX=q*` zYqf8PW&;nPTvyV_`u!CewOJNSv1`z;K ztK(_dv4^R}E1&1fP0mn@R}PHiCSOz=_YnjJQiF7PHFwcB=12Fvd$~9V_dkneMXvz7 zAQ5aI?~dbaJu=weoFBQ|l8=q+8vO2b6mRS)6u$*O7U;}2{0PDoIb!$)D0zy^qyJ<4 zI6Z|Q&BZxg@FV;$jb`(5=Y?QO`d{c`Blnjg2*PY%(<>Xbc@ zQ${|jIVq^R%j`GBi(K@?57W^765E{gG^l6jTce9jN=3K6yXYQaJX_+v3tp+WCo{Y& z^;Y~S4e=@G_aNf#NA(o<}`SA9lZ3sJhZK2#=BwM|_?r3m{)7bEm*iHr1 z18_c1f^%5TXFW&W;Qb~;V4KVJCaiFdG{@ao47a-cFx<|22fd40Xy&;NbUa`|;U0W5 zj#XK9G_d0M@1>@AT9!WR>aDKb0o*P^%`zvnxzYzlH4GOJ!N+;;U!H9jAI7^Cv?=xp9I+= zLN+x>W=a9hRSeE-^MLaLZ*nidU<5@j_2ID18kRsw;Y?yJ>k>K6qqKDUY&x+{YBU61 zFApT}W3p95mIz+bLzmvQfwTAb&0RYGpcc+ezMw9q`3!QC zJL!|$E1H`;l}B=VC9A|6o#KCLE~(*~M9*{dei$N==Q%{KKn&KB?(75Nb#5F)sz6*t zVltoPnW38zA`{nR%_WV9cNb|n zq3=>T_=Vh~DCWI4QX9@rL;r94^(gwn@CNl2DP#3UY3jH`Il@j-P&If6ra-r39D`9=TgpFq%cpa-ot$B`A*(7`rze6#eYb3SQyj(gV zHLqlpH6Nxmi|9@ahPH4I**HJ{0hN_(+eW!X`({*B#o4}n(q|6_}}aidazN(Jfb_ zLpqX*E(SC7{RrgNv8SG}5)6)jCgWg&BB#uE_f6vUKq{G|wG1MZ%eI0{1D$=}6j z2;M8{d|qgtwxbd;CpRCY?#N5dRi$x{stGxvpPO`e{-Rg%X$HB;LHZOykh*{2-aol? zzOkog8{FE7B!0w`1I!;atZ;)Ig}>YwsU(RmQ0mp5zGKfcLH3}0?c3}TyUJrG!7}XW z7D-j*p)8%bj~Q4=VL{w%)KFL7lW7;JV=jAba#y8yFjYMzD+|+5?t3f~dJN@s&pN3~ zb|PiLJhKsqrt_)WHx=b#rLbI_tJ*IA{oPOnEg`d*{!a%JdJN^jlDeBzK2j`XkIEH_ zvY69NtvC%*bb??QKzV4-qFnE;T+D9qrl~xYlNaJzd3Yg=Z~6o3ncCEf!+Ax&jZohN zQ2vD^^bnOd@1p|2$>(mw8pP`q@tep_O|5l6e2JBY_`f8fXAp-tRxun(VMq+7^Oqt$ z44*?BrEbLjjv@`WKrqNnE~ilImAqH&VqTn!r;k;{9jkV6xjy|)1>Qts7i)_t_VKV1 zSH8Xb&z_TS9Ves-iwAe>QDLzK*g_Sf9%qZAL+KlM%J&$+@ZZr8o@*k+1_hMdB5u9KHLW3_AmF?yWX09WI47T zzo04#ZcW3=UW_zRG3omwGw%2wrT@dnXVU*Nc*nWM!WdJ~VKGwX5y_{26Ciyeb#z7l ziO*)ze+OwKoc=b-`&Dla{SSY7bLrpzc0>Oyrq7bcHlIG&k9F^RxZ)NN$-D0XD{7T-l(9$aYMT4~m&nSAUmS_? z4KBU_jz0xXa@)0?R8SFlL2GA`(dLbl1mE{a^DIl6I6vRUu(>afENulGx-KtRh`=5N z-A4~!BERBzosMXjRtwzOXn71?MYGUt$k&-i{Q&OV!2v%d-GYDyPK<$L<$Vxw@4;i~ z?7YLPfSXiOhc_9{eiWmD_v3r@6LsK>zDwPUXos%FMA5EW?J7BRuL@nnp@6r*3UYXs z4j6$r;*LafPJg0gE}w9>k5<6|*#V4-17^9&yIo1EJWKUnV8?4qY2uCF(N5GCqfI@3 z{RD}ky)Oor;cc==z_Z)7 zQQfDgVoXV~XMe~TLw&BWJK-)KL#lF!Dpy!dOIZEr)$kI(V#iN{WI3h)s;`eVHO34S zt58W1h5brKfhBm?;ei*K%!ePYe1OvdxO+xUl%Al5pW2Bz;*m}i{Zh_*x#U0}#dAS| z;oZGCEz-@R@BM~{2E9U}?Q%LEl)85H>$>CiV6l#Sa8JP-&dGS&+1eWQWYBlaO@_Xu z6lrDP#w(9jZiHc>)1X+V!5drq)1cun3}UX!QiXHLQZml+swqZ8%vekK+1aod*-eW| z+gyHeI@S>t`5CyGbMPcA!XD4&1K!VkiNeC`aeQX6Ojbh{-Q*}bbbhc9j27j$8js&> z)U?xA#etfF{N}db=(bN5od}ROzYGO&>o1o2@s@QJk;?I^bn6T2&ovLEpb9W_Q}LZS zn{ju<#rur@ugol;A^-pC%71Hq(XaZB$bpb*C`9}h67MxWAEd8na}E>^SdxD-l5?HE z|Kin7r+g3m8CA5Z)A{!{Ydf85Us%`aobk^OJDuqdyxi&hKJikgbL>@4r?X2Z-zYxl zq6>}&4e_>L;-I~!gEsEln%rRe>|h8S@DFxOu%iy@4ZoO$IW6Vzu)K#&1q%TeaVi!) z-szl!uLwD3etXsP67IY}UsLuVfEpl(pPdC_u)dc}h<GK`Qfq#FX;no^G7IEN0lz2|&qcJF@{&;mDd}%;l^j?Y19|?^={;svI6pP~ER2`- zUlYzt!WnYoh;pCL8DG7&Y0mhAq85tZ6MqTehxbs_<~PMs_~A)O_4(mmKI?dFk{{l_ z$LEKJ$sRA@Qq6QeXgI}=`#b;^<3fJZJkT6$Kn6Q=EIV$E<#k+@GKzzBbS-bnq>ta+ zKZlQ>)hye`-t6|}!9}lzj-k7;(i^PnLHXl&U^XbZAg-x5q+STr*RN!0>n)BApvuCa z+Ot93Y@n*bpso#vY7jYB69!S21wy#FE(~ISf#{~3JAq?oHiIL9I6oifrpuvUQ|35Y zj_Kq3#7r~3u-oK{5=y>%^HTU9{(YwYQBr;Wzl_h||5{fb)_L+!tUQs2H57`4&f9nU z{BJe?L~8Py8zhguJT&cXEy>Uenv%C3z&0kEoR%KFow}l5`$?+padVnwz95(_ljb1ZN#;6}FY$ zHJky#iKG$rRGvq4mr+Ip8fn;OiTV&JJL}ub1GQ#7UVZQQEB;MSOHA&j5;O-vde*h7 zBDuAhp4CUQc=K?2)~~qfnU@?DlJx<%KJ${x5u`(%qgh8H0G<3_`h0)+x^Q{ku(!yg zv4G_8!N#F&_W|zi;A3H?-L+iXNUwiVpnjUBaI`iIV-;}zz;Zswj`hdX<(4|cd4$b5 z7h(&S!(0f{dOTHE6)9fa#ux_zwq9PxQh0iJBqY7P{(<&__G-9&@zjf<`U2NfLszI? zDkTMcz4ZPI-_E`m^ZO9{LYAKy_Qf4+nO{UT_U7*dlkVA@Q{e`8r%J==Y7CO}Q85YS zoD0{)m3C7L#4?9~%Qy}J`|G^$^0L$+Fs34H1+^ov%!Tu}V_u5&aIErvIRexozx44L zb8R@kS3ML&lPkuOA11n- z;;wN{jbND&;WEJ$fGYE_fWRo!V>ObQb4jrsuZH%lL0F~g(v|HH9-B0XpS#Ux*f7~% zhH2odN|EhIg7(}T*?w`L5wC0AmF?*g_Hq^3=OR#659RksSBLX^xI8+1ML5?6e|SQUZQZ@n#*t+aW>j~>f-f)x*Do+(A$y&} zpW*l$zfP$+GK%%OGwCn1rQx1fR7UMb&8NR~`O)9Sw^mUtby3BX#9?jOMMP8R4PDG? z$D<}l0WjgUm?jczb4576n)Q}&o~R31 zZ+*QdIWLrtINR|{ufM-rKbHC408T?eGBD@_qyLxQppE}oV8F(ogxPEctHvFy?$dF5 zY!r9{6;8*Tj;SEoxD<(=j(>Do7W#zxhqql8K9{rGOD{i0h){}pHhWk0(%h*AKXEjS zRmuCg(AS%f6E$^}q`Df@n(CUvwXW8z^Ry-usnMFoKeiZda+jJR=CE;sp&!x~&0*s% zt8AsKqkF7h7ieU&&0&b-@2h0)6&%p?@toA0WjxitP$)F={0nD(`F^(1G5k)h=C*JS zwnV|v5#i|leU2`I1cCsn>=+Nid+V6-eK1Ljb7@`_n%#cl(kxK!sy1|ux?4Du`kYl02}L0~vFM^j+5Zbt zG>h)Cr9Wv9t2SgE18;_Swo7nT8}vEz%OoUk27Sj&G4wU^Nu-jiM1s3g0m+n_oFSvE zsejuHO*i`b;LOrb)OxYRdMIi7mD0%QtD2Ttb_b{QIZYHowcR@T=$`F%m(GoTDj#beKk-Y4yChJ=U|$2 zI5-Q0=(XA~%z6t%*LrP}vo^R>#P@JO==AZK`x}4$x;(8cT;Gd4%44;sFc#mla# zFjx!sv@ZgBr8fA!F!ne|#t{$}UV~3b@<=2fQ4_GAyQkyne|H;9cf8c1#yYPd*=R=6 zh`P;M%?$`P5N`Kn+Ru5(eJL*TlG~(<3;9}OUa~nwT=;?mg63xKhxAGEvsEGd*?*GZ z&oWvHJ>jpZcQ2fv?yu)qKpsG}ioXV16>O)FD)t-sYexOr#aI0`sjj6lYzC~&J9}yK zT*T4B(uU3&;FS~y&GFuIhaC&S8DVj}M3TNj(B}VeQbc58d;v((Y8L z-d(o@8$MTZyyc5E^$624tQhq!)_Kih+NG|OZh(88pGvug$l--__Qw@>R`Ujt%*Moe zn-6?j+BLDBl!TUIHL>oOCz@EdG3J_B*3{{Ebj>Vl>aQfzTUc0A#ex)b3;a%V3p16q z=s{a87X7{K)S5^mYt1)eyl}$s7zsu6uCVJoDxD=}4z<_j(BbNfiEC z)XPeLhJo2a3o=IzI%nkO=(l`xlS5gkS8_0eKFJ-ZKGJGNgMZMi^akBZCyHLX$o*Qk z%wJ8}GS85I<6U_j)GyVnX$$NJJR(x#+vu0C8VtVfet?Urc;8d^1H|8tb3N=#U_Su2 z(vE|$F#81!DfF0GF zUJ#6%-v5Q4+ix)QY!8MLhp^nnWd9dQIIiXH|CYLkz_1|5wiLfO(8xD&cFE??#Y_LESB;HcF3EO6N!Wa8E^GvO~bfFxy zUfL=F?z5q|_FhuNB}|R-j3XF{{Ar>uohq-XG<$WRd=jPwla;p?nhy~*E!$ZE zcFh3zy8^5>0LA2F+%NOE0Z7URp@J>^LIHkd016dg_a_X%{TToUE5P0cAff>4T!1Sw z0KOg|eZZZD(uV>Cc=RFD!pRu`OBCSU=?aje0Q*~pN_%AhT%`c*24KC2k)p{iS^x9P zbh4hT0Dm$7>l9${QwF8B41nDfpxFR83NT=RLFw8IfOmG4PJiD3tWkhv&zcs_&H%Ve z0roHeD;3}}x6@@A0235o1va_J8p{=+et~JBb3%Hj4_AN|1JJGjFMiMT;i(LOJ_>M- z0ccTxK8sBYH)H_dLy7!=(U}H7mJx}fgZi5m#$*8eQ319y0Q0nkVLu+A?0skkz?lm0 zF?Pn-={X8;?`#9GRR+Kh6kxIen5_Umb*c2y_;f0LfCVx7@Mi-sQvtSHXgWP717N-a zTxbBUQGhj%7=T}90Q^P)b~6Ce72xg`18`&pz>x~j*8nssz-}&k@0bCwtpY5-{v4?^ zQ2~x|r|4S^>79OIXX*5<24JiL41Cg{bZ-X0jS4Wz0MskM2$x%@WB{D606#PUbqerc zt7+l*41oO=;Clw3Rsk+_J6)Us@Yzn%ho#tLW2b8rpw^w!AC61!^dkx|&j8p2h!=hS z0_DHCmk>Ij3d!a}t0Mt`4?eykC7SgjNPhUyYVO=oavnE1Wd;U0cWBN7Ca2WTIYo1B zGdab6PM+rEo18*F=k6V(oll-8j?Hp;t9*LWYCc_a&NeUwK1^TDInLzd_&N7(FSVU# za@Ip-ncB|MoRKDHou9L{=3HhlaQvKqe@|+=z~rp)bI#G6gvnXy=UAHaYm>9w&v|G& zscpk^+S7JF=K{@n)WEd(IooT_S_5;xpYteIEf~4gCTE_XGg@z8iK&eoK>QqFw;57=Q0B&p=+l}Av{E!=#`xErc%%No>d}7QE0^# zm>k`x_>)*l#l^P4Sd6F8iYXDgQSqNsD>g>_tP(M@LMx^~=|;svQ!B1=F&1Miv|`Gm zZdB|{Pa%a_49qGKqb;;zinDH1e06HYbuPy11w4!^BHgR_;M9uEqGDFbx~vsbn05o> zSFQ+x|fx-UsAalDe_KDty@P)>zr~ddXJ@UO z3cee74^OSTSnAWdXJ)NiIdzltPBx{`q%2VPHCgMXk~0kzZLQhqoM`5#&L1)c|Qnch^y2x(UoCLWU5V0>(%NOqnxe7wN`H_nJla z`{$;2g9ixEjXSZC$NEJ5#Eafv8q$p^Wc2t!Yj*d#@$gSGy0PpXO-k#A)ccLQF`lCV z4=bFdZ5WklOTPyqW9}r1{&Ej#<8Eh+@;1c+h7A zpg&roU5+@1q(c8x_c9_3dhZ1qg6*=%1N%h)?Bm65ckLk9#N-1X36eKI z>46;*0Nc}RZ95-Km#WEg6mrfJ9^}SD8P(Bw-D~j0As$t9`J3#kkas=qLAC`T|LQ^B z5QL<@NZzqdu)VL5Aydx$d!cr_gcaB_61&>zYRdHSm;vd)F9*< zLY||L^B(dbhX){k=Rtly2g-@a9 zy*BR%Lary|1q!*)mw|r@K#ukxF9|~CKm#SWSIG8&{0l%1@*ochLKYD6(GJ1(sRuj? z?HPc)pw*@A#)?2cBZM5Skk8%kK_(CJ`?;3~*%pK>B;?Ktd73Zv?hZhX^B`kE$YMga zy(ie#`GRkJ0J7YJJT3@XO2~^9@_e6f4-G(m*y8qc#~@@GAqObr7@u!*1CV!mkgE>~ zP^f~COOt}_rM?7PdazHS(H`VYLC7jXjsqmr+MyccY4ErsiNDw2bPe{@pyOT1yH|r> zYjA`HTWhf59Vzk`4bIVEUkyHaTk;;#ph<%f8f>q@>bIoG-!vGl!2ue4`KIKxX%N?7 zqy_^JfVusP?D&9Rn+D$B|2})9kjVdbdidu(%=f>KGv{?+&hi>koZ_{b|A4OGq+kSF z^K;GJ#W37bZhw61f%oM+3@<@#IG>k`mcnT5J(}+5q8IfbC{%JhhH4|kKA7S(t+=e^Rg&b-bMAUO|c;V3}beWC|nk$F?1 z0%i37Skr&34{&)1?%Jf*la2oD_ldr^tF8s1^_mA)3KG8GZ%e-Fq$LE3O#N9of zgiBQT8YS-&rS1pSZ`(6S4)l?B2>U4aiPD@wp@oBj7lnfIb$k)czfaWE_3OuqF}Cg{ zoaR2!K6gK!ctf{S?&i7 z!*A?)DNSRTfRWN+rVN@pf^&2PtC};*r}O)a5oSCpa3|>L_a5!t4|<%$z579TkP%&y ze+ZIuos-YNHK3R8iu*xlVct8xJP#Lw{^&6>{6XhzPaP~v`>F#$g}~C@`S6WbP{bS$ zEe0`<9l*jEKODNmG*7(-w0tbC0Uh%az8`kV%QzmJzu=yuGj9M*@6UpxO@E3hQc$O1 zj_mg<{OYRBJx^(8fe4(Z)bnU^!j$h_oS$GDJ#n7$M*f7S($H$mUR>zTrKq-_? zpSeedk6#z}e_k0nhMT(oGaFRuTb&VLX-=L~E_@x!(w6(ykiH|G2GyPo>SorCw}z_x zbK`o-oo!tk4%Nk5Lv>jogq!Qa+Sp$px+&+fy^9@ut8>%$e-1O_3scQIU%mzI|2*C2 z|L^Ah&y)tgi~B!Q8vHKq|2!>7((mH_&y)tk-w}Wc>cRUzPYw34iIdai(H^}2Gtfv6 zza#L!^8U}NaCvU-qZw<6T%l{mZ*c!-p#JaV{?9;rL3=gazIduBR9|#;|L2r%XJ3r@ zNr-(ROTUc!yuQ`_pC|iM<(u69Iem-W|9N;gzjbl{r%*iO1{43C-cUPm*yZ6P+r|B# zSs+9^huRI-2*fwg&RyOAd02@4-2(T2o~TQNv0Nfdbe9PKllOm)^cgnf!XtCB7sUNU zF44Z}{hxtGHe0siebVWWhA;@__UB|b@BchBoZrLc(c!-h=i2P+o&EyRH4k>R-g)`Q zA@Zo_*E^pc?+@=5S?^pIY@i3%I}0ieeag6^?uq*(@O>lP(*QS$_bw{d6gC$zxc_r9 z$N$^B@7v$S*L~l&Ks5LmxNiQ3?*D`X+?6j|;C0&k$g>DaCNKYx^kKH>L&P6(gN+3ls5#}lIKnc0=SG`Bot zKK8u5v>0x3kD94$pSDl%4gJIC1{(+;uCQynHt8RpQMNhbcWlEZ$4~4R-|ISN+_xLQ z_kS2NehEyZ^!v1VO|Gj;$DeI(cHHDO7TaL^z#F%Hc_j=q>KTzJH)5*IHx-21Tt?Wu zk2LeH#XMY-o0l9DuI~oB_+uw$)BcSQF5M)3(|q9z1mc^|j~V+L`Ud^SpQqASmT*?{ zDoJ%|>|Ymxa|=r6ITk*Wapw*W$2a@^xDQ?&%7dG@ANRnqS)klKnc|f7 z6a161{}bL;7x&}7e{&bMUFiL|4+=!rGsLs)v+c=by&pHauP>k69bE4+veb)ky{pz! zlfUKtxFdcVoLO?a83c{l7~c~2J9q+#vj$h**m9rzi z?CT63lx_ZHyC3(*W3tSo)Sc8cJ=;4Ql)IA}dOz-OvO&4CHuQd6I~=Nu`*C;50wH!^ z=>52FjV2Yo<*W_vq-MDvcgEiS{B?Qy|L*;`hr?U%&&coKe%vSF{`MRE4(`Vt6=*Qs zF^QM`kEkQlh=b)D-jDm`5Whz%sPzMCK_5rP_a(O6{kZR!82;=rf996GA9sj{Z+Qzf z{{Qy-acArqE}z5w{l{JqDw~7*k7@S#K!NC5j&-$ueSZ&MK6z@r=hv_K*j8}oar(51 z^u+n5Tj1yR8tB3G>l3?&kDt}-=~o#MNCmSee&uGwQr^dNzFH{+Mjh`;89@H1+@W{l-rW%qIVC zpeXY8ECcedE?iaw_EXYySZx*vvED=1^*<1ZZc3Lf_tV}VVEPyIPxbu$x%qIz`>Yv$ zHV%5Q=kCwF4t{gL!OXL9a2J02`*VL7XyltX#CB7M*f+aB*Q|ec&BmXeel8}X`uzE4 zyywTQOvhP&ThsclGlXg(>=ZhF;GvufC1#&B19lA0FUE`KK@iuc}np@p61B z5Tc&uP8_qN3O9@68tFnQ(Cse~TnP`kKND8xDKk{*n;mb;qrZhxp~;IS*?5ilfu~!T z0G(6s-YSjjL(Hwx6S)_FH`8X6G?LMPd7-mYN}Cr*ZNXk2D=BWTTZ{D*0p(0>!UqUY zN147ExU&@W=uoN!zvzX60g61qXJ7$T=qdLx5j_c(!S}Nm9dkDt`7A>`vhD5r{tKgF_7m4sGE1SUk{hO9XIS|rj|>9hNJP)&seT@C+C4 zs|U!Oh|GzhndSouU3|qMX{$ir8$rnD7Ut=W_~Q#5{VNm*1Z z0DN)}dOuoH48s*_BqwKS?-B;X%R7gce(3-zn>zjpk>{BG=TGJv(vO5=B z^M9Vx`gWur&g(Iwmo;r2+~juC;$E6l;O696)1KCxJm&Q47#e$tpD1fu-`kpYm*lkd zw&oA;+Q_%2U84Z`X^r%;rd_N#ebO4~YfU>=bNafCtkgydtZDmcBL%@m`dQN~1?ZR7 z$X3?05Bf?^wn}ScYirt*nzOYw5_?VC=xu>wTk%U<$PUAC>nrx?3#u#E zS_!>TbuorF$F6+RYMRE%5(96K4;UU>KD5;-#Ag~d{=?20Zq08wIx%KyPW6OmKg2Bdo9|N{R z41S$~fwn>n&I-X`ox$LUFbwtw2I$Vh00w+{m^A1m7&I6RhQ$xnW{AT2IjNl)TK?hC z7~3HZtLJ89D)~r;N9JW@B6ZGvlG^HGf%+7OJi#GJ`w!zVEPjACLmV11aQHx*Ar7Zx z;DAgLkU0D}1BVZgS^^v}7E65`K7eTkwmY!|fBKgUi**i*eO8TuartG&a_A;J^ln`X zgo^ThXcNnQF*%0CdzWCQlORJcUqiiv2HxD?&Ozn*5H&`q3|H>(i?pN`fpi%TvU)Y# z6wL0|5g%Lfs$Z>ejTyb zhQ&5&<6EUQE{xyJVn9`i!Ez?ub`Ti*CdF?3p6Qj*z~Pq8(Ncvu`gQRix?rLg`lnu)k3|eZ>>pC zRr2ZMrnY>yD(I-bTB?uNBD9lyzp|;VZ`E`N3$j6B?DN(SBe9Qxx7Gzk)xzlj$?X9` zOWmqz{Q#)tpr5YPw5{AWx1P`du&t}69}sIL>I;E-Y)R_}rsn<=jzxBV7wK&#U}e{~ zjSIZ(ggx-MEr7LtYr@uewx;Ld6jr3oxotGB*KlTQaJ>fCY7p0;S%b+MOwizB4eB*G zTZ7Xy7^y*2gW(z+uE8N1lxZ+XgFQ4Dpur9rY@C4Q|ljj~ZO9!E_BS*WgzYV07e{e*Sbu!GG3K-J@h_=X2r%e|JyxHF0_-Q>DjkuAgJdqJ`a^sBry)VhE3sdpV&U1P-Lc2sx+=9h zL(4z#?b4;!W@P&I*zJGJ$OI18{aI2y%M|r(o;^112cBIZHc^^caw4)fVUCpti>Tp{ zViIi^%ulh$KHtl0TP>b+o8$!)NN=481}w6BQ;Uc(7%)k$3udR-W4{VE9%6`|7KF@Z zkR22(WDKH!DY{cIJJlZh5+B9r!WMlQ*NPl9gVQt^sXv{(6K zcI5^uLC2&Wd(v)w8{UA$R?~ajok$GaM;$=bvG(fLHNC1kTdFHttfnQ%JUTJhxW=%= za`T&7dP#vBQwl(!`33T%z=S{nc-AUEvYJLAsX8$@9yA!LA}_HgmmqNQ zbUtoZequH4qecHREPfJ_+wCTN_q=&qB-+>8pW~`;m?T&_O&x)y*Ra_0t&0nW#vENj z+TRa~tYNVhNaDcqc@VNaktNILp;9Cutox)Ep_)JuZ~gf0Zh;!;Gc?%U#~u*7>R3y4 zXFJs1+82(>scu>yv6_B|yg;jkv}{%hO~xXpIx#R#@(o&FUAfL`IvGhL5`&xIks4P1 zd~9E#)koHhSKwo>Zu%-`d~TZ^`@H;FyYdSwu`A(YUsMlzwz}!P-Wckp4ZW=d-_eMI zNN|*qnj7&5!v7^c9ILf2a;&Q!5MBTg+j`rTUs{QO;5oJ!1dJx~`=WoxBzl()wdR+? z6|!=g(DRP?040=5%V=VoSBmVM;}ZRI;dIJHckjAV2xDAnsbodzyHiUy!o~ne0;QG0 zC^cs*97U@J2D!}`i$1SB zgzUh$V;&jznO|gPY7sFFmiyV41+zsvCKtg~RlYj;3v?F2C)gTs?j0q)oU6gD8r-PC zpES5e0`w;EOJDEJ`hxPsZhBw#!)sWQ+t0NUfrPPbSg_9D?_P;5D!!Ql*AhH*MW4R7 z7bMAG_!`}tE_D+WJE7QGHRs(5ov)wO`%R1Kw8g%hY~agz+(lJwf|ZXC&WAbXW21eE z6wimv6m*(Pc>5!N_R{8(7Kv%RJrG~+#<7Jq2XQ#>BiMYVr>3c8AaHK&*9U@y_PFc> zdoS&ux=gRnlpPxEA?W4sL>6efpFI=I&1PVtXhM2b5odd;#cUMS8))9Z<#u`^Q26?< zI|>TUj=kM0JAq7{9mCzRyt3139)-!xy~RfM07L`D4GMU`9*IE#4~0(7Zc1IiqgM*g zJFI!-8Fo$h6=JPHzMdiA@_`P*E-F$4u$eilWVSShIuIAqDcFLNd6EHT17ZTXxqM7O zL`>rCq#xL~%75rVt(~Zc6IE^>g*V^iIKQR$8Fl^Cyil&65Z&jqjfF!E_1H;E$6p59-ux*Wf*ha~fDXm%I%8fG*~9a3VWNd~(Bre{F--cx9Fv zPsptC`&nxIlhz2Ur4I5or{M%U@k2W?1T)d)Ay7t`6>NpgJ`!pokt`)gHi8;_DC1T> z3?P3BR5^zUyDzG3i}mfe9py6UwcyjP={4qtaC$WyoI$UltHbH_z&R=OIxS0$dt}!5 z;i~W&FVGr2dX2Ob7h`($1i`){F`JZNGy#11tLL+3{KwGCj)JeS6)TrMPof;VucVQ{ z0k*4cJnVa;j?T$xI5lLe4rU$a2~G`7fY(;uYV6FJuzM5}4C3UAoCy{Wg%mmu(!}G- zw9cB7M5Vo?!Lt%XWBtJu#?0^NW$v>rW&=g@xn$p+bCwGpFNDJQk0?4UhOTXimqC zN(@=>bYt+xqzi|6_?}EhlRNSnW{m$wh91~?FB*QTf8TSZ#Pz=Cf;Oi_xN%AT?nusc z{{Gs^PUoM)a&nxPQLQz9V9E5Ne#lvppNqfPre0Lg7T(@qx4kmC&3EeRhHa=$E9}Zc z$LHCxJt_aI2+D{p5eUNy`MT+N8-o1wqi~6JioizVw1#3Nj={w8?J?-bmv@s?C?MBL za|YsGRtR_2DQaSHiIe;kYR@zIn4OmT9q`-<`Yo;Y~zV#vO^kn~roiE)7spnf2FSl9|8|jFk0E>1b5g^5Gu`)MO zs5vSMeon_BFwaZ{_D#fai?N5qP3ZEd%e-T9am8Lmma>P0&wi%q+DT(%S9(AL=o%`HqfkQQC@VL>97CT(!Notid-HkRnlilb5=TbL% zuyX;T$unR{eq#Nm21FIIO)>eP)lxUw+Zp3V3mhH_l(G@$WH(yu{Kbt{IaO}7(V2>9 z@=MsH=UZbR>$H}v;m|EYID;Xlkr`2+a{+-L%byflzVtn-^yBlbaT|N1%t}=J;R^hF z-YRXkmaH3QZFhjRWZALSb|XO9wqmc^uIdY^OxetXMkfvhnG`h-zuT=PZ?HelA!L7^ z!@p+?4+cP`yf2@z9&9)3!OuG&q`4mCI*4O_7iZrETsXmS;Xx8us>5i~ zH*kqF2G%W#z>aO!g%jo?8jqqTpwlSTnnR8bjv-A&U={l`3%X+i{A3lYg9Zc6xxmuJCsfcFnr-#aTiy!z z3c|W8*edo8(Ci{&5yom5D|C8ECGr{{21q`5aU|i&*f6KG0ri&)fOhH63R<_-P6Kg# z?U(?UFCv3^A5{6LyIA~rU=g`jDu4U@s`hgZ5Cg}&c$#v@ezYB_Co%R$GhwIU70SFQ&KmVG_r3^_#O>#`)rA~ zNZ)$Z4rU}0MP~t=RYjbsPb%jWcKjP)fgh_y|3`x(V9vA!)+YgZ@b0@Q^58z1-qHA6 zkqgoIf|50;k<(sA6-TRE@LU~R1d|K(E`%c)uH}h&C<8a&m&Ua$#D3-KyWid-g90!1s|kac-;Z*LS+D(;_I@k>vHSA4uyEU#hQ z2Q?dwpIR7=A731eSC>X(w@7E{EVizCm)bP;xgEz{B9)(8Q+|umL};CmWhfFCT2szp z6)~>-8$fX^r?9YEAY2;7Lm{P@On!*8VoD;J{E$Rw3k=LZWl=m%1n7UD{nB!(hjcL;LO4l$VKQ{1iMax2Ck?AsX*%$A63SKwRzN4~7a0J=RPX zW3u3(NXhGj=7kItD`N-1+(U4r3NpMpUR&0=B8nxeqM)3?L-s9#%JGrv$``CDBLsy7 zhA#IB1&@hgU*p+haB)eTgj!-v*-c7aCrnixTUR}(#g2V#4_d-&AsYJGu{Jw*g&kW} z9ea^vT7w3T0joidE4lAeP2|@081kdB_q3vD?6a_no@YfQS`>q@93g9yo&<;a>RrT# zwIPR>r(c43JG9u2Ux>LnSH{W=(<(Da`jQE$IZrCo(T$>^Ie4zFTsRp=&z7?xQd?h4 zxvpfk#69(MV?U@LS4!q~Om`+9DT}!Y8eFVFy#{A%aGC}qCBRYs;p)qdLAIdsbotr; zEh9fMOD0is@*SS|Ma6dIhcZEjJhT_MMx-HntJ1I(6Le9DKS9AJ5F*a+9t)pG{Z3G6 zcqvHsQ&e&hNH&J@SJbtusYmH5sD_M%)!9;N=k=T`w$kQA@|;JQsW>(T;aAs|-k%|F z_Ow1Y?L$wJRJjzOK3KS?Pk}0>z}4;H6zF^WH`51a$xC$seK2iA_w>P%M?HP;8hgNuKaj1>yCY03S@+3)9ce*m1Q` zQ;qG}DJ%2Xleon$j%5 z>#AcLs|P(9#fuh$o_0I^cr>@II<_(j!#fIo0mq94@X;~cm0li=y=xD8(Jd>x${csR zD@pxY%)+G}fk>TayQU6grB9uTAa$yoQ-7%4Xd&^iAgCofK!u-%aq!uh8&Frj3*Eut zC(SS}GVa%dS%f#PFzV){nNNW3vWKvPI}yk$?dW$k4pB_GiZ%(^V=5vTGCTGK7LSrO#?9gj z)zR3ia#UcMaJ)bGcCK>#ff#&IVlwy`;coGIVX!mkna}HB3S;JBZduJQahzoDViMJ* zyDxaben`eJwlG_E7U`qGi;Sq)^*-05-nnM^ITi%F&lsR)o!D@Rt`QSs%P5J#zOU(a zr1M@5mvzQ35ObG?m7BFR{5$J#?VR3w0lA!xN6c!lV;Z&Yb{+;Dy1FV&Ez`Fb~&hI%( z9Am0eTh^kpO4cCN<^Ur@1VZWp8b+HgpsPRg^snt2OxzOv-L5`KY$eH=kA{=vg`!I*O|)Ky)+Y;U*$N8NiWk6mA3WEKQEzBuy>!tyEzo)`p2crf=SJ!!;;z-O z|C`KLqCoIrQc%-g_76^o43E`Vnh6tafHtBIhLkS;_MZDfvg6`*P7q|Qp0PsboDkR>3@oIid+5TnDRg8PnIq^+CA&!_ z*b%~aQL6xqVd6tPzo^PJyo(Hivt*)h7&&<$yf~NWi@E)<9RUXX;eR>JKe4d-C$8B+|PfKN@7^bweJgXF^C3Ig=oVK1l+J-YnKB_gq#0GjW9G(^)p$ZZJ zM#Q29qUZuf8|C6)ocaPzs2W>bDp9&*PlZo93X2Z;I+>y{Y*6x4ScE6yJm;Vl(aOiI zs}4s4YK32d3>1&X+M|_EspBCU-wswffR0DIpTlU4qIVg-2ciUCG@2Z|2z5?;fgR_) zxQys{uoD-;T_sb-Kc>SrYe*7eWy9_g*9j2x(FRh-g>X?-V|1{vSvs@|-S+NF zaGumkmT2&(1T5hFe12Ac?xYo>Z!dqp5k)8*WpcvE5!Qy~BhPZea*;5~#b}sp5V!33 z#ljL84tm-6^>AScl2s=LB~tuU(3t>(@Xm0DHACJ6#9%l#i}TejdI^~5c+7&Km>O6I z{X*JuUE8VyXA(nKh|M++7#aUe7lz1}E+Ez{Iao)SQrz4E2J2$UC#=qUPQY7Z`3KVx z-ar1Sa{jm{iDQTYj>qAV<`O{x%Pzr-{Cp2_0tqP%s$(Bj4|+_P`6<_thWSfJ8tq9I z;Q~_oq+24aQ}he!6slJ~bt#z7IIy~|Z;sT5*V6AZRa{(!1EqEhL2wMO9zxc;d{HBc zw>h&95XQS+gKH&7UXm&TeSU%bo8!vAXN>$?iF#EFwZ^n@*Y4`M5GtXm*#PB_&5{EA0qXugY%jGtSMx-Z>P6St z>utl_!mIztsj>GO&HI=^~bJW0>2syB3u7mRfS=ujR&%mTvS;vez2W( zKT-7lslLjxQMssD=g5D#D(iYzHPIpm=#WSs?c2RPTLr5JQ_!#=TqjoZo%9kcZH=&Z zOBYK35s51Hm}%=?VF(?C1JxUx|glf`&>FaaE!_y4em?p~KMqOb`v zlf`iW9G~=M1xpteNN2UrSW~q~breau<~4d_hVe9m5Y9UT*fPu*HfvKYYNb2`TAfm< z)J-+e8^<|7Mzg4cSD}mQuaN4^wMXDT5FMvrfflgE2C78Ry3N+>X!6u!tiU3mLWkY@ zCeGKq+23y6i16(^o9FRgv7dPpiCfvvY(!c<(}cc%gasiwkCUe9az2e-jSqOiStd2( ztpr5s?bsRk@_Y-jxg3pHiFxElIQHGPaHGN|nggZ~h=9O-6(}uc8a#2pXERZLo=sCt z3(75gGAKifIFl_r!*f){+0{sV)ue!(&z|6=X|k1En&j0Xy27pFB=l=I#%Uw(sKA1} z^&L?0od7lQI{T;gGw&eLrzXilwRY?zbYVR>4#;D?X4Ft@yAvp$-}j(^6!Is? zde#RHr8K5U43)mV6uRW70Vs|MohVxJ$WDk-`of+*(3L_h^E@f!ly zJ>^|*89g!0MSj%Q{z@7a0qErHK-yr;VQx~=c<0Y%RIvAyC=;df7VzzYk}AgGKVFWO zDbj2{gTreW0$B<@{ut-z@y9qvlt0F&YgFRb1mcuj{BdEpQo>i=b%OEWRla1+SU{5caC744-h5!Pt5oe*>xp_5|?EO*wi^|E!S8O^-HR(T;uAURZ_u1a={v}nTy8r?D(%?=1_t4 zqQqXRnt16FK~#O!;-r_Q4OP-}tRYQrH=D-ATWzp(^|p3FcJRcQ)m>wF=DBN3BGz%< zJ9RjPN^TScDF_h1U&v1~_iH9>mBVD;?)T3&kdyhf+Q?ng`en(hh{SbA>dd zQ-ThCHbQGxs>HLjL$m)aX2h^7vxG0)O?iqI9*c0i< z*YV-1ub4)X3?z)9QnH(ph+bRNKi%LAn+}$qd8p#UO!EL6!5m7eabnvkRx;;e}jAB4_R-1{&Z`LGnKm;DB&$vkn#t@<+BvD0p zv(-e=(|KmGaU>63VFrls9gCcQxjBP_IrS92V^#Qa{}nc8M25!L=0@RRG#l9fMsvD^ z?xb^Pdb27>AjC-%6uwn_HXK+gagPJvz@aG$-?6Un1vZ|FhY5`{vZnL|^{G7=-*?Q-I6_<;qg1V8oXmffP3lM!$CBYxXbPnsoV!(KHU3m*@?R@6YTH}k;+~AD0;%#co|7yHg zKIO+qger{29=8W^n=@1!avBp+PH!SfF+c>FPKIIF4n&w=X38y>=VoCWmR~7|rh2J3 zhU`QNbE1wlQWhH6S3nJ9LbH8OjofV}G@B?D&aphp0?s3%RJH9ul+Q$z&&$oTA`1JL z&lr_82>D~2qbzYV(#}7MMFrn10c2VA(;0g82UmM~)jtG%pV5zVjpgO)NvuNzjq41< z%+P@angvV;niUexHB!wX=ab~Yjio9nqNFAX5HW`LpG4ym$7p5`P91SD~p1~wE}Q|WzO?Y44i4+M-_LIOcQaZmp@gVsH^c?GLX0@*GZMUw$z`KHq9is3Y zYlsVe0h*YPjU)!R63F{+9#pa8KxY`cL#m=bHT+ZT{7^cj-gH- zw#ztCDf{WZLE$NHGA^o-w`*6s29bDXt9j&T32l~Ad2BZAxgaxWmiZVjMvyU2$qh;kSe@g+c|Ln z$FAcYXM{x1SN)}rU;hVW{J@~}NW$9(>a6cS=^$Z@pU~SiZYbt!F89~?@KZjR4K_aS ztC`$YYk=OAEw%oWa8vSzobjzONUZgdOCNP6>?)jeu?F=LgyS>me!<7=e6gP&cF`_uLWPW2v#&TRrIU3+>*|MU`Lu3XIiO0{9Rld7?hoSjP zGdTs%Ylx?r5Xz}CA^Z*d2zw(cUZs52Ej2-whs@G_69(BbVh3cgyGo3KEceoui3~GG zuiZI|m+`GBUkf?ioth`@rnR8XGwEW(&7W)LnV!PrP~Fd-p++$zrN&!LjoO>+HERBQ zRLoLIaFi3fih$xBGbe_R_ra1J7;iaI?=Bh4CQKLO{VwrNGn&8EcwYf4fWo-v$Gf3& zv&Q;N+HYpNk>X+@jgjpjFK{$$HOLC-(T1R7wX?&{GGn)qfTeiIj3FO@kAhGB80u+dtn#aD&%_|ezOQ^AvAJ|o$)xU=1b5N zaCZ&~_-q4#8ST%ZV&`B1kD^hxiPkss>~i>4iHIt6GXeb!jGim9y#}2Hazx!#Wh%|5 zn?|A5%tod5#&ojK*_m?`B1WdZt8BzP;LD{{q>WNVb_=1_j;VzJIg?_N5XTyJqtN-M zOarX#S(n=i>XuexJE)-`ek;~dNV!nV$5Nu!PN1%o5rm0RMi3@ZN<+ShGnrlnPZ>E+ z?LaOmQg!Fi7BFoyLtEYyK`U5K%eA#Clv=EMLRWJN9wGSefj@F$-50 z3&)sS_K{cW<>3zF1ggUIUr z7P*V%l6|BXcMuT5!Fjj*LHTanZr)zMc1ex~gn13pL zILP2jFg1M~+;yAAs{pw7*SfZ%YWhBUr#E;kS4vH&2s`z2x2#&PH-?O)m73^CujE|93imO2&3A5o)?Nep{{1_IZE(fGd6_&Fdl79Yp~jhRE+G8!KcjgRzlu;qDgcA8jaDw7JT!_o1Dc*g7OoA$0kZ)%Z(DmkN!9dbUwaUSv)vxF5-+k%7BN)Ji&9FXl`Z4o zDjf*s9zgw4!P8$=IGHrA0=F~h)$wSMSy;`1dziJ8WKB0swojVJnOoE5JR6bWd0c}< z8a%AQd=2iF0AfFnDkCrXhN_HK1j#JlH=cW!k^h1=#1=)Q*@ys1&GALBwec}PP@99A z#gv|nq!FYU2G{rVmhnNQ7N52=FGnB8pnfFP(q-D}D)&7w$m8 zXnfCT{B&=`4KG|NIor!_k>L{Ig@&%}Q$0pDLpn@t;uGo7W{Bg58-|+`;bTjdby7<|wrB-E=DDOCS zy|LgyYK^9nvu*Bnf>Zgms^9Th^nFZi2~p_@Ylr(~9kp6PCaA}*Y zmxY9o(3LB!Cb`QoF&OTu;W^|zmlTd3)^wWzi$p-f68JER%Ujt5=~zl7fQBahDgF{n zpW^t3Vo(6dC4wYLs-5g(I$7g(vexTloph4)FLYjGPkF13^XXQUB0AbQCfgtNKWnf&$sobObAVEFAEXJ=oCq>uNg?;f9A=L&VPP^hKj^Tch2J`u25Az02? zCB{9+3Je8z;6k`iTZR;jyQn(0yt>udr@CnkG{YNxqOrw@=JKpc)7!;WEo*yM_j9V2 z0=$rg72J8%wFKBJemr*E~ygT_BGONjKM&L*_ zv|%gU^nzBwq}MzpONk7kti(sQ%Cx5lUg7Qp*L1K|b=krgcLrFl*uMPD&YaGi2k@`c z-W?zHdWF)F%XO~92*WF<3k3#-@=6;6VnH-pmc9Ec_5c_6y*u2ys1{&r`+Hy# z!xF9_Dh1CtoRUa}uP3BTL%BN$I#cI?h#t#3=9PKAg|0IJS{AP5eH zJJ{7gSp)=e?DF;?DvRw0d{oeR(}NIHQaTPWzvDhwXve<^jb;2`+rF;j$Fb1y!&t19 zn7iZTvW|oq^Z#Z~e{=G)45fhWMrtIIC~CMT9}@u@9hqu%AtDD6oqppU$4k1HSKJaP z7DY;lQAH78b7I#(#8tQgL#>`C?Zh^%zs4k%DStCt#Vym;{C?bAvgSV)ThX+k*lL=B zj1_OTcJ$^JRR?Zle!DjSryIa=XBi-DBNU@#^`Lg#Xx$FTaYI+E1#86KqrtyF4F1bw zpZ4QD+juPGV|;sW#?p#cC0&yzbFx~3^0gfgIfrX>b@G>-mu2#Vh62BIdJefs=;`{; z#Uxr;K$FN_GSMpXS4_}S0Z+4&IF+^$X_+WG`&tS)*m5G|u7a?HQ?vqYL&!;hNnY;> zxv`#*gPBDkcdc>J6{}8Y?HCwaP5tro>+9BHMtpSCn6BcxAD7O>XNZAk>w>xyqlc4W#{A>4^ zVJMlwIB1=!T=D}Jdyhjv|C$`Izoa;On~daW$aB;j_@(p1;J=#rRmGX3vk8$}f{;#_ zAfWHTC#K!Ey?D}IpcxdLcaoN*##EPOfp_2bRln5tBV~+hblgB1EJHHy+t&5ytQ!qJ zHVK52Hdp}&E{KeEBNH{!T++x0exO)E!+DBACPd=@G50m_Q5EO<2?SUa-KeOm5?wTE zP@+LmlNxk`SWh&zsiihrT4QT9Rcce%Xj%hAHj!*Mt5{J{QPEnZ6Z}0!^@0Xl8=ggUT=l!0UcV>!iJCdt5gxQZT zaTjo$hCZH{A}g+!bO!A6+EwV3fZVJ>UQ*mDwV)#c%6EK2uUGloGUlZ#2Nnaf6+mxhtr4b=RgJ(!xtK(>v4kk5pH^l~4}g@R|$w{V!IkZca7VMp{rJ>P;7 zIk9-Dd@W)EnN=lL@!9nS#jOIP2*_IUae8zF>JgFD%!zu$aM0$wD~gv>n`lF$s_YFZ zFuAgBK0{^FkwQxwWR@Z0^d@YJu8;F~XrZ^hxl*4;-uD@9Wf`6}Bym2xu!)3d;&#rm&JJ<#LD@l*O#7Aey zBUPpPKa{?Bd$HzcbWTKnIRx85u<;D&(c>#Xk5-2uJBPIJww|dVQ@}CJI#P{HmD!gf z2#UQ=u>>$4I zfmvw;i7mT6!~WMNrtNtt;7#b8kSKS(n2CwI>}72b8#rs z#SpVAF?Pd&n^XL&CR8)93)^RX&x-vR(l!h2z7iV?Y@huV_`suTqA%7=dWy4i7#2`t zG*4nzI+TGet18ybEyfr4>RkO*@9eSo3Wv5zcSYryw`HCE;74qgeXm@i!}@PQL;yD} zO{eejMIsNnKZ2kTF+0$F#&4k(TtUvjkLlGanMcMe_qxvhwku?0?L4T8tDq@Ne?||o z6!OVZ79y%g*(3Te>>Zu}hA+9)x^#PRK)7JKo}O8Kbg-#HS_5jRTRnx&U^gxm7TnxMD}^Yrbtg)6H=F-S4W>e zA5k?Jvuoxoy#h(?@sx23Fl<7BiFfvJ8?kW%ze6|YX)cG_&W*nsYN$TZ^@TQWp~8ee zXkUn^s$^V_1lU&WJfP+(i&hQ)Tx7|1VLBpD*zH;3HFhp?PqnQ0m6)~?tYf&(9H=1- z9EF3d@f{+*4OZwoa0^aR=LWs(4gC<4X$l0OSGAn`FB+W#L!tW{&1?sBz5{3rsd`Cf zs*<%BIz+bO@)@#}=~SUN6|+ynN@f_#VF@m9LEmZzZR3&h&^}DP8=m+n`kd+fyya%C z->u9JJPa6SM$oi+U{`ah?kM-Z0CCiV0xjB>2PV4LZQ+V{LKgc&X; zPXQ<|{^Ra`FJE5>B5?nv4g#J{fy9gc7C9u(io&fu=q`Fq!n1VN470@!C6|tY(xXF( z_vOs39nuBEQFf$&kk(K<=oCARvB%1e045Z5uyRh;LVt{f=!C^=&kOSQmfX&q5H661yNI ziU+ztsa_!sG!>PU5?`4Tbr2vv%EclZq>|w3k6slf3MnQ2x96-vz=R5&Vn_h5Es%D~ zxQ1Eis<)UjwgCO_d(yDA_*+9_>j1(AYymSvVe6N_P2vCvbBfIdCe8`~M?ovPyttLE zFt!smVyWD_m}(&2g%b7d;;b;KG#SjJCm=Mb&qKhKLtFbb5oJ#YX2wU=`ot`EqbJdy zz=-Hmvw0HMKaMyQxP`pLiw+vaZVQPRFFF(@h?27lXR&4A2yFDE!jg@1_y!a%9-V

    TfaTHf0+t?xtB_qD8Ql4yapXVn~?-hgMp_}!9!ikV7bmZ@*kL@%$mafhX1B>_#j^N{e`-tUvS$K z7&O0(b$RT0=q!YO(-b_hVRHF3n_y?TDUU~yF}K+xhR_*dc6nRn+!ksUs+hNSa5u=Mnm z>Pfv6v~jS~e!gE^+D+=RUM^=(>PlRU5A9Xz>96HNyq2?=dcaMjuHM%fj(Ow&cR1!* z^h-n#=q~hg;uxc^_)$ZR5aQ4mRC2-&%+AEybbg7d0A(_`<8Ni`g@p*zts9Z?O=U1m`Oq3 z0+AnLYy7L0u>dOy!CJ-=Ip2#cC7ewzV=KxheNq>sGQOBLG?V8w(-mHf{v7lE<#qM1ru1;%(bFWj(AYV z4A($OiTf@JUWWemL3;(kA8=?r^AYi)t{i8claCEn{;O1y6vnIA7ioD1!JH)f6A+N_YCiti;^%J;WL@d1l2 zQ+xu_`|>apj9Lp>t3B%(RTnITf$5yI{1e^$Q<0w`TTaYX2p$Q-%h0C{G3F{mpxFw; zLs6Yb@bF|0_b3RV6vA}X0HFm108D{B{i|>TgX3RSSQ8&}IJ=~w&$}U}A9piKDm>M{ zY8qm(*FsbQ)vb9QGjO*K+Ph=gtrX{J&;lo6q+t|96YKi$eM^@#4`UB3zr(k#8VlCA zQQ=L07Ae?JXhnKZ*=6xOOuJ$*mu0gg6plYUn^9bl_X-}K-BD0nmDdH8&~^D#|Kroz zaip`#2SAVygq4ra?!ejcRe60TV~v~96gWl~h)F8;T&C)m%ImfMLbPc?F}j{~P)?k4B1|@{kG;>4co0Ekm=S8qmaiu-|EROC_*3-Z zr?y2K3jXx@md6%H8+<*XG1p8#sqjzR-*|2vk4)+bDX+IDRL`$f5vldBX?65JG*>w@ zQYD2X5ig4GWJduWb6ZUH4SdS{MGJ~;j;>SUg9Nn%lc#pV^hL()b*Z7c`3+?!KO&5egTKI{69E*g;fZT`N`_%TlJx{l%k(-^gA|v;Y zS}51$YFZx{Y~~=uQat33Y4&0H!>+FNaGThPTg+ST0ohQG_1y3Y(BHqYR{V6Vyxo8% zT6l;!vjvY*y>V|5ZXoz`+#tRrgs%}_0uPX>*4|l0R`M&rB}|wNgqeW4h#rd{21O=$ltjXxjDq!!E5AAAVQN3u#{L8fA!Bl zMR3pK2ozx@Xo&xS4|azX(0U*>VDB$L1742yDhAMS%8)Y0f+avuLn^!HK9h07QS6x{ z`{xr<@PNb&i>u5)Z$wa!RgQBj?=(Jp)`xh9pimC^Vm!kDEqn3nRyr+g;TWn6Hqk|` zc)}q*d<&sDx8X-8mjBRLco_C%#9@*6;>|Z((X+sdTvMEvkcphU#AT39`*R#I?cdw} zSY5Wuh4c5Hv)?{4F&!8C6?qD1<>$&1#_wA7W7jA}+U0^dbDOu|kO5u+h6J2~#sTJrN(k~w(ORUp=g`MHC=vFSu2-C)AsWR4ea{QZgGa z3D=vANu^iOskbztcKSrmiXu|%O)lI)5Cr#?CW}P|5C(jV05Wd4>;S_E$l=v0AWk+I z-iXaGs;R~!9}{cOzf%+Kmiaa*Xx1Ne6J#?XK?}J-)aYNB-`PDi#5Tcftd^^2QnO!_ zaElk>a|B{@>ZGzQvWM2;_XWYEm>kgOKkAc)R7^{K+ z?jGxp@Mu0vH=Z6{-~EnSZ>Vx$mk&3S%vI=2YW1PY6zOq`n*D84j9Q5Jtcv089*6>ii!452+}=TuFr!{3e=0Z*gz`;$=~S_p8fY>as{K)P)rg z3q(>6!^NJfo6*FUh(j?U$khR!M|aQGp!BrjhV($8as)1)xJSg zA_05K#~4vPliE11|8e`vQXxKff37}O@pxmdrGJC|f6wb~|NoDYR{ekMA2_=2{pBg+@E>Z+a|Ug{SY$`|MxE0;23XLzCM1M_eK2Z5>W z+tlQ?k2A|$ncMcNZc&*sEb9f_F;Mzc#sH^~#>KwxFPS^&YKmG4YLkL!l_TBa74R4u z_yi-LCk{6_Q9J3(RHMhW^c?H3+IA!t1X%IPgRl%L_RWIW`M%iK#>Bplt@1wi zw;gyuUS3rc`}Az7iau2peW@zCZCdQ0D*qGH!1YzpXR9V{tQ!Av)ud;;*Bw4BA33_J zy1Pb9jW0j&jvvjx7z2FMViy)j_UY)YM>keYdbw(RH@@obLjEVJqPEJvXn&P|E%N(x zeqU7-C3jbiPvEQW^~k@mD!N(a@7z!2??(PHI{%oeC`wLLjR#cS-D{Em$*O3t%D?0Q zm46-b7wY_lRZ*1uY}I%`mFVt9{@$u+pUU4>sPcCq|5%-WY>w>>==^~k+dEF@AD3f$ zgF1gO$MzQM{GeA(?Jd#yOLA=QM4f+Pj_obe`O9)_?_`~Sa*pjS*ZIqHY;UE`UzuBb z#}-dJd3y0={tNP-uj<*gN*cgNLvlC7fFG6fpA;?755Hmo|@6&XI`WQN5dhs}ZC$J0ZiuK2kIcN)h zpQbR>$50rW&VXIvtwetd`D1%CDG&86l!qp=fG$Xzs6U1<=72Auuz)T|E7KoCGIPL} z&{tSgkTzL=3^C0CUqW1AP(fO`{unZw1HOc~0=p2rQhy8q&H-T}8Uj181|vi10?Bj2 zm(W%~7o-*Fk0IGZ;w!sY=YTCCtpF}4^T>1#*b>qTBqr4uL4=fG?4UOrqRZHM+NI{ARM*_>E_e z?oM-PwyZhqCauPV$kGPEo=2v;sz&!!jfWorx8-G)lIrHDQbeB#wWTq_0UqJDt44QK zjqjtNj_+nEsm_loMHHJzN=7N)Ts7LR8s9~U9iLz+sVk;?fs?nRP z#@j^h_-9#4s-vVz5nY#E?)L<{!V;Mjz@8viSVLMR81`s_+>x7ICF}`mg+-)If?tnr z*b~%dR}y=I+N|1QPf#lyBCQhqdelbls2xmy>#q14CFrrFg-Hv6PL6%{ISl~G&dfUWFGA_r_`)eSjdODLOBB@BtJ z!8Ab*_{y#Ta==%1xz7P#S;ajEd}WvN9PpK0xO2c)cA3rrU)e=D2Yh80~Yzx_!EOpTpk^x}*duL}x*BejC zyu!KNUaY0!YsMvlFVNN}R6Q0r={Qwz66FO{Jq%Rk-h;$Pe|RKDatrer^;MYHKVZL& zL%6*(NiF}=Z>93jzO!o3j>&VJ)3}n@Va>g6`+yQKryZS^xqbQ4KET^|iop9aFMxNP zz#GLh7!ap%^xX)`5QaQG8Q@fSYYe;=<}f~3ujViUNZXQD`^j1B>=y*9i2KIleEY&0 ztRuX%<@w$A#JxYmq)*X-`{{XEu$>R5BMJW%GGYJng!E|tc+pq@ZxLpUBY^A}fg-D5 zLPAzUV02yh#eNFQuQ!bN1}eoKLRhrpnJ-oIFP-0X@trYVlFTHOY$7{7)yt5_BsPr~ z1-_=@APZfVMAmP=n9is-euOCpPjn>b!ii-Hn);>U_w5;b1Yc<;_?o9@f^X_g>Cv0- zewYj&qBjrID-=ZOr9zt?pIbQ;ydZFhO{IddB$15at9mkEc-2=kwIHuCy#*7~TQFK$ zkYoI5%QyB2-+$VW0a{;1Xixf8IzP>Ym;?fSM{Sfl!-!u#n(F8*7%q3UG_lL3d+6K1IP z8b(vSjC~KWKhC+CRpKx19H~?TCVGQPC0=u8W|jDE4}s!=GP|F!c0Xf9KHg0%^v*dI z|7K4E-m$*nu$u8WxVvvQ_7R#`Alns>|BSoTAO}|GP8{iO<%<2HX~TW0srl-%^D(c+ zr;df?zyihn2^uY0#byT4(WAhU!nX`!vfyV+oyL!BZLwPCV%$!bC*A8wGX{h|=IK+( zJS(PmT!~Wan=KLu8Nh26VEA|;KNBc%vsmANh1`X{CHBJ-kxeBG+8}C;T#2A$i~b5? zmg3ibkEx2Y~W&+o!~NFf}jGOjW{sHtn@&l0Z;LoVO7xd4Zz-E!w$p)G$XDT zclzv(DVeF=wE^F%(CL}LJSPDNG{j*VZHPW98g+L`C1zgV9F(i$I|vh@P`_0Et^B&C zPdyt<1oFo;@y8$29V!WEWH$z7dK+-+vn;5kZvmtMj}0ZVI~n#P?zXX&Ep{jyK*$8n z-HjLh>eQi*`xgAj>8$ z(4OaebU|;`rxXM{DEAlKS#(*E2`!!V0~VrB3IVie!5Fv$5_j0ni{;M(sSn5G(ExE!eFX#b%s_VGXxK&vg=mWEZi~F0^4pg%!DTd zclU#OJS-SdpnWXJ!eW2_jmy|{(5hCBXOTEWh)#&2gMYA(Y)&nZvucAz&Qq79Ax8)* zMIu$m!Uf*}`zyB)9L)(9g%X>3Z5?h+uql>viwwTN`VP__c|d(5&l-RnXzu7LDliE^ zNXJjjbO%3(cSdAZp8bm}lF$$@8d$HP_oqVd@<@sBh<*3>)wduf-T8RY8uRV*42weJ zfLuiaU`UX!-F|7glozlUxco(Iuoa{&e=dCyFSlLCLoWC#8o_u-QFxF~Dj)tRt6L?d z(o|0TQ3VR|7)0E*h5>2Z?pLvCJ(+?x^t%{B-WiwI&f`QR3=pXPGorePp%qdok?0;#0np7EFIo_QXPQ_v zx1On>eYoBqOt_8y{5RKW)(p=`VlUltS^vw}Ka@=DvXnx*{Az+J8i#7d*evQ{R3$uE zXx}1IVBR_EM-(ax*&&>s;k^*5&_4NEg$uGA41wUHX{^D+DukFBGW%C?iBWho{6a(p z?e2d~JBa-+=r?2Zo5X5jPkE1t% z-q!*7!u}>$UrB&DV^z4FyJBCdiTliOm$*E5ndB!&nM*Wdv;LZi#rt4Za+ z2b6Sk7$7T>5`L0e0%Sq6sxF~7tWb!ep$XoK^Nt;A00wm=eD3ZJf>O4kmZ5f|4}&Kz zEGXZFr6db1mfD%2jLj15X9;f*mJm}YUN*=OjhnGGdT$3*AU^N+G#@_%kd$yX9cj3` zsYyR6+@Q?}M3y2XV_?d*i|Iw}niNxJD^frSC>)CTlWXi@q8v4&htrDGRpj@->tA&! z^?@VxTtHx?o|^>WV3(x#=Xl~jwx<}G#JSniwZu>Qzk_{tnz~g-T!6!c5ZSQv_ z#*@}yosoUTZTWfj1@EBHc+qu#(gOL@3%vq~z62LE$RI%taT1yUYkfK(qvz?8}gPT>MrT%0pD zQu$?5wc9?=_#Hgz)0^OIA4RIvN`Lzjf(ZdJg>YSQ=Oac28^{keM#DT`5Um@M@ROrk`R;u4Ah|kEs zotDO*w9wg~4U-}+{=jz7Ctx6D<^i3nHKC|PYjPg~?e;=7UL>0qQIn!&z@+Rvgl;_G zz_xzF`&SAq{FjQaPtO?&o*J5tY08oa^#or+y+UFIa44)~grkvJI1!<{1gZ76nEGKN zJp6?Hvs`?X3qGcJ@BvC_!!DQs9PDgqd_XgB>mZ2>n_^|1TNa|3%&Yto6I2 zEE)LN6I=*oir$C>t`ZO}$aF2JE~7Y8^fLnT<) zKD?jX=wxc&xFPFv2V@)%Tl|oudMv326{ON{&Dk1%U=mMz(BF33gOAak3ru^!&$SNy zL^GgOy32ru&ZH_!rmiuN0Dmqd zg|1_$oq6Rx> z3z%EJ2(&>zx1ztP95+Bg1AT<1u;Pd(q&03G;#_ymQ2&GgAQJ&)9n68!X zRYf>`nx=1S417|+|9}5s&6yfsS>f^e4-Fb$T6-0eiOUR1!0zdO3^3eQ+zMs_ztM$A z<)=zhKlt0DPyg5ahc##D_Ghi%>p#>x?H5c)`%6swp|QOF15I4I|4?JWu0O8`FwWK5-Bizz>p)Smw{{==3^-TtibdHsh*hrXg2pkQ6i5b$VhZo!!5 z+N9a!PzHXlG4RwT;p_jb|FGnAjlZn$y8eTUE0{eq7^Hh;#%q9?a=ixdq&s*IIU34^ zkl|G56EU#Lg-GScx4+=XGpxS_j>V(|yhBOEi|&5<$h3GAdJ_ncf-svn{c>UkZt^R-FB-wwj{+?qmD9*=@(8>i%E0>}9Ld*whnFZ$iSeSpjB|(+?sEBS z`@ zQZB_odJ;;+-BwX@3%gn2VstAmD=qLu)DONP_sRbC>@4NgwH3+SJ zPmVtS{aiH@fJa0uacKV+hLL6PbrJ%`}^vPnKcJ5urAIa%<} zz8F#3(!zq!!CE(W7I!=9{v5#z?;?1CNsOoBXha<69eERx4ePjzs2q;{JT}P~MYKyz|8USKv4gI$b+!%G-B#J785Bg)a|G9ofh2b1af?LKDD&MhRyp=5@`;?&|vpHA> zc(EKg6hmAZ4gjsL*y)cvirnYM z#~naa_H?rWH51;0ra(d5&_$mDO6I+uw>mHly7Foe0AXy$b5mH{*6|j5=inH09)~v- zzJfZbT*EbcV(2+;no~3SHN)rw{!UIDDH{Ox#x7?q!jt9epd~lk2QL!7EL4}#>M~3& zlU_}H8=u-A+`=r}(04E1{zGo<*X2U=g~$x`lpQf%wTA26$PF|Kzf}I+G)eOV{NXTN z8`>}IV^oiQj5^N8j!Rx;jC6-82KZ5VE@K`LtLeJIEc2=Jia|)Ttq#=7cLR>*{K6ky z&H3jCJ(^SGFJNCBa5SgZmuN0{L_3sXaD-bJ=ipy;0wca6pN*PV2(a?~?Lth(>kJCu z*WNug8NkO6b^yHNF&Dt13;;fQ@X!E8U$k~VWmUZBkH|Ku@p0Ja`7q+`nkN6MeX)6> z)w3Sp=Ybt@DkCf{a*yxFG^4-$Y~h^et=%}H^6!W~!sI812q8fjSKOIDYXZ00L--|j z4c@(xmM=V<_8cl(vjZ%ja<%N;H^j8yPA>-0I{07Lx z2*X?cxa@obR}F#yMiN6LW^-rW+^KXSAXlOlWktpqv5xR6XpZ7V&%R#-u}JPXl#Zg~ z?j7z%3G%TN!>JT-GdLa7_&@ynUnMUPtqoEmpECpMpnHukK~otI&dh0dM5mDE zlJ`IX{q*VyHV*Q(u6hSY{9tQJ+`y8qWgMcgKVrrG@pj$;TxmMb=<&81>=>~BnbJ}U z4jO5iOA$(L?q?`|xy)5aC~Vz2Zc5q8QYzNY{vs^wUo8Lv_N5FGh|cTCz9w9?;@Me; z(pdzBv`24dKuBu);JDb+u1>i;sxH5k3z2!vaaPYL)kMM7H3w)sS&?@jp2sJ4qF-Up zSD#%c7eJYGk^XPG^t|G|aEQB$L?{|6+Y7=>sL-XS{uJb68ZjiBszPTxqe~#O%ah_%^y~oom z=uAp}W#N#`vRD6ve2|%bn@Sx0=AjfRNX@|?<+_;(JZt3@za$D9$sj^gm4ZDEXC71N zp;{;pSbsMwZGU)(kmaa#Fd)_X*wPp9-AsG}7rPl8Q(`cR!q9d-xQIf_Sucp3L6cw` z!J=KG(nh$DAN9#VoSwQ3*bhwn@7L#V7ru&4EQZwjq(%0Y8(1#g`|EcNgT8NpG8B6@ zt~jRLIi*j$!-G_$OUN@w1Qoi`y<);Hx`V}G`V3e=!>6&~P}HeW*5kmY@+xO|wx_uZ+P!mtg#q=^J@MD@H&mYqc zp92W!CWRCW#OYuLG_nC_q<|;UOzD5K?~nar0e6{Ik?_aaui~qae^m)-ULNW|EdK|1 z%E?I|eAvP5{+$(AhUD!VRF83#+Tp);mD>GxdZb_YH(;MJ#v}bp2?$68C`{lrm%j*; zKqCpq;7|_(c&Ehe8D?aITUkIFJK7iRR+R);u5m7cuD~U&XOYeLD=(e3SLKhd)k{|^ zt7a8cUVP2_m1VeAT=Q1tvA9mS=FQ6EaXs;xS1NNH>t%~;5Duz~YP)?e58;H-4B@mjf&&Dafm{Gd zI?pxHF|z41^hyhc5*`tA|qFvZDu)aQ`(#)n<9?ti<7thI`)zw&v=%E}#iR8Vov6A^e1 zyf1@5V$Ymf=Jh2GR=X#Q>){wT6oBJ#sv~dA!%z^;ATXVZY>k<-;{!o}HWFL)uv0FR6u5@*<2)dySg znl<`)EBcD7Mva}w2G;xj6gyGng7wid7pz%}JXq6U9h3uDySrrTWBVtq(rbvw#p+oH zMCK(b5qE7j$|Kx*ey;tEw-wiu`lnYFUOUPT9}Ye9CWb6ze|0F8W|o5=6-=uqUGGUZ zc+!m~4Z~E6wDLQa6xT@_hSk2$V)wF5Hk_xNL2D=<&_qf?bY>JYR^8vefHlYU+J@Br zw|*Qiz3G}1jsE&@hw`^PUn(edje& zB#~2YMPGwz7UcwU5TYiv2sQK>j0wxsnv~56`%D`gjTQOiApcEuQ1KXe8{=<3g{huy zY!D1!j8fA>Qr!B#Jr2bOz2;vvvv^*pyx-s6!Uq_wTFg)2PPJbpIiBYsauv_xfG_8^ z22)ixRpEA0S0zT-4Mn^7VLCG__#NIg*Hf0ee=vEv|jgGe&}P>LqI@ zg4_9dTQTLrim#FL3@}FhCg#6Hq<{wg(60tI$z9k?#UCHd+^A5MqAFJ}BXeGw|7U_c(q@&ZO4kN&qg)Vc_H$ly10yo_g50|3E zmBp=67-mDP_)2v=1IEc7s&qZ+Yp_Ia7@1>Z7jl0z>BP_$*pSdiw^F|K~ZI-st)4MA#Cl-o)GS=V5voaw_ z2l?CnfK)=?9^8c0ncW9ViAtst52=iS@IR6LFn?PIv;P$TJpCc)KK`$O*2le= zY2)Avbj%166Z%D8SL-Wx`xg$w(mcBr^LxxxPRYhKq_38~*76D^Rqg|P1QF?wNrU2f zNRwh^yyAW{sy9;5Cuz-yTlGz$otUiz0qjG?1y(B;C9twM#48BAoq5SyEEH;?{jaP! zd+95SRq0%03SMIjEIX<2owwOd2vD()IZgG}LP^G=L)wOa$nFf*!oTI?cqhi}RY|CI{U2Ge(q;d^JTFG)+keZN zqhld{VRpSsZ{iDdlp%GcF`W+B7hTSh4Ym~asO-6_#@}|eG^$OG5m7B`lQU3EnwEt> zSR0D2pw$-IDD5G*M+;E@lHvhFG>00}wgo$AeU=pSyBerPX5*A+uqL`(DwZkpn&<;k zGiQ2n>>R97b@XLeBheUQjX-+EH(skG=+Rb*coVB6$YP@jUJdw^&A=oi4lAy~ye%!s z_lzZZ(yG|uzv<#MOENgQcT5Qa*B(#${!blpUwf}h?p;60NbW_S=0a|$l&ijuv%ca$ z(@4^q5m@$}h!GLY;gKK>*J&M{#U*mT!ODNBy5fER{OhSRD_-)?r>(AuzFduY;t4oY z=_PBzOFWATD|~TubH41r5`C&Bitg|7;xZ|tv$z~ruEq}W3K65+*5EMM{J-LGd(I!v zKTL}6@z391ihrHEo=jL<6Me>-(8C#O7LPf7iKjt9z0e*k(2@$Uei!#OAZV>rw5GUE z-mfXHllN={a|^j4qSAU`p{OGp8&UUI)HI55OAW_ zy5=iI0`Wk1k`^R5h?2nRHV!uf&PLIOdW~qa+;DCLtko(v7?HE@y$BU(Nwx2962JK_ zxfm0(geHdV66TvMhOu-NRlmPf&@+xUldGG2=$_qcLnR%gV z2k?SFrt?4-llJK}q_~htwtG*dxO*F=7)YZ$u_E@xOGrc3MCA-$6513u{#9X)d#%)` zE9voACA%{?*y^e{@hp`LuTfURE5M7iZts+0U9HkbzJ;dmoGmm0pW*g-|4o zT#8G-5Cj8xqRhuL8L;hbbmhr7OSDV z{OxZGQ55#*s1R3l*hQn_D!9vkx4L3Go2e;r^TM1KE%o=5JR~CS%c;KI2(?Ghrdk zIaT!FJY>Y+3tU=qA>tp;9_dKsRs?uep*f_-g0MX4+m4mR7umN6E@+xY7pM+OhiAss%eAezfl4S|^x6?4 z9Mm(5LVXXm1@n~Ds!AH^Z@-3KaB?H|e#1lRLKXqYg9J99Z-mrAeFKqq+awLtOmOnY z1r8)qj4;8JWJo@OIz*ce@ZYphu^;-3(nW##vhjpJs8&OWdaUr!W3`taT@F3` zID%|&dSosfr}qi|U3w584?RArahvGUqw76y6Eo3+)wv7}Rj2QXxkNQK82~*FYMPxw ziv}+(q+?`6MtVns(c*j$pz@{A;@~7e6Pq!3gNhIKw{PIl*U5}<`kNj`7yxQnH)Jx; zE3Lkvr1iTK5UUeajuZ<~dgG?;UW6qq)NVgHlWvXiXMV~aB^bF}5T$&SkfbR9{!>sA z;3*_dJHRmX>H{4!iajH7^GV70lHvyPaQe87JjU``f7=Dj*t3>gi0-dkQ0i6z7A8=x zoyFUE%?i-#^bC~^!l)SYsqXZPck=0|wDbxYjd!yr_PgLqtAQ7=%FVhpKrx!QgS%Rx_P(?fVjZ!3VWs$(TwCW@UNmZ)(#;l9swxrA5-1(d^< zbpT_qA%^`CBV(^SMxh}DahTiMQam>=ENqhp5Qsp*h9*)rmNA?ZFFpFvWAGKd4GRaM zf>$QL>C zdO*{uguk4$Cpp{yeiY%y(3CtG80)`jHJ?NV;DFpK?-uEII}cW@pmOdCG#%(Isc95c zQb6-Y32+782=m!~WH+!L2mbgoL%_Ut;oif*O((EKvYy)E`3UnJdD*db*d_ zvz3E3W-7;N2f7mWDRdB*-b zs3+5Hh&+y$R!s~h%gFY(92r?3cV(pdM;;lGZ$+QJ`PN`EV!HhtbE1ahdB4(k%YWLc z_}D*xDqyyFrgQ}pPMDNS_54Mbb?Lx6_7zEL_P0HuEjn)#-!L+$GJW3j=1f8O&he89E)XCtko-no`tP?qK0*EDOv^$gHR` z6$(0-dtp_10dr`td%@$E^a)H_ziibvVj66HBc@@s6hA|Cl)7|Xn>}W!Ptv+BXL!c$ z`6j!6Gii6H!~8(5RUJ4CSsIP$B)QUfFH#6n5n1RIsL+BDGgSu?A2X0Z>@?arblog4 zm!YAyb5H>~wrnEw&j$P_z63F#qv~kE9T7O84a8xCP1g6)-HI2zp2*axvi}L1#p#12 zxhMAKAE-wC$Hu#!xq=m7zL2CsE0mXkO%wIKbU2I+i z`Z}Es*mrz+FqS-9ge@nQEPNU)37gqLks_`MH z_8U^oDF6zL1*tLF8kP|+`fPHhp8Prg$V{LUul^4cXy3CHfuvC$0!h1YpF*HZp3(&R zz7Q~7XPm(dnm}LB>3}_O{$KW9@ke8BoidV$H`{y0GZC@hGqE41INx!KWz zQ^<3I8xC6_-+qfz|3-Tql|>G-)II42G} z3}m5DLRll)RExh{anG48wneTP-nYv8HWj%N-{;;c%A@J%a}e3>=OccofrMfFg*!x; z6P|MQ0b0-WVA3fWer~9-Sx~Jz2fn7o@PfVk1#I zP^NsC*EE@x58W7+*@7|y_7y}NzL7FfwozOv7${wF>4sr0<0Z`?L)T`{|0*k~p%cl8 zL9oVBVnsY=Y+WQ6x?=$FyOVSU{`NgkF#BOUWvyV^1iMf` zMv4>#bJ0|VEh*Q7Eh!rJ|Av}B2qbodaIqKo4lnQvHSm0xJMbW^6e++jkw7Dud=}*a zUW(ShC(h905t_~;{A~fTA-O1H`_8ZAK<7O0joR850>)lKJoBJkyy$p4m^v)zjO5A6 zL^F&BWon=BG=~yNcg$ti3458@X*iFquR8tFQ|7t-(cfI@>5r<9C=?fLp@!Eyq$I)& zw_zy3(b&MQM$^Im1>s~lAL(!V8>be$dD?$ZR7RHZZyOghcx#)RHe-b%V{Yu-Zx~c6Xrc8#EF8CJdsM}v6{UzQ0 zGJPXXUAf%kqHl->DTj>68z~1jLEcC?xGAw8tx)o%dB#;u_!TA`IIhzb|WXk84H+bf<|T1Wb2+l6&!aHtsGx4yA>lgBX!V5gQh%d|eK zPg!~g|2hE zNdqrZF1ZtFDU;mE8z~Ss*ze@Y)Y&>#LXxopR+_nR94W!~9F>v$X4VyS*WZ`1xg@>L z$8uOV0c0Zum7Wlx(Qjw~4NJQRk8FOsCX9y4f$iK-(a(7P-%J?Z-vq z@uEW?J~q{$0W({L-#EsE8(ZIqmfWsyf_D7@sqh`Y+^tyr}L) z#}1Y@d+gwMJa$lh3bNYgzR0%HWdqu!lO|~2gwO&^ZvBqdjKQ?+dkL)!jUlgu8;voN z6w>!bk}TeXseADqe@t)oq@eH&7-&Ps@NRzR$0F1)i?@&W6T zF9)o%95$K&I#@p);RAfA?Z6@a_VYf(lPHD}h4_e=;1xOKs@o#(Vt4hToyLpe1lEo3 z2%fNGgF!(0N8hN!7jcR#oXvXaTT1xoQTuSXv#q$5kKk~1$SXbM4{R(Z)Unc&9@QQE z`K(I1L2BaQ5WdhI`~!75VDG5#cJRL>WJMUi)M3JQRT!znq#nZz6>I(TizFx1qd@Uj z29ILntO;wm8H^5NQxU3IZzzDQDejOfju4Sgb20>2vK%0$?Bzb?R|QFzM)97s)@ar@ zqPbh;2E#T|6)cdxS5>1Ys_z8^lvK<6c+uyRTx~3$kPB_=nan<3S)GCL<5Ts+v1Pe8@s_Z375eoU`c$)OmKdxW4C=4Sk5{6fv@^1uL;J1^e87l+N^ zWsv${(*Z^_suA@TzA{yT=bOT0+JG=rXK_&2C5>t4tP-Zh*{U9fZQw)Au$Sp{z@B=V z%dl$dUD~i);t-ikyr(;rIds4coWMZ9wnh%lV>(b)$cEVF5^1{%EK!s9f0&wfJgNQn z$iILNnckv{fsR>;XqT}vilr|s>e1M>qmBmsSrd8msZ?jK8b@sfcl-;POut{Fn=?2>AMxz$ZdNO*?{x*q6 zG+vp$+80@jI7*xcOJbidX%l_)iNq>q?ez~9gK?t|@SO?taFmHL7;`X}JZG>N43M}> znhU>_SKOD2;)cVl6?aJA*UP=02u4VB1MdB+R?6HtqoN!69vJhX(d7E=icIz=qZEx~ z)J{u!xd_-@tlAg=Akz{ZO)69Q$c>&1#=TAOuq135gb7Q+*5@f1<1Mpy2z2@?!7UH6 zj-GBnfy-LCAm3Ku0cd8qA`+Wd_=kwaw+I}>RuDm{w=a9*-|#r^9W5TG?}~$+5(Sk~ z;{KUSjI2YOQ9I0VKm;HyUaMM_`hMaNr&NVt!ntN+MmQtuf+GJqyoT>!Jo$QN zj~D$)pXQ0=cV@bt{NKLr@#NK~)SIo+Ufr8Y8_`P4f>Q~P{;TvTBM8}P`}!8Fa=RG+ zaEmo^mP%py+ZlXO#ajqzkU}PGexf`|j~kSBMNCu^4|omoWCa|`1fVQ+1iE_42e4=mY9t^rkW;2$ zN1)*Bi~Y))2AIRHL2sHWoBODQF_AFHmwahS+!Utz?X!+YF@!_!j}`UOkns#b=wce8@`5q&JJORxB};uU&v{$F25qq(2pW@bgZ zOJE}SW4g?fF7%`Wo^;Tp(X{RSPMVH!I!ROSPCK7eoJk**YpB3=LBgzHS8+KC{^=k- zfLU=JuZwN-Mdjb4b>JiuY<_DPy`i%kJ)r6WX=YM1CIG>jk+ ziXx?4s(}4q9wj5c!cCvP5qfRaH$l2Uw%mkCH;h^026}I$3T-pK(2=Oeo-Om3vPBK| z4ODyqn@uROx`pJ0i~sLA|1{DigW9O_d0J+y5BOL4yksRqsn8wY1(>cA#A z{Lbykyaj$05TwFN1&FL@ji2()-I%cj9%1vhF|yU3d0HPn;L1C&PzhNA3Ipo_01w$l zF#tm6bfbhBL6};?%M6NK3^K-7Tjil_Q^(oafUZ6;1)%dY0d$rN z&;$WUdA_2;{p~YYYrJUqzRHEMf6QPfKnYx_e2FhG2MkKnbdp!0Egd;WUUhgfa=PEk zfSmovq9H(W5So_QDx-S?qX#e9H$SD7R}5M*cdJO;ph_+nyyU05Q()d_(2~cn)aksHz7-PldA@B8KBg*Q z4iWl_jsEu8^1_5^)p%oLQ` z>nAG&iV`R%0%3`?Z`aH6Gy=Pj_P1~3P-DEPaW9R)f4$*BV7W%1x=MHb_{8n_!ropk zOM57(UrLr#rM_B!k;3gqyqn(m@Byy2uuxT^lE6d;_HLS+smz(L9g!^cPrj3(OpgXS z&I7*IK(BCBRJ`c=(Ym<tex~+5 zf?BR-yDr0{NgEU6AkOxx(}+3T{gI5gOzzNC>A`8vAc*(p+Dqjx&v;S$n{WxJ3t-wH z?c&~*F8!Gr%&Mmju&k)zzF6rEe4|IS=IeC8ZY*ZIwQFb@;KSpoGFt%z%ow+Epd#Uw zJ8$!tvj(^kr3G2*dh33-u7B!uz%Ce~F6fl3bq%nT3cD%2U9WrUdc~=0@lk`fi(8>& zYgc8~x{mbJb+}X4#3AaUUdvpU&?r{=@O`RrU-8oGHa^t!`l(I_?5-m{9Z82uP&8R; zfWT{4m7R*%T}zRZV?wG2SKwa|!2_`Quxo#S|M^W$kzm&>MY7B1WXoQ&W*`)kS`O+X z1F^ff8vhHzzwO7d3=RGc3OwMViQY_RhK&qh4oY^T8me#< zVScIlYsqGb(Azaz44;8pC1Mipo?E2 zZ%8blH7IE13E2L&T>yesuY}I8Ss#xqs3F?GYNI&r5$~ZY(UVZ$NPJ$TKo#b*U*r1o zc+rIhil<&lfuhnt@jH2w21OVMa3HuzRNJ*`WhyJHx@X@DT%)NLMkVq5v! z*@60+Kz7eD*o6hRB?g4iIW6iQKG54x32!wvS!B7dk_#7zvdvn)KT11*tT>Q!5AND|ut zaV#LYLQR(oQK66|TZPS1q1Pp4IYZY8!(q2zakZBPQ|Vc`N%J2t#4#{*DMBb*`WBWX z0D9AjtgK9=VPY``8GU*7$@MP@*MY`_3{(Pv=te~zFOfYLx5Fgm&E1E4w|5VW;?2Ar zJU0aM&Mi2r=vnKkjX0HP2fXvp>>pXr@8Y)a!nD(YPfkvcwDxHwB%Z3VCeddzvf*tu61a^OlyrzwxIVHGUv-db|+2fIQD% zl8zvkM?ORM>18-V*b)z@^2eK$*=j#sg(TTaUT_Qs#oCoSpMCw`b^=dSVNrVO+=(9l zAe=>)+)Ve6vboRSna7aj86VH&3S*ZYs7J%7qDkNcVK>m|6WBy|&WUn@*gpuaI(SL$ zpVs%*^&hg2p^jEU_7-?BR6*p>Z}PwYh9!9KUv;>9E&+y^00R^ftOpGOAmB%Ufa6I3 z6zesnljvG&h>E~X8HaR4wj;8WxLXEgZnyoH8H)2mWEYD%-!w{Z!l(y2S)RYGS%!sB zkbUfNR04KTT@I0pC>@ND4t|Q`w4)qQ(8Wb_k`RZWANL&s5+rPqoql8$*@pT+T#J&euZh&d9$ElpvSzw9P zNT%vn@J>e6`9G&`rm)F#$0lhUc{cBD=$5qh zuh;Dll9|d$X&L#iCDN$)>LdX*P-b;+#TMvrM@bKi^7PVd9O3U2PdL(hgcX}W@e`ZT zei7U3aA^Z{)ekrk{%9(&_p=y3wwqIfYVu)|A-`NVd{GE3aN{Yh=+Gax> z#0k@yV4ZdSlgufKe|z1Y0Tog}Z6rqx0S^$Bg0Tpu=De_UKveCk2ZP`-m~woBJq$ok4E)o1`JgsP2_097HI8BYN0@-gRxJ~jb(=(f5) zIn#=~G0Mt^QQSJpn$WAYt&sb+ucu-bCz2_0Hwd}Q(IOgx*cn!|5}_7wj(NhIk8V30 z_fx;j??pRvq7f;mX1cWzIYgYm>R&4VzP(nr&mxKHXs_zYwyJ?hH1k8)zF{rX!~))M zx46ZJUGr=vEllE!)FO(e)s&*S(Gy?;c@Dc17M1?l| zh7GJUc8GP=tB@a@o=89+=CjCp0Q!CGJ=d=|cOC;WA=dTD?2AGbZ5o9(D8HwRV&du}yFhf#?=D0R`aCNnuS%~rX*e@W`JQ+~dYPmtP>Yu^aF*RR z@ECBzX=|i}?+pG0X*8pF+ekN|1D|u_LpAiM-61(cY_k>Ps58$>StE5o!Rxb9LULA0 z?_KU3KSqtgE!mDUxcY@-lR0S0gAND1U+r?x)u(tk2-G46?f;O&K_)m&yF0`P=Fx~O zkdWpF6oIsaXd?{lozv)908fjVTYFMye7&#|(ICcKd@3M>HMXATNUd9A8Ik3!5yg3a zE(}Jc;^;d+6$hV@3jzV+P@3S}K)Mn+NLbn9C4I`+>=D`63Qd-Eq zEjWq`R0|h3=yOo&)6YSfca0pFkxi@*x$u}|qAYvBA<8!`mnbiv>>-L6w17S5=N_Uc z%xa>rd60)eczH8h8bm$ULbEJQbda2A&|n zC^ztg6g(IT@}6yHSAU#f*(GvN^(Vu4z@0S#kF>RavU;_95MFQf$F_xYIlDhYzFo+N z!JU5S%)O{H)lFnwnK%Z2ttIpf`%E(PA)_bDlUx>4+YWDSIIAyPZ6Bu8_C2Z1^^IiE z1;YTqRd8J@e#WfP^a4`B#2HK*h*PQT5g5Ivhr`HL#V7u_Oy2i&(NfV`GaF1rfj{yR zpTx%v&w6m6;MF-ea4&Ok;Ck4XGWN^T4;Bx^wC0;G70?( z<@bM(TK)`Eeu$)MAQ&#qPyzIk)L3U~tf!n5+KUchjWv)zw_1Pae11NKyY|fau@xPI zt}Pte{+YNJt!)1a({sr@o8otc#CNb*DVvAd(NH8mL|t5*4oTvNn)GEE{3S$KmxjKTqFBFtucKHC&Tj@sk zDtaYg7cX@btG3*LT6v5iuLew|&zjYm{(DHD4-PgQ0lsaSV@>fNo0m7QiAYu0CD+<# zq+`M85akEwN{?Gf_^kARIAz1>+s~(X5WDVnaJr((#p%S-bewL!XK0+J;&bL79Q_2F z-3qYzqb;Y+=8q@>GYQu>lKuFgS#kXd2Pv$A?X>e>%M_Rr{3;mTgehk)hwIolJ@C?s z@AUHAx8_VllmKfdI5t~{I0^B{ul@Gw|Pi*}=kHjQ3@71$RqR9FKv zGoc939c?8302f_#uzxKH62^JIVXB4Y#jW^?0h}H->9s1Jnstg5eUBT0A_g?Wayp4K zS(*N^{T7x1yFP)dzv76lB<56n=L5e*yBXivN=t|qbA>5?O!t{|48z5IABB2CdaSFJptrtD?cdtqfsgfR+ji)v;x<(!aDyY=yslj=c8fEIKZkxPh~Q&O~;>NF2LSf zj0ENbrET~Q`eBh?IW)Ey-!YgGa_ys(GGK~&ywDB{H?Ne8T$R8Vn81(>T(b$}u?Yc= zEml}jIv5zmm!`uN4A_&kehU&*REe-v!M7p~dbgw-6iV8M-_T+-#D}rc=jLi+>>$+v z{ARfp01`!v-!OwF#v?k7^VxUz4nmBFSVnGK^ZqMpL7YR3>;Ivb5dpu77#!^fU_J+{ zbC-Y<;0?%`w0I}$VQ+qp5ohQ$3xY6PX7?*iQbOFpUSz>ttaSGrjRKzTnh6E3GlNFK zD>@ypdp{ox1wIjYWO*1+wzvb=bfl6$Pe)>cPR|8V5tLfgyXW^mi>9 z7pZJ;CtqkB+@{k3oLAcG39sV}RgPre)tA!dCq5if1{d6^9JPQ}x}J7!!p6iDX=5z2 z(Hg6b$sQ0rQDThW6?R#q!_~(M&P#;Tv@lDAstEJ#qBmBmtdM5O25D9?tWFk#uA_h`)z{z?9TusECT1AUCbgN|0T+O2m&+QWi zjKNz>0;u;z0wns|#jJ8i&zBXNLjn@VYC|?Ob(~l~j+g#?Nf1or87iLsQ^!poeX8rG zw~u$+bp0*NY`{L|7R3NT4z`IRgdK%e#KI0z(P%?NvH9DMU@wgHXgd(8gq&f?fHsVP z4`8RMJoOq|Cw&BKn5a^>h;3Vhk%JV(oH&Yx&9N5{mR=zzb!2C87t^te(l$|2Mo;At zLzor>=Wp*JgFt5mh2fNSRm`WYt71O(l#dfVN$SLl{`^P1e=Pe7%Fx_(9R~#^Ud8Rs zF4D&f;v@~JpiS+t2h+|WH2!j|{1MSmI?`HLq@vp&U!HRTY9QlZo5^5|mH;x1R@VC^ zM3v;N7C5!!b;%9Q=u@)X-&;SB)_~Ux6KC6MD!OMMg3+D5J21ZUq%5pZL%NAWL0LP# zH=Q9iR;+^==2c}l+C(8qMhKSZy$H#53r}&zUtF$(JxSp@tcK|YBv>4CG$t35yMj4U z*bF)kjrF{s08Z$`EASsJ7)&mtcYgi&%a68PcVNbeV4Gg4k*) zTU5jvy_ZD`D_x{_fMTElM8OaVe7REXYk~=`p#9+!n|9k5jv|NE(BGACQ0=woUMvMz z4vA7e42eUK)XIG(6YJ=xx>Aijm*n&a(7cQC9vL{G>BPnJsZ7FHP6DK|^6pOQRqF}L`3P1sB(F@@qD{dSE1eeB?#mMxSXJPV+Og8(`9ixd z$LciB)BVuvzTC!d0M*di*zM*W^>{{|Rx4;yI;~F9n(6B0hD>+S3;dE!ELSz(m@f@m zlKD_%LZ!Z>DL6vDRHoo}zfUs-3vpCk22;=~HN)SKE8Y-!Imd7qHTr1HmE6BD=g`)A8@m6GzDy1MpMuVMpLF>{s?0V6hc&E z6;gC#1vAi$h4za$D=!t5sLpFd!0w9$YU!n5IicLpRf13~lN(UVKJUbwj0hzP-+&Ee zuuz9-p~`egaW)g84}csC`57&SaVi{(;eU>;&=zBAAsUJo zM{G4)jGHpP>5>}_8a@;_(_4~F=Bplu%*U6g{bJ)2p;|nNt z@(sWNMz+}lh(56|5=7^2`~*n4@;G7_aLhJUW2_;qC(|gRr@o|$RKruKvsG?jQ4MZnrgbKC?!gI`loK&#_BzFZIm0PgxS)cW zlenP$82u;*E@M`&aoUOIWafc??XA4WJ?DYfHYC;XZ(wrogEC<%B^Un z>;z@=p+qPulZqh4n~(8$F16Hx5DhW!Nh%R8VV^WiR|Z4Tg?nnSUR-OWYhZQ?5!DID zNlb^RWwQ?hxC$$Cm*E9MRD$+2Iey3;Ezo*c_aF(4D<+NwFwvGP)&82EfXnqq57w5% zi5vw2YI2Z)!#+&n(5y1>{5lr7TpQ6v+K6^KMidGF^TaX-fCgW|#39u7?e`Lym{p{S zfy#hGX+QJ<>kN}E>1f1Z3YaNrRHkdw&mJlgHU#3 z2S?qU!9U4`xD2fi^eX&>b7{2>*jn9l}ThPAjqe5Y&y3*MOl!?%PxkHD2`o9A|{_q4Dkr<5Bx~Mi|wn0KtBIu4jbN zFnfvE0dl@J%jUF2c z;l652M!3i11UFF$s0I%3w^y($4um^kw;#wS$qe`4wp1oJ$j;M9Hpuqjo&krZUzTML zAH~P_HnW2YNe4KvvFMT#MV!=aTYz$=wBmNoxQ>EiyB)DKGDeOk6b;!fYFPj6IST1N zG0>81s8oMIIV%IKl_RM3pO5Ynvs);Vx0BD~MTdMB2ZeH^aJd@PmbID7i*3GzGrbvC z{+OmK%OAe_&c*wqP;u~#=E~Ez_a2S9Jfr=W|Jaem!#ptEJ~|^$mtU8Q zEDF4GOd~KRHjmgJCu&!mEfb?l4-us35~P9@$gcFf#=mrLh#A_EAf*{ov7SDH?%R!9 zg>J$lTgjI-o`FrleipN2cANxUm&x`~oqB{!;A2N0JhU2c@kg{zP-qVyW<|#o+qG+1 znQjZSvF1Wgz9#6%C304bbPK|Vm|N`$tMEybi7&$pL(l;U%@nCCD1a;Y4t1HuqcRIk z&R;AqW)gL9-sJ|~h}S~jze%A&>uF#(J1RUd``g-C8sJuS12>k?Ohz-7tCMefHYlLK zCQWW;ZfSphw_fT*NlSq_t5>0R=fS#-p#AQXNiYNZRQ3ENb(2kR57mmAq@_;$gRyS1 z3&N{RB`eIMrC;iJ#_^&Te(la`hD2xT{*EO#^w)kf2W%b}KP|~l{@gz8CDloj=zx8N z(F1@n@oiL`_$L3oxkIn?@{9*eD_*7t19}!>Fo5d?`gF^v{rF=m2UJqh)aLy0IW@US z(=JLnUew>G98de(oOo8;$hbq`W-GD?A&@po%x53Eo4S{rPgt!^|+IPuD)2IbxLAf@5270fGX`A_};nxI8f;xK+d@zwh^)nPU(WGMyN?an3m8hd2hg?+9?POkIK!z;R-|KR+UbKAD7 zyPdfgywdHwcI|83&WgR9Zs)SXZ@Zn^4Y)nEBd-j#bDXYOdMcQtpLqsJr{#e*I;T9& z{ySIH2r^t>dgST{7$(i=u{G!^LP& z(SR}P93&V^ivFQX|IW+5rvNEd@jv&$8|)z~=#7~p6)@e?zymexR)*=4b4QsCCZMia zvQXQ(_{!|m{X9(FOJ{F|y4xND6gUy(fJPjH#?XqxuFl*hyCv&iGstnz4X$dt#pfj?Cq zYxnq3^7wV#e$E@wP2E)%-5*MQM-cuEdQf zIM}=rXOgg81Ba&8vg1_{VkIxzip7U6Y}hZJE*=t@g9!<)1>2I}u*-_B$eh4t+M(-E4C&Q-x0KY+|CaufDRQ|Mq(&(6r`K-rhzsb~>WA+R(b&Rz{a_n4{ z18x{r+{Wmc5%CYRZ0~|Y(z!S9^9LtiEI92qg67wtAddtK7 zOivhojOUz$x5u@c#(l{^{%+)i3{YfI=Z^~)0XzkYDD(*D+S9>3z>l=> zI!D4n*A|bi3A<$)FDKS0u{yn$DlWKEmZsw~%H#HFTYkk7efPjgz^Vc-FC;l#jClQu^q~%1WvJJ?!bMoSF zn?+hmveuKk@!Wd5NIlt;2a7B(2|}&N`^4l1x}jZ-zQ&cQt*$kS{2@|U4Z2ea(WmR< z2}mm{6jEl+>=(lnHlC_t4Xfh8;i^}lYBth%W|TKMdVOJm)vE`r%qRFJe3ZXU=iPYr z`8D73L%!!5{S!pt1dNE#i|j+GJb(kpNUw0{yra*n9FN#yUg9U)NFqy{AK`_-Af=qS z!K&+bQFzD=xn$YPpSH7GJ_sM;!sIwW))Bk9WRads_(2FPlZC>8#em}F`2m$?^7BNY zOP}VtK=B~>5JofWo#W9enf_`n;URhh%pSR?%Par{1+jf!f1ZV}R9LctHH!0sHG;XH zgmP7FwgzwdZXtNC6td%A5)_BX-xWipPqRNfnlfyr9Ph2P2}okGl9_)MX0|peQ#Xjjn6)LmHXutXI08e1qb59?QoW% zOUTPbqDT9|&cXEs;WfJ8hJ%_I#0+8+gz=BD_NZFO zo$N@EsdOZeC=1ql#X^NA)UZ4YEj?S&{~pxdSq622h%med6duAsZ|Fhk!}zCP{gdU9 zwIhmz*oLE}4bQ-^;D0IN$z~k;AzoVaAfPaPA75qY1D8I1kGlE}{fV-5oE6`%qLs<_ zWSzDBExgeIc!BjURZOs{9UX02OT|?ajfV;X=8VpnP=bYwvyR6!&3q%8^$PFUePKRXXyR(n0h!h^#5;FY@V0r^yt5K1g2 z4``uMX`u?(A9fSS1FjVn09wO!mxOmmaz^_%8v6eme374yugT)84Dc6NulcX-|3Nuw zEdDq4zrn}@Fnsd=G4`LmlV6U^cRKp1V4xEi1AdzQxA@Bg_ygm05B3Urpo6p{=mR$E zN8776t~C5t1n^h-_3x+H@7dkZ_5X$auK7QvXAB(2{|!BJulRxfVtZp859oeWht*$G z24Y8bRj0_aq2c~G#=`$e{R6h3PHX`lGFvA`>)?(5Yoy!$AC3Ry3sw}&DNkX3rjIxB zs)Qx~`Mc(=%tf*zhfAMY9fLot72v>ca)ze$4JkSfO*M=$|G=nGcsl*YeF%4=P`c#1 zcMQb-24XxL#1E&3AeOrjVf;b6vw$V=eM$>%f$V7#*3>bPa_Q>z{? zpR(My_3`Ddy~F;lbw-*Cui;xW*@;YtD~0bU@Pe9O4r%490enmXb`1u?Bv+VZ+ziJn zaOvdo3GIh{5zt843F7>!nUo>9YjtFf>_jQR<{PZlba&ZZI9*SLb8a>JbGmM%dgB+? zufdjn^cVUOhNj5W+psKV=sFH9`^wvfwXTU|%_uFlgO>c;<}8;SqiNDkb!=1YbHs!J z1#hzjAM`+|a@30Kg9^I$Tx|$QfvuD-In`FbM5@aK#VVMm{KIbYRVzicJ#4ju!&r?< z+P056IZ{_f9Mw3XKh$xKx`PO$Q~A^qN=mhSj%t}M`P*AYSkGPU3QIX1?Q$ym7Imf@ zMRq%Dz1t5wW$m~fU@1k@yzXVx=VIy2v~cGsvJ4k)-?a3mwjZa``bHhkaX4U@TpKl4$o`KHbMVghT5a^u`JGeK+}1tlfHZ7qogM=fRRnC*6? z(FJZtx>kBL>No;`_NM7N)mPM3xo>i_erd8|Hfyt+HPU3EHOvNI$Q&SM>JBDEP-&*! z#?*>>@b%5XJD0HE=VubO%tf(&Qi!nCZq_1`8WDxSQ!gDnQB> z`vM!-W$5UhuxC73Xc`dGStP~e1whoP-Kp~}30H+laIFMMSm*L_Oqhgb$ucAumD$aZ zpm{x!aGH|v#%o3jpL2VWUkX~C%>xpq2_CA=wbxS$0kxUm+tml$5=VXTwnj?rfL?65 zmHYG`E+z#6BdIpj*9*N*4P%7zL{;S6A$JUf*6!>@ zSJTZ-(3YSH&7d95nYyz(KMA7Y|GsLZwcs}aX_*00n4RT{>Q?Dt=B;Qwg$uK(9NcOO z?&>3tZV zc4QZ=_t9mSnw|3uXwm^v?O5w{g?d0cV(!TWXd?WSz`0qq+j z{+NuP+7jz*OHh5h&VKqe3M%GtBF!>a)Sr%kpF`90&NujjnZ_~<&ywjG!Km}AERaLZ z@~3GeN~Hax-2(He65iw+nZpxr-ltA(Mw<05r)0_B&zHYvmi#Tejnu&Z1kW5UayxmW zY8cZ)zLNJ!zD#gjKle=Dd}$z^i07~${6}80XYy~yGuq7j!v65EU%K;Y2nwQSE1S_Y z-`Vpsz=86LH;Y!%OlctWT|4MxDmdAVpT&5J$bW&ID&PdeQ6vm&d7pvnvaurp==zJg8<{NLpU z=nevuZ+gH+zA*1We9vg73$Zc_!~qKNK@0KuOG8!=&WQ{dA}03vlcXDQv;ZnhQLGEG zo99Bv*&Fo%L6=AViU~sgQ5M;$KZtA=YoM`!M_dT&nLsq?p!6TUm$66MS)0CA2zFk* zk~O8(tle^r&hG!r$a6KtIY3bQ0D2OD04af#K?w;^AJZk5E;H>h`4UP`$jxkz)BV*r z4L`(IJ3*@18X(1}FA2uLnd4mY4;2H{H7tgxGgr*FY&+Dpm~gOh<&M*#Tb9TD?QlY) z3r=e^zNFnd(sDfTp<8l|0_z^#jnDP~;zN2Q_`dSXhVvWiLwYmO=#4j>j5;eCRPGR* zZ4f0n7J2HHtidBq&zWqw@$=hgOJ~|d#acU;W zZGISJBr8Zy#b2JHT%ZLl7i>iyk#hd>a7G@;tx$6QV>CVPad?hh&*`4f6Nk;B5 zh;tS6J3OM@;ai#E2B~oVdtBQvBi3p>hx4!S3jjt2;HAI zo?v}swOJoI;%u`%@*v8g<9)T*biC~^^mjbynl3$lIF^!xp1YDtm$chDYcFu?nC^-F z^)~ZaFOxnn&X?JozetV@O6EI)Fm)Juo;0(>**ceo)3usJqE%)w1Wok_k>H()tn;5d z98W^g$m1}&4(*Dh^B@)`jgQRPKQ?V#*_n7TiWOq)7yd3kl75f`w-fhFUdJ#b3VA!nM&DIyy5LRr<~s@UNh=ywEG#T3o* z^>`t6Cb_ANcQ5b=pME@TD%p~2`WOn^ytYW#_sd}Vw^T+yl_4%a>}0<84%{rjU~27< z;>q)=C++y3>7RUjrtwc=@l-X38uaW`Js2FviD5mM#A-Zg8%@HB_*Ba$*j_U$D0gb9 z-#}kM5a{l{oe6<&DP+Sx&f<@A!wY8${PB5~Jn1v1Q~WWVjldsOX}o=ys1Ly{GayvG zfYJEWY^A+GFLfMjguE4Q?T1_4dqb)Jb#cbsZ!`Ag+i)|P3GEYsNXAnGCUPR&49s?E zQ{LG}rXFEb7J9At>nSma)2z~nJW*NX=?#gc&h@n`fV7FLkaEC~@A;uskgJ$c(}nO2 zOCBPotnBx%7v{?~Wv9-KFEl=QcWzN3;;?4u7RD<0#*inORB>=7Z3)shH#y;+P zhOv)g7N9e$O=;@SI^Q$92Y>8btqi5Xj0p(oOv0!Obf+S|d>y}ml`ffdVTR_My$>}f zv}1=Y35~(K6!Yb@$PEjiB@27y5%nU2+Ia~tCu&d^A3%jj`4v=%lKA++EnaVryzw=I zTIKu9lSIv=v%MA{)Dcg9mhE19Yz3JiOK~;8Ke{Hlg5Phi8EC~2?~p*GfV!H=ToEFomX)*8VdTd1`{Pf z27aDCn)7d`8v82tty#(d1#H(uJ`-21Y8_3iQ8~&38L$}Vl;i7xFjL=v5%!$r>)H2) zF18&Xr7#J_g&#MRLK|?dsQH^bo0l$myWRK&{~5zEUIuH9S^R7pw*?M;F*4;HQjz#N zH!|flJ|$`YWz}hEyn886DdC&QK+xEmIw-o2KM>tGT6&HRXoVR66gjb5)y*g+t#lU7 zxA?CT{4(|6`&4Rk6NfzZh@tbYJS(1rhX*yN+v)*ojxaz3{nzDQ$lXeIKGEUmyDQaT4s!tl$}`UVd=xhwH*kLj)Sjv zM6qZa((sviiC>BjD8IU^fvHCmUPIfnZStM%6#NHm2K;wtlMoMly1ggxclf|x9Rxp) z;1hYIi8vnO%b0Y@6pP~zAsmB-?w`p*lq?+N!?9BkhuqE#9B5MF7@;`!v^Wk6;TTcb z6OP3x>VXV3C6DY95R=>%0~|$);~l<2!Tax4Bdg_S1!eWlVLjov*oWgpKaR$|DJQ5P zdC>PiCHt{J$8qJ&B1ImP%G(FR$mx2;9?alvG4^>g20{Bg&G%e_XVwbq>)?}(eWCvS zJZAK-udo6lkofq{e*ODcj;5C^c-}DmMs1MkaeEw;*?6LVSDLr$)W7pA%&`8Y75qgO z{G<^0r9Fb*$p^l>f1uq$`Zq{%^s+d13*mU-%PcxB`uB)=Sx*-33E~Lp-vT*#Uh>E1 zjF|rC%%GV1{HG@zC;D(45yTPFzh5ejBQ1_oLO7~=#POl56RVD|+{q`afd1vmOQ({@ zSpfQ102>DP>i<~(W}oQlAI7vwswfAym^`FQPMUA1!@Ah<5E=boXWE7MYFE1Cos!+f zTH^7}*}G^Sp08|W70$TQC2#Gh&qYq-!E69A@AQT7RL(*%;fTo0FWf&0Eq$dVtDL3y zP$3WLl7F|1aSyUaFb8Ey*6F&Q5emcfjXS}}FFSqPdGzh)(id0yl0YJ^b0g_nyn~_)(s#5+-@!im zPIl?5`2qTtjB?vIU9#~gV_*Jyx^hMPvil$-LNR*NFGo(gi~li>LF!ezq3eg8Hr zJbfCO^;mde^q@=@#J>F8hog58$8`pW8Q(5c9LHE3XM}Jp=n+Skyz17z|EGNeV%px| zFymXU;#m8%;p5X&gM760h~q{dj-!G&{>TpGjdAym6It!TLiofr%*;9#NSEZf)$(K% z`r>C=A~hD&!M$-3hf29Om=2eTFu6-?iCeBz%bla;p7fRDD*OFzIY^Z74q-W;d6NV4 zb|n44H9w#+?P#mF3AmyC4vKbw8K?0G|ey5y$e)_y!#C-yD3&b4oI{^r?+ zo1S46W8e1C=OSmu0c@Nfwr?y0KMX4gVSg0sgVXH%mqc^_)MjanCS2jv>a4|^Gr6D{ z&o9ZdEZ}gCKGR%MN#opgFxy24(X8>*Dj>x7k|k?ySu>ylNP__w!}BBqFeYt@>UB~j zp7s_6qD~CA;Ssc32oL1O7Kg?6iWfQW!0nJF;`~Y2JS!8O&H0hU@uVkRoWO|)FL8g9 zm|Qw`L+|ANoM9w3=Ql0dDmv$RZ0&MOI785f-I#e0^6VXqY%?79jmDi1^0O9o{<*8B zms&C4!GNI97{UHtOeqLHRLHpkl?}gDnhzElP7+@C&}&PWa2Ot^=HN5!IH??6*kZ1^ zpFE#!Egyp>~jw#wFTRR*$Ss5zigEL@V$vLOi^)Anq$aEK9z89Khh)f)T){|J7x;8s+bZ8GE`( zWItD4Z9o-o9HYvAE0PhNDAA^}(RdzbvL4l}b4}KGWSwH3v2ENLAa(kT*z`Xd1uQJm6Srmjd!&H{-BIC76%udQCLBWHkh^ja;J3Q z4+C|MYwSrr`SA?Mq35VbsnL+nJ`zwK3Vj{Yr!&XBnC}LBo zdc8)5thg&!M~WE1DpVs_=|AOc)M*Ai*7CiZnlX>T`$>}%bLy}z@FP$W=raaH=o6*CbLSKLHm&X6({*AJj~3ib_)i(Q8A zY-$-!p1%mhS&jANrR%p#hCx(%e;MQyg`94$rJUHOlGH5oJ}lpPJd%?$V#o_4V5cS5 z+$x2O$ed#_WDRK@)iE!hVn8lB<(itI%O+K%cBm>?4tMsj0VCnPVsyVohx_EJL2o9z zsteky@YVZ7TOJ%5ly6#AoqVCKGg@8nLUK`CXMRB&0w=FvQL??Qt5-pLa_#bu3f5L9 z+oTLLvy@rA+$mU{Tsg2k@p(6{or2QW!EYYj?mTvHcyScehUGE$D3J%ILs9o7N51h| zj*}aM&JjLN1|K(IMu3-9jmJpJ5Zoj*1#U}jm#io6DTzBEadPrpe5_0zwrAU&{e+BU zZr5AR0DbJ$^_lt*W@ru6OnE$pD-Ax!_6BaOb{?GN5D^9>3* zL}M8S3(y^zgq4TyalQA4sLpsKLDvAhEVQ@Riy$LSt8r~*;_LiL6HMD&5Kq1s&z+~W z?~&SDQ2QpW{c1dQ)F^U`bH!ihH;f&XF1{(X$0)A*pDp4#X%yNCjTetR@@72u8%QS7 za4_2L)wr+mX7a@X>Nlm>EilhR|G+AJzVk2kvji?^a89~MywH&nR6$OEc8)a9y}A<4 zf%1&o&d!5`oKhRxv1fS=9vYArb+X(O)fR|KrUoLa-AP8ecwP^px)$|@F1mg!1rcd^ zUu5((CLFKeNd-btL=tqsAP(xXhyynb4sDx&>z`ka=XQwBoEcAaM;cB5t}QjG-V!-n zBFP1De70NXCqoxq_d6@FsXl?74uRbu$SJVmyG39-OOWi<^?(n}Ap-elie_7h?8pee z0>1qHnq0iT*&pY)*Ze_1tdIa8UT``zrB#BeWUtPhDDKBhfzFrUCxHO@h4pjp5!(Mj zC6~3LM$K@?5ne&JyeVspUqi8$@F`uqD4qh9OnA~Jqz@=Rkj9Rpy-O0uEyZvKVH(k> z4*fIU(T4BzXu;|0`ILMqp7WW8qtb8(5UA2*TA8 zGnBV&C^iU1z5mC8Pu#C{Zqhop`0J3{?nrV26;@g$GQ9yQH7T@LeN4VEAdn75lN*?VXt|UUi2J2Z$YTP@vS?T;FLeSGi|LhGEx+9<`n@Rd z4iZ3HtEsKMHSya!CGSfKk~y90p}Wq9nz&AbH5#nYV6g`CHF#EoIT}170ifK6nSk?W zO}tx!J8WLDtP?>@@(cHmnTKlsz`Gae8vQ0+P0QO2%gYXuD{Z$0s!ul=O?4~#2jgo@ zTsy{mi4C3xrGharvZIx_DZ0l@X$8seVn#RBdrvS8=Mo*yPj~ zC)d(TSeab2ysKbw+xh`*-wa5V4_cKhPmM23u42P|nXE6YPQ?n7ZzkT)ts3N1Cu4=U zUn2RY!pL2ftj|x4&hy9fQc*@hR}|Uh?armYlNR~424gfhLxYngfPnd$3swC71?C48 z>;k&paQY#TBi9?mSHt|^DHMkH)&J+}rQW5{J`!yzn~1Eh@hVL2mc{c1d7dlp^ATMn zQMQRfdb<&7W?Dq zF?}ZC#?2tZ7KO|qR*NLqZ0`{T2BbJWNn5*V-d1ZrWoxsP1y$r+!ovt~(kI4~+KfG* zyaFhd)KZYTtu9iUN>V#R)W;ekOAV1ykBgLk7X4K%=8!x)zWi|%z6?&3!U|Bh>yM!D zUh)nx%8nz5QR?Y3Y8K9VAg|nzSE~Z_Gi0S4bq;Z9gG;;lAf?T%frkv~;#{|IQ4 zdOgr)W@RC{-SUOX%|$aY3!1w@y$UVn3C*pMjj($13!%kKvr4_pX7#G}Ah%8U2!&$C zB?6oK{%GBI$QJNd8vIgQb^ocJOhpeQTm}-Tjy={}ZTpOKr3ka9qCFeRZCox#gUeqk zmp=kuFT>?%!==;)m%t?Rc+*)Gk|>W+ypR&y#Irn;=W|@aq(3}oAa@r&*e6IYtwA|C%SD3QRK2k-Fo11MYql- z8PUC9;zXZKboXh#E4q}4Z$)(fhWIEy=!noUXzi zRRvfIYr|81Rly=z@XD($Xsb#tLQ*eDTCjXWb-{wR_Xo6nHz4t4H*kDCAn|GUz%L7y z4*ae=6s~VdJW#t zV3h=bc|OjcI31eUs=?D5Jg&jR5}@QA-{9SN-_D;g=-2t3IhW$u%XCXwJydMOmvn;k2cX9C>wGQG|_dMJHSMnH8*Oa zbsEhz(U?X*8?G~iQjM-R(PjnpiiytB+(#J22*U5P(OYr8#ZihBMi00jvfNx}GuC?< zxZ0kJrW|nGs>WL(ST*FQ@kj{D#cVl+lu>SfDHq&+UMWb;7zO}bY))hMzX4w4EbJn1 za~{CZAs<;(uSuxOsC1jDlp|o2cpp(hYDT3oSt`Xm^8ByH0~D5-dTc=}0Tr@gt1b&$ z8$VLE{FUS*Xx16jj0__GW$HCs&Ro$LakmN;sOK!#rfgW^ zo7@RkBsS+x7>8ITK9Mxx*LXOT4`;I{l$yT6=Wv<<9=h=A&> zfLLmliQqmXk(TZHY17M4!*(6#B}mIewSQTbbmhQ`!TGDdobyz`R9I+2iA5fDdb3p_ zB$XwJHdqC}_9o{O!u1t$&Xt2{SXmL&dGI>Xmcn~>7B7SH8-X8540WpUYgpZCJo}Qg z!CXnr)cA#3GpUk|8BBgU|B8XtE#y4)u~O~o52Rk|)$*Dk>U;?Su+69|1r^78a>*SEJfB3lhvwOg%Y*u072CIy9NuX!|^T~H!Vg% z8?r8YxqnMJpY>xqc}1-)qmi({i<~poEAUd$Td%0|t}#_w*ELn1RPp^mN)^^DRKzxcGk{F^ z&^tG0hbhXzp883ch;u!AeiVgRr%f^-t3B`Kv*&H^`?gUo(5jSwp-?arUnHU4K_Wph z`nfwrgx6w-o1;No-N)K>xT*rekWY!DMR=8D_0(JT;e1axtax5f(5svZ4@vhe;%X zK7lSWup!WAjP2HaEm;KGY!L@lvz@d}F33$7JA z_?!m+(%?}I9@5}G4errkh6ZT~K#~1<_E7iB%-l`Z&#-m|XRii+V`Ev7PU20=o`ki8 zoW^>Jr{NsPYcgMD;W;kP*f7$a)5rzP{04q{>PEZ(8`3rK^C2rGDZE|eiFW6k-wG#R zNRaH;HQ(vfly@~Hw`+!_m7zy(y1MKxW4u2s0@2(r?Vv;rgeouRaFxMk#QBNv9MQA6 zvdcPwgpCBvG>A;&!r@77?l3-K z(~QmKCWL`*Zd7xpm|U}6=H7T}uDVJfx|~EpzbhEx5ac=_!GZyFKVYsI)W$0iPSmBN6HtFe7e8}{uic>+)liuqq!dgKr6^5bGyA|~vXC#- znaM)bL`unJk%^QuBJBmR6^uxuAtLj4X*on<&Tvj`+_Kuylmgg=6i7m;s52NVDIO3l z<T60Vbe1widw|wsp@qA5>exh!D3G4?N`$6wtyJ1mZs)p6cgHB6)5XmZY^Uk9 zOq)F40i^aJuXjFzsMCG5tpD`tO&h?0*mi~PDkNt0+|MK3> z#(xnlGHW(}8>0RTy^?$6+4Wzho2)wOzsZ^f6o{U~CY)c{)J#?5bjkiUbGT%3x&m0G(%LhPwKF5;yMi6Sq*;HV4Cb%GgeDokb_C0SlYN0#8BsUlSACAOk-Jv}VW<|!%dK-onK z`Iw)lFKXZC?nL`ule&O=)1+U@PQa$J{1Nz6ax1JwUc<#=bv~nkyY6T40eqEnX@LIY>T(OgH2Okmo^-`a(zwm z9h4|-s94z*Pj(iptm4A6pt9hP4L!D)&uD2GkvI}1c-J+5VSO7HHTs5ef|qWhui~4N>L7F+m(MCVRTpL0ahxbs ziXT-vK2?PGEO;2nhbSIK@S!NNK7!XPZSUtzexGAvK^JnWQ?+?`vx2ANi^6lP%%=FzFzLWMa@o(S_Oc_PdsQ?;x=6oJC5lqaSgAx{dk ze-P&3soMSu^DucL%tPggFb_%9lJSEPD9j=9M3@!wq%ezvFb|SlP~`nUc_PdM|KmsQ&!i5n@fve_8J=X{H$71|_#aY26<^D3V}f-A)CU&}v5&e12i zw@~m92Cp4U_U@X?=F?wB`)_aBzXG(}l4H)-jucvCsjg)0s^c+yV8G$hoiq3lIdIv> zQe7-1#VM2fBWBn1bO8486TFKNZm=-*eC-fAmjZq%k6GjXuj`Fao-LwKJn$P%>rWhi9wM^(t;KO$2uJRcp7_|;hvS=-K0X?M>pNRp zrSR9XfS#=tcQaj*Z!r!CVSJ=VjQD3uRL)VI+_LmN0pA?M`332Jsu*_)MuHsDN|4HXoj9wE6G^I@o;Apn>A)trvIt z9#2j3A0%8y&M=eZ{0Gw@GAat@+0%63d|Cejh=lt`Wd;fI4+Xiem8nP$wIY$pj%wOW zGY1f5^Bn9V=SVsT5XpN$2^e<}hKOWtS*s>B`;umA(q(Q^lk81B*B3t>P)>5=GX=0E zNS$)Dk@LjKDktKwxjGAC?(a-4(Mc`@Jkb^U=|<=lHvM#nTtRXEg3^!y6xkUwQqgP; zkaP726bdwl&h1)sKIT7g2JZZtjmO(P7X!ExIhq>pZn$8)e`>tlZ6W#GYf;2~%e!cOv-iSo>xdPn;>FC~4NJ-O z#cGax{YU3BANMu(NN!szWnY8e#$LQ)$P4VPk(N_sX!*Jzk~jlT=(S0nYvR7Jxo*zG zd`b_#1G4bS=*jBZ_rX6m}?9}#A zi6#}>BvZ|7|DTrpQZ}j#-~uT3LKRHdk+u(06x=t(4$Y%C3Su#YZ#f|5C#gRk-AuY$ zlRRptNP?xd;Wt>M0!mA1LoMUjs`L>yx_n*GFkGTSVZ%XM)NDW}bt2KJ{{&I6Z$i?P zhY@iOyIAIB2Wqgd2BjM8rooOHY^y<@2H#yI_5P#5MhzSd-qB#K1}i1NatJrAI14n^ zuk&ccz@Pi`?1?%5m-mDEv53tg?YJ0ER=U)WG4!h>=R`iKoOy(d`XNr2WXj9R^k7fo z`BK_SJd*g-G<-aLz~Igi2IMM99GB)*0J3vVU_E7uTGjfqiF*weN!(z|c&a5=EU_Jp zYEjdDjh9umcHo@HQ>{Hv#WGJll8akCmYMVs#0)8TjINLb4=wM=AzCncoMRdNlO%>2 z{lC}BO21YfMlJah?zoL?>)fTmZ5rI9K|+Hj4JK%Cxds<&P^ZCJ8l0-Zi5gUEaI^-8 zYjBVTVqs^}Thm1;4h$J3I4(=mefRP7h z!`e2K;%w1|-C*M|c)?*~Y_Nu!MAOSXmUgqHrK@}CTjVO1 z5Jhj9ME`iwDwIu~gS%MhDXwEXsy=k>20b_(T5>uw=$DD=h#mxzUHvgxsFk~tb@_&2 z9*;&TU0cwi#}iMw_zqW6Ox)ijQc|YncaS7~SKPLtBqZ;Twc;2 zlL6f?`P8J%lrd{9Nu1p5=xy@5f%I<8f2LaRnE_s=PBN+VE4I;btyKEbuzu^yDR_sY^=18^Tjc;p8Kx{TeCpWTE=+vw2#DtH;ic zW5gNRR)ahZzB@<#Ukx^D;ArrU25U7~slgHr7HIIC2LICFQ4Jo_;64eGxt$+!0t`c) zn1|t6x;u^Xirym3prxK;5-DNu>k0TyWLN>)ffw3^EAbT~m20e4e%M&;h-bCYb-Fuz znt!C0e8z9P{kC@}lOMDv|53FT3%YHF1?}U5zrd6X)_>5eAF$niOExKMP1(8@u-y?~ zpZ1%QgR<6_uBr7m^y{JKhuWt?HuOc4Nl+4Up`FdM9Y`gswpO&?%>8K*> z-Bi{g>+44^r=m0LKNnokhX^CXcKd6~QTB}5ciGyRqn1~>;c_CV#vKiI9BVj3Qv5a> z_Q?!#4YQ2VFBMhzSd-jTpG9tRh@<2Q4z@hX(~2j&`bFLhesE?k^nPz0T0*|>TGjjDT;n;DNQ=y^SD0SiFG<0<4X)APN)5(paDfE=`FI-Vr7#&WOZJVQE!OpES@FddGg*qpGV`ry7fjH1#CyTi z?y3Fsh8exD4e?9!aI=d|qlsf$zDYxFb!vFtsO9TMMILeDSct^7npvh$VW3ccu+W31 zP&81ea8%p6UIrAf0Cv6)@S~>h~JTFQGYd-3gXAQJL-AN zZ<0#={gqh6hl`*$OQ|A%sp8C1IWT;wvx~h_teP*0rA*{?R7i;;g`b`(WtilP@F(?# zL)bgK_1oz5+hIw!vySIXa1sE8+JRku-g7MMTZSTwVP^rt&DSJVoa(p6^o{LiXfl~gio4Ond6RtK3Bm>!{g~4k1KIl>pfINJu z%b_x1NYdc}DH??2>tqTj2%5U*GcT#jp+jNFVio|oe-LssKoL6wt&5e3&jq&&l4N#(nN^_ghm2_B$0rC z(1<@s5%C9vM)ZDyh<+WF=sdB^E_!04jrDYf*n;?pE+`VB3lczlevuHLnDq0+q`UYC zJ-{x32hkCDfL+4&VF zjd1dg$IC^#y<#-tN4W91GTtDm%$Kwd@R78GFNp&QlD_Y40nII|0Rkj-`I7i54oRzh zN!*f#qy=u$n6gIMNRYx-Vv>jj`h4dYbCXYI7N=E6#l+Zo8aw~Fy+AzJac&h3zP|cE0nvxsyjxc+8)eT+d($;88`AI2_GXFm3Ur1s2? zx-L5kC*DJPMGxsk+0#j?5=K(*;N4G1VT)yu`ZX4;oZSqmdhmj`8|6=uw@6{pJjhFW z@Z!aP5mJ8Bsw~p=5R!zbA%Pl_K>{ylaqi4lX(=YoO2qctdC*eNxP3|IdACMF!2{X&6+!+iu6Z-%3>roxzKF!B{ zV(u+a74f560dLX)S>)@K7$Us;vZQd+N*2a$aH_hc2OY{|ox*5#ag=(JHkG6{yWlgJ zCbO~qcZL~Bhl&!oC+WRi;4x2=LN%Q83~>L=RYsFSMG0J0my|Dr#;cCR0X$C|C{)AA zHNgE>vgN6cgo+aQI%zWkzr=K>P$B0rxv9Ywt1F>DWKSnhHAw{8zXzaWvR8Jhv!@4@ zNSq6ZTLbmZQPRylbU*=Mr<* z3MAqiPjo-Zuy-+z@IGqUWhJ|m&;r;_B2ypP0$obDF1pLO2+->obYjPbPGbvCa(T!u{)osL;PYlKDTU%7CGIQ#6>8Ss&bQ> z%5dY7GsG9iW_o9!8=onNWTb_&CL^9qQ+_XZJIGl9-Rkabqi!zb5}y&;M?I0h@$cV^Ot*)dI=ANY)etp0c_(Mpf-VBZ*F+9g`DkW zJ|iVVjL`A03`d>A4HZhQ3tI1o(5QMN9!kS!G3f#<&VIvm`RV`S6{X+G%{hdZ7Na8P zf@0T-+YBdyf3eD|0Y=W$SY|*GQ*__-~ zW5xdJ9m^4gU*_VxXu0C_7u9D&o{dDEJ%qe5Wfur`h}kkJlTQzOZ|Si(6yhUP8!m(6 z)zV{TmQi6WPZnXmv%6ekgU?U-0BK1BNJYU0`4hDOCP0h2CP0W~{`EhQf4Vn;*8yL?IgF(X0JRA162u!N*5eMw6+>3m-j8AquaUy?Cu zLw!kXEtLA1FNtl2q@CQPpf%y;Dr%0ByjKXC5wksjD?gfbxn%h?8LjQ!QRcHSAINoA zGtC_dIuyGIsB0Ev)OmQZDv+v__F{d8S~}moy_H<*of<^y&XlEFE$W)cv!&daGS@^} zT4Yxim?p4&3N6~!KmvUH-GFp0AjCD% z1L=15DW)7XNm|M!!`KaLM54~cOO#4#m@TT+Oi>$&I>j$$7S(EOj?3TDT7Z;8`%MjHakZz_0A(Uq1P`iWP^jJf@E@ zwE71+i!~JQWs5GcH-&1<9Us^P)iF^Hri#%o8z~3c2^XpPVglT~` z)4hL}d$A$%s^+3Z$T!fOf$fs;8T$qItwAA|Z z?6t1P&3=jVZ5{)TrkXG-z=M7ctzgnKlAPiiP&Y>V(ZPlFr@a8CH2&X@R%x1Jfk z)l!GL> zZ5w+PyjGR`x@vi6Rl(O)iFb0Vx?doIg-Xic139|Ti z!)L&g8dN=SX>|d2cPGD+t?iq0BU6?mN3P}nB#z@DIHTRY6kFYM8u3Y|);!)tliUi#A%oUxhA@;W@u;A?*fQ_qu-z#$l_8liYG|! zCKoYh(M)g=uS0^1cnKmd;sv;GfXE+3Vs_-W1(2U9c54zVHH-(2q(Ry+h7p6X@kE2H z;V2IgUIwAg-r12n)ZI#cA2E#ap_?WhCeT zy%XqkZoN`mpn*GKi{bSCOmmm&7+h^oh1E6N61>%^4%dWKvnjzf-YQ3K96{_I3kM)6 zKIqkW?!r;YO{3bJ-g@l+BIKeQgUU1TbYi*~R~POPPktItywlIU%CIW=Iv|{%34!%? z*;>7|sQ0Jr_4uul9`GgK!qEISs*M`7j4La(qjFnoaM(tuCe9iDx_I^FqZPyJ@(-&E z-j)|D1sjuZ;!4tjjkxEevTY<p7D64seM=8~Wt3vUl*;a{L+prop z-Q2)|ujM40a6YODZFn@cERe3r?c1XkuvU{Cb`nbtdxw&f9#b|>Wvr4(4%)nZo(^ePye)(h2fQY(UemCWvP;VAHvyoiqHl101DwH z|LNWMp6`I}MNG%%c_F0uz;;NDKC_P$s=r5T`uBarET5)9 zjRqq$I7R{*``b~|IY^=~(ye$A?hLfCs~FqK#s(vXHa9=d9;-iI+)n=l@5k%7$%?NN z-o+a+yUB{L6IQr!yU+~j+G#lMQ#6-6%JOg=IyFpM$~i>Vl+2ZYaY$sMt92+!={}Zy zQJyeNiCb6#RmaDZ&;y`t1{W?EK@@c^Wg%W1CLD(O8#d4IH;{pR zvRPy+F@GXh@aV^Q+054)y1v!eX2#2$e2$kh%JLJ-8}%xtc3) z5Rg>hOL_$uk@TN8Ebnv69@V7vzNG1zw9HLvk}sdPy79Tv%{VHyu_J#Te^_>v3n{a; z3nmu8$ZT}RtzWGB%CPIaJe;3^%Y`y=TPAI~Z=!u%JkcJHrw`cY0a}2!F|}~^vZdTx z5~7jxFq%_ajmL25PTtiQiDNg6lan26S8Eka{uRFbL_0`en}rLF2o#F55DYQCK=#eC zoD>bhe)_KX@^9j4%kMY9Tp*QR2WDiCqdNaUh2_}x7ayw?L>j#1-KzwfRoerqTLU#g z-#82dSIHW)Tno0EcWDKPJJVMqIT&oqS?53z?m`Ta18vSM_dUH8mux{E&5+;~ zeLF1TzxxW9b|}p#bX}m3v;(H#c*#d)LZehLBASIrr85JI&pmb%W08A_noOS$t4kI3 z&%-#U$|FXZQ}MfR@a2ERoQhvl(>jl8ozR>rjE6gsu5;^LrgiLv*Ji%wdd1?0#UkmS zu>w@04wz7%8$fE3{V~X5Err)icK z;{79Yux^1{oYv#oq;GN~Q@@5?Ogf2m5o#8VWIU-RVKd>`u1~wDUEEbtXe>l=I{ueB zdv2nk$Ey=5ruAtgO??^djK@v;%w!oVMBs>s$E{N5sNhm!94bpnC37q(F(===dQy;z zRXOmxM7J9sOmT&HOJQ&V{-`s~lu{zJl*N?qmKupt=pDaasn3h zo!0SnKAO>^#R!muXpv`SPLh4J1A&>IR-LZ?#Uj(!s&B>dvOVdvIeDGEIFPo+8{CB`u$rvRBxGRZs!qCN;9(8!*Wg|a?$qEG4W?-@ zS%cqd&>(?-yss`>Z3g=rpT=O%{UQ%ix6&oY?{DsXJa7IYafo1y(R2{qQ?Bx1vKHmJ zaOb!AdUYkbu`bh=vnyj@D`{;$9^HPAIM)0yN<^Kyx4(vs#Op-@;gY5pn1V=J_5;gx z``59fq^^U@axxsCUjU|_xa92PF2OycY?g#gyiK}^owkOb@RXaHPp9Wr!xOY$=R|Ji z=iV_vgvDwmRCXvem6tLD_xww-8{{gj)Is(-A1*)QfEuPe`MkF>S^{j?cg?FLeUx*) zTB>e!!GwIiFGMl)ett6GJJKgSj06uS`*>(EJWO|az_`*xPgSm_mRoA48(+7jteJ2~ z|IJhIL{1vZ_F|Nbqosjr*fmGbJ|=zMKw~wdBmg=>mzgqM)bRAcgnhzw1c1%~LTdYHr8g~flEjEo&cb}U4 zrjPIbQGu?bg>*cxEgxq##iL~y*J})CF*E#RKmEBI~ zA>VX6r>|Vzy|B;INbS|`0>{HbXeubDr)5`(8 z1|Sg@I*_{8vSLX^_poK1mueQFD+^4sc%+RIg9cUbChd)3`Ud|r`YwAv3w;x7veEbM z1uV)=<myG8=~36}O4HkEbGM#DjDV*uhf9Le$bk)hS*fY~gREK^uo}r9rhRv$kiG#a|iVFXrbD zXq7)vBjHa(kI~-Tj14E;>}M=>UOY}IDW{k#)IWi0jR*Uc?HTg@_7TJX=m7p&KYp~X z;+Mu1{L;9Bf4tk^XiYN%Xa=i{fCO(Tt->jVA*CY&R%_54AP)@9(1ZD|zvZqLjTXBc zZm$|kFJSxhp~6oum{xj43xr;2Iqb(eNo50h(IbKH1`6m4CFYmwFc8M~#y^dG{&(%; zd?A?WOo4aME=|K19pXQF?L&L+n&m9^ybv1gQzscFVodm<@!~H~P*d#3*?$m>@}4}> z)n$(H-#h*Rq0oO9Z^g6&7Dnc(pXm+-dfSL+jk@2?VvRl;navvYe=&13XdS86=t)IG zgR~bdGTM8ibJ@#S>O(I;#OxEEJfGyztkAJDKaU^v@4lTa?K}D7W)!e0C9F(if{zu( zJys6Q#>#-KtRxT295}ulVObG0WaU_6Tb;jP3kf^Utt0@wsGbq^3t>y0-7iv`HgTGY z7n6Zzlq8?e_RwCtlF9#~y?*{ymi9VwM7H*-Zq974sLSLm5GVr(mW#z1aGt}y6Y}*S zb1ajOn#g$OJY^yi8EKV>E|5F*7qb_ei3?^)DyZplc?(CBWi0HqJ}2hx&uf3^^Jb&Z zU;W+4M;gPg&jQ+~1dMx1aL}7s@8vAKgu+#BUUX$M_Nse=*S|8sE)AOG83PM z&}k8YO;=rk%v7$^%{#u9vUMyxdHyT}kXh$7cG?k;*CuQK9yIcjcJu3tfc43XaZg^| zYqQAfqpEE3YCJnrUaeJDUV?`DQfFm#`HLzmGT_Qe#7cefOL3yJ*~p@qWg!a+hzlN1 zN!X&DZSs6FNBx-kYXaYfGlLshde zP>N?9B8z^%OAH>u^bEH2j1JI~&7ZMW#n7YbBW|qK9kaT*8h9vEkmQ|9#u%=bXE3M2 zsXmHiXZSXor0R5Y$fZS(l_ch?Z$m!9^pE?ik>?Nl8=eS7o{t(TMV{J}oM~J;LJ0%$ zp5rmY7}Su>6c3S9dA~4yOCB)v;j3nOWxY@k6S}|s`2Z16zH{%$@AYi2g}_$K@5ssB zd&I@hCLdn9Fteten+hEs?!e!oY-iVMp(-*(7YM)sj>LW1D;Z)KuDLI zyT>k|1TQ1|KgtyvyfEm<^>ANea9@(&QDtyf8QfKZTbEr@eNNs!F5T8?lq=6m*~V}t z>9o;P1j~mGC=x_?EEuq}a*yYoQIUo5efFD306rYSQQ7;E>Sz>k!=}VzUOX)CG4~sJ z*VjPb*i*T~4)$(#Su-MJaeTZ(?HS^bqB_v>oT`HB{!u(RTg@JJoh2`+MZbW*i`_ss z%fav@J}QHRjs-E+#(RgMt0rx)NgbN>HkM6?Zk}{RJaxrrY>J7eFNtC-k3|xvqxs=~ z9R@dPStM~RVpv=2RUMhryL#G)9NEsdx#@*?>XOl1bu5qPeh2*Qm&6~Na2dYvhx>%1 z@g$bUu*Wje@@54r=&;!O!D^zQJ498mvqVS}6WW0pF?QuEaK%Q7o`!6v`yFV72`u zl7`8Oz)if1O*VFB1D_rr6pT#aj#aY4ms1n@2Ol0h9i|R{V*G=*R%6QI{8QxI$%~{( z9T$1U{NlB-fQH7D#)mG9OnE7XMcCBXojRzaCb@9yG@?EKz$(d?Wx=jxDkF&wG!;Ie zdo`}yk1@eCGXx4sgic6Bxw0IQ!aC}@Pz}R5ZxR-EmZ|om9nl=aWW5@#%0+9Gj5_Zx z3 zEpdbdpx197)82X0C zZIBSJDH&3-GmPsxw`?7Y=5A)KWqcg8h)eF*od(mg*JHg=8WUUWl4@n1O5c2Mu88eW z>RfguKb&RY=q_$EC0fKPXdE=38Q#!ugc1ucJs6Aa@XnF0lv507@Tk*o0b94l5I$R} zgaa-lf*~O%*C8Wp3Hsn{1#-a_fM2x<*m>IpH)g`Ux()$S@bl~)+V?5rUtRns)4odd z3`LYSbeUbe_G^f#OKZ6TS%>W&-Tz+PjRoAgX6{)?tUnfwQyED-48vqvcr`P`q%gN* z0f!CRyu^30$c;@bm&Ds*NIcSVF=N&U8J`*=hD2r?M@beirz`U?NkvXf&W_RtXnVJ} zylcvD7^avLn_(}v6GDo6k4i4GUo}mxLT^P@7sa%4Lrt0c=H(HbR$s3+D$S>E9zE)i;jOsqI6#W*j}O8#_;)kb=cWO@mF+JiQ6rWT#Db zazWdMe2UT(N_2QY8ZN-gkfT^4DkYZV`?A<7(I%HZ1J2jchNDnoTb3wdiNwM@+3-Iv zG98|?FdXBS6YV9gIhZ`3>e`O)v(im>uGCOrCUW_^Da*s-wsiM}|!_EiiUhJRjtOYW>V4Lp@5QM1a z{dkiMT$KFWUcK{3NuKsL^s2mE{OQvRfBN>upMIK6n^>6Y4aVZ=!1?SujX!6_LTvbq z%qhYr?_!l*6Ap>Ysl|TTOL7_c86v$9>BY!yi1bFJHzS1$`wZ%h7d03>qU{U&l;An1 z>od^$4@;qEFLeGf>>m0@c<1wJFYo=&`|_;se}u(_eU>9B*Lmxu7rLE6alHSzf7tik z&H!v~bhbg0F6{Fd^7$qxj@M>zryHTt7<_S!;8_RV81mPW7!yF2`pYho@U|Cf{8h1}IuL)HvW&*=4we|0!j1KUs<`S<|Gi*54?tCDMS=T!}98=jt?`}+9OvZ{fA zwg&ly)wwUUwe3)qY|BN;^5N;3y+3*B*|w^IFQ7E?@ePi}ZSA`Nd+uU`|B*=3moEPP znfUixc;oRd{-R9$JB_S((8XVzi9h#uBfoR;;~x1i{?&IiU*h5~%f$c4SJf#Oe|aYU z$Iihw!7TrSGx0z7+L)w^zakU=UOU`6*2RBlCjL9FxC`_f`B!G*zxlXp{_5f%mWh90 z{*$9D{=Po+Y;9^maz)iwXsDK)U+uH!f2szqMOkDOjVO2#T2P(4tSb3pTWcSv153^5 z6?-DqS~YMUC`PHlQff_g?h1U~x@}$ol!B$E=bm!*c|WTfxB_%T*Q8KeH8dhQudQu+ zge)|(_uo6_bXE;~5yS&je<`)RIu~EcUeVUxANrwWof|#qRhO)CDV2q+)B0EUa>*Lj z6Iqi#`15K@*2rj2bS?U3<%cd^Wj)dL!P0jIx^z|cMAxhRj%au3D(s1_8@F5ag-cgy zPjqeQfA$EMu0wmGt54_phg`byd!lReUp9X0(pB6OU2A&%=3+UJ1jm^g9mVN(>Q;Z`Ev!a|@vGEmT7G*`*r(_y# zWFA?T73I)nudqW_WX1XZA71|enZvT;+}w2E0A%K8#d+Z`X3rN+LXu_L(-@Dsfls8o z*Pt(|3eYwX{~|yu*4(yM2mwP0+xPd~`yE$R061v)7g40r6q&aTlC+iow)c#Os|uhS zXnq=@{-#J<1WDS~LucLbZB+p@1WK?NMJh~@_MMTWE#7|CIT!kADa@qh_9cb4_-QH6 zq-D|<8@v6qrVU1Ps`v;S`Pc{$Y=eufBGOd6n717(J9J=fT~BYnD7WFL_da@{GbP?|*MI>r1kpr*3%3S82PR zxAb7F=V76*5Bx985k7D;o@55SH0zj|P}ck4(v8PnoCzm()a_4sc6xoEd+;^aj#$ydYZ3!GV#T{HU3U&v1M3J9 zVLik4XIKBRPg}CQBbL{1Rny@Bwd6gC(ge|m-8_H;|fhO zuguwjbAc3>g)tYxqEnxWEyw#;4a=DxV|i>2!Ll{vcg*(gs^Q9Qyj8=mzCIovJXZ~u zP6(|UhUu-@I=#=nVb?!hdOH?iu9y>>R*z*FOZqP``3R6c0Tl2(g<0^-LV995m-HeZ z>BC&ocMOv*0vCTeGUr^;hizoFvlJQa^2BXkAqA53n3tZx|1GAyuyi)PtQiHl+;OXf zv&*JQSdB$9|1v0^A4CR!@l?FtEQ#L5XztQ@vc5i^s;!fSoSXGf7S;k6mZB&UF*(2S zX;~Q9#0A1g%L9nu%v}t%M`3lQVyA4~J*qyN0EAJKR>vS(J zD$$JwO%yd$)I^{ry2y=PG%8?Js8JMAP$Ps&ThN3BWxK4Vty+AlX?-Xvt<*7Mri z+ccO;81*d)qsI1?!4#oh#S~%G-}5Pn0o^M{va=06U(PebPbZGLPPXkMROlc*ma!c} zWn?Y!94>~Epfy)~Cjr&j2&h69*Ob;`Q7d0f z0$c-N|9rQE8ZK2(B!Dr7A2`GB(=?7zg|p-;aF<_HpThGqi)q1h|JA=j&@1LAlYkvG zhMK@1HHq8Qo#PVc7y->3_LykJ3JGm3%`8VTMpbLELWH3rJx5^mF%;l_6=yqiJ|1Pq zS*!x#0tvX5Xb8zZ28(Vq3=)mF+Wm0R{h}t7$Kc|gf1G(VUt_Qb@wBdvHU+)pB|xN8 zW2}oL9_bIh=MUDirz8@K?vZF^QmY@)6NWf$uib-ii93O9W z#FpAW;H!JIKdguLh*Zf+a~fB4uE`^ZlU$y#4Tf)7-}&L82Rq?bMKTg@U5qtO47X}+ zOBGAhE{}tUf{un;*-MQ5u!k6KEg`H}lLf$V>rUEvaJM~+gg13H0)UlLr`1PUB&-38 zgjLESFKUftQRM?s7AXk{ShW3$^|FCfi81xHi%d&P4vW|ZS;YEcS)@otSTu_g5@GmK z#S$!&$H5{&C#i|5s4UVA_+(WHTWHTBsLI(;74%Btc=VQqP!7Its7DDJ6bZ&+xzkph zBkp`RUeZ~i8>TvO=v#4)5LsLt`c=^pdDF19t2p%CEEkDG|B7dh74JOA7aQWO=e9~2 zGoo>5e3gqWOB~uOh{VRB3+rlfK}uovdVg@e6?_*v-Vv0R&y2?hrC-vDf(e4sFO#$X zqoDL*g7Hf8Lg7l_X@b&YpXn+neQJH8p!DA9iG$L&d?U*zA_%6kGlM_4U%ZmeN!b9P zj@NQzwzytadyxx-&ziupgpE7Ir%^~ozjo1Q4PwW{r@b?; zbgIz^c8B=1cjg%=aUeqA|2Z;kZ^l{`h&v^>*8cDd885adXi%_T0EdmUH8h>d(DZY9 z)zx%62a#{&%yfOc^v)*b|Lf57A@~$P6BBGmXnMXh7^{yO@AWUB|A%;Q>aH=7_5R=O z4*ywO!xKY#_s!IU8=<7%dfz%Rc3MV-{|o*zVM)`ek% zW-c%%Gu2gglla4I)xP>oZ7T++F&)Yejh6oj%FEhk&cyn`9Ob*;|?sm>Q{EU2hkeClt` z@dZDwsqfzTl=eD@F6=9DSg{Cx=9+{#zq_1=)1m*MmCD?VSnoj z)=Hya`7-uPqkBl1Q2LcGxK|q8<23q@f}|R>gyTU-c4+@o{hfa%h<|B8-2kqeZl?G=nh}T*V1T{FJrGZy2BUTBaJpWjeby&RE?Gp zLS&;JFxwZbE~u!zV)cYPvW1PVHjRQwXmmXrt!AT}eHjgGbgM7;g)jKEH2R4zqgfjL z*q5Tk=w3G3!bUgwGOF1q1bd4w_^C7sM7B$#xcRX*`ZfiU(!0rNbQ?Mp zEg@vcMhAo0zMx%DQT^`E2R}`=u+bf+Q7{RO?qQ=g8*T7qw6ak=@|G{ST^ils%h)W9 zt|w(e=>`fU8(r@-x)mLYmJo_&qu!{VKGhdp)!llU$wo6HdYW`a;E?Xu(=0Zc717gI z7Yxhnemxz^Mu$f9w03U)@!hYdIczj1qNin(UY*hXdODnq4v*;R4}br#y!-VumyPB| z^z<)%K6tSE^>ic~9U0NnvON#NOmwrJj?Sf_9YEub?|SqLO-dP7AKW&qp=M)WT-%Xc zv;L6e(V5Wh&B>EoNn3-X^CK!~@tNJu(iz)CBltDSj!$&ll1DJI1_<@u-_e z>CyRd`1MbK-%!ObmH1f^L$U7R8h5uFiqWE=G5zaFfM1T{*Pr*le4 zG?qcNlfOX;@Efl9d5B+8#QfYmwCUt-H$S7X9;+RGX$kPlRs04Kzi|;u_0;^b^Sj+r zjV_AAZ*T(qMk;=3#IHDF)INA}*~D%)YNN-+;pa_&U%uiunD|YM*uVD|?)^pg+rQ#C z{4x{Z=TrQ=#IGb`|1Q4hr;EGa{!NU-FDn6lmg1L5{H}@EzZ=<hjgZuOtq?p$YKw zD}GtTZ)(K;)t`{Oto!ZXHF5amB*3po@f%9~rbp~w-R+mX*8TQxY8-yU6W}*a@yj88 zr4jr0^?{A+y5IgykHarF0e;1b-*Dnr7O{V0e*2eqyWjqm#^E0u-kziUjVtJ2XY2r<<^t%DxO*hAzgfVegPE3I19L2JjSS}VU zO@Qi@^{Xqo-9`#y;;<~~h@~i*B07mbd6a} zoqp!g-S3Z9qscgQ#e{Z5ms+Ukk{+U44;V47{#z}0_}y(>$)PxO#gKMHm%6Cvk{+V_ zx}a;`SH7@v+3@Z+vE)!3x?)Z{qDzfbbV(19ZDIAJJSBz>$bg7hz zF6kk!qDy*+?p8t97~KmuJak3(8(nfJ4&5qT zN?>%Wd6`D{VbVi%KLCu_-LG#xWoq}^U2-T6T`{m7xl5%~bgOVgbQ=L9bN}7*?k?+o zyGstmp(_TqBf3;dMVIst-G2(Y#_rxc`SID^Z+9EfWE{F;U^}8qv!&>g9-?alM(pmO z+4b|g-|mt_ap;PH?T9XwQqd(nM0cm4YwT`nuewFuZ+FR|ICRCpc0`v-spyg(qT3?q z8oRr%?bXWecW}v}ICRCpc0`v-spyg(qWdpF*Vx_W=|zuqzuhH=;?NZX+YwzVrJ_rE zh;FN(YwYf~kM4c7`|U0{6o+m#E+w$Ljl3*&m-GByZ3S$Owa6I}7$<5wPT_NOCXwmC&QtOe`ma;LzAUibEeja%(w35K5eP#LZ zLF+WN4p%)Ear4#3ubT#2uWi?$tjIR4O_gm}tf-VnY4)7^wO%hzk-8%NL8q2y<7>gf zOjC<2;q=($xAIxpEP$;!*fn7BPdrsC%C!_8LMgO7TkAnlk3A~Biu^=X+$vQtojY1X zBdiBWnlx<^stvVUnuVFZ=&{-_^`+R@b*sbtB85*RZ>8g8Yq}mwiE#||a5YWBahHYv#g8eN!Zwcakp8Z*Lj8x}s~FfP zbCu`TzV_?IMxS!92KkrOx&|3pF4Aw5<6g6RL)>y|?Qtu09rAcwPO^WoWnY{9@h>)E zGe-IYXB-FdZs^ zQf6t(K~-QpzJncnWq&6=+e6hmVVtwSb64y4vcFRnzK7Efi`;mJHv*mR?_5lmfV*%O z+`%eWrLJH3HMe?N!7OXlh`(%@&n=(Wrj}~0I;r)ZAGqxkXC7kM; z$6u`_TSO;4{K`hbg4;w-%zof%JP$0mTlDgz%YKV9V8LypTX&v%D9(TdH;-O-{At(W z3|Mdj>7D04e;3Yx1^1C2bV~p2I0F{kP?~PNFa~G2O|u{s9oBa*o#4m^S7PT9F&^a0 zK{$QxV`hJ5*;TsHQ#ux1jQym4ir%VlzG>?hpU`~(*q6sHUSM#IQ@m_&WPZmrw*KkzT?6Q>vR|P`DfR#Q%tW!#fe|*S;0S ze$T58_OG24hdml(eirwO4;%TLrCUnLUfoif!z286ezG3E9B1ZY2l~eu`v0f z-f_ceuJp-|-f;R`^L@M7oo_h(h;2F^6Z7`{s12w0@hRDGx>$~?^YtviGsD%owdFj6 z1aX6DH*7dP?p75C@vCNA!9X?JGkZ>d0aJ%Pr&j^kJD!t0M4u}7Kma!neZ#Fgx{=Oa zCs$EHjC~Aa7d9=N?l+x(5%iAxO}DZWu*cK|N#c*ksWHbM=NQ}Vs8z%8MZ@VpHT;mA z@}qg!iQ33oWGCtylt`-F{FOMee>VeVvB|MY`WoKmE4a#ys5UNe%Vvwf`I)UOfIV`s zpJpGqq%*Uxn5m5|ByQM_3W;XMFZ*0x$JP~A9JNQ$U8(qB0+BGgQfZdZmH7m~hF*!} z_+#UdU8&3DD##?%Etkt#AZAx;`c=^_mrJC0f-RR*kJVkNtj8g-{jOAL3C0?J8xn0x zJ8VI*9z1Ow%9FjUY)!|v;K!iooD0UDD0m`BVQW?XdeOE?Vq$DI@(dZ1_mD)nuVR@7x_(T+k zFnHn+F1h36OXLajBm%~=NRf=N=p&H{l_|1FYelX-sSA172z6JgvS^WE(fmX#!v5V+ zs#8M&AUXU+$1mtpvG}D0^h<|dVDu@=w3R~$oyi4+&iwOw_TE!6F5zl9s>0P*aTR_s z&&oAt;_VRIQg`6qWIpXo#kR(5SG^r+6H8H^8~!T3=acR`OIVuoM=@eQM$aX5WOIJ$XNZqs?QNB#sZ56n<}M#bk>(CS8a*QT-|ZeD$)0Y}dM z)jqu1VgIUqUF`nVp9(4_wW5>runpUNe<64c9>GDrW>;<`dt$N`O|gh#R=Z+3F~wpw zv7+1&?aF1fD;L}B4&sb0vD&P+T{+1yn2*elsPC0_<)S-mu?x1YH(6%NEoxUzQ#^8$ zEz2Q~pD8!zc72g2~EmI-2G3J7Cf%MsQuCT@XA8^&zuYYJ-Q$FMmd)K zxc)_BR^$fqzlIdKlbDLk?qnS8t?IkCZ+j9*LwY- z8?*eOSvmgDTyXp|3mID7^%msTT5;^(vbJzPZebE~RW}wUZSe;`wrc7R_6I+)u#X7K zHs!*GY=1_RHEcD?Sl>3VSVjx)aV*D!_%!@mgON7IpRv)Z*>EiOU-&aNS;Mwqkvp#< zIYG^a6Z{!9IOo-*{e{7g3X^KlsD(YVicG4t;*D#+JdXQx3zOFQFAjrJy&uA26Y=7`uKR*>q z&V1$1*ssX!5jH4iz5;HF%$^uz{!y4zgGPZ&nj({CtysA|Siyb0g-L6W3#BmW9pNdv zX+s1VG8xE#@8Ed7AoGqtW2+$3=nsDB4*~@uv%{b9wIb8x&j1?~nH~P%9z_Nn9qFbI z3X`hQD3I|gGG1#%?hDq}-0zDXL~5kMq|L%pBC|Sz44DjMz;_^1Ey!&4XEX>hTm8W= z{K(KQ$b90@XjWuC_Gf?%ip(eeV3Q()j*cL+9RhZS zrr28X$@6XRa$j-}+L;nXro>utbMV(+aldj8+L@_}%v5W|-|stY1otubpq(jIWJ;|S zryo)`g!`L&(9X&)@S$ZjtUeD>Zs{LQ*`PlTs8RB~?k0 zkk^V&hg_J;-PAp2;}(?D%cr{)-!W;OQ&SwF#-r3!vXT`qyt^pGz1KZwBxQ?BLTbF7 zQ&SS5CQYf4Nb!nmA6j}VcWU>Xm6c5<38~5IoSLZ-YP?EKmGsT5UMF3~ece50re%*u zLTYk4r=~PQ4L)&16RJ;rkZ?8;K^SH_BS=iNL(o(u$QFWz{H5RT zxfQ<$2O;5XB7!i|bViVvXosLuC8$LRx_Mr5Blqt2;5a0lO+b+BQ0knfYU1zEG+SwE z6`D$KI=CPA`}g2bB+N}j(?Y?zvwo_Np-IUXb0@E}R(yBr+vhPyKo1T`!rw$>Rdz{M zLo8Xs0U=9X+^sl%{qYl-SD*)nC4F7kaX$3$;x5VB8cUXNK**AJLMwKixaM*uBIv=v zN%)(nx2n1%t1*@=;ee2p#+P#|K6>unhncyc2Ztzq=a!(imUKy$9ZOafPlPP5koDo@ zXF^PM(1Qb&@HbI!E$fo3mRPcc1434okhSF5TSqcCLJtmC!rw$>E$@=7)>yKH145Q8 z3s~{_-*$~*GKC%-w1mHj$g1v=EU`3Z(0W)nAY{pl?-e6XpEro97kY5q68Q0j$6XtL}a;$VW$(w ze7UP}zlVhbLe^{{Yx$;Ee!@H#JveR&e-n|F+9g?5ELp+! z=IiLeaZC7{h^(A0$tsN{OE@58EfKOd{-)ql=Jn{oaZC7{h^*W$$(kKYmT*AGS|()8 z{B-iI%>U7Y$*PJaOE@58Z56V1=3R3n^O*GDxF!5eMAp%NTf*N& zWXsC0VVpWC;g^ ztQ;ZhZwKvtHD&SH1A1`W68U@(+|Ams2&`*gujW% zs_&94u`~`@!T}*GU&xw2H|4l*Zrz^MgX5O)HxXG4U6Li1#vw~MAY@rW*6%ax@_+sG zhM)A{xF!5eMAp_W$r4NBkR==tvWkSPm9MTpwBM_L9NB~8mhd+bSvoRx_G&>ajYF1j zK*%ZSwBgX5O)HxXGbV%YinExGHw-=cL9vStfehZm1If6)=X={+}YnH}WFcj?FG zaDOs(kq7ZV89T{?_@9j3kH|R)t zSq{S7^MW&>gTMHE1Vaxn`}vC^d6+_FnN$795g)i96;+4E=_q;#hfQgop8Va%R7Q2ygocEr4-aKB<+c0A;_Rbb&w_4dh zFzHa)0Ty;dCt+vUwnA(U3zlVC+*=!b&&tLWYJ-)%+6wHGQngyj3ckg>SRfXe;kc_A zk7NOZxye?r#g986AK6rpcE`&^fxNaPZ%3@zy(IgW%d661o-H$%M)c4wa~wIm(DZ7gSzDDlu_o;9uwKkoAG$VHTRAIm`1psya zqVgX0Wj2^(T4(lw(0_(x56e&jX)KSXK?tX}9Oh&~V%AY`ELR1o{yj~rm&?oFUPC1d4Ow7ddOyMKX-^=F%DP*scw}^?E za3c6Pl?XmKNwYJel`ts`szU}D=IrtbCMQXnH76TxF%CSuQW8^ekQAH-7EaHciUMHY zbm&@W8dx_ybM~<6DWi@~nmJ?2s7jYR5E4>g3RdShDSFUk(G>K{8J7Dc+Zr)Fvslv7 zoV4)3DbW9g{>)zXQVUeMU-C;CNi?;U2w*$$OHF*DbS9G04xTn82-8t-wMmOJ?ou4l zeaqQs#Tv>*t=))>weAD$Z33o@awW}7r&?94t|T25JA^fLH3y(@QwdT_O&OJzG!qFz zP-KUTMe1spC$!1Ov{Uk6HBNCeO>d_1<_Wlo%vGO3fB{}0NEw0~uBIzvjtAnH7Zc0# zb;)t^jDnCM@1Qs#H1gcK`M;Lu7Y>Q5%40t(wftCle&;Vno}WIb6M4Q_gyYA_^L;0D zl;`94{C^|Q7ey;+FVAn2eVURNqoStXO(@TQmV#Z$bD;I;nG-2pNIT$&>{JNgN$Zk( zizH7$YRGlI%wE0iuSTQQ|3-3A_l(C9OYZTLMRJc30I|AMg-m44NY|zq>001OSFC(# zzM}p~^bh=@Ycf-Z{abSG(lvn1fbknv?a$jf*B8o0PO!3J7V8-01(=k^&L{IsBLqZOW$S%Q3ZA0CMz6zS`# zpc^25?GrW znv?=Y6zTO|?mJ%7>mI#64uxIrJ08Ww(B&zGNZun4FCxK#>ePbJ)Rd%VJYURaORUK@ zvdMYefooaGv|WR&q-HBTovnUk1$SGITaSm14jyBLO*TrW_^DbvpT(VPgLo}G_~|+( zSak(jlV*9_x3M!N(nj66{mJXLYI5POeUN=AS~$5`(OD5K>I;763tp2seA8!vT`9q7 z$TqKnhOcGDRzLGn2mRo-&=vhF_Ot$3MWM@5&Ee>}0_AB+trRew@AUkPlrxxTNk=MHF!M| z#l>-TOoGRTn#Y2t`6s_QHgA(VFc4QR2&ZgdR|c;K9-g*0g4cUOmwD(3ELaVZ3LODK zbT61E(xr_=SUZIm6}QN>pGiUBc9bvgQ^^BpuNu;YX5KI=k!BA6B(Y{L6`vpd1$_*s zU+`Hk@v5kjRA>V|_Ghoxw;|~ljffw13-%3j30k@=4B|jAhRwTGO2WKo*c^O6*n(7! zx1AeY+xCWd^*33QcS#5T98&pI=qUf>>cT*)XQqX0nDat6y0ZU)%tv{9XAUb25B*IL zxiH#l+Uilo?W_FIeA+PP8KeF3L*!_?;FZwS)!OGz5|KDp!AJqYUd?ye!}Q9D3XT&H z8rHVXh?L+xbFdLO$dV5;yBuHA+TgVQZPiW8nQ8BysP?zIpsk*&STfe&efZdE)r0&= zYeSQyqwo3w>F(O%_6Rc_UJQ;0`!<~l zVBaTK?XwjuR4`1zi3*NWFj&E%3JzA#SAk2x-pfUxzEaSvV7T0CH~#uEtIYiA+tv*_ ze`+KLkgp(pzypdf0mMyMebjkWobH<;2fv z1M*@Q<~6y)m(mJB6-z<~`Z4TT)~3&=U;=q0PqGRFd(q2>qKF*yXW7ZyaAz=kJzF|X z+->wcv9j`3yXT$BQbEKT6k}#l%SOy%zm+ZXaONXD$YP$fPm`ShgdbA~e)Ito^iuvo z$1@c;{&?(Ec=>)fI+hGi_4gehEl2cQ>XwoPC1?JOH`EO{H9B8>k|O!~HU$ zyn%G*Sfu+WOx^eDY5_vf)_=x{4^ zA*OUUVLFSBJu=5MbG-22o8ZYvJx5w?p!Y-a z3l2BPv*0$sHb$7TsF}kx_wV@sR#v{8L|Mi_woQ z-x&Q6;(?&BUPhqTA2A~kx=LioV_(0nzRe$cObbDI7NcevggE|S5SpHu1#!XXRD~NB zTwO}auTF7&-=G~-h6g^=q!eCAf0FVVcg15zdtww=sj;@0W&;>d2hJqUBt41y}jIb-zg{* z29afQ6DI)^aoJUdJXtT{%PG4txX36t(H}a*A36sTh_BzcU)t@@TRXD=xShBzIf;?B z(61mWGaxEAWEF})elznZ2;t3~od;^r@iIAh4824~*4h_NkY2I`V9cuuCN(c&{10I@ z#{VcV_><}f&BgrVv3Cl2&a4u_5Af8|u^&e$F0m52q&P|gbE%U!^xE^jrlgIdLi?eS z5XoFJpS@!RQ4yLgF_u7#gP%#7spt3uvP?4|&NPF1Ofxz56P(C6RIMK`RIOGpO@OfP zI4g9dKXksZ&!1O6vp;x~ye`?*bQ%Tm94QDcYy(D-9~%K4a9pU2vG!Z%b0fcoXzoCM zSV0jcw>?XkQaW^G+h!(S^(-Xjfi-DblQt2Rcdc*dS5hC`B_uj6Lx>jQn#$>-ha5{o zf=7Se7c(yh=KCQ5C;38`U|!IS%VYCCpE>gU@Zhig!Cfqp4S(Z%aF)s9t;bD)gIHjS zFL;zMcu5NSf1LKef&v9+2~g}h((70J0y{0Ze-}Qa%4Z+{J#*2{p$5)D-@?;@B*=^d zvyO0j-3R6?9zT4?az<0LtRO$*Ai=W~5i8KkXHmy#N@VqRoeM{^fglOJn7V&XY!KbGz`_#w!PxQBzEA{tIl zf7Tn{JdlP|8?&9yqcr>Y*ygQB^R!+$?V9h<{!nedVrMy;X3U5q4SzhQt>=$+f1yAG zxxtGFyYBik*<~zlDUZ^K(Co+q5+8q99{m{~q|4Ydr>}nbC={GdbD-7-6_%LJ@zGmF=h)xFw;)>J(?jZt;RJnd#<7Yu z<-m>Kn3xD<#$gP9!t*m5q1DEvN!f*dV!Vx^Bz`1g31DI92)+R*b0c9bzKvH`(;FAR zww`9lsRflG?E%vmrIs4WZ*XeTeZ#2rNsxb1AdVGG!J#kdh)UXW+t-h&zV zdPXbK;#ac%&7UznB^(5dcKm}N#;$_klipOBwjg*`3e-3C-H4>t;GIA3efQmekvRj$ z{J_3`?gbaiX$w1Js<{IKbZunjyeFzn+n)3Jev%Q=DkIEt8Q&nU!)PY6;1e6R5B~4eyWWNao91G`3OV zbRU~3Qq0|f3VDKGGT3h!B+=27iEMnH~7mwom--<>`iWNS&1clBIPw$QI zxgf4v^I58O0Ma?{?<*d>cs*;$_+n<+N4Nv*&cT!_1uq+e+jZV%C%ar@!v(2U#SdhY zJJ28(QkE42;h$he<#b_SFS>3iZnEpb>3863jKUYxnaYYi_HnOt&Oy<_>-co&72#dz zIS*MFdOA%wxzz9rQR$VL3&{vb1#H~&%GA7X+<{~H^t4@gQwl=RA(+CH3daohWoo8I zaeijG+#&aO9o!}Ni{av$4w!d4l`Z^`&X@!H5Em7Z3lD~UkncZ~Lh^9+HPnRMnPtP_-}T9$FZ}cko(Wvj!IiR;!wQIj7kuSdyb)R+yi3 zM99h=I7Ayn_V0l)SHB^R;lZ{32y!gD)wzmqVzi<6VK0BMYCcb~4pHKm!Tu{7w?}A{XF8I ziZzrtOFt;P`^Ck}A!Phg%rq;sxC+NsXvt!NyymhatzZfr=NCR63K60^{%9uUhn_X> zh|7K=ZFRLgn4a>|d0Nm4rMt|r$KKr!MvD&_pSt-CD^#3?F*SMSG}dE>y=*q(9>9(l ziTRQG&yLg&!vmj)#b9sq`Nw1bj&(aUmAS3Ja-!lYdsrKEHScm7U5+58FO=^s2s9=Y z;N6LP{`ZjLVY~7dK1~*crg}H+!lxcXMc%+C$vCUo)r((#3{3^5yOR0cM|9k*g9YKC zX@(7W^E2yM3=$7MNWz!cs0iO?3@&LQKS*s$3>)MUY2(OuX(X?0j8v zBDF>S#P|1iji$fxRasyBd=2uI&k2Eh{vQORfM65~2_~VFUC+W4GMxooL>Ho; z#J-*U@!xTqzMK_0`5)#u&2BzbKLg~YB|bbsqk%QKa7|3hW$AG0ln>M4J%*lECNRFk za>3;0k1#l04K5lAqx6M+nTyS2=Wr6q^S(I1@mi4Zw7fCSle(h7u%p^pJTMBhe-ELdI#Aw!Jq^ zeX%$2=`j+F2V=$LM=oO|sZC+6Kn2B9qC){jfT>w{?IUv{c+i%Wx7!_FO9Guy?E>M^ zOGoe$XHE$jr2?yrswAVTJ7W}>uz!~i(*46gcOW?xwIW_lvs# zO1#aZ@bouaLLcTSlQ_85>`IQ(Aj6r3m!|EASTsVcF()LbXznFwHz4h@)>%^P;EnW; z#mr6Og;)%Hg26tNJxwuq7m7a`l%X8M@_Pr)`Ku_W1{Zrt@f2u=RJT)JAjS~1nHTC- z59H%@H3FdZ4LsHP&^_{cjV1@iXHjOO+~IcvkH(j|9iB79OQo9CPNtSlipW&bby1nx zbx}u|%2@MbWQtNJvurL%81}Z>c3@UQx3$=!+bUnG<0u;)hTZc&;XGZ3;^k>LWfAjxxm`%z4E}FEuyj!H{)HN1|og=8c$;zdqRqD zKtIYG@E_Ta6Lzy7HONBylIwJ)d;S(O0<6jBcQ=xDIE37J1{=!T;GTD~wD_hBlPYiW z?hiv8ofv(Q_iw4>PUQ)rGV?#=1*|8os}(e}H*&58OCR%|6vN9_E9P}BM!-(x@V;G9 z{2kQGytJz9G5*?FSYw{_MJ5)CLj6*%h{)+>;u)zsH2T7ha{9}fA0ww2{f!DWfmn!Rs)2R@7B$I))|sND2Fy5l+FC>Nh>ccZ`&`1rsPo7%_7&wZOqT zoPyVIo7-vMDVIm63QUe_-^~|vr0UV?A48P|SEcPO74+BRW$!s&sAy9`e}pIU3MPc* zC@}Bv$uJcWUn}qHnU{#+{sG2@%2yPpj_{l*bFen%H8WlZQ!A@LW!XzSQcYaiUK63b zIWWbm&@_tg0jIqBCVKN{ZExV~WLzfr5iB&_E9Z6-%5L5j#M)ubKeE5!n4_iyAH%}~ zdClzy$?e4vmgZa?W$ECt9a&oR+K*u=-o@tab%%2?ZzW&L@?!M4Y=qIbnLhL)QIs$-_COi|c2LYNl#3|WG+G7m zWB_n&z^tQVWlKd2uOh)JSJUZQ<`7ecpQeQTVTciV)G`BDCY*lr)(oJ8n9b76YTh>= zV6EqIIDJ@@lb?&{p(^+O&mx@EwGHZVB)|G&nCa-*Az%vW8iMINH1Qw;qk>=ITetT3 zTt~B>$0jAKq&W{D0s7{6kqX(%W|583?&p)%DqUnynDvt=?ne`MnIDAH-*0egXx$r!{Wd&$ z0I+w5FX1Q+#j9xkL0AcI5o0{;n){BIc|lAFUn=Kqo>{_n>{r~pEbo~e#+F5_2UeUx zoKa&Lc#5$$0Z&<20>012V}(}?WpGv__GA&{j{J<5WB#N%LN}ow~RB}d*&5F&ygERLY6__t#sD$cDG;9;75 z+zOzaFv#Fc;t{KmFjX|p(DrQ{nV%`2n;|NQw=Dsm;gGr)1VwaIw(~a5o$E|`ih$yQ zmqbu3naD#$(I1Oqk)l}s@_&!w#Dpl8S;nPW(n|FVE_4kbfH)5oflx`NCzHx)YH!gu z?Q*`;`U}Pi*sLl6&ClcTU}|b$-_dj0(RS*^5!&X9f1|XOo)^P@L)&A^|9jfX|J{N8 z*mD=y-OJjvo3Z)8XGuED=DP!5unYK_Os3K52w+*-*G7xK!7xH~%lVIzpID0{lE3qt zIGmuxSkDvHV(|)QUTQS>00!k9{ncB!F}kbX09*x#yc5z#$a?@o-dI`2kp4CaW+Cd` zJZx9<8vK78qw68)4tviqnn&rikO@-)P5Z3=B-OE1T?_*5`3n#x>s-+}5Pbiycyb{O z_d_m##B2pc0I;tIadbGEwsq5|Ft2^-Khk(%;)gq~ra#CMqJ0Bc>=+i~D%6VR44N!c zjy`77&03C&37vAuD0eM=eN&G9Lem&6$FGn(%*AfYWfyKIGvLGUMM;2?WQkqj_dK`@zOWVc#MFbj~0`5_ij9b_@5aOMc1~xn*azYE`f*Bj@)=By74-qN64YT__aDMd4dyy+r*+5k0 z50zI!!Ld{uSnLD3R$ZMGu3JlB_MgI%Bd%xhkBmi7Nr_tNn2cl0J+WJrf4myKK?o62L!~cM?V};9 z*iAGw2?nURmIFMnb}x7n?j46^+M?HSOvf|IwYxXq5}yKB@e~Ad2GpSd@CPZ|^tpl? z06O&F&eu)r?A?(8Ug6V+1jlpTF2f51tyG#4)xleHyb3HBG{oe%tOQ? znguH8V#{MC;r$|__>NcOo-a$s!s(yhY&OI_U{+b-1q7vzGi|P6Wu=7@|IcDv1kz?4 z2>1MFh$mtS@HEVdLOCj-K6~s3T0kyt;0gW-^)4LltV=qd5q7~AJ)wj1DG_fKVFWzF z7`}vIKw&9`*JBs5LbF)WIOWajF%nn2O@5=IaQb`VH|S{f{%K~kvU8RZ6TAj*zSD8$ z883lg9R+{^pS|aPamiEcM6-M#xDD5z*6T~7*9-CO?LbT06udKa&o3wa=G90(T5ml+ zGHi~GK_u_&m|-)Y+(@%;{JYA67cLYfBq|K?j5*X{j9vI;NBTeEJA_&3MggB$>nPyE zG^>VJa1+#9fSYhAQFeHUdCpx34B%3n{9{jRf#=q~_8-<8`B%xBpIL>HDjrGVZ&a$u zgPVQVE0kT4oTqr?SiKr*zJ)c>5Zin!Zxc-){9Q`euVcz#6TY#tW)5+u1S-HQ7&VN; z@Y~(<&gBE)Q3IY!tY7!j&ElXy6RK;?6Wu^iVVsszi-U-NpKhs$!h${ZdXljTD3vYa|*mONt?^ zewrj|AQ$Jp&DtSImP6UOO>+38p%$~XOzzwh+w)B&NJsR ztQAxt)FJ5J)s6-{h|wRCuDH3Hj^@~r+|&oaZp@PTr>p5UjgDO?)flr@h8L-kx#!O# z)y-+{Na}mAAg$(&WOV${`TfLoW;`llXt*Rp^^XoVor5E+ zx^iCT&%DjeU$}%*EO~06)5*alRdNk^G7uDK!vAN2eVWdqolb5#7QoKNK56%XO?`13 zYp=}u?=^@K|BP=zaGdy`OqjOD=%%Pa?Z$p=5>6^$RaTzFsa3!eR z@b}7Y#Jj-t_nwEy^^RE=lNni7a1fs4IEz2fvXh_UZFRs-e!?85+0PH8q|0!OhaxfN zgRRh^QeWuw4N`U--a(+E61XH-y1_w((UqwlEOv0eh`ES`P{3aA%o8_%dcfvju~#SA zYoUyY5p*P&m0)V|Hdx0Q9*B;Y&y}^pFSFhhA^b|=jBbcw(7Ouf{PB2+9)fdcTA|B9 zI_be2Y$BOp-vXWDO8~7wNHx{K_<=KGEqyS{CJK>Xwt|&XGx=kbuS%dc1f?yI5n z?+~tH(l-HIL1~3=P7Q6vi21N9G|Pppj-TTl;1375?-+%f*uPP;`+(3hTsxVu8|#~_ z&?8*I6)V?8bzDXkIu}^$64}`-F*UT?%0q9! z0=fgIpe&X(EeW0GAOL5Uo5W>v3Hm=525|Q|5l`S;f7Ae;dZsae9HF3B^aLh92?6OO zezB0%Mhx;r5l`UkFYrt(3*k;lS;%Axo^k{*48wqaNPhquv1AftzcgJn(aM~ifbPs053L%qmg)d z99m=aS7s&oIxLT1zc}-+g<>DfRN_v~59KwIhX*wN`VQ$EA_W89Kx3k_D6Q*V;pKZs zdIJwz2l$SOZ@BIP-3+xtVjup&Ks}Bs&P5YXa??GeIUm}X=2*FqnyqZSBtUGf7}w40 zORu-Lg;=p2V++ek5PxxI(IG}7xXXkP<3^|2|N1A8kR=2fmTumTK`4_}(uAqoIKusq zVMe%DaVBlV^LyoMvG%e=(SWGSK{$QaGrDnsBTSKw-!j&SA5r=o86ndUiJx+%Fl=Z( zty>o3PHr-+M28r)XHpAXbf=P3``2eRqY|@Z9pRhaU!CAMBZGJyOqUoR$4#jtoWA%M zN1&_4(H|@Lsax?f9%NyUJ?1g(p|s{zQF=qSc(APZLxc;;ux=FVCFn(Fw^*Z=BdQK3 z_t{s$i3Uvaav5TPR+R!OKjvr7c8;N8mV_Wk#6y`ya*X!vN5NrE4fAn@qdW!;aweR9 zL)Iy0H@1E5*Eu4 zkJDk-{3C`(BF4Iu>_*xb;it#G=Tow1er7QV;IIwWKP1QtXXPne zC^7Vbnuju{nv*p9ux~+ZXDq5%Lk>n9!QVhIxk*@=8SM}A{$rJqzx)Kl7v%4cN`{?M zPkhB*R>@TR>YVR>aP%Rj*K zXvvbQaG-K;uVye!*3O`Jq2IDz1&u-C!Bamm!wv`j@z_sjo6k5no|mvq?LoGAylFFn zzH~YEZN22Uu_@6KTs0Hwec+IGYT_kdO4xca)Fhw#sW$bEsLAoWsm7`%Z)ZAc;{B6y z4r-#EgTI*dK!996_lv&Fe*Wwl_lxVXPY&aEV6SK9d5FLk2JI=jCV$U%yasT;vqP9TX1Kr`=x4GEsa}|)%a7Wp0yYZUUV9amxqT|h&;ekLVsdzVTHI|6YMU+ zEW;t#>P^A@I3%m?vz+!`BBsb@ZjyyjWs+CMsT6dwc5ldvWl zn|M)sK2pM5glB7J9_dFO32gh!nv&N%qYvUwX|7uSO=@nnrwo>n>q-G}b)IEV=~&|t ztWhzU6IbD(TH(P8`YLb zEwdoAxn?3Rr{Q1L{hBo_XX81!LX zoBhZOk-PV_yZi5d)U~ba{j7J43tI;wu{+`%kM9S|jeUX6I2tQ&fv%>#Nrfeb02wC? z>!0_p13K;0TM7?lcq>JoNZ}Je(P;Wgc|szM3`z&qcq4{yqZ6FEmx!4kg&N($@xlGl z5%^DWE2DIH;p$$NB691T^JHF>_Lthd1ZdnVqV+9O?jabMuVirOy#}Vfe zKFb3g)j@8tygrZXAGt?f$fpl6ZT)8nJ#0Nr`=W8qcJJK_S0rj~fuLdao4~OhoLxemG9s5lAI+R%Ls;IXnKLn%ZG={yB>Td5m_iJG z$TKX|!UYh6F*j$p1agmCT-}zphnf6_zU62Pc8tvQV1l>?+r$COah7b zJkpWl-}#l}!bZHXADU?&A@7kQW=)lv`W(P!*LeK>&l&s^+v8MXE`3B#!T$NbyRgS% zV^Zm@#Mt8x_VS4zZ;#{gTl%cQ&!TL=sM4VdA#Dl*V$Eg!V3Fmu6k{_M?0Bh19h8gV zLe$TKF_TsQ7|)#PBD_LImDI>#IS40{Y4#II;G$XYs+IB$nMHrYRL`E{FCM@C4u0I5 z`{j|nK|6ff&+$qLqL@aZOvRfuvzW&h+#KAB^AyY?7}LZg7$4GKYcLV-!}y8}tWT}ub9lmGtBl4sM_x90#i_NwcvoF^ zxX&EpL%nZ;*FllD%P@yDsRv-%_+HL9$NwrgTv!J#HXV7W;FOBv)A1lH@z}}tMVw=2 z9A8LOfVYP}fEkwM9HEEEIb<(Y^VdlU@w`Y0-WCxR=^~YV^i$rxxu?2c%*LBRY#1B% zjqD4|!u)v`49An9qk?PUX%B10I##S7K$qn0o3jbs_;njF%&El&w&u<8{NMRZ)IQ9? z(ju`>d>4YY9ce!Kkzb-0^1hsv8Ptfb2&oLhY4(9|i9mxFO_YcROBsk2MH*#D0=mO% zGZu@2S&@QZLPvR>7=sU;Ca|xB<#-_ONGJ<71Yu0nk6DH>v*-XGS?mgM%#21}n?X!4 z6I~4Z12J{V6nCswaHq_nKSC!l9xMG3&##q#GyE#ye3m`TACD{bP>v%pO4K$L{qm2R z<}?h5V}i{!%}h&j9uHD+FgkxB(%aBpQV#4-nR_0@?6B9I{le@T zFc#FafAB}Tp_nXABT-(;nmwVMPEq^ZFUUb1s7ryq_H(~DJ#$R{!jmW^=JdSwr$8(A zu8*g9k1PU>#jqsR!cu98s`9UM_LUC!vrpD0%s)YY!cR6}Fk| zQim!dSKQDF?oqDjN95E>x2F%fRSg0d&|bW1G2KRaikFsGgyQk}ZmGeC^N}wGMdu^i zD&&a%LjG*@T(M zEMV_`ad76s^!cb@UGF*g8$b0B1mp3W=-{V1FV)7(KV&umGBHBX=-l+>e;n^p(F~;n zPrwq{Bjt%7Ki;vkpk;V8bVMjCI`x1zfy>as=%oa+4|E|}1gir5geCa4fn&>@Gnt$$ z@M+(&L0`FZTl!dS>t)A^T${C1iVXV7a_8u`@H(LG*OHiQW?x|himaKBe?E5EsASl0$dOJ z@wlev$@36UN&dlK(xASsYcF;8NWrz#J|U$QC6gB*=o&X>ZJ%s}h$Sc`xd-2kNr zG^cWmwC`F)x{UDVqeOUigp^}rNVyF&d(ns(wx3h^W7&?5C}rO!=Cgf2HX^&RUQmAV z`nq(9BR{Yw_j{uDBrL~jPpn#dcz?Agvv4`dKEt=E%`W%=Mmn5+RuUX+UCRpl=)7MH zr=RnXNm)&9ux-21Qnv*b65}}%mIPXp=U#~_4tI*8Cvuek;q+e3j0LVW@23oR(s%CA ztJIj7TWMAR0|?gljZ-fsi-{dj{hU9c`sEKAOrq7}NKFtsKcAJS*_(c)=)rPfv^e?5 zV`%smxYh7kMhoNlH~lY$e~Im#2XwOuWA9FB?!w-A*oS;|2eKpf&cEY-=ik5ngwwx9 z%=tE0a;Jt)vqC?EZ8JJsb}RSK@fBcR&8(~FwgYFnOD1L^16Gz9wGiu&5!>E{aCXG8 zKZ!HLA`3Z68K5hcwHoaw5%l-tJ%7BATY&eq({(Nw&)@zI{y8%2LTJDwZ?dp;I^60! zd+xPF1(@MYWxZ8Xoov1G+T^=mnfB$yh}J%{m7a)n0L_XoA~H~Way!j%+tSg6t0lzH zQ)xwpKoJ|6^PoB`GLR=XFUMp0i^q53&`tU5`n(y>suIWPH76VkrZS~bDGFfA z^d$tzO{eJ@x7Fi`$S|f zA9~dEUkT-4Dutx|IEGman3zxNwbwQ0Wc-~l_XHT3&Ew8P&m#Nq-U;r&U8otNhSi77 z)G9t8ss(-{On|*dP_X`)_Laq&?ModLRC3pX4#5g^8~UlepN#xKS9T0l449)%Gh|E5 zhh$2{sDS<`skMKfBBKEB!5e8Ebo+Gtb>IF|{5|M)FaGwQ)sNKbWRF+NTI8HvRg3e3 z&Hdiy++Ehvj2!Z<18zSUe?4VblGi$*?1N^!U2Z+3?0NhhSoSyk9aL7|{3759x0Ce4 zr1YV3|FE*ZH9v%F$C{cMoMubMnRA(D@=5msu80+Es7@@Ceft3X%_?KMne4KEHeJL5 zf!3jA*cRD(a@l75J*DgoSx18>qQCh5Uiv4~-#K9a-QoBl>*RBEJYn=9V;Xzdag!%= zM|WNIC{&HMCd|E@%7lnm`uR>Jq4Y0r;1hHw!s*Xl%wt(iL2ge#HWecvD1hoESkGbT zEX4TOOI{tqCd_#P9hg&Vzuk)+==5LmZT(S8zqyR0dy*JP@wl2>X==2850v{OI=w$q z?oa4&Kb}8}s*FC~ni|!|X>u(3I9SALUGGbADarohUC_NJ-@;@g(**^*8|HLf?~%9_ z(YW~fhf4j(hY6|uAI0}Z%!9Q1yVBi;40Vz1LA`2IqB%?=^7)0F>Wor4E3i zxC!EW(;tb*Ph$Kz_C%wR;fe&WvJ?iu17Q=C6r& zynOWk@9;f25x%(z@m-w&--F}vJy7r+@#FEG`0#(l-?_IZ;P1k1iTFF?=#KpT`5AHi z9Vz%u`0@BIeJF}A!WzcEnt-0*NdG_auVm1pnWTRu{;!OB_~WrpX{0wRQNjPAfA!si z2H#@iUpc6%fAu>ej;3HuB%Vh%>OQ$fH%iCjh~GPvepA^mbduqW)y4y!K<^1&p?bhF z0*m(qr&!`Plv}iuW;Y{19r1v@G{5cLq?<4>!8*^@Eg?L@c)@KR$<24bEt>ov{Gt9r zFMObX_lnxz9da!8_n{vs0qc6dhf7KJO}D|>IU)!nyyo{!ZT2yfaC+p+aEgW?++$bn z+X8dE6eT0(*i|sTVBGjin0r02Gu8e9@fjxsz1D7kbB{SfniaalQy5%hg=WGejB(N?Rs&V3S4=9HSTLz}%9K;w$8dT~DLT7}X%jm;H?7;(pt8^i zbcM~&f1ow{Vz!=b`-9gcOTD%3V|-{>v9*WC;KmL#xQ)fb`1p%`s6LF3ha`K6RKz9{ zpWGOHocjI=C)lCY)aMVKi~by8=b1WdSR~tKomo<+`g{0mg`koXhe{|M8IXIx9?lC8tYOtxOo9O9yT$`YK7xtO>9bh~j*|z2 zM#_*Z(-v(Qp8T3mhPkW;C;yg{`I#QrBkLrfii;NZyW6{X13GJ6W1Xf|%<4<^L~HiP zxK~*)t}ytWFue%ekk+j5QsH7utMfB0+|`Cxuu7~J2tTAr@`F1N{06;!H=zA5nRSYnf}!dp~SGanSf!p*2B$GSoV+xvTm2EM&0oS zVmKGCx5n8(+9*Wh^TfzOanY7;3bT)6vFQUIqBia>bf2plN zccG_j`9#eEaQxiVvIErwNIzK%cFk&GwiTCs0I|gV=0-Q(x>cb4AllGC%J<=2{43c+^zTz2YQ1YXmjbew7t5L&YU9 zYcOpS3NCHiQgCI#&kJA!CS6gBJ8{3Z(~m`EZ9_8gm-5v%gy&OPs;LpSxoyaiJQwyf z$oU~W2T87`YB}%6bI7u*X_=h&;yH94`;Fy%-!(Yrq^YS=&cEV$AD)-X`3|1<P5E-Zf#(eGHRZ_pYMx^sqN~X(=PP(lb(T~X zZ9|^rIrhxEnp|@KDV`%KfUAi~#M_4andcn0n<#&6LmuEc7C5?^n1i!z$nSWLC8VHV z&VR#mCP8XiCg<~bj+_UeU(SEUbMC)ts+98?JU@)*c4KZTwnI4mMeWt;j#y>lIsl*E3De4?Nx18SQ5~7bJiv7A>OsO6URSLnG`+jZSw+D}#3aD~FgzKVbT5z5bH1^Qe`@9U(# z>KZLq{!8Uk)umdl^p5FrKfjMY?<&>jiq2J7InngbNx#&3KDklgva9ua!_T#!Di7A@ zTYUOl&M)=8XN_t1N`=)2DJ<=+u<|@DUwx&Puew~}ve%TaCF``Dces{wU8&GJL1F0% zy3x6B+dd^wqx#3E$t6ux1a;x@h z=^*VNPhTzXeMw=<%j2Y)s+Sa2FW2kkOGk|m=`HG~<-J4oe#5qDf}bntCV?&K%CC|%g+)VtA=f>3DQsDwu>1za&-F7s zZ@Ao)^Us#@Rd*_Eth-*$E8kLB{kt2qzIzpx->n86~C(ADJ=Ox z^|AEi%cWe-2!$obDr`APq378P<$l%hiv)V}6gHfpaM^*%kCKBFdIuv?snLRW@fuS!>#-`kw`QP{B8)Ssm1 zt}j$>%0Dr3`HI5Q=M*-)pwRn;%8w^UVf8SD<)BfRG2eLVaaHPRgWqxdR(FBNrlU*6jtA>uw<1vze8ceLUa8=h0Fe` zuymQZj~%c4dH$@h==Tb}mFD_#h0C5-SoyHRmOm=Y`K`i|`xSa$(f)0zF>s~AoLYs; zsuh;LX0E?#uCF!M*C=cmpz=|4g2I-82KHCjn5xi~sj$e_alGhbg)RRy@DQ~REj#qQ z@jyNIe6QzKZ3^@EEA*bD_p5I+Z}o|K?s`|xm-RLGf1w-3|3frw7Kq8=sDKhKi*s)qOfJV)|>x{!RHjcUgn42bgoWLeJR>y_Uj~ zAqtmWrLf@w1CLdhGfbiPRD~_+3QJEm_m5DRe}zKN6ooAl6;>Ulu>4Ym-tp%8kp||P z`y&)qUZgN5Ut!B2g;fI;Hr}MLXu87cYV|7`S1a_aF@DBcg_RfTcTCY1#xGx`=ap^hw>D&}ez)w_ekvVq z^kR_Kw``Jmet@34{EA1_CB{w^>UsGS7fHMMNq)s|=J^6k%M`ZUqOft6!es}XC-*Cl zED@MLP4V+yuQ2Be#jo)@<6nQNF#liX{9A>UA1U;{t8};gRbl?q3d@%&Od9aiE3aJE zD~JnI~3GQvQ0(Jh9{`<&)RU6HCTve^i?JROOx8PmNR5uD6UED}1d!-PpJPN85RT zS5dTYe-c{gp@+Wo2nwM|=tGlG4J9$5E%HFfKIi0@`#kf^>^!rx8>$lp)rmn=Ck9cS zD5*|NKj*~DdTCPb&u7wnT2P%BM0KJ;bz){7rybLa&^X!3$%i1S69v_Y`V+^WpgPf@ zIx&;#h|Dw2?@gKO{9Z+MMhfM-raa4Z&pS71e4#oyJ&?}dg`D3ns7^G8Ilnh_B>jG> z6Ah{pgQ!l751{;}Ix!`$Qz!aUotPd=c^vM1PSZFWpU?UJq2sAvw0Dkne`=q)PXF?! zI#E3DylzmPXcu*UUQ(T?Vx9NReU9JsLC(>i>O`H_(RL^2_xMwtm`VL#P@S0arSrKM z8aF}*IiHWA@l1bA{3o2(1=Wd}orXE>-jC*2{`C9iJ4b`+M1QIiL%()jSIz0?8av-N zbU%%AC!KcHrJVLL^E;o9+36f5)rpxqoq4xE)rsjD4$q)EF{Y%WC}HQuQ|t1suQh|&Uj#T zbzaYmbdD+AVskl?>O}R`NT>Y{J4b`+#Gp4u<-TqwI!8_QqU`C^hyGM2noCCKeonmZ z9QA#tKc+wF)PwOYoTIMh97Cy2%uI34SGuut9uUFKF|(<245d0z6mjCBiaST6jB`w> zks`U9v&e6{690k>h zK~yIO3Fmd|%JAGcShkaw_Acib`kr$PqB>Dsaq=>yfpaveUbGuKI<$y$Ot0V^4XP9U zsZLBUiL zGilu=v#Fy)sZI=P<7nN|ImQogjzLr>##8;MIy$dQsuMHm81t6%xLv90PUo2ZKD|$MVkp&#@l;Q$MCX0A%W2YU$G zZ0G1tbz&&hiJ?>{N~#lM{&GI2A2>(rPvTDKfO zg6hQhwhlLi>O-@$Q=f!Vofzup%o~goPQ7JNooF6+v_I8}Rzv4=`WxrO@I(qAKaUp7O2pdAqcuW6IL!sZKO2I6Bj<7b`p3tV*v_y_iY$p;^;;J^qZtwJtix z%%|w{51qQu|0(CZA*fDF$?s^@#W~v2ImUEzj#ee-=udT`9ZH(tP#YpM%VsJ=9- zIj<|K6D8G&>1&<#O?lHf#!PiSm$|`-zk10z27Ttdp8l0{w7zhTDcM(ZbN=~Loft%Q zqM$l4Wu)`IKJ1)NtYqhWnEA7FO!>k&ntPmM{2}KUbHX`>Qk`g>ceJ27QBs{~Jnz(r z>7mX%)}r-pRl9wzFF|#pF6QV=x;~*gJ6hFrj_IazjG;O)vzntrgPfzRPw!WBj{a09 zh6Xs=?B^Wy^Ug7p>O@_}(H5<{%f615R43}E>HQXTeAYRpQ#~6`b)uj;QH*omj~VS8 z?N6yLq&hKWAI+zzUd-(2oNr@7sa~WyQ8psZQRn=xO={nP&M`j1ImTRY=4FEFMC+i_ zZ>_yf{UxYQ%zWU~S3y)KhEkoF-q^|aAgUAnsZLDsclcH}$A_dk(WLs_-bCkR;nazO z>O_C46aA@9lvF3C`#qD}-a%9+8lxP4D%v@wUvd0NsuTThINH4J9MgYyj+y73qdw+* z?@+1}t*wqW+&ZyLLR4fzluuqK%Y;vv9oKj5xbFM6&e=zkT`0Qmm&?_4|Dm(%#?9p) z=`uNW^BB6&b$#(HHP3AGJ68C&Jw?5*Y$DFQwg0cOq1-RvQ*-3-giR|WhY#QC=>ONB|9|ygeaqH>rse)COuU?RkdT3G=;*YfR^Eo@ha{l?`adg#BeH~4| zzjUtp`~9-6_AQjlFX!q!<#Tn;UsK8X=lak2Q;Yx96&)#j$X_K_)6Z9SwQu2Ep7BUK z->23i?Y#d;=c%3h`8to@cRv4=tBEgNP4VjBYWlu3ea-e)JeTL3uCl)m>2UY+Ty1GbNa&HJ@S91M|tu8?!nIQ`~R)~{yx5s z+L!X>QU2wR%$FSi-$%N@_?(3Dz30ZqZ)8}Gmafhh?vtbaTwNeM_w$c*{^215KUNT-mtNvD#| zAgxJfl1?L?MLM0d2z7j(BJD@oChbo;gLDAtOwtDFEYg9bMSF)EMB0yZFlm3%l5_y+ z5Yh(eP||^0r|Sq$TM9(jlY`(xIdSNk@B-?2a=8;9YoqB9ZWi!v?LuvI)rpA=}^*&bOh-*(kAJ6($S<7Nym`3 zNXL>+Cap-Pkd7mrN;;mjCY?w+jkHBNopdtkQ>0T!+oV%TXOPyUGfAhB&LW*oTJ)jz zC+$btChbo;gLDAtOwtDFEYg9bMJH;1(tf0aN&AzQqytEYkTyt%k`5#tK{|-ENjjKx zG-*jXhI9z&Skj@S73m1laimSs@uZ_kCz6gKZIO;8olIJhP9YsfI+b)hX-ztjbQ)=k zbUNu|(x*tLkhV#ulFlHlNoSHyBb`M$owVpn?N8c|v`yNdbOz}F(wU?U(pjVfNsDKw z{Ym?g4kqnST9OVR9YWe59ZEWobOh-j(kAI(($Sq+>~kl2)W6NXL;jNyn3p zCY?w+hO|XGmUJ>{MLLCa9O+cj@uW5BMAB)bEz;?vlS!W2%T}jM|^HA8DJkKj{q80i-iY8>F*H2a*<@sr^a&kq##9Pg;@=ARR*5ARS6NkaPs; zAkrr3VA9c~CFvN_A*5qThmuyLBS^=QHc7{mjwYQ*I)=1GI+k=YX+=7PbR6ka(($A< z=|s|Lq%G3vq?1XXBAr6oCY?$;gR~}{Nji;m7U^`-BAnWvv>$1kv_I(#(gCD1NgJfI zNC%P@A=Lh){YVFs_9rb#2apaSZIBKn9Y{KYbP#EibTH{?(voxx=@8Pfq(ez7(h;QN zNSmbNNk@}TBppNAA{|RQnY1FELOPCgD(QIAnsg%RG}0F7bkfPBPmxX`ZIezVok3cY z&Lo{iI*W8VY4IGjKWRVGHfev-8KeV9XOcEZXORvhExJ(qllCJWOxmBcBppCHgtS3A zlyo5J2+~2MP13=nqe)BBF{DFC$C3^utw=|Zjw5Z7jwc;WI+1h?X^V6$>15K1bPDM> z(y655No&%Hq|-=Sq|-?!lRiZ{g|tmNm2?JaO*)fw8tE+3>7+#jwLfV;(l%*-(ix-! zNN18ZNN14_BrUp9`;+z~9ZcGvv?LusI)t=AI+S!E=?Kz6q)pPnq@zho(lMk%NXL>6 zC9OzDkd7m5l8z@GO*)Zu3~7sWEa_y@igXI;IMS)4<4J4MiKNp=Tcp!TCzC!!I)$`N zI+b(=X-ztlbQWbS&v) z(u#Bn={VA0r|Sq$TM9(jlY`(xIdSNk@8CP7S|;sS9u}6p^&vbgd;3EeZNGVBA^SV|N|*0kx_$Is&VTq6 zN*I|Be8Zfde{|_M``7#A4fkjwpLUU9;U9-bMrCg=2@B8JgYhUsbAM0H&p&G7ydUQA z@pblhxF*>5v*Yja`}4RvrX0fGa3i+~wrq*=$RVH3;r*6Jg(ZY#?+l3y&;DO{xCg+u z13B^edxOb80lxX&_y68-;_<#4jel+s`Ns!5zF*Fzai4-)xBeqUJ|%}QKXD_!uXKM+ zPK@(Zbg9CpPybcfy8@yuJ|%L({?9g!%9D-!zj%Mk*6e`%-Hdq?n4iz~0QuvY_ai;u z5A*Cl!^4*6>?3kEiDiFDp9t^#4i6idz0Jt|g8%ydIol!rVent?=WK`g=ljK?wvNj8 zj}-UK<5MU+ET7B&d;Ihq=-m|a>6frPJFS;TWp8}S?*7@%mgl5+cC}@HKkof(uYdR7(RqBzXNM>Ie{KtV=Ev^r_j9+njC}0-x%ux2_*Zk z0&#qXj5s?A@x?(LAN#;E*Rsda_3yvw%z4B&7``Z@^zGnFP56mCx`*ugfSC?>p%3vIyj0QssAinIn z#^Zat!i~Sh@F(n_RtNDl)4rlP_P=&>y<3pa#K&a#3#L{bhWMf;&QGHS_8*d1t|8*v zO#Uc_|6QlLmk?iY%7>xJ^!T?laF?_2t_P@5u2k#@k$;Br>_P_jWY6HXA^L?v+JQ(p6Gkz+DKO^G1&k$cT@v#|x53>S{k7D8@!*P6e zEojyR`4_cu`-{F@B_LDynS5nud-^T&?B{xc8W+Kl*;iH|W1^Sjqd zHxXYkd@Bm`3mh8M2=O(;7h^EL=H2O^BfezfuNnTk5%FaaUo-vJ7>oU%DX;Jik*lrnEFn|VE*9` zUps~PiiwY&iTTazF2?mW!jP#oj?YV-t)~%RbMvdG-f%J~ z!x!VR|A>Jvc7XhEv44F6ub&hiJ}wjSO^&|=um5B`^ERxH6vJ2V;P}6sJS!jaubA;i zreOZsB8Wcc;!0~^h#_jfqZ!qUCV$?rGVe@}am^L(_|`Vxv7(4?GV?3F1IMT0kg)NHuNc1Y$LFsRoBAzAe8I%uER5rG z;Bvv6h%XtwJcRS}yW5T7^&`p5|HU-SFL~*^^2oo%#NP_8?MvKKipSACztoZk4v50l5Eua(SqPeCGOs9EAB#nnhy}Uo-0i>N4hc4|@x)PfBKe zK-|Rq=_%iRjr@zB@%+tX_(2=vw;{e{_?qGO3CVt-H`pJg8NSNI@$vuh;$FnJnEaHt zFu(ckL1z$OGkpCk=GU89?J(jiCO-CU%zx+`TL|%O#=pFS`R8Z;ARvGavCO(?s zw|)Kdiioe7_!tjyd@2^1B_N;iuNeMU#RkFsX*R>xKH2^r^Us|h?m5!)9`w(&ukywG z;yr5iM0{}skDoFR=1*VIAsX=whA;AB{`q0!-a~xJ@Krv{-|=wtb;LIrzRZvL;>(0Y z#1~JjdQ5f*;qK2g3t;}dv1xxHzGD0vewhDK^Tu#}-eUNQ;rAA8;q`sZ@O2^Ve`3Fa zaD7{J!~0`p&iL|}e{TPsTjyu~W?9U?^Kb#IzZAn4otipfvK@bgYt4DYW|H*x>98UC$eP2u{3 zT>SX;9e4bUdjt6fI% zQA~bX3}3e?3C9P`$VgI!lzGC&Rmzhj_ z%!Zh6ZQb$-@~;@aX7~@fT`Ge3;uaqNjYio2&u=CVM|{cf6~nJ_?^g@)MO|E9+YEor ztiN7Ie97=-V;mnV{xrP4Z!+^k(FF6)zSR`Y|1BneOoso-q^BmJ_$a1*HN(F=ePt-( zYsSCP6vwB?@Tzcstj+M{)0iK9bayK9ubBL?82Y73NQ~mPH`G#qdRI%r9_#;q!>E8NSKz@9dvm2Jvl%uNnT+v)_k&@g2^8 zqYaMFwNh1@Aphc5=KS6c^QW{c))Mgz#=q!*`6&SpK1O`W_}9;1{)!#t;QGH}_^LDJ z7piv+&aX@+J~9OJ&;D8?48=!t=NE=QpzZth5npoWmoC`*zYF)5DkeTgAMC$EpQ*Kxf5r4)#qdA; zePjv5*C*D(^$pn<`|tezr8f~@oWSQt9ftWEx>|V=-(dJgIOabydn25GN`|i^Fh3^m zKDa)qx%?S``D@xr*uLU6oVe(&( z!2I{ior3p2Du!=FWB#@QufX}6WX3mh6y{g{PRvB#(Uvm9-H1;17 zJpVVuHyHn7EarFFaqkA=D<=M$;qSFfxW7U(`J={R{~^^P;PdyyQ+WKh8Ge%w^TG8) z&BVtVkNy8z{t$frhUD5e2J=_{Fc3apLNWPiGJNxyG@EiSH z8z82;Tgzg9(j#qdQO_TTi~t#ExzG2@TP@CQCO2<{&f zzv2F+8UEPB#&Ca^WcbEH9G?f>%S5C2OJ@904FBgMO;#Yj;PRj07wSG?5#(p#_*;u` zd345_)m9!23{Z6Onsyoe)D0ouA=yep3M5*avYzLo0110zRko(CSd-_ zW%=;^Yp3w~iN)}@EZj5$`8OE8Sb_atJ(2%o#20t*_^(%De)Kv0A>vEMzp)DQ2MkYo z8S``F4V<5tOnl^O9G^pX2I2aPX@U0iH|2%VkKY-#RgK&J*iV!cMrp zYB2d@&By#x5eM<}rJ3=^d>QjwzjFxhpJC#o8GfC&E9QamXW}E{u>a6CpWH=!li?eS zFuz`_qHumLnEtC6{;MyRg!{uK!?#|+{tx^*7(SmzG4T#_e&V-rpyzIcf9$7c8&gI~Uc`0^g+nPsj-HWMHFTgf|);<=P-XutvI+pN-*P(wlROx;`EWo zzrpa;Ma*w7q0`%lFB!hg@J~!E2FGW~#9v>+{*Po99f$l|O#e0ZWr z`ao^`eA@bpQa(Wb^+i0tvKarRzIYGLA8dxNui*HPH zzhe07YTSk6r@{EQ|CjkP1IMRe{Uo^mN-^;f*D(K1ts(PK{3R0~^%Lf|8q@|}KQbA< z&F}~OSp@FC6;H2%{nz{%`%f!S9@me>@c5t^{>ge9;ru~eV)|Da9^Xog`V3y5vKYR_ z@VhOGhVxtNN970F)5MMI>jXpR(e?IfgUx=@m@l$;9DvbZq8Xy0O_!h^ngY$FSjB#-O zBbo72eU8V6DGQpw`c5AZ6^f6|@a=YsVfyt-(viS-q;D}58_SCH@?B~xln6JAc~JDgX3>8{K@Ug!0Sf_ z!#56M|5qxeu0a0fXgvS182*?=1u7uEV)(`(?EmMNg5mn7V%k?%$LE*AdCS55jgpDK zShCPPrFk6P^^-qKuNr~kBgQcCe+l<5y~3|3;%g>8YCh&K9&w-w;*0gTf7zjUe$x1K z3%GtL8NT=)kIw~8e-7s-l8L|hGLBEdXK!9X@lgz4oWuT)&Oe9OpPBf`1=xSF| ztD4~(y>a`#JHFfm6d$nx=Z_JG{kNP}5}qF=!`DpzO&=43&yNgWEX4kI)JXan#Yc1b za~|ityd25Z~m+&kNXp>shaThWKJ5&L0(z$LB63--Xu~B*Par z_8;HKw>0vv8NSWrPqp2t8u7&@93Nu}Zr>RnMASlj#k8+pg!8}oM}w0PU-V$kkFQ|< zJNXy%MtsTe)vK64TaUkw`074h-xfdM{FyazWmUwNotX9w!0Ss%bK4I`e9?c6$GpZB z?)rws@E2Dri_ect{KX|4pTTLDTOj|M;p^l0?+ZUOcnnsG90gO7p|W@fdPO2 zR59y^HpB1lw*X!rQH8L7i)4IvDZv@W&7a~5w z>y!1cf6ef}uM!Ne|B9zDUzWh*|LxsduA}%EnK(Y`6mH*d(|Wf@e9-{=*Hv-;zc9JT z1;n@h!v4jNczphT$5yyMUo-p1tbRCu?gekKk$+Vl$H!vEpT^~j!u5IWb$v=M^1Qxa z^~L?G?t>TL^<}~Auh9GP`sc(qOXfrIF&Mt}1kV3nS02FmiOukhV|e^8wLN?n@-MF7 z{InQ;zaQHUKzxJY%lirG0x9!2Tx2xe8uq9034sH z{y)L>TZ`d~ukiTOp}>K<$iHUz#z5?U-HXMVAii-G=chRh*I&LJ55oJ?HM4(7GyacP zUh@O;Z!-Q>0cRq<0QBEGQ%|9wV-*&kJ*V9`m4FDv8oi#~_M8`BzMQjC(jf zVM(`NM|{oj~VDcN7X+$1Mx)_+`i(#OCDTTxcs%cI`QFq;YY<;C{*44Y{`1N0saDBu0 z-gA6YuX@H`7R32EzTu#U$iMP>zLxF9xBKDxYub|bg%MwR<&S*L)4xu{@t@Z*bw1>K z#ozMci=XlMR;fxueEr=k{%(@H*B8Vum|yl;`xf%Ayy7Dkd&Wl;!tI+UV#cqCZ+iJR zz4%rVj{k~sOQR8AdBy)<@wtiPleGFYyuKjv;QpnV_zdr{7uJuKSA4wv8z=Gkeb?mD zWl?-=hObuQ__wanCl>J~7oU^`?pN|Sy5sZ8uUoN{ENIy`!f7v z1q(cZ_>$q9hw%L2_Oamhh_4vF#pF+`Ruf)9eDj>=`0qBNJN_7_aDEP&dmUciR}5d= z!TtBdHx-T{{}#vp4f9`nGIccKYlbiL;`#G=|2}DmFY@91G*{vNyXE`wVTdmozQwff zq;tPOzBrHbM}^?}_k7a&v(P`oH%{aHoHFxkxc}Mo;u}jm&yV(D+NP+2=BvVZ ze%P12od3F*e>UPwD2k6{{96ov{{7wu5#MC`uf_D=jOqoN zBfjwS%zqj0d4ADM{xm6abTZ;AFTQvGHO}MnOXwT7DX z{>4L_|5HAS!q4C4_~JG9S9%=X`ejSxM)-U-#qf=@c>I5&VV(1g@C! zJmRZc%=}>tt}oK+?!?zmpT_;m9EbTK$zQ&M{EKFoFYe>~jIUnIM0|^hkJyjLhjDwi z*@&+g{}$7K2kM!4|6VtI{*?yqUvC~?lLz^?nfT~+n16iZ3Alc!d*b#rzQ^O+ZE+Po z|3p5E`Id$K&)Hud&L1?#XZ-i5b8iodzhs^dpqc#dd8-s$Ki5-me2jBA{-Mcz>LCAm zD#PE1<6rCa!`p~2vMh;@g>7I&g1yZ3?GN@zlgy7*JAiFLFe)K z&-ho}mOd_g!kwS^eiaP&PilrQ^5XO3<|`|MQ2aF$AK{P3{|&Ft9*FpAFy6mmrsMN( zr33ll{v{EK+gCIGe_6foMdV*Hd~*q&pD+AA6Yif8vABO37jXX1888U$&sSdlO|SW< z&CLJ53%K(hijOFP?;jWWaenR{n|K-Wr+dand-+!-aDI+?I0~*GYOnk>mU_<5%@KHg zrmtLw&tG2gS6+PUEj&IK8uAd} zza;v3_FuWoGybw9Zr_@Ftxu7E?d9L{;;UpF|HB_Hs)6`&hG%~MD?SBrd@9M;yCc5l z_@!`s!mDII;0WBGC1zs(7Bl|L9B~xpkM!oh;n}|ClX(13ex>nIIPN8^0|HcnEKL=KS$3pQ} z4Buk-{aznX67i*p>myMZ&mVs69W(;*HRIpxiu+e)m&tJdn@~7^j7vEFt(tuS&+lRY z9)DD6JpZ{=dC&qBAIb2I_we|6WWsy95nnTWSqA%`b8__r#8*uGMNynT^A~J_`)9;J z9DmKse?H&bqAT(*8NOK`&##_2u&Wi~D~2x`V1Ay5?b8uoyomGPV)DQ7ju)YS?d9L} znm>!OIDfvW8N43(*NlHr9OqB<(?7udGh!}|kMPI*z3Z19LH;GfH!kDxC*jO3c>O{Q z^2~qj6@SgdzuV%2xc@T#?MC?e&R^dREso+NO5*hy>q9)gO|ASCUO!>{s~~)S|Mv4$ zccK4zIDhPhn7=CQV;FzQ@GT~Pg5T;0uP@7&uz&L-9RHf-TEgoKis2hS;`|@{^vhz=(yM>{tG>8`{ZGtn)*kW2d>nt>6R*$jKJWm~-x$6qg~zwh=3VAM z|ARfpAMg06a(Mh-|IqJc#8=Gx(>#IuSM7fBPb0o${L29B|Cx%D`y;+$`1W#Ke-&FW z3GS~}O#50)ezsV(_9pVLz5E-?J=e!9W`43F#_wCiH<|e^u zzbKE}_g>}f6DnAr)L!{%d-2UNxIW!I?eH$d7p7rZh%de3BUX6DhiTu$llEJX@8#e0;#+%g{8M_B zPepv~#s61)%HjCTd#B{@h%e)C{8a^<|DC%whWi^8!#A$t^W({WJLVz(+KaEf;x8NH z@&D$~YrjGMLL46%gzK+c!=mB(t@6%KYGC*Jl+Ey$&He~pe-VqYfBPhkPyShz3&Z#@ z{w=0`>({?`74aqG-^jrE^FjLp&mz8J__89-|F0_6nuYk9;oG0#@uA)yw~HXY7~(np zdyfxB5}rTE&s+C}{8w=P=+CkLFUzij>u-|rZ!zt=cjL`Jk$=hf7v1psVV`fW!}>@w zeDgH!Up=ju3-(uoZy?VY9h%a8l?Q8sm<6mm}I6uUfLzwmX06c#0fOgo?4FlD~`{^r^~{JFCe~r9*>`*5+47jl?;N)X;R{>m%rWr_N=jApatsiGNePKHuS^-R~j3QHoig z{~C{P2l6dygZPs3Uj?`C_!$#hAiiSwW-~m0INx#_o*y!N<7eD|KkzLEpAVy7$L*`W z!SUY}9RurMu@v(y#(#;p5IFyl4Bz+#$LICH8(FY@z517TePl8G0*eQZf_%omeG~Vu zb_KpHkNBG53j^nWi&4YILOv58Q5BCrQ6JBP^H0UhKh5uP|6085BHW)KqCC%E-tDUy z|L0eH3GW|~9DgOQzv8o=hxadtWw?JCH*o$pf36}NpB3ZZY>ekWCATLWLHQ#Y|K>qF z{v2xb3+6ND7kLQtGrBH=^__f!iT`2Dzt!{toc}0>uMFJ3Mz-+pjN-4E{$)(T^>w?- zhw=3tCV#}YI6jBY&ff?9Gw~5M@c6cNNkLdYisiU{MNP~<-*Qxa|Kaa^Q$rG$#DK78UNN1+<(_hZ9oIC9Np)|-^)bc7$7aTdFaA`eP<%AQH-5$K`|-hd zx*)!a#^a}|f#-)eQ+vbpAI|FS0b-@m6`h2kSu_j|-Ic+eRCa?KP_Y56;fG%xMgA2N zf6*5EuavR$Bgkj`i}&!~FDPC;3EyAI_*czve0l__I>^5W#`$jrWB!oRHQ@89L@At~ z789SBR(6E@R~5sz&*AZ>ddWZG{6RB(^#IS`K8xr9_YcU@I6k^Lj{m-8?W>~p)lB=E zHF10%M7~%Z@fG7=q~Ygh9=(69FyhPRI6p-@+<%*fHb_Ez&G7Yk9G@3^c6|czWf|PQ z)&5{!#4!+6~os*V!rR46xhBN6MrEv z|G=vEMj-#93678W29H1aV>-h8w;BHyQ{P=Ket0eP?}OKGbpsrq8O`46kN76TH$KGq z*=*1L4-jAV#QCooeu1mgIw8L7#k6ldJpR-jdlBBhtU3QoejcrLpb_$~IsXT7evV!` z8SW31y>WchRh&P?4`yFr0r$7cFoypmj(?>FFP=httC(m0TS=b#lZ`ug{p5{=)&~$@ zO~L*xhF`VG*S!&6`g{6!*Rme1Up2?;V-Nm3H4*U@v;LrbaQohRud5H@Tg>{5d-+O#e4RL%zwvB@8{|3Vshw%Aj{fyRd|BqD$=Z}3I`+xs# zDqP>z3}1YN=O_C_e>0Nfv9G5yPKh4aUEMK$<*65ES!uky@KbrtvD z7hXOVi1J@C@zFoy_N{)g(NBo4z4&sqr+*QG>(iRggv0ZTQ4Z&)48rHH*DH>mjQq>Z zIDbSJ?Ei%+tKsuE6vMY3;PLI?x2ew||C-^;LU?~y&7>Nch%d(C`M>cH`~Unzi+Yg% z78Cz{%iJN=*3KG^?W8I}V1jDOJ;_b;P0 zzG@>L|J$vacn9$%!#8|!`~H?#0QN7%@XhJC|0eE89ftgCCO>5@9RK8^8}Rkzad>>Q znE3e5t#BOqmki&?gX7#X`Dro!tMpg^*KZXQA9Vw_ z?}Bdc;_EMrf7u+LAIqOn$R%&Mr)-a-yZ&S3#r?O; zSHmsDHyOUg@N1Wgz~eK+H}YZsi^5w`>JZH6!M z;PI{Uy5)F(=?&bzn&H=(bOnz82FGXObMKV`r%`+)!#8*1_)l#8D%{^oc0+%Y2xBV$W=N|EA6H_u}|}UuzP4zKXbs z^WSFp`L0cY=NDZUk8kP|?EjN~U7DiymA%$@oOfN}o`1!!IQ|bapV^D}>f~A%>2Y-V zHp4Ie;r2fvpYgA6WB;Q+=z-_|4Bxtg`5}3SwnYAQJ3RkXzhVA!vy6(_Amdy{KyK;>LLHSGv*svnE%A84>uvcbzq%GUb@1~PjMIX$6c-c4&rMr z{~5mT<<<8PUoq{g|HS_N(!MhwzYiWCtb3S0pvLNBkbfHIr}_)?KOPo=*OwT+c^~sv zJQ)nHU+AxK{>TTI?{{S47brf)H<)ic#Qa7Tw=6__#qfo|*S}KVE(P~TiKCX=r96&q z|FRjry`c9#|6?}Pm}Oo@#`e9@c9e_zbsIph~OKU54~<-z;V=^D+clvz}Tz?SE_+#Y9{NGaKXV5>x7X>iCW7=A{e^@f{w;BFBThl*B z{slAsXg};f_^;0Q5#MC`uT>E9jn{vG_t$8q|EfZm|HFV+oDp z{}=1ugY!eh__zEqf7AC>Gf?{~CjP1f=J&5!uMy&F#=lt-^GEM`cQxXh%=jZqVgBno z+T;7nIxzG9(wLw2?vL}4e;J1RmnehzzAr3Z0r`x7o8cdrR~zoX6&-PWbXn~GY4g|i z&_Bbs%3=P6Cx*cBM=|kH0hr$~ZC?}QUo-v7ERXq@WNWzoAej7-6)^v5)v!;Yes$?Ej0%ZE$@|9B_FS4@3jRL6Xu6Lq^m zKI321!2F&;mvR0u^JkmkivoofA^##ApTBfX?Ehqe!iy1KJ%{;LEzD28aW)9?HN#i6 zF+VYBF{ieU2pNzA{dHpB5#L}I>r3iFpgJPzw4 z#jKy3^)Y`{rE;@S`~}084KRO8cijFRL-;H(p(9Bx+y5jsHzB|Ces3VST5V@yBe6`E7h; z7V<9{|MF?fKa{?=GUPMkv(XImU+Qt}W5ibsUj$?R-ZcyH_{rpt&F~{mCc^qxGUKyu zj{VR2NUuTh(G1^ef%%&!myUvbCO)bq=4a0NEDz!v$ME$RvlZs+I{xzzUoi2Jtugl^9;$yYL{1JAC@`$gQ`K@S%?>~Ch-{(ohR~)|u=9j)(Y9``~@A3R!x5oTmPQQuQ z4;j8{gZbBQWW0v_D<*$bN6cUL-e2(gtY+p{vJ>XNxhOBZerGe+uk|ySe=NAz9TXqU z#7FeT{9T8i2uFO`1&;1k8@)`ds9P@XK2k4{g!%Kf6r70sYsSBthWUQ} zBNgI{r}6yO`Uu~DQEXwkwaNVZNy`|5U>h%@AL;#^ak9fX~1C(?5cIi}5d7;rp-8?z~eU z`PYnp>4)3*_3bzMKtAXHGaUc2L0#eg2b*o zfjEDb9xnvfpLJE7pK2eDfAmxJ@%?3tf8z_xA6v33T>n%C_HQ)7@qfBj_6?Na`I(yG zYsUYEE3@JCDg6`9AM;BbpFvk%Xn_37>Nq~)E6jiKWLfxp32`0!SI^<|`@(O2HIaYK z@I`;jKil~_ynZTv#{R|gxPRRZtq1EzQJabXP|QF2Q;qc~K9b>E195z|UOo!fXCxCJ zi^-ocTiex!{_EiQsMdJ?`CxQMc>kaXWcY({e5$Wl3GYvl4By_5``4~zFWrFgd4lm@ z2#;@3lg~^-e9b(+)F_1Oi|9k+qY+=;#r;dR!Sj>OH7{f$zUKIaaeO)-eB~fQ>HwxqYd3X3bC6RwIneor? z8x-!1`xnDEieUeF8`rOn{40iUG5oE|x?V?o&G3z)*#E+$;JbDrx1dV}ZsfVB|gG{@fkY&>|e;g&G@&b zF7;T~aD}_RQ8R1+bA1{6w}#>K8BNK9b|VfzOX-^bcJS-+YMAzh(f=&oSM{!|St}@oz21{wto`))D!a z-EjQH7~HKm+x! z#pI6|hvR?gz18skHJjs)#Qx5M++uH|9&|B7BfFtQ77SL#8(X8D2C@htvXkN z`!{TcZ!!Eo+75vGPXzP)KyxgP|E)fFe~v7O<1fcy{{0HI&Z77m%-<&xMR5JqzCfkt z5MOYAee^|Y-ZzJMcjDPzbe15EWcq+WVMl*crkJmRgWLV?lQSMuyz zi1?cM`$Tqc%nuKr39qk-KAz*JJAZw2eQ-R^&wPQ=cz?Jr&VRKP$3HWs8{B_oFnlou zkN;;c+^&S;FPZp=7#yENJJTy8zGC<`!!I}L2;N`B@Qvbld=B_=Rd?v0iNBtJHIp zHN&?vasK?Z-4~9Z;yE0D5sUNx=IGwPA^(!$o0D<=>}g#Z_AkZot(Ca`4LTBtuMaYO z5r_9@PTN=D5{i%Z?!TM8>X#09e)Va-0)r4=L@@399L~=Be(2Be97>Q_p$%+zkUYSKTU=&r{MhGvVZ(g=$~uf>A3%@ z<*|{7uQ>h;%pbX{>S4%d;$t!MlgY+(c>k~E#n)c_%dCRiw|I{i;r<`(#rK{+%Rg}c ztv0RN2Pi(8iN9`#^Z(^rODiJ2&G;7wF~8-)rXM4|h{WTMy$X-doBM|BL43*Z#dhp} z#jRSUubK1X zfyoo#{#etCZ@lGse$@SOd|Fp*`Vhv)i|^fkMKJEa`%iy40`WBye=!q}pTC@1KLPPA z#=lyK=O+igFlr&bX83Xu=6~M(7~EfKGkh@^x9|MU58?W`$cOu{;g9RzO0P9KfZ}5? zeCszn{!DxLGu*!+`r+|Ye}KoIy6w8d`|Fh#UwQQ}^$zwg${28eh4$im_b+=I9)AvP zZ|#fXFPZq8J8^ujzC8)X$7J}{E}Wm!diRC(kz)8_79Jn=j%tI?kNt7}h}oE5&3b1z zioas`Iu^HY<4qHA{l)Ob9PEGOE?l2lO#H3s*#D_}r^})EhyghMY6j*92lncL_>$q< z9dQ2*JiKia;wy%4&cy!Tzxq^h#Mca8GwVO4|JvLZ@|pJ4zvA&>yB&8I@x?&gzGeij zufq>rgY}DK_~Hee|1-xwgwKDlnfRF1aD09}v3WO&k7E4GSvY@AR{IQIf7cA(V$Scn zT*D`ke=&$@-?g}Yv0Cke>l>2cTd&~$`)v_FynnkB_OF@igX>q9hV`A5AJ4Dsruh2f z_Xo-WP&IKNdx zaQsDaoS**9moGu_k)fXZKfUMAA|2;Xo!!lF{(rdMMS2|F^;`W4uJ0~ZnEovCuU>o8 zgX;>HFJ8v^)Bb>85yZETSuWD!=0g%e4A_EINZMeJ}aI=e6uLdPq7H|JFe&nuaC=khW`rY2L**R zMgC>~H6D5C3O7GxJdXd1McM`;zQsJB#9WB;=iC=Ve?)w7j(NV>8`yu=tqu1O-(IxY z4T;CmjgMU&&p+qP`6B`G6%&840{h?6cW7V4*9>1X{7Jr3;QcjnF*AO~z-mPfm~!fjvu8qUwJ7mpf?_!bi%Ycb}(J2t8V;!7s~WfG3h zTO*gj{VkG-zgUU+8`65f{f&zAzZ&zCx|H!j@zKoqY%Im`f3-_e8^jlfnCCAo!}!;zKdlVUU%C}@ejJF`m;U-W z1Fk=7X8vO)1rG_-`cP_>?JE^fcltu6+}6`*zjcze0Q+i{o$B!TCA( zi(+tmklBx)c-%kt{HWIA@nJ**zclFoJf6SFb(p_x(=u3pi3^x7EX?oOZOcsLUow2n z@JGDc0MB7IJ$OCd zzr^s>M(qE@n;lyq|B{K1+=Tga_75wF_=4%bVl(CkZ}97m_?nwPF#HX{CE)s);^O}n z_CL2@7CgTzF8;}wztX48A1FS?VLU&ySK{-_lVy%qL3}v~kI!NY_Wwo6zHok`nDsr) z@ISlW7vG=D55)6Bu@cWeSG`bv8R9D@KH@#>e`)BF~90d)!spT$<(KEFs@HGooxF8;;V^F|5}anb8hM6{)jIl zaQm8T@%&*&{Yr5DFJH%exeoKk7O%My`8S#RTBqXn-F|;2TpyN9eyWc#zs=4R7#}N! zY2P)teXo60zdeeN&CCxC3&;OP#*JEtFTcg}Pq_=n=WIY}y#G8M^Ywb{|75Q<+mU~x z0N%f$nd{g2UY#8a`ON+MVjXVZrsXHW=c|ih`1-p!6pugWm-$2@|B~U`4e|QVtY3Go zLVTNfK7jESZr|vBlVN?T82=&}^G|i`_!jannfaB?@JDXU7>4+o@vn#B_N`rP(ocvl zqHz9LTXB4P4Q*Hf@g>7oZ)5(4uMRAR_?n5o`5vC16npAgIO2;wc>Zh*$MN?&J$V!2 zONMWKjQdxqv9sPle8utK$MLBUy5%zBYld%b!|mIr=r{2CxKRj?&vHBF&k0!!*Y^~c zpP%CMSEUzE!~E9_-(vPJ&3*YWT%R$S_^2$rfAWo{Kj8H-hHsUe4eOUr_4@@-{)iEH z{4q*l{-m|vpG16F80U}q5zha;8;g}dd@&09x1;g+P_S+gzW&u0k3aT1IRD27e0>7? z55s)19?wroUA(vy@g)-YkvKl;J)A%L=Us#K zm;EPxKB3r*;}g`QBU~Ra{=$5tG#=mb^|~H`;%_p1<&XE@ewTmh0mRp%as2Hjcz!Y_ zviA3gZx_Mivv>=~|MiWDFh506%s2Po@jv$NS%2i;@Y-MPc2@WLwZ*h={X0wX^MM(@ zQ3mJFu`yo>lV*kmtPs8;ei{V=gf6$k&;PY=W&-}68_Pqbv zD2M$I84(TtzJ{KF`mJG-3A=9f$P=+oR+gTMF{ilEVq^E!LhFAWqetdo8`p@@MyK#tbe(E_sxIu8^qXM!2HMdXYTp#>T|EiX! z|9|mYWB%~CJ=-9^Hs;$KF#oGggKHzcVEAU|$InmP_DuFg5X|0iEFeDx0IAG=&3=lbJ+@|9QqyWii0f*0kM{1r1LNGUO`1X~@KcB&k&z3Gba_ZmzjKB1Xzj4!Z{4|E*@iXPk32!65 zs*A@TYX~zwY<(l=`oe$u*L5&oBtQQ7Hg0@gI{IE2#20~>uXkX6-tOJ{AiiYyau4PQ zH{H+`@fFL*?fdP4n2Lz68NN|urboYZg&Y48&(*}^Gc$h>(;i=+y8H%>w$DQT#S^%F zjpEq9YJ1~Z#Fq?Tl)(Hd=L+SlfBt9wD~4|te0=@u#;5(kl$(feFy}8L0Q+D2;o9bi zuNnVxAs#>Lb&lDM_>%FjDq{aNyZ6gkAO6qy3ub(iRWZL;&9OQ6C;lhjWc(Y|F#oM{ zt8XE`Wc*u!n7`su?%%)oPydP;pUpLoUq5&Av-*(AtC4@r_}8!fAIi=J-iqmq|G$btPPn z^rHS}&a8F*W;wI=oP0j^y>rj`uHRaF?Z@odGsEXki>05J;`pk7-(RE?ua8Xk5A}a_ zc#8LY-SGGr>eqJo_%wL9*XtZ#8v0N7Mt=Tg5A5n)KMmC{4E5^{Qwn5-%uc_+kZr{$*wH{5DO0@YZib^;-tMYRdETf7tK^$CoDl)jWUg z#f!^ueA~dca(I4)p9=Z&7Xx41#q%q@d#AVmBsBioke{j#&+j?3*kX?F81&0&Jb&DZ z#$!3Yyo8@0Dw}U#?)lQ<`1+ZtY1>cg{QWMT-@aK5#qotfzwX2H-#l;VR0_bBpEM^k2RR*Kb`%HxBoCSKgCME|B+Ad{MNU08^-dVoyyj)n8))w zy;DwhNo`3h^#ccdtQoph=Sijo9U)HR^+dm(w z-y-pG2;ben>+kT*=!ZGJsL%Hg?oOUxrRLW^b9`-RU-oXEKmEpU)^mJG;)BVG?Mu4` z{^3X2^GBQVe+$;H`i|#6-6fkH9~I?;1E}9M@E7bGae#}DMe?7<$6CveFV`$TYZ1p+ zhW^3c&&OxQ?(@CD}KKs$X5o^Hmjoet!O| zSNG!hn(%FspUxxv`sB>;MLbI=k#l$AHSc{zlYcV z_HTufxsrk%)PQNzj7khdB_|{{t=lGJ0zb>g?`x!nyLo1B%o<9@Xzp2K2d}J2S zuamQjZNKspzW-D^`1~pSc*{^uzhxMItu1{2mfffcyZ$TO=-;ZARVW2k;j`H+n` zzRNK@e?*b5y#1e{e6fn}Kds4p|9pL&3g>Zr%fNST<>%+4nmxq!Ka%im5`Xa`um6r^ zosV$(ZNkUSAQk_jeE#&i`#SdezG%SrpW+w3ea#)xyd9@sn)tu+{4s}*)aCff5Fe{M zU%!*i{%#Y;cL-mT`nB)i`F5vK{QR86$2uHrzxH~5eLxMq>={nKCVZMd&UU_k+jnQn z@hqRj=Rf=&e0=`7Wj^0OWC!z8(fCZ|+t=itudwGAF46B2zO2i~|G@^ezvSX04eM8K zPd+}ir?y_n@=1JX{OtwlpFfY|`_hJk@8kH&#P7}Pf3jd;QI0QG^ZmbbC(mzF=F(9d z-!kysyLf)ZLx&o2d~Jx2NWA_m-9L=~4b87^$?+x8FaHYm5AG{`{*V5xG&}zne0+v>Eqf*xA7wcI(y#LI*-+0a&GAJuUcaiw z=g**xXS~PprGf7_y#8Jf-`0oYD-(Yg-~Siu*lQNY7gzD|ku!Py!v>9HuRrSN`SlZL zH?RMnvJGD0^h>J$NU(jW4*dS)kMFKHk>iWR_TO~>F#q*4{QP-$LFIoqzBKUd2|QoD zc+EVHuV{S08PqRI@cqN2FNZeg_?qx-5`R&W=U3@mgWbRF8rHX+A^iOL%a2a}$>|qY z^Z9ST$>)DO*6eSNuMG7oO7rpA-EHSCjxULRP2)e2&!0hqUuNe&itw>5VE#C{eE&J^ z;B5B%R};QpO(+5WdRh^S}PEd9%6rh->-ytHr$jgHy_tVEHfb^9O4mpa1h# zp4ph=ORE2PF#ftDKmQr|K~=u}8uZIa{P@!0-2SUL{mQ_1U*P$ZM=z?x@@ahN{HFq+ z|HIGOx|ic?!naBM#Rq);+;R8VWgK6U{-5>_)?6$0 ztu35>(SeV@sKN8!`DN>M9A6sRmve~E|6{kGVDCSb2L0}@eEwYb&^he>J!R0ZU*^Y` zyel5t%Ed<+^xI$X`O|37e0KkmHt^kneE(K{*l~V+^?E*kI{=cximd){{fiD{H z`SZ}AwsSeY_}s8Q)|8Kb&l)4XWBG>trPfeB{-+iUVb^b!AwJgc{P@zf)k9-A{o25H zhV%NnSNrP{jxRd$`Qtvy^DpS!mg*7b_@WaZf6@+CO|M{quG4`uO|XM?S{!l_CDh<>PZ^ z&USWvQk(ei@%uyCE#J=WzY>@8`S1R~>yKYPhP}Qc4SexAzklHPq93!l_}hm41I{0O z|9^7t(v2Knlk4veIsdX2@$nz?eyj<{7tKlh{~3~OVt9Q`E#>3$?xYPxIKH})@Sy=C z-&x7`|LXnsuH^XIz_*(4^YcBg&tuQuwc+}&xPsrGJfdI|dw-=g%zvDH{Q2Dnof>}5 z#m6!1-*yi3`FZ2Q5>Ik`ZP>rD{N^9MB1Zx!eB^X!Xm zVDJC63Ew(7BryN6O7Q&2&pyIFKSvQh?cW>&zxTPT*!ibp;9Dhm{dvzHY{11|80NS3 zbiV&w`qc$C|G}=G*ku1X&3|!|6Q~N5(_y6y0yq{m6H}Na+^Up(V3k23JZEqs1n+xerzoPNs? zA9Wg^KOda_0=s@L4d-`qCEx%5wz_>WPQNnn^=h7^-NxR(<{J3UCw%?Z zo|9XS(=QGAFI(~TTWd`HFF3wq;5(1-{oAsi7aiyL+Q4^h{`s+QKRE3bjxP<*4^e&i z{Mp@iB)fj44194fpFeM2ntd&&UmN&#UtWK|B4v(pd~pSTer-?U^QX!cNBQx^#IMYs z|K9rSZgzZE2EOjd_dmZqe;K>};~Mf)4B+c`sbCoetknc#MiI8`WTy^(!jTJ z`20LH?k0Z!B)LA~o(!%JsL}lOyG;YmWv_24!vD|sJBW|Zs|TMibdrdtnkd}ZK^cX)o&g7H}_-xPlX|IUpir*V93;H!LIf5Vrao5Atr9j{}T5GYgq z!^?dC{Kkx0yEwjX!t>p_{P_6Cy@I`dB!1-IPh_{`_veVCyIoGdGwHQJT<{l+k6y^f z|C)X!*!|CP=?p{$O60q9`SZJZ*PZ;9(=Wc}#}{WBKfat<^=E$l!7#q)MSOg&eoL|Q zD`}`-wV3Dk+yB$)Tzu@Ye13{)eEyFpUy+|b-^$mo{R$ud@B1IPn$xcn%{$#c%pdtG z&oA+O{xOd4Ylu9fw|{VA1E*gb=C{@YzJGYQewjTi-!wkX;q_nNe(F|^FAUFLat-`xcQ@$H@ugw? z$~lXlpJ(Tk;pcCr=kw?C>l<}+UH1MXX^PJ~eEjcUVR-+kfiGt8?Q80&P2Je|8{(t$ zdHwU({>1M8RHS|#a{eV|@%jsT9AwY$G~r`shy9yc$m?I%^nP}I+cvFlzs~D#-)sYW z{-_Lm=L^1ns8hV4A6LKHaDCaTz_+jV_toCX^2zx%jlXza+6)m&D%|M8ARmVIzG97axb{*Mx6X;`Q(EFl`RUmxlM7h&g=zx2|56 z?>|laH+lZ9JD2mqE=FD+dBaq@_JY&Ldb{_p4V`-|}XSw#Zh5(4=P3;T8D_~N~n0^hXX4z5qS2Zts9 z9>!m^v0inKuL&QGVf&Ro`ul&v`0roevM9$_5Af?tqPw3zVf=|V&l$n-r9r=j{i|X8 zy`B58d}ZLvU;X`iVf-bNj_l*~i#&dP-n!G@UtJ6<21?AICGEc5&GGetV17D9gZYp7 z8OA?<-3hP%49|bU7sY~n=eAKv#bNxOn+mpYd`;qG6F%-w4&&c__=(Rrz9gxB;Vf-KJ3vd5k zX#Pl({yY8tA&h@y;;6kGUm5t$9lZW>BhK-zzlZ8qBtFXi5JpC!lC;91; z_&acYDl9%X?d##4Ux(^<2w#>6<|p=_Vf-(DnCRVK7Rt8>pT-BzU&8o{9{jBp$JYiv z_RnGb)hj19;`pwCkNsOwbPP)DpSRZjXbQ&{Zv@+~L*kF)V;Fxy>s5UIJV3^mlEM0w zr~BhO^Nar+Jty(Ud#1cur07-n@e5}6x%u_UH&40w=3+%^ zzB$?duj2pzUZhB|;_zAV+0k>BCE_zNALdu~Pr><>I1-%y;Pj=AZcd0X09p{(X(7dh_RrqEC9hB=u{} zgU^+Pbz``H-+3oJzp_(M-s8tY_?qw);p6@Fc7A&Pgl2WT`!7P{uV(T2gZFpKh3WYt zmo=!F&Qn8Q!(U$68ldV*0<%Z z^!o3aznE=b(!j^{Z6}0(c8Px8`u{{35+6n5|4uMJas4^$J>(6pDbP58L} z7WN+U=gZ&sqbO1&G=D6@$4bEXJFq?$_8#(?_mA=R?}hTEfsgBBVecU?xmfQIMT>^= zHH|+wfcgcjUpe2W&!3Acukx;MhVsQRzWq9|{-gH>`LQ~_kU5`~I_bsV37MZbBtDwf zFNr^L$JX%q?T>x(YC?Z!E-94H0W1VecjGIe)$_rbngPt7xz~#&gs`gKibFm z)BHiM1$OrR`R8~q{-~eMPh7&c@`LS*<`43_!G!dN5!YYhp8y8klzObD9%g%H0+th=W+4T`=jk|QQIIti-Q3yJ8!v%t6woYI6qM&{~v_`tmn+&EZUp2hJkq94AN+Mj^;zpD@Y_#k&I)bG`Y&(7!SR}%e{ zPxA-)b>V|&+&%CYmT%Cnn)>%&J9)nTGQiD&vS_=>-u?Tb`EO-7e?z_l{LXFevlINR z92g(92=3QVBtCLMu>Iotn+x%${Emm-_RhbuY~bhr9z4IZNqii_r}Go!V>$AXHwSb1 zqX-||PUR2Hf8@4+4|W`~su5Sen#70lY5pMJf&84c`|F!HzBmzVUnYJYeBgE9ztLoE z0mqja@*ecuAx{Ef7Ik@#!Er|paKJG5$hVKV-}*Q9;9BtF)=!T8Yjg?x<9-=FR9o?qHIAL9@4 z*#Ys1%xuPFNJ0#3g)@yYzzuYDnY0sgS! z-%R58DnmYAEq@&R7{26re47^Z7j&Z(iuNuZ^G|&F3qqsegj`;}HFL zf0hgD11Ubl=cAqHj^p&(BtB?AHUEeGA$Wd@d@M_DyP*SDzl!n~1>;ZKFLFnKkW%W6 zQykx+eE1&fr}IPPYWQ5yMq}D~{ih4@-$?pTP53s|57%FC{iG~#CB(mVw|fS7^&5lr zTepXAITzMjRgd6&3%>sU{s;Ew>#ctNAYZ}w_4E3#EBu?E)5Yf(;Q3?3{p~KCUmzdn zClj9R^2fjVR&Sp_81-*|6(t8sjDKo>>^DLEXy>EH20y{ouS?>uY5d9gHFDd5n(CGQ z*~0a2lC&>$0`=4WANe_Ofv3a48_xFPQ%KtHQHZ~IBRGCpi%I^I{22uTDnR|-IO4T# zoPLY&sear)X1jj;pMnqMLfL8lg4~4u)cnmM^EYLPk5%5ko@CiR{|w+Z1^wGQ%=YFd zokd^pyO&OWqEv6_-$qS^Z}^2G@uBl8BT1o`d!jL*o5yA{d9hXd@Q$KZsl_MuLxhhPx6Q4KXN;Q{wYtyU*Pgb8|s(lKk}ym zzg@w14|DMsCxhb`eC_}J8=3zgzb~vWt=}@Kb5B8Y2bR1W|$`l{czL1aW zh~t~xk;lbf%n6QTK{`=%Z$2qbK9? zcihDBg}{tJ<$j6t2UTT4@_;=w$QQuBcyIowME-=HKPs9(Uh7x_59{b6c}9bWsw`Ewr3Z|g$)vJ8BDzM}jn z7#|Dt_qyuwom~Fg20oo%p?-|d4aJx2<>I3Ze0qHl`3m&^b>NySxcqSpd<*6e)_1}9 zpq=+0ZG5fQ|A?^tCxov_`=b31a&6F`JG4<%F854+5}kIP2CoxcEpCAD8fH|A5?5V5jRv+I#*_!?61=WKl!@ zPxEK|3eHbq*iGGkanYjH`#HWg#Gmdzr2Hw_U!TwM1<@}*2(~XVD4vXjo$I$R%D?IA z4&MIW(D>+kren$kN^D>DA)o6E3G%Uho%i6|-u~V!9Dm)vg4cIs2|oUA2fu%i!-IUR z-wpLXJ~zRii1quQ_4ATE-}%g+zv)MV{9N$%^IsQl`nUf0A3^-& z)cnf1O(!KD?cwL4BKhgi{QTK(U-sxAKMx$8vHD9-e^z#1pC^6`#$OY@_|X3x#>Zao z&!5#(LH=0Kf6GH>sekLY@ACD>VEx=S#NX)w`T2B^FF}9yWxsgy=PU{OvkQaz72(@N zzpU%)7vqEc%D^u*;d<}+KUq1zmwyKNn#}(-;nV%Mn4dUL(S`4L`=hdK;JajfPZSU4 zkF_iqf4V;w`PjZbd$FE(|6QtopzX^wv@g0p7WtU}^A;`alHiB-4~mXoR6o2w0{5>X zALElV@pkX|x>wR^2c9p!RdDhAJQTpIGrI7ZY20q>2i~8#V|E77TRs6Sphv=6i|Ed1e^DSY2FY>eDgG0MK`I5(vL;PdM zNqh+3A@NT=Kk&<7d@w&(FX`>wf1TxE{r(!{OWM9Z3f3=Oe@6XSzn$_%c>8a&@{v#a zA2E6=h9p_qgio*UBj18=O+4p_cYk-52EI%BPjPQ>exeBO0UaWFpk{0F@|$jA79SNYwiJ-&Q#s2x_UeB)d;uALi)RYf- zjq?+_e--(#JSjF$&K-;wwOKGpxz^E>3@b;$4EuJbf^{;N%Vvc7?Q7k-%ZRenw) zKAmHbKl$BsQewN_ew-!kA4vS^`gR3y(1G%)E1sI}#UIC)|IGj2;^*gdeE|7b#vYq9 ziaS5G41AhD$Q=y&pT2S;yM8VW=P$H=k?+sig;;T8$xqq*$&l}_`UqiT{SI2Mll2Sd zFNHViq-Wirezk#*&yUqGew__*c@oM>uYUA0r(c{l1wI)puzk_^Ab$ybeBYRn-u;gg zt&sjrlKRyoKb`A^0#Yh|$i!RN0!8-skj z4uAafmB+dG$PD9y`f>k2>-syUa{Yrc@k#%H{9_P^@|Ev+hHGCoi9f~<>z7_XMJ~=y zW^R1yQI4++eA<4IYeD|^ezuFs{5gREgaOQbYP9!$pZ~1i4u<@64Dqol`&<{+Zv_qjFu*S#bm3KAd~!nCmnM9d z=%@SZQhd<=&i*RjdhIu?|91%=-B0BYIloKsLI3YfAMM4}uXvjuU+DZK#RvWOlsWw& zPQPW~)A1|ChkAHrdOo}VO&a(#|B+t`^xt^tV(ba^&@v9@GpLCruY2Y&I;mR2>G)F;xEdO{9GN(AG$ty zE;xv#noz$Xr{B#`KXUQ>`nAGyz2|>s;q`I)d@wN&dqRx7Ut<2--Tl0=V1EltI};!B zExhr`5byotq2r^Z@c}c)SAB*jk5~`-c90Llqc~%b`q+z)8*+XvPbcvwd_B_Ne`3S+ z7v#h6A}Zxx-`DGZ;@G~(`hz6#v1$Fn{2cG!K|aR6);}lCNyaCHZxg;Id^-O}em3|y zdchv={@kp5;M4Vai}0;ABtIv^{+uzv_`AR#{)nvR@ip*cH3=@=kG4zJKO#IapC$4@-aSo<&$4}d^aRMn(*Z(!T8YY7s$6kf0e>J zyyrWm)`xAfKB>k`g--^HRQ|*Jubm~q_+$PQRNwe;f*-oS#Ua-h9HO6IA4mO|{~y%; z{;-$-Ve3nZlc)NVB6M49$)18 zy0T9O*Pk`vYr@C(JIfE#$AACaQr$g%UdZ)RVW{8K`HG)zsQ*6Q!h61AmIZv;|40%aMfi06 z4fzrr-Ep$r-5x(1_#0{eL)JH3!l&!=$hU!iYVSoOlktK2mE`(}dlP^E3%)-oJzIM{vFh<`1^tFPb*Ll54+$jF0kDQorzgax5QiMHPSWMjzu#HM`63v9o1{)}AIKk1G4w{ZFu;oDR{JU;}Ve}a5m zpRdw*^26NxT#@_%`KkFoou4Bg*O?yuE4Qgvzu3Rg^>c0Flk;EXV|>aspXH5@;q%+F zQ&4iCOvRr(Uj_Nq;nR1V)?g_&KX*v{HHkm1U*zKc{Eagwy~D*v8uSYiALJL7_8qAB z^T7|i{0WUe(QkcD@}I0FH6Kgs$A@)gwcSv?0`#^sMT z@k#z8{}a%^t;E{FoPKcz-#^g)?L6R)2mQT9bZE%wmnJ^Zk9-LOcRgue@P2l+%Afz#0{&pwKR^G$oo{)3oWH@JMFH?Nncw2yv42aw ze+2e#Aiplezt*Z&O}O^!7~+$9zk?gpzXyao+U+>IKVQ=L!1qu;&41Lt4)nYEquBdv zl!;IBr#9HngZkA+TbK0WZ$te$aDVPmj6aEwP3xER&+ynZf#s03jVg2fvrG82erf-V zT%7+jwBp|V*I79JW|RFnB0o5OQ9gbC45Xz%e(z5oUB>Y(6Q8W_As_euANzYoO|Jb) z17A+_*Jl*0?;*b(IQmkz9YZ|**uL_}`iX7e<8uQoc>V_R;qrwj->BJAuK%akpFfHDN$2N|LB9j{r(5uRE#zl|kaKr$_}Z)AJZQf;Wc^1Iz9#LL zuKyq(^XL6NZIb)nP=5~DKdBA<4_!Y&K3-p|p^HD^)vv<&A@n~Rm0!Q2TyT7`z6_2p z^!X>q$M}3ctLSZBd{X+!`i2-g6{|E*V*TR#mu=YJLityHoqWD%RvhC``6T}p(NEWZ zkdN`NeQe~!q<-Mj_A7~gm+puMtpyw+7av@Y{&+y>~AYPzJ~b7L$d>U z;*$2|koe&9?G^0*ME%%*e*3`i`dt4hN&cX5%ul-hgM3_nSYNhC6K;I54Sc%)0r^<2 zTzcbfZhTiJKDj>*x!6B9(B-`K|Iq%~q5O@({G{h!$jAHSwhW(k8yo*}!TyKFpU&Tq zkMrodx76v+#b25DWd4SHxI8GzS9L5d{+h(cCh?*3E94e}qq7eEbv+j!ZPHK9kLm)q zD9&$7=k4YAB9>YFF9v=o;CC5!%R-JXGvwC;ep8sA{CM%qeTn?+yb<~b__HW=r=N$4 z^v{~aKlT1}c>cpB0GI<6eZl%#7N_4O^^5ky`iHF$7u??%|EoheHNNN5`H4ZleXW0eR`mA!f2dX=f!}V% z^lhAei|B{1{r~(r%b(v$c)lz0OTh<+Zun)p#}B3(iDI4d?(~% zd~z=R^>Hsgg%BT`%%5$-cS(Hc{5i#k3hsDqUOg^8lEfd}PUVjn^i1-Qgy#pQ`0&B3 zjmh`JW{G}&yZO)cJEC81Chd!yza!rU{%vC#mGj~g13ta}u1Wvq5I&uMB3}Vt_gM0_ z$G3o=O`fkM?hD=@B(?PqMPZ^dER70Je$#)ce!72iunm-&!=gpSQm>HNSPq{8^0S=g;_l zb4~af9O<^?!#5NB|D0dvkM!g0e&q!8<1ZK=d_TAg^Jk0?w%_rOW+ktmhP7XZNSFKk@vUj=$2tcgXcY zM*@?d;ivvg3hf57~q)X=6x61kBiiYQhlpy>k|2#V4-|g2Xe8&(Ux__1O-Q#&vIleOJmpy#_>VCgOej)fV zcVMlp9v{c|{G-A38CijkzkPY!C#fKRF!0am+_Q(rmoUD^$@;k%Hx)kV7cOaEbbll2 z$NGKsteeXu_@U#AsPaO9kH295M%SN_kM|KaoHJk~m!H~Dzx4Vb@^e7Qwweci_u`WS z@yR3eXP5Bhw&3_k=g*WsaA*)**|!}&Yq_usd+jmK9Z`6EgG*o05#&y?Ti{yNJ&emsP)2wzkF zd|$tW`|FYKKoo8~{iS<6z5{&v{tijz=hpUMeCYff`PhD|x@!W@=dsEB*2-{w82M#D z|E397T*J+;ERsL)wbc0+U0+2$wy#wihuqAyFKOV@`7QFX+%)+Y_W5JBiBHBauWt> zTwel)<~*KwL2!bf5BX2$Cvv-kDU&P}$$$6d@yX*7?*DBGpB@V5rzQUR;T$gh$`l_m z|8EWazoBe8|H0`TUz_-gef>7Pp91+fzumC6!LdYq{&W6V8P-1?5`UM(M_uXjEqK0- z1l+J0;Qm=n`1o}kAH@y+`iV>V5dX^cKbpbir)%KT_KW(lJniJqwYmAH zSQH$;Wa4tY_Yd{c`8jfNp1x;WbN2bM7U3u1|Kij0(-a@ppO4)&U>6r3QE>_)110LG z?Kj1T1bVpYq+hu9D~W!G#E0q^uyL;xwBrXl_I{A#+k}r!pniJ&y$yT-%X{A2@jF+) zit@ka>leABAwTb_vAzx0ex)h?q<=tuWr)vt6_$DX|2yOP1^QnXuU{DWvW7oz7I6RX zRRAap`uFBeuFd6-L*oPA$N1CvC-UtM{s$|~eR!~^Uqk&!Lg#6JUw9BQ%S8(;KP4R*AU)=wX ze5~JxMwI@L%YWCzhxr3OADHrs_Pyu^jxQDm`!}qgRQ;0u^T^MJ57xe?*!vvc%JBRM zue(MCi zA@E~l{oE#eMfmjm2>BZH7hKl1v&R?r_`2x(aTJ;VyM#~Y|HzNS4>Nx{yHtYzpZP-^ z+OMMLU!V)~M=gf)FIeA3KAvAZw{YC^o_<^(kcVdj!7p^T8G+yN7v$To!2AT(w~>DV zeDI%5OUttD*U*2``7`p{0e{2NulV()vojlik)I9yf7_kCy!}@bV^F_zeyd3S$eqFb zr1#IZ1>Ly+`TDOf<@d*$^po)m`Egh%9{tXXy}kUgKtI-BQyjkt-y!;G`)Ut>`-=HL ztaxZUdw!=0AMIoQ(D@JY=fDSF%Kovmr#~C?=j7<*Uz*3K2w0dVp|KYp@f1(3?8Xr+P*nY*gLH%NcKmW8g`18-Mz?V?= z>|EQMz-D3mbIAI?tU~zkJ?!7CnE(DS-P7lTY=Qh6MxK?+@huV`z^C$4)%E$Vot|Iu zxpECSzNGO11E^oM^!c(^dj7#PtF7etiuTWh@6Po3P6$71!u_wZd_#QPw;}$pe~HHb z#K7;GaC~jxt9N|9b!Yncoc4FyQ7qq(KQiCvs}TOLz1RN9@ufk(JPh%_E4}`_Pao>W z@s%Ne-2KlbzMfdG>64zn`of7`R~p*B^f`R{a(emuudKf5`JFD<|0c&52ELf{yeD6% z5PpjpwLj$e(!kgAeZCvQA6MyuhdI77@Z~a}FYittpWclMyeDCW$KO!D_H_UG?bdzi z`7iIT+MVSa&R_I(KGzN5kH6@mvK(I++LwFdq{P>SaQdg$f8>VsGV$lo_}GT_rOx;H z_JH*KLG#vhc2{;ljKzb)kW$`BuVeHr<7E<2B;kI&``9S?JSX~<6(p4%b@rRR@odPWP5uMGO}z1lj2|I=45 zCg-=fens1_W8l;KH?jZG3DMra1q%wirPk2=)CN92_e8<{p~%N_;LOJ7B>18G+ilbO zz*@h5!}q@eXD^Sh;d|L-jKx-TItc>g2vu{=Naz1RPT#$SHG^WDVb<&*#5`F_Zi z{!y{xgK4`HKc@1Boco@lmBphv8p}l|2{Ik{&JQ7OwK>C{r)FDhWxR+`g{xC4@LDC-PLD7LO^Kz zML}@?xv&2J*O$ffBa?>|<-_`C`F78aOMIZS#`yQq$`t3^-z6Sknt8eFf{21_Ua{rXFf_#VYRdV{2=zrot{R;T;o%*W8kD>jW zCVco>YW>G?eElkfKcr?8?*uT*0{t|9#Cbve;`?BHoLw-!5Wa-`Kdasi{Sy4n*}!)o z|6?P3j(BD&N=}wG;oB4Z{MXN=kB{@h_ui$XEE%T1W>CK-d~2_--+3{}NBu2UcKpH9 zp96ds^k;*9N%UKLgYj`+0R2;ge3kT82)Dxc-oBKqYI!T4Ce`})P)ARpWB zQ?35g9zPCz=bzy9M=^dXDDex2@bw-Tf8jg;`#0qG$Xd70<2%5Qlm182@nv67zw@on zw?p{L$M(C?6^XyKKNuhNp5OnwgfA0N@83(S4&9z8Li;z@Fn@6Vfc(i%um8JO z8*fhVv)mB%eruk!kdiJ{>zYzF2m++O( z8eDi&;>XbWi6(rsgyXx_#D9K^B7FSZrp}GO^wwwcL+<|)AM*2Cam8avluP&)82D#b z*=mU&L+jTz)$d{G-{3qO<1c~#)_b$QWcjA~`9ObtQp5eZ$k(v{>hH&{wG#Z!IRD(( zRVRKGV$NNDo@>&-CI6WCAI9Hp;q%pd!TQDir{nyqyh~zP;y#}z$n{xqK`?*h0snKz z*N6Q6$0d9TLMBhy=3QwEjlVLTKa%GwVtg#%H@@e?o!t7HGSn~KpN4!1{6(|ks}nzl z>URu$y8jUQ3ixa5cPaF)FUKJM4vD`ee22uJUf)E%1itm@sR4-}L))(;{WE+ml|R-; zkpCYB^9T8-rZ-NmZ)1M`=lqED&y?@1@cA}8|C#E)HNNL8PQNngU*+?42!G0k4^Jfc zq4g_F=f5lc@kOo(#z%pp3)}hUrxw&N9Ump-7m)U~+UGlz5BX*Q;eb9+{dtke$a6`OTpi?V;MZAHv6jbAtb5cd34|zXJIR_&47)KKXnq1$=sbYLWbu2T6XC=QAK5^*3#F z@~GGU$1(rO^&QcFIw} z34UsRf?~5r`W*Sd>v$+oV*QHgkU#K#G>nggPw#$XY(e73(EeE(+Lu+qUw?Dq`CZ7j zfIoX|ea-O|sb8DKhu%MedHQtZ&xQ}SZC++FxBpfc&M)ZxH{{#EKliD@ z)wubgH1Wy)H{|2Kn?R z_RGDTewCsAUZDSa;6MKL=*=8oXUO*!Q@wvT0{`|}C(q{iqSh4u3*`T){)g;Oz7syz z4$1~&54Pj--^uX&r8IE;RV^WUEuQjIf}fQS{c}E<|4UN8inL!k|EK(|!|yBW@ip)r zvOaGUzDxLYe)Vlf5}SlJ8uMjPDh>eF!xTspHXCdkv|9fAG$vl`8yy^ufM#p zs>gSc59hxHclaDhu8%l`uix>mL+}kN731pZ5RA$FhBP zwIW>mbtvDLmFhof|BT$r;dASj&!5TVzsxZIk?&3Vy?+O~I+YXgui(J@U-YTq`lq`5 zMgN2N|Kh|iR;^&kBP;R68q)+_1x*iR2AQO29UHMT?E zPo#gJMkOVGM?UTk#Os9OcYnP@HuCc-ar2A7w)}4p+JCsz>OHLe8T|D<+<%Ar{9Ldv zE{kTrnA^8JXJ7om`;YtcEVz!Z|M30SDf>l)w*iy>L;p8d-{3vpAT<6ms(n}oa{lz~ ztH#m%?|QZM3TI#Uc$rF1{*LvJ``2(EhB^%Oe>vK(EMm|X{u_k;Uu%!^?hgs|U(7Vr zKkPrieWdnZzJ1XI?eqCJ{zW+rM4?KP8-+_mpG5+Pt9QEEG5Ncn%;cegk??0@6ygrTl%*8RN|0{g^ zu|)gd_}{PN>5(XU{%-I2ilO$Ess7>mFJ5QX$07dBeEU|y|2UI>t$!}#-QN&uUmWJ! zKVE;v_&X;c{>`J=e{#=(KRNqq7N7rk{TQ$Fs=p!rEinEuw*6IUviTy;zBbv1>&vLa z{>Qg3T1K;9Q%_pN*?01tG}-h2^?Q2#&=vlAh>#fn$^zfT`fm{C|2M@Rzn`-&4f%)H z7jYd-7lHMdi1w#7S~rNZuMGV^UVp^#$0_RDS8dS$BBC;r{~KCf=RF@NJpcdX^Bl2K9dp+UNV9J5Sd4?%xl$Z^(ZM*B7w=b-})9i}laPzuuXrnsN4}Vf@4E z1GvvymxB5S``KRn;aogj|8vu6C{0zt;CZ+Z%tG`rrB5rrn%< zZR-Ev`U2hurpiG5x5N79$N$TnmYTCKOydt;-+=q9?9+YwqJ1>`^Jkt{k+Uxim#A8Tx;`K8e>6WqGLolzodm|MpyuOI}Z&iT$ z&++|FygY-Q|K-+f_!4Jdo9Z9tJ$RqAs0j7n8RO6A-;3XV<~{!=H2q5sG89(azis0Q`lHJbfdV@|U3e=*yz z{(<)I^3Q)1~RpIPgWc@+?GBkM_nOc9v@fWT?Sm#6icSryE`S1MEh08emDx&|X z`_O7a{ojH1`SHK}3tP|S?AwO(UmSn&J}(#SiyqPJe=y|j(>eS4Xte$h*PoqQ5dWTN zpPzrWSoUBg&b~Mn%|5O}tJ)BM8}0M+&udPvc7nAZ(SN*8L0km(??n5&|GznN`0HO0 z`~TE^3}9c}70tf*ZCg{;|2w1gKX{*mzS#F)r|k3jfBWvDzjF3<#QMkkAe?%Te|@6) zKWp_p-uoBA`~Tz7@-KBC!zEDvebGMezq)HU+yCo`@ps`q0oefJe|I$d?JM5?4(Gp$ z=s(^^U}Z!6bJ2cNcKxqM?wLPx_H{)2c%Ol22=VWS_WAa|Z|Az}IQx=}Ke&eZpYaFQ zA6&36?m_!}{g3Ybz?qzVO~xNLWcEpj1eUqD5QcJ9eue^*ichx-WR<-UC#(f*=u%2#Fmk2wF- za36rx1nPeP`p?(@(=Q%az}eRk?c+Xv(G=?cezaegU4L(}qD@iGzKq!arPkrVzQ{xS zy#J^Dbei}6i_rdGMI3){{ZC&3^`ElOpMRZkVFmB`%Axi}#QqQW!8^^M{vSa9`Tp3r}Q2!65{AcZ3n@@VrPY$(j zo5nwQJ~^(xiq=s75Bv6uC)Qt^;-LAj689fW{JiT~oPA~TANIfF`lAc>#UsA`B8lsN z-2St!M-vZ-Vf@zy`xtiz?gP~l>L2Wv62(Q{zx>7@(f?iR&wrA$uMF`=`?wFmX#@2? z$oD^N{~=xyT~qQ@`NaOp5jy1lS~vTJq~u&Uzg9(p?H~V+e#PN)_UiWkEEPkMil{MEOg z3tYP-u@8Socs!zg^nZNOT0;`{C%7T@ZQvIGR}=gAceEe-G@Actzkkcs4i|s9?&Typ z7q|+Z2aEGxd9fcK^@AV(ctrcSCE)aLhILBpPad~1B>&_RfB#7VaP6~#@weZbl+=gz zW1mIyAM=0fme;E%@_%et{@Y7^`?<)iNbEo1{~hhe@pD)qAtlB?KJKbZx%i7SrX~pm zz}3`#3qSsGh=1(!X#S)9l6U=DfwQl`elBpWvxELyorCt{8QRxPi+Sr$q3vIN1@#YH zMeM6CLHn^UGV_1d>~98g_8qXF3tX4jcXNaG;~CnwP8)GCXJ2Fe16NiG#$SEy?`}=i z|N6}QuPqym;SNMO|nEzi! z^B>#)3s1&7bK{=|`?artQ~dT1TnnC;o$7y^ z`Tl3`gYj=eX8w2RQn4uOe+>FR;5s|~`-RcIc7yig5$&V@FSlv2H{t*2u<>7_|G;(E z`1gaNeOc7km-2sOH2=~5$&I&p^S@^|hK#=o?B^nPZP30rD`-ECp9|^t@i5wdso>pz zIR9OUe*tiG=2eGBgYN%_AyGyj{mZR_p-cseZq#B+Z8%LT6bJZN8iMCw07`#&tb z&s%>T6K3Cn_!j`z`66gv6Z^3((fltCiMXZJ%KXIrZO`bC{Fj|z{tsN|!eIZ4f5*Iz zN3@Umf8y~O^%MSwwm%!}7a;edpnd#1+K+9G=0E1&2X(4>*B?UjPrd;44_sO3KW_v3 zAN{&t{}TG&c!u`huie?Z{~*-9>I(fo+P^gDzjeOf|K?!-zb!NW+pehL?f(q5FS>#M z$ZZs~Z(kU+AIHyOg@lwC|1atvEWz1#H~IVm;M&B#CiY`rW#<1GYpYJ;>`U|?xSH6v zFCqSCX#e4bJ7;kAtp?Z>uf=KuV?qgQbD)fS(h3tXpp(7x3*Xg{8z z{SW_G=)J!vH2-9G$baCv@IDW0e`1S2|4iloj?DbOrQy5Y{a4}kw}St`m92vQyS<73 z8QLGdKi_-5XQ+K~2lx+MO9t($?Zp4DGxL9Yw}pLK``f^O;JU=V7(o2b(Ec6c2Nmb+ zJ7B*6xYkuc|E&ju_G8~<=Kt;IuKAI(uhD#^4BC%pXn&X6*n56JX#HDX`TPRl z%4>uE+arVaV>>hR|I?E>%{lv`7xe$YRqccJoo9pg;~Cn&s_DgDIs2;b%gGU>0J!po zpndCM-+msBe{eru$gq<-jN{Mu9j~~JvoFTM{1>=($Dn;@C9FT;_@ANuy^YTKp0h8( zegSZ;PC@(XZGV2I(Er_;)qmUi_j>o=gyx^#?Z-bCxGu4e^N%?CpP~I*M!h(UMrFT1Z6#bw0dtdMUqv7%2 z>)S5?uId)FkMj?-A9FMF|Dy@dc>5ni?d$sf^R;q;Yu^^MuQ$W`7v_J4_U~S}*Sr26 zYG3RF|ADKCeVl)!{QoX9|7+~1?(M$`x8DH#2d>jS=)Z*d2ilKkXun#|CLeP4)qe0F zxK@v#eH-QB$NcdZ?Z-2;zx$P$-u|a>`#<{j z3()?Z#6HY_(0=TP%<8|<$-dt6>B8+F1pk36`vmRd{0HsFGqnF<@egNm_T^9DKXBFE zLHjuVIynCC%gq1tzkbiV{t)WFJOus&SLO!okVIR2vj z*n!OaZ*%>JEjjzD(6^rpTq`eVAID#`AJ5SK<)>D#?N2rG$Dab^J{Yu*<1gBe6=dfB zgj?&f=ilmg@E^D~v5(^~+K*>wzwXA>2f6rb^dGqLp`iab{-XWZkD2*j>FuxB^-u99 z_zztDaL_)Ezi2<6q5VG}Ztt!ChW0-i>=yvneI#ff$6vG`JD8dO5B>c8m7INX82ksW z92B&V<1gBeXK4THnT@>XFNONAznLHjuVqW##R%>4gKRP*lt3yr@%3jPDvdOT+fhko}v9cgIX`->?;BLpMa~0 zeH?$$eylJv{~KLdFrKq7nu7nxeJ1EXuD_%GIDQT%dLbpw|4!;*-u2(m_=}?8KX5g% zkL&MfKlWQ@{y(G|x8UrnVzB=Qx$&U?IR2vjIDRg~Z#<0tPs(m{Cud)S{Q}_H#6FI{ zXg~IQX8ymj`0AyceOVm*2d+CY=s%9XXg{8z{icsx!`m+b^^f)^5&JOyqW#z(nfbr@ zveoSRn=T3d1J`;nXdlO4v>%UXAFu!4bn@mu67dh+|L0ugufG={cXrUeb%wwGU5Mwu ze`eTMLFMoY(MOOjc6bJzvC(A0O!B@1pEiCruJd~r3Ln5 zhcom4i$SkfyGr_ zSCqQ`yTiZ!5=a04isnDsf4b|$_c;3+?B@d49u>52Pxtkq{dh$CSpTUU~{XY`Tf876g$$dY1`=3Mi|2qSr{(H-41x zKXm_}M*orfb#VU^?*Bvkv7^!a$NH~${w?0Uvg-BYcBs4`VU-t7qJif|ImIsqJ8v#ggola zKf~)E{Rghv9kh@8AJKm7cr^bp|F6q)dnNLJWZ3?Hjrk8;XHU?+r2a>=kN!WjqQ=!+ z`**>90dV!VLHoG>GUfk?X#V5;Yjn^1&P~`4oqx*5Ape2uxN zMeF(;X;L0C@_BF8|I~mP?w0~QrAzyRtPY;9rFTi!jk@3H{uWuvHe=@XR ze%|%o`fKR?SCxnPKXB~@!SUbu+PClG_GApa2;Y_68o_t(fmjI>xYy|JR*@9|FHg%`(Dt0MeN7%b8Pezx4gjLAKi4` z95(*wKX5g%uZjIw(P;jo{nuKbD&XwPv;Fwz0@qy>^j}sZ^&inb#{bEZ+aKrRudejl zUjcI8584;!2JOd+Me`r+SML7$P|m&v`?|e+;oJk|2}a418}VmgZ3r-9qq*<+Q<3NmulOg#Qf*Uu=`(a;1>W_ z5&QUev>z)Gt^RTTQ?$*z583%Y`VU-}*jLp5i1xAlotoNuaw7hr^M42Z2d-Qej6eQ8 z<$uX&{$u{v(Ivm&@=v4xz;%dy{5#r@N3@UrPapWH_xy|TVfX*YB`+t@1;DkJ2mM#q z`0FpR191P}Y0>;g`>zZ*)7$?TYG3W~`MJP#ehu1pD*Ejs2kgfq+Q<0M&VP3{7k{TK z%>U5-??L#N7{}W%HIgUI3 z(fwcc>mRr_v9G%L^V5|7r8D#Y=|clFcm5*=`1W&wtBHM0?8hV8$NXEp_V1Eh``5L> zf3$z!*kn9#{ZIet*MA)Azf3g$(SEJ2t=lB*ht9vPHGcl(0@o750`~0^F#p8-k7ytB zuiLque&ph>c7p%NEgH11Apc?o(Ed-4=0Dp1^7Ep9arQOX&jqec>^sDMJfeMU|9vm` z{xq)ri(TM9a5b@y?LXyz+06WJa(&hJx%Mw%{XG}BR}7Yqmards|EmJ~xxjUaeT&$SN3@Uizqx+Y-}K>A-u2he{!dPa{vWvNte}1OJ3s&OApY?T?dy6q zz5SP=`R~NRf8bhGgZ5=#fBu2?W3kNqKlb>S#}f8K=Rfi}-+nG|)pHYHeS2}xehi+695U@l9mf8D-0hu4vi;v>;6HGki;4Yz{QPsU z{v+DQ_2>7xpYonR5W4 zfB5;o5&e&7ALCyuOC9Cn@6Lnz2d=&;=)bP%$H&F~uY5HB(f-==dadKef3Xbw2d>p6 zXy1nZAMM8@+Q7 z`_2H!f8^d8wC}d?|89Z(IDRh7FdRqwXAN4iEnz=&{o7dt`43z-@wgK2AI^XA?|>CC zcpe}A9#-P`=bZWbuk82-_H%(NZx7nn@OQKmk7ytBuVuk?8@c>*(SP7N#6JEV?Z@DG zfcSe@iSe)hOvBNM_=ont0{YinsNQLny}vt*FW52emh2g#42e2 zz_oh@<8S@uk3abNSmkK`SB5WKJU4Mcy;P}6`S|>qKZEwA^83F$qWzy*uJQKYhT7L}`|tlK1fEUotMTx@*I`qW7SMj>bND!*{fgJ@ z^X|V6weQ07ukw(00r4O6{Sn`Pd=A!2_<7mE&Y;@f`dg^~0%{BOFGaLpYRY}y_!Dj)u74H+ z&nif7-@V)SAMIDJ%KMM=uOBbm^>M;}g*fED`*$$^)v9S&Wq}g&Ux6L$&#<&R_gVS>+Q<4Az5R7C4fCMN)p-B0|NCp%6MrS_ zHwt_IySl~q1D&_(2lF3~J0bSMj${0{e&3^<7k>xhuTKTzua)nYI2ZgWP3+@w6auCF zfZrbD@LbfoA)g~(OZ|%a4m54!^-rPt#0X!X1ALq4!{aG1{9wUzGf>~U4E0&S*)XZ@ zs>J+Zq7$-zP%ZTPMOodCpFA@dKL_R&`2HNs3-!p$NehJ#zg+k}m^BuI;S;aUnLQv8 zzfgS|^tr%yh&~JYR}0>6TMoFI=qpu&&-XCFlUqHpJ~988k{=R3`Gp?`h5V0#`!7<* zi2*qmz6ays9Q;1{oaxuAD(~IE=@YGeJC+6h*BP2bV*ijjP7FCK{r+0Oc+n2OPd*p+ zE9S}AIp^==`VYC<&p*mReR$jrv5&{GU&YexI1#^^wnTl0a|^xwrQ!YCtI)p?3e~61 zOP^nO9CcyoZuZA1f=&I3`ri8MjMF%Ma!s`OS?8zMhsRMDmTuQ*@mn^sj5j|Djh~zr zEq*#gA09_tSURuxd7KIu`2piMyhT=bR^OQEEnmTQ5TlViKegit^=hx zeJW!7#0BZ~;c?W3rL6pXTEFoi#;h{>2T^hsRMDmiWF;j2A62ev3a| z;yqtBRG)PYi~|Hf+lQ*1K7M!{bzx~&juyY-vo}rT^vQ_&tUBrS;c?W3rEN!RAGh5z z)7u{q8b1|reA6NN@Hpzi((UZe^GUU&e#QDNyL#t1PM?n0KJ2>ba zX??=m-ya%36*2$R#p&aR$59uS7F;Jx2_iqBzQuhX@%I0Q>eCVRS@qKE!{ew6OZ(ht z@w=$Ox9t2>d=hPbszdbQanyySbAB{^*En}g;Nqtu=AV5@`uO2-)P<$06HQ-{pSrN; zM>?WDQ9r#tJdV1sbncHfetbOllN-4B>4@W-6QU1~qb@AnVbS`R8!nwtl+_n;ekvQJ zj~^aKU08}u(e&-SvClM4pNKgAx*_`TIO@XE-5gEdch!pb=k&>l`c!uM_~CKXg(bd^ zoOFAsU$Ostq1jWtS$z@5H>+WKeRv#oVQD=cEq+JFEq|WVC!A>W10A9dkE1RutskQG zFCScf)z6$h8L@x1FHIjmJdV1sbR+7!c1f-6tiFix6OGdA!{ew6OJ`rS_?3C!C3gOz zBIvqb@Ann$h$f{BXcYPM>%!TKjNA z^x<*Tg{5c~P2a0)Mtb*Ght98LM187p`uO2-)P<#P8BJgPipy{1^r?vYtjp8u!{ew6 zOZ%>9`ffhB;&o1+j;K$E=)>cv3ro9qwD$Y#x=GDBePTwm{Ii>+j~^aKU08}f(e%x1 zQ1d=cpNiN&i>B%I;c?W3rPDu}z7Jnp?>+x1ynRHBpA(`FkE1Rut-;as?dU zX!R?vNFP5uj=HdPhDX!aWptJDoIV|~e%%m#cpP1+H?74P|Zq45(D+lSpEef;n^>cY}l9Zla8drxfT^r?vXCt9Z0hsRMDmhOgV`WE(R zSccQ*MAYYm=)>cv3rpLLrf*Nb`)hIfbVPl!Rr>hhanyyS`YxKj1HW&m$LUiM^Un>@ zhsRMDmTp9S<-2@7nA0b!M7;jdI(_^U97kPPrrrmgI^zxgfb;wEuRPF$)2H8#HojRh zy*@mSy08>Eqk@9SZ>aC(OaEZ6FNj&u^yv_NcpPfy0CPXN4vh?<>MlJ`-rGdT$x@U9!FhRO2d94tlwH|8YLb^5FS7A=~R*#{;%}< zpcA4GkE1RuRlR7}KQ3?lZ9b<@&xw|Q@~ZUl!{ew6OAGd!rgc7X5aT!PiLbo-Cqnbj zirBxnA^Pw*>cY~wEn52>SY!HcoIV**pSn7I{O~yH!qVCjt^HoIzQ*I6K0Bg5>zefX z@Hpzi(ut_AmzBGR(`P9cjHu6Un?8Pc z9CcyoUKCB=qf;K6!s$~H^@(fK>%-%y3rn$Y1coYYiSZj#VYc`DsnGVTBhFu(5Pf(Y zbzy0ZkGB5Y?(L5cbNb}EX#KNnmp*=Y9Ccyo?2Oiak93$fp3|oy?!R(F^x<*Tg{5p9 zZTx+0d&hG)ePV62_^I~kzfUx6@+A&p{_UF9tQn_IM~t6!U3z_Z9CcwS z-}T?)YSK5P&qV(E^y|^aUmc8@Tnm)Tj`uO2- z)P<#Gc+M^Ad*hmo-t+xK^{I&SdvSeweRv#oVQC+Twm+wI`)j@DZ-(mA5$C5)h(0`y zy0CN%&&kF3J^58-_WHD#9j$)l4e8^D$59uSmf<cY|~n*Q_HsQ<^>o5x={{Qv*cf>T1-ib|KH zjwP}eVJgeXk{3}bbQN+WN+GEjAqP>i7S(0TiBhyslxfqUqNGy7)#_MF%2Mz2<#o;T z^?ARhc|Pa)d*6QkFkO$=S?=fYoafA3bImmu@ST3onQbXP@yIB6OS#e>Uj>)BzYlca zf-*4sX_foX|2hD^Q3KW;N%2W${nH2^=)eV~xR}>}Y#iJyruYoWRxqpIx^QR9CD87ivCoXsMfeu_y274i|HM0 zI^_AAVD`WA3b!Ai0~eIxDBk$5&o!g|eSR9=zA*V>gb#G!f>O`mjb9#oDoftKpqPBB zt=m7)feT7GlgGEbrucXNW7I#*9G?bPy7@o{E-0f}y!hVn(RU%mCz$6S8sP&SxS$N? z^7zhv|M;IMzL+^b9$w}44|L#yQhv!BU$xIv`itaawlAWcn-6s0f>Q6zTmN@co2R~{ z_(W~q{CI@$feu_y>ZZfX^%wR5w9k#YeA$TPd!0G{Ywz|?8XdTx)EDy3KhAG5xiQ5j znS3$A2Rd*;srKQ;_lQjg{ky*w+P)~}_(fgq_78O6f>I6V&F>y|bnSodzef2axBctj z<_nAtTu|zHy!LtA!awNsIZQr{@PQ6oPzLjP^BaQ(eexRVAM^Y&?CACnbl`$AK9e_p zwR^%R|L$*q##b@(Lv(WUfeu_yh9B|#8&dOu-zYvYj@Q0K2p{Oc1*KTXtABq~>-#Xp zCz*Wm8n=I-0~eGLldt8Xs!b?9#T*~R2p{Oc1*KfX^KYm6kRJar{ZrSv{R17ipp5R~ z^=~JiI+Z^EVDbgmx%of`E-2%WcYofiBgT;H@8ui3{Lly==)eVKWcE9>#|3Z<2k^#! ze>~pjA<93^Y@bzEw|}4m7nEuWZ~T{jvBn)FA9H*VT<_)s9k`$rV|nvy7uRpql;RW2 z^#>Z^10A@aluLQ-%jplC)_~%ZOuq02w|}4m7nCuRuk)hA$?Hdo$tP}f^MMXrP)3V* z{f@uM_%iuqH@APF0~eHXGjD!lY>$w$U;IT{Se`ow~$ww5QV)AK(4|L#y685FCx!@T1sxAADZ2uxAUwE_IKhS{-O4yeQ zckTOIt$X#)W)z=b&L2bwALzgZWyIt=@uxR6 z$v2gE{vmHk{DAL=GGlZ*e(Fcw`P;a&4qid=DJEZx@D&k0nd9-bK4$Nh6rY&J^H23~ z`xVSLS3vlpIy}DT z&MlDhr{Y~+e8bz^{$-7B`xl+TJO8-#!-eB1|I})pf1;n)@F(@egwkK4bj(QW@k36F2Xy#tRW`M%}FSKQ&|OBvnf zi$?JJ-?3LtB-a<~n8z0(e0hYAyFca~Z8tTf{F7^WeDY4We;K3O{)Ibv{?&i&#}i1t z%{;ys;VUA1;zb_cBOU+lMDdBGJU(@o+rOOAZU2PXr+p9I2yp(fsnCsF->Db##s@)P zH(%Q5Hb+pIH@~s>kZI3R{^@x<|1`o^K={J5d3+sar@B&naz2kQyxZ+x*66l>(dE4M zZ}Z7hhEsfD9p3s9(a+77GP=#D%|6Js4#!ac*46JowlDfUo_`U-mq++inm0bJI%aQj zeo{{7@yUDK{$-2~zCam&%&ULj*Qr;Ximzhw#Ry*!<1^1e0A9O<_*QQoTut&Z>!0fH z_Ah62`}?CFJpZPCc@w#QBVzIe1KfORquYF<505YW;lpPr|1^_NBYXvfPk+nf8-GpD zLnuBmftMfQK(~KcqwnG0ue|x^d$)c=_8*eTC+>CgrHpR#X(r!E!}6C={wXG3gz)7N zKDCwSU-v@AQz$<132%HL?{oW?F}m%a{Ef$Vb-vboicd4|Ux*RDBEl!O@%TP#`|@Iv zj~QR3-2UZ^Zu_UU^Z2&!_=#RW$&7Dsznd>@bem89&g1)_`D2Gt{)xAE?TbeE3J9P0 zgU5GH6Fa(%mE zwlCswH($!=HlN&!$M^cI7X!*a&E$&^zC6Mw_U7@;X?4s9icd`9^}q57w|^O<+x}@L zU%zGBCsKTp$rmGhMTAf7!}IUFpclD5LNWQ&P`7_Mquc(;6p!zOrjzLVFE{ey8w_*v zrHyX$=_)+FHrIjTQ%t__ zS+{>#quc(e>OB9B?eZHLU(Mtb&$;?br3JU8=sm&KA%%D`GS#dzO>P8K6x;YZ`P`xpQ7TcnS2`IDimo>WWpJwvq>kJ|HcT`M1@uHhAWptZQ9m?}Bp1uA-%0JEIix9p%!WUl4 zYhSKgv|%#ECvIT2f1}*~WsGk7Cu{Qjt2h1xGC#z3%=QoADWWpFEt$cfvtK$nl?I@`;z+d?};be4;jw@17kW zlKY1#W_%-rFOTrauDtxc@VL9ilJ$?-zQ~u|{$-4A`=^=y1uc$|6rX1L7bAQ{giqDs z#rOGnL0yVZG5u4oxc$o+-S$t`qaSpJe8TeBJF|#^|?&Ghe%?31f0K5-Ind>V{%^QDb$^Qrp0 z_zvHE=hqaUX2w?|dwEpeqa@{eG%Pw{YUtQ+rO;QZU00A9^Yx-|Gt^@kJ-M6 zH{E0gBKG6+c5Cbs#N_G-}A-?YJ%IpoY8InG?Q;z-*-4(bwHc#aA); z#3VOg%IG$qIF`pZr0(vO6rX1DMF?LW;nPgM$%keir}z}J{>iu9{$-4A`=^fM`S*US zKlh^eG?Onz_=*UhJf6q*-RhooC_eE6v;BL=?O)F5wtwPIUiEFeVeox+C zCT`%(?~18zzLe2zK6L^wzUO_j4>>-H~ablbnUA1{9o&dgjx@-f?& z;5|2A+UPc)I*AwGrQ6S>=XaU@X@swU@X3>Td?(&if!<$*>0dbA?O)dDwtwOj9^a@M zBbrn36*u$xH!;J_momD|rh@4NlW z7~S?y+{??~5qCDA^OwmNBYZ`KPoBo}@3X_sAlK(;CZGDi?O)F5d-!)ck8jvxqv`#x znE4URbn~T+Zu4m--xGIDBll-eOg@e96%amgKd=5}`-&f_{LoCkaF*M@tkG@%)ET_^ zPPw*(oPQSGneE?)ZoZV!Z9dtQ$2VZ`9pv=|@e{NCL-_ItpJ>M8Th#qUvj3G#|D<;N zmod8SpJwt^{`l=PsQRavd@;gTMEKO1Jpba zZu_U1d}9}nB-giVX8jW%yZKT^xB28yUj3VQ(KF=w4e<-FeTfjhJi-?-`Oa9dp*~gr z#L2w%5%Lqae;K3O{;5{H`gh#kJIMT%%=z6I;VUA1@;n}2r-iqlPx&XA`JooN{mU8M z_D`J8J8aX}?xAOYm7~v}-e5y6izsDbl$ou0p(?7Mu?O)F5 zwtw;>9$(4wRxeQT6`OheZ?M$Omp1wye9!ap_x-K4@2B|W*S!5{G{RRv_`+{_&kxw} zRf=3+r%p{?x(j zBgpwvxtkZ?2;s{keByFmevDW?hdjSjOg{OA+rNy_ZT~cr?~rjzmr?Q6OuiW5DDjifp@d?3epVe}=e>tP?;UAN)e%sN_NWOD;e8CDgU)tz4pSp_YU)#*mH%Pwa zy!Kfmd&sV?d`o!##Ry*!;fu%f#xGy2j*g=EG?P!QcKeqzy6vCn$@6bl z!{0-SPi*1&7kuUBOB>zh(?fanZ@+FW$@Wh%`82{;K=`8Ryz$jh-xlckapv1SgS6lzx$G1>?Y7x)B7~v}-e5xBSzIw0Gk5GJ?$)~<` z`cXM z`h-s?KBal}FI?;PFKcw$Ke>k2KDRvVEpmOfn9bu8>)d=PquYGpZ@l?~YQ3(mNczX* zix9p%!lxsif5p8|qwh~)^2zmX|1w6m{ZoJP+P`BO-AkVT%HMhO2Qk7|MEK&JJifU% zKSSoPn8T}oYJ=OqoY8Ing6nzl-RFa4^#0>azF?!9FKu+2Po2zbU(R}GF+D!`gBM?o z@D&g~xt+(iso~S~^N(hYvVVH%v3-6NZgTsVHM;#F@pfMOa@U#5uO{0UW_-nWZoZV! z_uw1KJ3pV(Wm|QMPc!2iA$)m+FMN{6_i&?DHJ=gr^82wxH5lZ$!t2OUn>M6M5$O#jp`ZvS#dFK5m_2k{PG z{$6;^@f|4t6q7I5?B+`w-R29b@#6c@mEV3(@~vXFe+XXz;S+s%@zs~z--6=POulf7 z+rO;QZU4gkc>PpV5xyeA7uDzSWzXwF z-aoCGeCjv1e>tPu{;5VhzL87MqUZ0K*Dr%@Zoah9ZN7Lk@BE`#-yFGri9YQm`=d*b z?ej~G@D(t=TY33WtLq))`W(gdFWm0-FKcxBM+Oym{*C$b)z_)~5MT285AnO3FJ*L_ zPv6G#?~rDn#1x-o@pdf4Ke27~S?SsLmU|H0}NxJ$`2L z#Ry*!;gdso^Y@(>R=J+?PcX*^>QA?SIiuVDMLl`-Z}i-zFN#kx`NAD;|FTB6{R=Dd@^?q?BQ(XQzT){O zcDngeMz{H*4DbAW*Uh^kicha8&ljEOUSAR+e0hXVKg-MCBYWOR&L2qT{FVHV+rNy_ z_werx-uV8spZ_5D2UbkJ7~v~od;@uWjcZ*?_HT-rA8MD|znsx+|APB@d|f;DCf6sb zOL*rW!QXDaw9#$8_zK?q&=a>$|Ax$unmj&@@D(t={=Dpt&o#fB2boiEnuEl@;9nWsDBKK`CG0wa@M6pMD+1r#|FTAh-vOm&@;$b?UnPn! zWb%o<+C2w$*Z0KC;%!u;+Nqn;r5ZxTD`_76T>?c??@XLNWSl)66e z{G(!>ape4;Wby?mH(%Q5zy)Q{kavE$W@6jlsrsjxd>Y{^V0;B$d?!>^7g2obF5dVw ztm5`BYjp4p%3wEd{L-mrqnju`$;=N?)yRY)_zDPL(2vJAy~Z8n_)1ma_hw(cU=Cn_dijPMl^z5tn5wfSDY^ALJ}y?c1=i>l%FFK2Z4UMQn?9)^E9 zOYpB`Y{QmRd=--~IMB_PHu@eAEqLq4-*~hgd4GUr@@a&xfbhwsy!BzHOlV84FA*>D z;u{|1_AhI6+rQ|&A+}WR7~*@uorkWW;;RGR`H?u-&6hHIxiA0cAlSwmUp+Hm%RG{g znZFUjmq+-b?L5BsJ6uLypAoxx?Xx_@?O(>|d-(S|kMF@vzt$rCtH+CPjPMmPzRJA* zT%L;?=*x^yn*!FJ$@`)^huoHM;#H^$cGB)^VRsObf|UaxY}Ze`+{y{L;Sbr{w)_ zdLXa<1$EqfX`|bG!Tvn|7SF!5H|3w+pI84h!dF1}qJ_NtoqFg6^!n@rczj`9w|`lq z+y2EN@A~QuEpr!;{xSK)5pKSe(f8mh@Yb(g-SR|o{gtTB^Djd9@(5qBFK_;;>xYBM z`4`C?-^+S#|1w6m{S&A0=Et)wpQhUvrhhTQS48;2Sv>#Ne4nE4UuE*C!0lho=zI8g zKkxkV(FN0IQTY)u#|OcYZoah9ZN8`mZ+v?0goDWa+r`1W{Llzr0pZi9^XlIX9i9m& z|0I(ytnc!UiU%<0wS1By=^$vpo>_=*T$bO?{{!bYQ4 zP<#QCPaW;{FK6^U{5zD#_gRBCZ>RVqlP_rK=1Uvh=8Kqocl8}qjp7TLd>Y{^Abdeh zo_`}|zcPa26NmHKm#~rBzpT;s@Xx~sufJIJ9VseQ7%^$uhow^Z<7@Dz(o&6jp|p(6 z_vq8_NnTfNY&pI3;J^MS=RRM0RI)Moyi_{j|F4&@{`uNDBdU{pos#)AWjQk8`*ptKnTPIfM)Gw|{L{d9 zL3uvV?eEd+%E!0(Kl05A#y?2$i7?>{k4^kj79Z#~pIBd>FK6Dr$Ll=8G2^;iN%6@s zNq&gq6292t1Ks`_Y4N4a`|a2Hj^7!3ayN=k8UG^l`C+Dfd_lLrCw!=UeiZ&kzVCL% zO(;Hb!l=@(md7Xlg;$p61Ks9>_GOr9U$W-?_UnAd72BUZo#K-lMwWg=Y(8IhD9;DF z{XNjW0AH$c^84)9`Hp-1aO&O^pE7*Pe12r{fo^{fv@gJy{~!4->a}Dl#iyGj@eNK$ zet*=pynmqEe9*oCU&g%Oex2|5>YeY^p!mc|37>|~zrH+QnQniP$zR|r!u##ld5#Zz zyzaLYpEP{oi6%cRKG5y2f%XOXa_0T^>wL$x|N6N<#ivhB{1Z(~{q9lTzcStWBFNv! z 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,kMpAdl$o5uiO$#Fj(O!+77 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

  • o zeziIE-;%^H_}?t>C%yPO0zaRXp33v?)ie$_#^gWLz^)X*zx^qDyYq+ni(lFOkFT`* z-^BIdJ5=HiClY@!h4`TrVB-V$FA3GBF8{;sg`!8z+*!Tec&7u|GcJU|7 zUlRDktL*&E?c|ScCjV=1A%3WVT`7WpRL8NiT>NSa%Aex5cK_AYcK#-=58t7Z|8Y$G z$xXx$tpFPz!2i!J*J!!ED$l>nU--_>A6;edzx7-n+)#->Z6N;iGUA6C*p(vqS6|}X zmizNX{)G8+-`n{WzfY~QVg8Wk`rw8t;WB6sCp5GGlKV|;h0z1F@+ zQ=R9})mD7``;(ntePPd^dae&{sKg(QB>#)d53R=k!UtV$b@{J5kpH=#1^zGX{CTbq zZm2YVChybu8Qo3&7g~*f;w7PT{SzbqnI{={c6pPye+a(|VWKbrYV%!BPA|J8SP{^oY_>+{L~67xf`KU3lVt-}v$c|V-U zpDe-S|8MsEQSp zA8`IaO>UIIpWik4(ZwG!f8kF%zxvVce?8X+H&o&eFJ%9jzm^rerh>oUF6W)$;!l?1 z_0L~+{&cK;{h84Df1T|ru7fh#Yfrmr@5W{ z`Xchb_y+Mq!D}k`CpX>dy#5>fhsK}c-**4iFZTRt;`;C%D)}FbCH|QCp(Z!V;GfaB z(u1!2(MwbQ6x3LJwXBu-_g6cAJ=X^}RN_yVzmfT&nVq&OfdBgqE^@B_ldb9aCD(!Y z)628@lMb-wPoC?88!GWf7nA=9^Fv!9XFh^|ubm!qUw_|*_)E-#ja2;q&F+75JNeZm z#9v~5DE4P6_;(-tgY*8Aa2a0z@Ourh9q>!1G6d>;3q*q^E3Kk3MDEf;^n{JEv<{OWgm z{^YqnxSo|0*88gv?)9#?G() zwEJJr^}!95_=EApUtoS{HU4W)IsPLTzh?eiN8*o*#`u4%-Tyq-2RBsWPnbXD@e^8& zKRRmq<}UuQ6X!qkV0##U)L(Z0o7>4BT}J-TXMQO5XDagN!fD$&^Cw3Bce3YCaH)O$ z*~InXJ5=&tUC#N#<7dHh{d~yLqnyVtn(tpKENkZv7uxTitmpdRhDz@hR@3PEQC+vb zWvij4??FCx2L^{7IM}+6p=I5&Tbm(bM_-k!Jql@`C?M+Wl|h`tThp z`JXa>iTR-!=vD>r|B^d)(aA)|_}`z;|F2-@PsZE(Z#~xsH&o&et|b3s=7+XI&U^%a zzdo&z;T?AVCaw?Pp_2dl8uDMQO!IF=Gt`R<;9uk77cY15hsgg`?EJx< zcK&*<4{oT$A50|vl>LV;CeD5W|0$cU|D}sRV*cE!c7An*eg4XGeQ-l1{%8{Mhs+Pn zQZFunzu&tnujAs^!#V#Y{^f-6zn%Q*TH-G-KXfs1_7nKOdu6T3F8+l1i>ulFkB8Wg zznZu{e1}T@$IKsc{zJ3Wi%Z}?_R1*d;#cRA|Aj7ges!0S|6Cv3P>Dahj{Rr;0{Zb{ z-W0Q#UCC{>*wMccK`LgcK#-=58t7Z|H%!+ zpD;hv4^882{VJAKpy<=Xl(K zg4a~=*X{6q7Z-nm{QrmD|L{J$|4m#UzC$JdQ|1qtA8K-=4F1C|>Eb+pqL1L?-?i-g zYPy}jp6i1fD)H->{I6wxD0oc;f0qx(IImwM82{H6_&eM4C(rf44VCzl2I7yHA8K-= z4F1~=p5}c2PlEgx_*23E=63Q&lZn5M`Jv!775qQ$zc6&=e>jTzZ}FdY|I^|2%(`brUy=MS$Ej(@p6xS
  • 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

    N8!+c!-Eu;>hQtN~M z%*XZTc)p4>SM&aP>OW`oulcO=;t`s8q5L&p3V??Bxc#-uk5QC6lOzwJEc1f?{u1Qu z%$fE7FOvT|`GJ=Q9$X!|kMHqc!+ab+oqResfBU^VJ4mEA!8k&$1YsT*{vm=u?<+Wy=a1Ywb9Z-X?(G}=KVN<_;%&eD zl+zyi|2MC{Q2rL5_h6mp7fAcDgHxFMN7=Xk82{(_*5y_CHf)&};unr!An(lv6_{7Q zQvaXVzrg1^e`^2C3;v7#XSu-=Sa^*tmclSE*x&Idt2MH4xlD>;0I*s4v;}^AguU0R z^%-ryozYebZne!1_20ezarx)VgE8Lx4%ok@nUBlg@Gf{1Vt`>jj$buTzhE0fHvM>I zOY*V)*LspTg;D>*>mQGwm};i(ndpQ1XFl%#YTl`j4T$_fWB2fzVLr7#rv1LrIqwU9 zbnDM%%J0*Ejrs21d;?Vf)9W9{KfH0#^OIR`^n(5+K6t(yx}6CO^NIbDg4Q3s2t~+s zLYDc&e~@=`z8LSF`OWWugT2$A>VJ9tllp7(TmyJW*Zh@cJ}tkKzg(T%T&y;5 z!+acnKE1)!f$zG@yUX2AWySJ4e$f7N^}i+mcl;ji|3DhvqWYC)K5qZall9AZWtfls z-|_VNV>;szEHA{T)mK^O)*+b_-y?%{}OBF*I&DQ z&V?=Wg8q^GTo11PujK!!{Ji}%^FsZ%IzB4v&wN~eZT_K8Mv{4gWj^+wz{6`? z8psR`g;e!TPOUip1fIqaJ8lDtSa^_{`Plx8$=(bn=-lpsoacFK8s=mD8@}%OGJ`nF zeB6IAkGv(jv-NRl0I0s1*FUcRl+Wv+nHS|_=S!b_Q<7ivdHI(4#QwDWisd8fdGajP zfcoZA{ktVk$Jh4v)z$f~nUDSNc>0lg_t7KVXzrH4IxKx)n2+t}j*nvezIDe(OZ8`& zkLxex^EOc5!t0-uzfPWVXGQ*#i{9}69-fk~lz78@QvO=L=kw*a%*XvtlRctu2&TTJ z*FSE5T|QR=&3w*&IEP=>pZPfc(CL55U2?l``dV30e!}Mp5C2o&O46TrH*Wo$}r!xAMvEeU40)3+>&C~e_H0__T#w$)3-`}8?S%Y{&)4OEWZ?gbbo3`lcpiOa1E4PORBz!Owmf@6{AXU$ z-}5E!$NCrZas2c3|LzR4X#Z=r3e^{Q{k!q!y|8#zEP!UdYd=KpP2cH#=+At&{UZ6+ z00*+!^4EN^{OUtq|E~Qx`B$m)f(vTq#qtkuK*N08|4^mX$;W8jEL#f8e7F8c)cZ+QAZY=nmSuKga~8dU$xcm22K%k}^5bM+72*I%~n zQv}k?i{&3u|IBymkLC9SAe%2glF#`DBrd%A@L8!Z%+)_7uj_Asn)z=3(eR}TFwA$` zUr3&p4P8zH%e+wkoP00u`}6?y9ZLH5SD(Y1FEhHUNQU`yTsXh}NkVDff@S#IB_mu6gnUDRi&C~Jmya$+){QFq{zA)m4(I4F!w9Lo#*Ft&%fcmap|2Y1Dzc*h# zVcX|yN2Sp@p014=z#Hb{_yPGlXIQ^oEczZ{nUC8qcnTl1cAwum=ZvYoo7X?~-;~d9 zK-A24{HKwRhuuM8ZX7+%*W+V`Me2iq5N;*1^oU|x%}#Tc>QDl^YU?T;e1)fE6sdd z{|R4ML^aGy_G@LY%vOM9KK6f>-(U(u8--dKlQ!x`rke9Feek;MQZRyasEv+FX84Y*nvFVr7ZF4`+EK3@kdi+Y@u?4*IWmp znV0$>QaQhKz?a4_pR?bq?0{5$sSK8R$$z|jUw64#ocbcKzvMrk#0?UC{-c?f{HNjb z{$mRA@d~}a{TJjnd|tj9<@Lwa9c}eR7u3v4_D^^*h8X6h_*?V&;#Zb=$^V?s^u7Q7 zVz0l%RxXV3|+aPs|tEkNQ%v{@Z+Z?Tt#KnNR#T=IaC=-3V@&m O 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

      62<14rW068_g($W6^ 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
      wNUnCp2Q{?Q+fZ>7^Jr6_3e6EKS3?# zZJ?IrzN_Q82R$a917=bufRVuHod-{>UbdDYo-c^rQuNk3)>%VPJBbY`rn1OuB+opx zVjf?4f8FH$4u3`P)_?BN zUqeYjg~O2V7+J5C!G>6~RZgIL$PKi&J0y3u*y~=sB;%>kKWp6!(IvWzz)#@Yz=Oc( zeg^KDU%t#b?t|AoTH;+}|4KjUobNsF>?eU&Y_>Lnt#(y6XN>A?x0lI>_uA@bBE_ot zi`E;W3Nd=8!cPNBfYEy%Jh6InwBBd5-g|im0}p5m5M1Y2+YrplHM;S3nepV9T#>DF ziM+dZo2|C{*~D@`)cSX#PxNmD!94cu0LK8M{}gz<{yP7CAO|1s2G?K2G)+%5Zq}=zolr-I#2LYtezfPHgeX^h=)bF@Iu@PR}Ovn)JK@ z{~CM?OnNd;O>laK|vyOO&VtdS_ zBa-`BldiKn9vPZFcyTbT@1u75kL^=)Rc_MAz!9f(=tzY$N%8LICF+>K7C*yPe5TWr zGdngP=|=dS;2vPob01u+mH9{qp0pDuPt7YVE#sLK19DSVm93s%vW6{t=XiIMQk=}L z8rREo(nkA-x9z(|wf=ym^R?lgS7k z^X|sv8b#6n4iwXt-In|lpl-lT=+_`1{j@DxL5+?+lEm4;8T10~o!X;9`l5E1O92!t;VwT7)WJ-$MMm_?P^82mA@} zD`50J3l~ddJmb{0xkac+_OJiaSUhLJ7lJc@(OUo)OK3dh!O4E&nIF>nw&CB!zr^z& z@T6tz;{%Moj&QLA#xq`Bo8wuLqV+CBkBgo#xCj0K_&G3oABT%2G@i^IVGhpnRSnf@ zOTBE1{ou7uYq{1b{le++rC=p6I#6-W^;C`?rm3f3T&vK*$VQK#LUlEJr0(b#f0Zbgvf{Pu-IL<9B zUAwB7fG+n!d4&vcj74i5mmkOghqCh9YrWOzanU2G2(IKEB_IbFz2o3wCY}lCXE%;# zW3`p`kzS8LkKFxz-|oQx?_bkNklXMo$8vzhOIc*MS+RpaIwP`f3338E{qHN z-GLT*Wx8+X_tqpyL6Q{YL7J^z@Z(?8MW-vYN~cTu7^lIPfg)hiwFWNMO8gh+oN?fH zM~yOa4~Zo_NiAArnVTd zJ%MC!BRHxe;v93kX}v}0aXAiyC*U>U*TCrgEnKXX_#RYndF4;T!a^^9w{$PpUF%F= zt#wL!csl$%Pyvk2OWyj)7SeM!AEvgZ*nqE3R zxocx_zaD-&xEq-C+zS^=Xx#Ng`N;+A${Ot{-`qGwRd21c9vv<^B!A}=v(6Ko4vfxx zxR{B10`hkgl?Rr$^ZRJs+tB5rD-8Yt|2Oy!7~NLMVT*5T%_1*L^RGYtMW5v7E8)+B z-vFcUcW|+HzTfP9f8{^lJ%s$Mm@@>gpR_zs+g>|OZnK%CPc<;YCN^0vAnisa~ zyUupX?K}Ah+~VhI`mF~59v3}f@Fe^N zuoD=)zlVz@HlDJ8CGIJC^C9*8R{J_LN@H=&hi?EE0;BU{xL6|N+E``VZn z_gwO+S8w*S|;@E?Lpfzf*f zTx?vEea{cPv&J?o`|i9ksOi=lnfbqx-f=1q4eD&G1F)Fcb*hzjjMiI=9v3~?px-+7 zIsqeq(VGJo>p%M*?|bfC{e571m#tYviOQ1ByI$>^TTnKouxwRn;c|a5uy-?+_q5DX z&9H8g@QzyA;Tlv7;H|8EnUdOH4G{s6AzJrFbcHz<-LJ#n0e=QY_ut@R%Rkla#}0p= z&V$k&9z3mJHIHIq3s~mr|FMwYLt^!8q z_3(u2G!w181ta@PJ*Tl$&wqZF)?0@j6W0;vhOBX5A~1SSgdbkL-a?IiMPJxhbaa^3 zTZtZ{_e1#S;45JCehWVgdYf6mzOUpQox5H?Pn7(775pdQR$z4B4Hq-- zb4*D7l^(s8c{Uk$3K_SS7igh!3vNeK#MW7P!?nIW_&0GKx*=qZ0%L*EHwiA5(DLOi z9NkCYO^pP4UM_XVd6RPJk~vdQNZXN4^OyGeJ~FNRQCesA`LS}o0e&&K3>cl)z{L_< z&dtKIeag}^D2)|%{AQ2VI(MVP<+u`I{V(9UgHgcfoB|iiAF12_PiqdLA7I*3x$cFf z^YxGuWobZV(j&oZlkCc*4C*GPLceH_S2apKq}0$r29twrXO@~Cct|CkRF|(QZ}>y|DZ$8i)UD=!SKA+ zy%Sy1_Da0FRFHST0AO?vgNs%A;|__9HP~*`Fmb;xqc}JA%9cn?}3t2SYtFseJT07pJiQY zJ8$tC_yBAB=)a$+b=RXS%(3K?u^00$DKHZl-3#DiiPSCk^-T(8gXr5u^pqAvqa!+9 z>)wVgqx+wt@ki`;0F3UA@WZ6L*kFE&9B;G%TKLLIPjP75<#cI-Y zzD`8B^y6(RlPYzw<Z|6<$riI^8RkljSYVeie8o`D3Qmn|_I|N4BA9A-oWr1&rQu zxY))~i7Su3UcVBlQ6B`nqId*y$s9#vcojA|n$;)_R8kakReEr-CxVaYEUkMdx=cON z`Nxd^fd0Vf&Vq|2vL5kuH&=<=E|o}mOO*&ZYERX=E6`=??cc$7gLi?^y$3Fq6H0jg zm{V9#s%yWdIi!Lno>sC+7V&gc&e6JaE{(O5m%?uVn}E@M58Tt8xOOsbv_gh1OiOu* zZYfW*?hI4}t%%oBerm_Hly6E)nLc0Zu1A-|TiW;Wm+?LeFbf#ni{N5k&DZhH&eh+C z&@XAE+t1{+>QgOyBC=UicG4JiML_-CwvY01Iq#R6MJ_)zR4uy1>K1DK)#wXzyiI=G z$ecIO85sS&;9_|b5>`GN>7P=#hFyPkllco>PP>zY`hYdpE2plkXEx<@Ectwq)?I`y z(Jl4hcKENsOTg&f1s6+TyVFQ_vpqsWAF!4*+exW9UF&u)kLixWF8~(0IiGu@F*0E5^#yAJ8Gx^-gNL1WoIlM+auS;f66jwOX;J+t*LK^&UdEP?fjAKzwck}9c5Ry13acrvW~BCUHnx=fta)gh}R z=n9PPBjI9+k8`XlZaIpm5S2k?NL*LtE=33Rz zTyAcyxwPumXx;8LG2Lt68^DFY=)N2-mcaHhUUx%td7o}BkG3|;7=&&vZ?NSyp2o&+ zE~keUT6rZ}_il8V{4wxa_Id!v1EYH~Tr83Kx?z-RP*$^<8|f`IlUC$xt-BIkqFd^_ z_u-#{FM!egEnLjx>qOLdak^ubNZh^r=9ZgDE4@tX&aI5;z8Ss+{1h17kHQb5?pUFW zyCL4(ax-aVuhY7%>tgo}r@$A3d|-5+1s6-;eM5Zyj=OjGpRTj}ki%;nt8}*9^~|+- zTd*F0$W@HS(axN!b#F(PsZYL#r(MsQ7+`ewf{P`vK9SM)_?qO0x=5aGZHd(+AKNx> z@bibL{$MAuNrblg4O({;T}Jm4@E5>NV08ZhE|$RhMC)#-Ng6udTiVj`t~p=p4&M-~ zPtJy42z~^N?yKQq39L`F?q+L}EiKk0)fZ~LyU`OykJKYa-N>GY;8le}Bu7D}Z1W1SA`CY_ENv@V1fg0p~0$2z!}DUVMb`u%WA z>F`Tvz50{$xw7k-IJH$uC}ZC@1r&GmUA?ycL!0;a%?bX<4h<7s^_S^%>_n&JpKYMu z&7=t&2TVFnfQy;@o`8OLOX-M}fVd7KSH!Ux58tOF?x`Xjm+N#?qSK_~@9@3gzrds; zdDH*5bi_(V({$vwl8!5NIu@eSq~lR|4fr)M>39_`mcaV5rR&jb4Z2loPzK9sWSb6I zc)IGY*6DC>iS-YD0KW`e1xz|_f{P`vf6!7on(HfUmA(SwjLr5HM3-Bs)3FDgF2~Xy zL~dowAIt_O9jC*^GP>&f(FF7>8_n9$vtSxOnft5qp3G%h+X7Vq85&^B(Ul&LKP-I$ zdTOrI`nRFa^c%i`C*8*SKVbBChl?e&zKz?awoyIXa1V8Vtmm`ukfWz3a--H?gg&Ex zJN(z+C1CWw0T(m%Yy!p|8^s^}`}TnD-+vD%{bsE{e0!`sp9kLvt_DW`&2X`VmgmOt zZ?+uY-(pY5lVe%8`1v1wVSbYQKlToudx4q2=wARAGwnm7^FQ&AwF`0gR$PJ>dPbfc ztM*nu|D(_7{}vv)ld)f5^!I=tX8p1DAnqRP{uX;mT7UiRe*Q;agr6k-kHDV=JAl#u z3S2CK&%yM*IB^BHN?oT%@`hLs$VeU?q{oz+W%p~SaW1lI@6>uT?uzw`3gE?{3>du^ z!o^H^O+de>LGQjDp$GQc5vsad>#jwYVyB>bHbjLDwQ{63hbgJ&vx+8bTbYBI(1>6aY?rm_f z1lEfU@ou(4e4zD?P33)B_g-`@MAtSjd2`6h1M`5^Ek1OtdPwVbx5V=ITKEsZMZoC394?m7`owz; zRHGI%u3$dYYK2l&t#$84R~TK}z<_&s{sl$@qkAG;?6Bx=wnFiAw^*T6J)(72qRZ%h z7yb$O92ni-z{L_;pO| ztwO1PLhH8f)AL#L(H4Qv0SkfAy#g+_1xpwSlQ)%H_*5l)u32q%+O zB2~)D8L2L}c>!owXH;?*%PMik+dNKXpQ-IyZ#8-%9Or=-;kDomVD!EX7u)kEt<$Xg z=3Tv6gQu)2;LUVd^7Z^LR;=YIerBb89ShikR<*3%ep^1Qay5>AnD5Ws=I1xgxfosy z&I88ZmFD03`_@KxA3}Wmt!ok0%UjX^PraW)H|vqWpJd+8`GGX28#}%K$$n1dc0d0C zmG(2mw4l-I@_?6UJ}REnaoUY8m*X(#|5Mfhff2yy9t9U`Wj)wIU1C{S6SuFGUTL>| z-yKoiUum5c=y1^y1uwz>3ibe_^HaE(Sr2xI>v8>sON~{wumQ{V<~Cr-drIrhxjz=) z%i-69n}N}N8(i#g#5ZnYhh$;@_2i9%T<`0wLx+nFDKEo*7P6**>A>im2^TZ*J;e2) zaq(@U((Dw{usd6d6;r%U9pkBBe(uvczMIhH@>>{u3jY`Q4=}p_2NydW@ohYC&|-)^ z|Jl!Iy}1v>;(ROoVel9*dbh*H5+CP%)wU(nwZ3la!C0J6gD(XufziDhE_T@C99t&V zY<`YDtK(da9v3~5pOYU7S-rqfz~~(S7dy;xj;U=+u+fuVqxDD8=W-kdzlXm8-Udeh zdvLMC6YrLmxiuSdPe-+{JN$4g?ia&v2DbyFy9zFLIO5)t>b8VF?>Qa!J?L}MCvhKL zO&*zlDn>KF*C)?l-iR&+E8G zejbbK74RFtCSdg54i`Hdag_(It%TI`oBe`+{n6p_i^Mhi5w1Nr2^gKHz{L_DSEF*j zA@%&%`g%8^$K{tW_z3y|dtAhbOMf%yPpPE8J>d=O%Qx=#c#SSNLA= zUto0Fj~~9cHmKZxSgpJlbzF1NlOb^fm&0!Yw*jN~A-LG>dHVTU#vS_mz-}_u6f7-V zQ62NeiM8@VAln@TTccmlB`q4s5y%5QXf%v|Ih%nD1{l>E7c-Zq0|C3 zHjtB)lRVza`>ob(J)z^Ai?#{y>0l->x);L5cKPp%sr291bI`ikDM89rg`IbiUBQj# zlWb1EJd>Nd6k927<7Rr>>033vzAF5O@gD*2!T$z61xDW&a51z0#3Ad`V`Z7qvr6n} zJJhj&G&)4FHQ||RIDYb8(s9fAg}&Y{zg+;o1Z)IG*VS;bL)$+)?s`uwo5R%mW#uKM z629!5VI2^cZ~b718YwU6GWQSFzP{b~ckwUf=cw&G0|3VYqwjdQ*d>3^_v_}q=b)}@ zcyLicnQV+WC~KyysMCw`EA1Qf-Z-7Dr({2zXr|hV`;*i;_>KNf$8kM6(m572&%&Pv zFZ$XPzlC4b`ph`Sq3WBuHd@YtIF*j3bn6G=+OrB@Px>$QIBkSqm%~fJxxmEjM{uzn z{`krv<+r%F%`QB99ovVLOOL0@2W4}y#RK2_H{yE`93{S&9Vp&H^8O82>(2g_E>}7H zS`NP)Tmy{mt#C2(-pfPEA4a#yw3+#x*!v~}ECsUZqMt2xp*zG&4?BXMYJy4)bWTl8 zCX4VAu?e1@npd^{dh|v3r3g%XiuwNFbYS#H;bQfPs27a><}-JV{b&8|voX)gtIv*k z*6zSn;2lR_^7gYnNqtmQ^$K)P9gv(H9KnF3tz>XA`!$`8?dS}nb2s=eJoRbT005H? z7cRDKjLzqK#~wm{H|c1|?0u^OTb;WCc2yu*%B5pnp~mn^v$g6LW%o%sImkO~cH7Q+ z8r}85EKf(pAGGc$x}==uAoL0NYv4^_bbkdGn|qDcYkhJEx=o8Qt+Zqwx z_WO=q=hO$iudDS@fg~fvx+HC?YR85O&Rlhu&9pnae^Li^ymOhIa+0hbS*uQ0bL@QQ z95sSJUgvg(nwYXMuu`2A(z%TGX17k~!e?|kq>SGHzYA;uCY=xa|K8N==KG+^w;4Ci z>%}%Ouh_HNks7uzs%*dH@#Nd>Cz9y^jaF}oYb*aPt#2>>z3aelN21D=Eb4%7mpZx>w5^v@2de1r%4HCUtmS^hS?&+s$SvB&^iBE&+8=$R$5gE;|Y*dQSO>)M=d=J9PdG^IHM@EKmZB&N8@I z!o~^wTOI#KLOQ?x7(q>=hi7%ZzU}yT@t+62hiCknJ})r(j)IFNXxwFAbuDa9iCSej zQX_e<545bm=y(>P$3>6C^F{cpU>7iYcf-Y6iDy}P>AGd*?s;y33+2rNW8J0hLA952 zOI8;ayBD+=WOP*dy4@FJaXugZ6L2dqy6=RGwHW8aqPm4`S8Ey?RI}}>mH$^A|2^n* z(JA%V^xyEDA1nkW9jC*^T8Te9rXQBXm_0*l8n#crKppFe;UoP6t$!Q(T#ln4d1uJV z1bu+f-w!S}@;lwme&FvD*|5Lx;Zk2~*T=uTBW!UIC;@=GDd$2@6d!pX>U2cWna;7) zft%oWfqQ^S$4}v6t+XdAOV$(=uN<~=P07kEHVrLg%dJ)A8wO>q^m@3NZf2h}>-j*$ z`iz^Rqgf52dUL)?-+w*lH~uqzOAJ9jVEi8g7t8R+xteJ&TJm4k#Q$FWcW0o%I)+IK zwI=hbAf(*=x)GNMDD& z_%S-%mzXCCh61DGSh(0h=ndbSq1>r~R+aC6J?EDgL_ih%LGTDL z{&&K~O#iW&`{)+qpsz!86~iV|K@u8h=5A6$l8#m?{Rq{g{SfYQIy#O+COv_7Q{yN4 zHyxMs-|4t)H)Gj7!ApGP!fo23hqkMuCfurMbZrfipj&Fsl#Z4)NPPdXs+6Z_4o=_~+oC!1(_LF4pF9 z?E7z~GI=ZzSDEB}qVqxIm6-oRcrhpi#{YWw!T8^|5_tyqekH;QYkdDToIlKQ9{3Qh zUJY5v!1(V87c2jKv-f?;m-;)d-xOb7!-GxwbtO`1FZE`k4NX_+NgeHu^Sz_gYi(&_ zs+6@8QNyWBx;fq0*ZDZrn};yg3C>S#yNAxFb${1!Dnf^g0?~N`d^5NY7@d#8#mxQu ze(SrnCCk^XDaK)DGx z$v6237*HFsCVYKwp^^XUZ-O_Iwc*V_Al@c!N+8KbLAs!@K_qTcSI?Y+<1fcJH?^1i;; zN12GMZ*M*6jAvJ!$86Qv?vy22ahf_h&|$!2H9hGlHC1(18X~Xvry*$_+Oaavs{6N& zd(P`R?%Djd2)+!g1SSq^;bLv9cUG0nSXEwFTChg$N%AHzjF(Kp@~!WjOf`*Of^y|# zSY)sEU(NZ+(%yA{AO08ck?&KkdzT)7065T24$_{F`HZjeerJ!x|~Za z-}jsShgiSmO!!(*3QW2$hI{trX76ji<+zFehO!*(Mc3t7dHiz(kzh1)b1+kNVSGQL z202Ru9W3h%^(YV3zR~g6iC-xPo4{Up`Yzsy2aK-1aIuZ^biPbmsJ~mxmrZmvvJ9v^xazd($ldC$YA$96M$%Pgde@F}OO$OrgSvFZOPmI%cKC1uddM^MM?H=h=C@Js31BiX zeiy*Sw!E#&^?v+@2XlA2k};aOgRtXX^?Guq>Zcr)rUq00CaZ3#slgQbI4Y2mk`hP@vJ3ICJn$N2ht*Wi zJN=rV)|4GJbmW14Z?Qi(7zK>ZQ{ZAd{C?xEdabvGd=`Jb ziwX;t>!4P$#KY^5T`onVPFdBVA;=3-tW!EHPZ_UL)u5zckjs8NW#xX^;-WfP>)V9? zZ2VV&-@^Y0-T_A6=(n{#cZ4plwa2zvZsYZ>C@H0t$u%nbR;xdDd|27l>LX8OC+n3^ zUuv2TD$Pk#?VO&Wk!qxqt-i43ABUYd8hxxJe+&nU=s7sl^#j$BYN6_8ceRu44#70L zYr9?n@6YaKccMSZe>hlGwo|_e&=+J}CS9j@Bk3{i!DI04;Avpe`!ZZC`d%CJ%~CIG z56UV(w`9sHu38znyjrRNKDMR|P$lXprbF3whXA$Z)k@Dg;=ZDj)|2rk-Cl^-N$_dl zWMK3Z!o`NXdw_b1_ffMwVgJ(IsU)^1#51J>lyCh9IpY;?RGY2npLrJbbrj)8%5yci8NLPl z6c`;faIqZ$9iJ(7o6EDOV@hGPya+LD9lt%`Z5>~Tq#DP%IJGZ5`a-qYR+-Ke6$+-< zN7_8CpC+;Tu?j3u)aK~Q(D6!tPuGhz{5BSz3uXeNvk)$}yVt?#Tp=3k)vkcI#gDW% z%$~Rrjk)S#n_4)}l#rFF7ZBx$! zi|2w>dcDu;y#to$us+~PE>w4j;<itUld6XJJ9X`+r1*8Q{w$oD5 zk~{QHOAk)YO&UjEWwaWQl-@O{PNnH`l2iQeCdVB+1V%f-Apd7DEh(5xJ0KeaPat!u zTb1*;9oTFq4NSh;R!`YVukcbo!pr_%xz+1q?>ub2!j*E4i zuIkb1eRlb(wS~mVbO#621UK1!=k>?-vq8Hic%wte>XNM5b_3MOq4`NY1H;vbP$)P; z{Zl!?z=U8I*=#bKe$kn#y?u%kR-br1@R0%6cDb%$tvm0}x_-{LSiA|p2wVb;?kc$0 zC7N2ivX0`o+{jj{PZSup+lk#f6Pum^mB-8XJJ4dKa+#eiA z&tk9>YS$y3*`U|h^w37zr+VHHK3MCk$A1RDZ39RAC1ec&!-3H^4K6mrpC9P{SL9SeAH<=qw3*pDgi9;%aMLS^kv z8$mIguX-lUqNsLYY%G{U=V^}m($>|rRWnq_sRsYju53fl7x1JH*cS;HebeA#8~>`y zN%hKB%L&iZ;^I_VBDXD44HB&ZHAye3XOrxjq!&7PUv*MqD!W@>dy~f6tKLoqQ^(}y zO1Vi7+ezKjf0f$GKS6c2y2oaeG$W8~kFY1mUvjN#$5`G%^_neH%}xm{PP@Z)Znf1_ zw&L>&<=h_Nf4j==y(;bavzp}=fjYd0!;e* z!Nqb0>Gao+*555&pV<8o%K-e;myvuP-26g6xVfJBM?ASHl6=jWNBiSQ?)0R^G_D!W z@{S$5x`FX2U4mrZ<5amifeP~`J22ADa{9_Ef84QJ_j+`RvRtqYUJV`xM)z}YvAO@$ z`KsK{Uv1F6LhgB|6cny4@%l^^_JtS8XmFKnz1KcdouxW3+RC_JSe>kfS~b3&&_`NN zIzGq2r-GA!(X$9HwsV)(a{&FMH6_bdPb^;U)x8tfcq4r2(>mJeeGpY)Tc08-lRWO- zvdQrIFka_qMaJnkRpD1W=YW^tZ-PGoqw90Hn2FQKe`|ehjgv1Zzmz#KU38SbYoEzj z=`m#Et|}v$2CQ8d8I0~~55P~IuPf(ox_+wTw*q)M*Z_>KyWwJc{rj_#SGGCd$#{X( zqt$_*2ffz(Jnu7G!j)|OufsSV1i8Eyp24CCYLs)Et%h+ay9fKEous<7?+{21Cd*!l zxy~cD`cSDwf!`o1KceGVkFGGfBA~+_`oiD{V00e^7u(Uc`}66=D@vqGml-{!L^4^_ zdM7Kc@;Lbd6aEJ3{Pt@t&!}(3RlRpkw9YAMb{Aa?&+L&(7f8&Hdh3{Cxt9-xt zoY(lh2!1KJ0vNy7!w%ti&E2pd~Ez`B(?T%LscITFbYH(q1)lL0aIp5m!g(<{LoIgd44@8xG z_9OpI(Ym6>FTN|`cYw{n==z2K?@j%(pZixm9?6}8%B;7aQQo~_GKy$J%QG2^Xs5;{ zW$-yPbiN9vFfBV%y{IJJHB+@7>+d>WN*j7Kd^i{djGhQw%*^LEx>|@+Sy4%8d2UHr zd9i-npP5(4vy5)cNZuh8u)O*)qIx>?BxkErXKw8DU3u%#SE=G@+o^qaZKW@GvPk~7XhRD zLAY4ZF=v?`xMn>OmqZ+FQHqdEB{l;+1I5xm43p z^@?XK}8KfXqt%CCRqu0tg);*GYOq|Q7BlEa{vI)`;M z)*PLl&_DF}rKlbZ9}0#8G08YXUfb;P{p5rAonB zbDvbb9#3Ap zY>SFY`!YRF+~r!^uC(=AL(DzRkHZ(SdSfO0EKm%@ z#9y1|{}_KKmX@5$|LWq-wUS&pk66U_R~_f?P54{j-8g@3e*SIz%_x++g>2kqTcK<# z>ify~QukjZB`3fqfoVWY()FGHoZFPY7P5ORj;9o^mr(*8#?%O>@T}VRw~=#8A5lC! z41WYX5$7+`WM8ZO`kT#jjs|~e5ziSP^}fHooZIDC{Pp{1$T}Jf24V-5PsE?(l?I<3 zB66r<&<$XDPC0Gr_ zB%TM{K6w5bh4bhL=W)-_*KZr=Hs$?I_*>xJIDf6I^NR1U#_CVcUz0)+j#&A=zl?8U z{wBevg6Tj^(s#i1r{`}jFP&EU3J#8N7W$jMzm1&Rl!wRR+rd+D{%Q`i{#+_ksk2t< z-h^)6hY-M8-``%&ZSv>f|8Tzph66E4-vPHr+MkrcIcruelVYzc(JU%a%w3|d<9d9! z9E8|z~)%|J_i?TrT@BkHH#I>)?L{fAcj+{%LdnHx`CLSrgAG zSj9>|?{SB2tVX$b(cx4}(YJ{I$7VjzxA*mU-N({Y?;m*}lKMoLlN17aa9HZ5S8=Onx2)7u)g1 zX7AhN=ly2m2B(?Xrwn5+n?TfxL>W6!^7Me>?fZ{%e$gXd?t*UyTYaAr-!``^@#&Sm ztKT=*NuK0|nA2IczTbL$o%nV%<~=LUc_xoj9Foo? z`6<9!s%=Nwutr@_YtA==IBmbH7<3}`j>4)D2?(%(0`rF)J+$a5h zpHZj(6qA0dP{(}_K5{v~{N61<`oU44A^mW%-~0Pi9ANr==b3r=vyycB^CbOqc*3f! z!q-uRAJHJ`zZHH5xZC$F>2GuYb)WP%^kH=bX6p!4oAl!&kMm0z=;5STM}mI9q< z?DV#c2gJPV%-7&NuISH=FZz)ce*VXgDfhR+?*MoEz9sz!RnK#8yQ(D1>-Et(>GaRn z>5uw;_uxa)?t&gc(hvFqll}p4u>-H~8#|BH`U|76TAx0OuOo^d7eAuqYWU6Ic3^Z= z!NpqX&n+H4{0wG97M8AH<%)aW1tht}?yXh1($D{#*X6wVAdr+|bq76x@!JP3X8Lbt zyuYFU)|}V<^FuAX6CVcbP2uDw zttlv%Y0Se`uUlGJ%HH;cWy7MYR}L#(S1dEVvL=@-FI+ZFZ$xV9=+X?>w&T|X}5xB2jVPza2^v*2PITb_S>0Q$m%mx;m!r3KMwVdFqPEty=FU^+`Ma{@pQUwHqeIG%aJl!?+skg5OqUpIy+r3`WTs-a!d%nR)%rRYqC>8A6?hQ- z9C#5Joo~R!%==Du`TJtV^{);a9r2{iOp)M|>*Stu8(Z5fn$PNfCUBRr$JV&>-!a>?dZWQpUhgVB$O% zE;jDx_juoX)3$iu2N&mQs|wdF&nuTZ>H&4mFCA|Su?xJ6vOkmPK2ricG&jU8|9S$!2 zY(&NGmTL0D^MI}YFdj}J2A>~&(V6L z=#jEp3toW-(o(DtFnTA$#U3cr*K7L*{e7r<3)e0!T)w<;`K+a9v5Dy1lK7k^1*9@? zwS8^Ct_-}Zzjz=iN1yAjYm|3%zW2Gr`y57Ut5U7yW0<;?OX*2Tovv2EGeS$fPcASA zsf9`Of+0x@REMMt=LGpLd#1Hd?$JdSF#g(>iGgRcncQ;w0*B2CSGRjxB0+gNPsgi@ zcu2hVfd9eAw&!{S6R(TmVh{X8$0_=CV&YXASD(x+nc66N60e%Td-k&dyC$$pfBi^* zU97(@(_eqkU;or!Cwu?>K#3dELF`H7^rHxe6Iw!^mRK$E0(FLb>L5A~d9e$fIQGx=seaihc?^EgRJUH{PdGP zNq>fqq{g;d^q#Eg5Ug;y!(ROac(-{(q=aJn&% zF-`vkGVPF^A!{~|pyxJSU8h)ukQs*xn+3D-m!racWUXJtHU9M{9nude0>e6`SmVG% zVA44UF80+Io4xO5=Hnkg`yIRf6PGVPxwz!q;<+25g=M6g<+6|HDc)U3b)DknFEUlJ zuPU>E6?p2t=QHq)Xhzt?L0Rp^ua8v$R#{|i*-nEq6_*tV9(9S>Q*tcCGs zlV!52PJ0r-t=%gmpit&`4rMxs6|K~I^U&j>Ck$?f-vjOgCf*Oi#mqhz&D>`nl-|a{ zjvRXeYt{mJywS?ryk?A&yf0Sc>)eZuJdPKF8C|Fs!D3)^E`^ItX?eZxLFt^r1kU+| zrP57hPV~vEie(U%UjGa9`bBb`ZG99tQ~eMfya#N7Rdto)Y{qDM6U4<728VzmcG zZ&$e311-;=Jt)1p%Gy_PrTuG}vZNK)2Tr5uk!f~3$<|hJz1E$NF3}wY_rf0sPXeR+ zMYvdnKd;=}cOGhghZ;u$TWdeBcPCr)Dg$8j&lFptoTL`Zlu(CkON04Kkz&={p!KGA z)Av_WXUu~y0ZW0=y9O?1#wQQ${wh{qFtt|n=1um-5No-yx`w6KC(w!P9Cvp`KlVng zb2~ajqtp#)-BYYCAOjekN5aKg8BaQ>{6M`nxulpa*CYo|^W=g1ZmPVnN@r=&1OKj)v%M8#Q0RwlA!MwgXHO-5#TM~lo?u2tdd zi{fAEgKgk>_*>vTVDxPufGqqKJfL;E%B=|gtAcWueP^DZL40dHtJa= zxwzi3KBE)0fjT0aZFN%A9^2uKh^y6;w!BtjA`4X+U)iMN9XTS_j$8-74O9V>p8Mcp zpZfE76BTbgVicbo8j;s&L>x&0drJrB(|NY6RbItRf8rLcw;nwa^b~=dOs)l(35?#; z;9`0H^Or=`$5RT;2p1h8y?yhdXT2)9T3N5K`^m|sN(aovlaFvs~|axd}#m<^2HC2+CjZ|Hg=QTagh&MKX<${RPSw(q0@X5(ua zH@PNhm}2b?9e+lGW|*Ett*^HVJt6c6>f!$c|MoR1ersjk-2q+ydBv-x+de33dfCL{ ziFwm?|Az>tTdxPw*;6zfXIP!DE7DuH4-tOb4Syee08AV|hI^Kf{<%N=lodX>>aevz zph4!1u+cM1lDQ%kwpAq`*6FH03CO=w=bP*!W9i)qe--QkCcSUL|Iehi@lAN+L7ZmN zyE!hsb$)u?KC$#Z1%CnT1SY*N!~gH3H-4C*@fe>s=+I1>x5TAc(vg3czRnqLEZvX6 ze+6oQN%ybe|98^eXs~YI!Ioyyy|qcY>-==5AEnckN8)C{&je=ylkRo?zc=f09Dm&4 z;0mN!He+6R{DfWx(wVwuZr~c*NegtQ%9%U<8hb+8cy0tnv)9QgFYI+y`a0F1N9sfu z^yo`}5A+8{??AYisUHvZex*qv+NYY#%#N^+RM6TL$hVU6<<^D8M_A-;tv8AusX|?_ z1O7YkDlmFqhl`nYR)?myvD#+D%4@VlYA9dYoUS}q@pWeO)A?WW!D{$=Z~-tne+U=r z@Asp7`Okk2E*~^~e%1KV5BqvPYBI(y86ijoJS(2@vpyVLx;rmY542lkHF~s6I`st=g^5!+02{$ zVv6N$%r(B=i~+j;DP?RGd<}>KG3j?4p69{wPnRNO(1?{55s%mw9mi@kNPkmwbv-)8 z>H&HJG0~Oo_uCHjek1s*K?qw^{Fv*3lpuCuj< zzd>iZ=*;(ZS_5M`i{Pt42@pGc@$~!ix|7*BoaQNkj%jDnDpqN(f1KWoFxMXR0b++y z?|vHEhS+vBdTV^W`Dl~hB)>im-wvLN(|dkGo}V|JSJ8A7-X(*&R(F^FyRU068chD2 zJ1E6k02Tu=iDRP1)f<snrhE6(rL<$M!b*@N}Chv>Qjel@rbh)LWMHE!NmSF@w%d0f;yt5=@qJL>DJMN2Nfh`u4m zq*%kiu|Q1pC2E|!vAz~Y%eBHIMPZGvFNzkU?^p0=!1HnX%y@WW>u)_Ou3Hp0f*i}* zs`HsOBsMSaWcVpyHV~6|nepz#>d~X#I)n9f#h2^rsl>ObFFt|)1AGyuCsE_Ajjvy` zquaU$?k{TSxA^+9hwAZc$zPT58^Fy#OyZTOaqNBcwKSToRUWNXa=m<=yU}FgI3g>> zIu0BUjLyRr$9+eywZgDgSncauj}}w^{s#UMcqL9>qQ0EnP;-QNuUVq|I6#M&vzeS=ly)2dFGj!XPz0S z$>+)+p1eL6;Gc#nk&WAHyQx37U9Qw_U30MxiF+w?ecb8S|6nO)pX9%Aah4NBxkya% z-|p&fPUmae>um6b#U)*_GM}#$mN?&q?GzRSd~6b@mGqX+QUp(l}T-~Jw7Y=ZxM zW!K`E#`!-QXN6V&R~ZGoUO5LL)~Az4wDmrBWMqo+9h@nRy~3*bm3iPYl*)Ta zZiOx&{W_m_H9S%T7NHOEKSN(4%lkFH*zRdJr^Pj`@mCl>l=TW;V?4^CRgQC%?&qb- z$8Y@eU&8%?uQSm#__v@tkmbA^U#x7mj5kQ*Y_~DUQ}B0q3Lchxpq)v`Vdm?TdCA=H zHp3&VaxXdjz$|APnu#p$iTGm8yPtO(jYE2OH`2Q+MKjOsk3h0&JH(zf``l}VlRd)S zjQ=hA5n1lQL56$FZs|u!i?ctJs!lx$rsUWr-tI$=3^O&9jAy^!#J3a<$+I!E8vkZ= z8?u~t;Cr_F?%Qr9ZWkHO+%LNIDD~5yJZ|w zS{&PLTo#rsIz>)Br9dfSyvE*M`U#)69-am4DMR1mheu^OImq(nAwX9FCz90-oia`I3Y zWH}4)#cFo{{kEMURFqvQg~H4xDEva9;kPEPW$={2Bg_xsKZX8)EbsI9Vu{VSdfVMt zA8y-N<>v5bZ=9`H)E+NIEw7&(B?;F*WO(z(B-y>CFLvJU=WS`cGTdm5 zTB*6X63RId$(!ViKocu_*l@0cL+%AqrwPY-ri!{E%h>~8tSnQU$)Qn~sg+U?G$`j} zF%lR0-*bUo$?|SF2D>F2z>QYt7L`L4Xca8yN2xm2*X@oZPpZSpxw;fV2F^5(Vp528nr;=4wV zM=ew*+jVM;{793;0Gx3d#H{5c+nvQ=mnu9F&e0;I(#P@{a)cJ^h zLauEYW7hH-_Xf$VDYFzYAB@l007ndtGW0EehY49uCuBLh;frlEzPo|4&1>HqTF+iY zdHR@t{lg=9who<*e<8XQS>C_mi%k#S=4~fO%r@mQ|I(^?`|LY>PUYA&mCw5roL1e`Yl^&#i@zPuha)GZA~b+719_pnfi*XjCahk`oPDHvf;VL?FakkUFQ z64H~igL%p4Vrxz^{OeEN?r}_HNTw_*^Qc)uvCqv~)$2#hmpqOs5u_2&SC7-9++#VI z1u7KH&+6?R%4B76e?#I2^KTC2ZP_8g8&%|JwZD6C)-d6yO&IPbxFr7d=zIL=q0||W z&wfg?xC!%y%Z?_eFG5eL(YziP zR4m`e`+?q%^HJsVtRuaAz6;M5{4dbg$nyM%FSea^2Di;KV`=&P3VG?mLmCWH6r@sT z11D{7kfOX#?=SF_@OdU3ZsHRo&HuwcA6t%CfHat$1&$B=n zNKek!b@;cTJCWsi0AFmUdHn0G)fs!gHB!Ro*-CoC5<`QJ%yLGe5@dPC;fw9m`I7i- z<8*zNe!864i{^Z-B0U?Qhw-07FCokGH+-?}%=fm9{F9!U`;nPPpnQ}oSHE)pt@?2F zcMNsWeWD$+DZfwR4X6g6D>gaFH5vaTG#gp2YW$t!O3Os;U|peGu!=ek^s$`yOD0|o zq$}}?peFqH(1$*Y)H8Q7PNn5@=kmf8xv12;;$LO{{Gw2o0B=?stMk(@J}R01Jp6@d zF%sLk^!*IXitY$FD|b zAe$e4i7)ok?@wMnO}lDjB=IP;2a3De!;>;bE}yKr?i~Unx#@4)~c)2N>$4p!VBSNyb?WK_fI&mBqu+86&YG#OdGU*LOuem$}1+U?fUmey1+tU7d2xmW)gP!}w} zEU4;&PlUY9B`!pb+0M6tW2lH1hYogwn%;wKm*qKv`R{8T>@lowNmdV5*b0V&QTBeU zvV+lFmShSC!JwP`39Y}-p$k>`U|pwK&vk5YfQRSAUo*$k1aF*O8&KdF@&oFI?C}i7 z7h65t97o;Y?H*4#HMye}E?Ks8CXwx18~959s8wy?8^w{-1)T?}WFF{N>-nlrG{*Y2 zft&!fsblopO0VN>ZdNF0e8X_9Al*{Z-HPh*e~+F+mg_xyu_r1G&v}2`E>}uEIMlpT z-nWSvO5O>mrod_5=k>0)*`U2mZ+M$|3)fx33u3DjO^kV=tWm?=S&{p6(@v+{?#G;rp)-Yf}@6W?q&Hjs`m)S_?kqtj^TAj{DcU(Ako zYUlmCZQMWS5l>##pSYr8&N8!FU$eTxylXe{bDBG#v0`>`gexzVcXh$3(AP_{p2goX zd<#fl_|~CcVj^WKqCBnAHIH@)pBdA-N&oJQCVZPjC-$LwblC>#)Lm1o^fQ zkYC?3@me8le4okg+wh-7FCxo1@>s*!{JzN#kyF=r|Nnn;CNt`Q`e4<~K~*1Y411dw zmA831%iG+%ak>_GR74ywDFa8`{k}(l`8bK&oT+ zD^=lHx@Wjkcua>Op?%aI0du#f(fpZ-V}dxz{Z!8PC-^@i_qb#n`{RohpJC!SbNP;z zqw;Q+-1E(8F7eLqhXHRhPEK>s`7AtuZl~@#lG}@88pXn}`@74yjNCwu+N&e)Jsr=i zHia@Z(eZj%_~__xJ;iBk_Rl|D!Yz5=F8tr3b;zFo7xBgHdt2>17uiO6b2Wh zH)k6>DJXFm;?9~#oae=UdbNIf34Z%N{KruO**JWRFIG0woVU5h?dbXQ9=CdZFp&3c z)_)3qu2r)h4nuv&xi@kUtMzr&liks-OLtM&?dg5SywCe}zdl8gKz`58K~Cdy$~{v5 zLF-w%EZo_np}*!U6R-Fz6R(-<66aBLu*80pA$@GnX8c;<#gmZKBCSV0#PzbE|p@cwss;&FJ( zuT$Ecue}REZpaPV`GA|pe)p#_JVKtirz*>U#!bFrrtF2;~lL(`DIsOllmh=VzLNLvvRJ?8UYC<64j@JnWb=+8sh!D zQ}&JF$v@HX)UkUE{!wTuvOEj$#n#U?=j;9Bcl3Jn;=+Ie)4Zk+|1HyppOCt9!gcDi z4$@sHF0Yc>$S{46QaJ%0&CcS6bi6w_Feb!w+0#P3z0|2Q|JU#);F0)IOgo?Be}jHN zmN)+-!@DtRI7@!m(fFmb$K`A`s*Cl@bVhm^|Dmkm(l^Nxgv)eJV9yBCe3ww1OqPWN zjWvE}_{vCM_-3Mu@mHf8k>$GwU(DvScIH{wM)@#fsa>3&65=f$YpZ%y03Q9hFJ?)9 zZh=0|Jz9EmXY2bUInSEDH(XmuSIYPXwAabJ8-q%aDA~HKN`N(q%U!c zqXhmR(W}VveSj}!uZ!23cl5lr%w)EIrK&m^`R(FIqWds$XE!&Z527_1ar2n&!CC3< zBP}6mS4@|UQ+$dkXKV3K#IHm($a0;BFV^Kt!}HKTca&>V`O>kA7M3rS%+{=)GcAlg zKUb=m8R58RX{RyTmrB!-x(HK8v%r)sYrKFovPFzrD zB(`(u(kNNdvQ~(s`fo`!=BHQ6Z`8;Vz}eaZXz{JEb;dj+i=^V>D}VypdiuoEM9v>a(ELK6Cl z;L}LX#351QeT^RfW-o|EMaM#aeTj>i{UGs|$UKW#^OCs)oXUu2<~?(sS^-D6Ln8?|$9 zHfx-_YZQ~0c8&~p^Ey%&=*JVlBUzrcfL7CRFIBFSh~Zofhvj?`|1W4WvYh|I7qf9) zxBrfE9=>EjMfJj(3KnEH-Ey$5Gpth1G;3$0$9flSzQVC?7u_|K7tY$Vi{-sm$!Z>@ zQNtOZo8(-Ee=hnZvYgl9i`jUtnZ2W&M^`Rs4>dik=jH5JSe^Q8|NO%vajioADmeFO zKV*3)pFR5t>Y@@n7@Ul{ks)oSUyUv=NKIGgdS-D*w>{htB zfrH7kLmg*?muW`@zEq(}Uh3TQIvC!1cx*m+1OGj=1zFy2@Kbqb?!BY&9lK~zXUd|lZUu_s0{`&znkH!BK=}gZbCQV{}!!7mhVM;u{u_R5_8LT zw4CtzWbGB#w}A@j{Q=d$>u%oW67LBO<5W8wMDwyNI#2&O5Q$~6o9gNX0cx!DL%zbC zXFUvO{%IzkMPN7x{}415iS1l{u~jANs}3>wt%RT6Dt=qilGr5hpGO;!jmsu{G24Ic zZr#!Iwz#5tenmT%TWR@}ASF^Q3$v4{fK^3r!qii%ffB1^dR~Pe#We z%XK`ym~DsskN$~y461qN(cyz=62Ut>fuv!moC}!1o_-0cMF-Qgo}~tPKkLML z8jiK36Jyr~^dkP-=sjdPf{P7@?I)Z$ZM*r#a*Tk3$9N-ul&5%an}>NXO2>F@$hlPy zqRK}1E^8NbjdrGS_$MVj-Pu=bw@+@cBMFYu7igMf_b7dq?ik(^wpee&I}@Hb->U#T z3;#-VEwa3C;)|`i-Ee-l&vyIwE$_bY4rmNM8hkmZ8iT)&dYc*E=V=nhj?TMGUAmv1 zO2>4up35jM>c?uF+d;pfWTBI}a-V!x1-f?V6YHe6s_3MiT~%JNYmnZ16&@Ddm+sEr zDEDuQf0r{Quf}~{vu?-3S~DVSf__E0WgUN%LvWnE&2h$;B+KL3_?2iWvd6g+Uu*~O zTlP3dO8$tA=B}BAq#Lcuc`VqE4x+wpG)QOTBwghFZk5lmfpomZlps$_wH~snAI;B&bd46 z1_Of~XEf8sKC1LW>DTx+pjU*aw)p>-;`8H9-M%J{tKbuUdG3BU{yOv|viz^(i&_4! zf;*I7_80!TU{l0<;C@jA{<@CNMN#2jtk;F3k*t0y9Qmz)6VTCal>ukriSGk?r3Y(# zt`xD@D8pM)Wq2FecRcwSC}}*Ki{Ev9{_LPy1+BR%L1%E^0-`y^%3WT ztdV+js86>Fw@;@Emj^`smKTcIWr=`rAjb{%K)pU!90%oBoV7$keYhm2^Z9=) z3UH0mi#wdbrONXT&*E`&9M0+HIA*f9FaB^e64~R3PJX^A_C`=-8$^E7Ed0L;d1Lw(^Xt+vA*Pf?p{FUfDWV!0_cOpI` zO?+-P`8Y%dN+{>N+};f39!S;+InE$=i7KMJ@i{h;j*ZW^_@QN4PByX}d*X}z#C$CG zgVyn>&E0|cBn~z4sj}&k_OI|SM^_`ub-n-h_IZ=tZliwgPas+=wOc-J$mPQsu8(2t zx=F8z-;+9J0hQK0m(zwrbC6B%R(!Edx#oWI{)27qD?9KeOL^78;fr}mXxP{}b1IfD z^%mY6P#?Kh-5gQ%kzWM8&9wnDe>gp>y(fdmX)6pBsf65 zp}ch#PUgi2&&rN9e2t_p=UeV0y;o#8!_dCS@{Pe4`)ZSEKeW>x{V9A5I;>n)J*UD# z4oj|Ra+A4M$KI?$f~=kQ)mWD9$!6uE!zicqy< z>#^)^Zr(yjqMzkGOoKcMd6RO!Qo}v+PHvG*fH2NmD8j>W;xi5Zq%)KJH{jok9zd4= zNqn(P#ht0jlPeo=CutM?3%CHJRr9AcGGQ-~tpN;#( z+AL=}nuRR?e0;GC#y$NIS_l88fGy?IikbRy!`}cO)qJmbe+*x2oj(uU z?ye_HE}vboNJc+!kNuffO&BSI^WKytq#6Q4yjyI6OYLwBZ@|J)HQR76fXi|}j{hQh z8CmYP@Wn>@>$mN0Zt6Fw=rtC-gF#RD=y`5$sp0tW@WkgB{-Se}{6E9L1YM3S|Bd)! znTq?^B}*!6%4=A3Y#YIUwVmL@lRww+Z-LLoKmLm>X9}8*EdOkLu?)t)%^|ct3GcRZ z5}JJeweZ>NR-MOtET|i@{C)AoGF9&I6xOTgZ@-fHRh#0cslw0y@LB%#_QDy7)QIZMXPw67&4);CwUxj`S^@h5s}3b0kI++sWUh`6pZ2(eo>oFi2xg zOS3AqZWiZ~jq|EekQX^RyL);`JI;K=*XZ-b*gfEaEN2ATAK5rhz!!UBm*$_`CMU&A zDr#YIKJV@op&X#gSY)pc&_TC9?{4s<;fR)HIh`_}e-(UE2bTE0j=u?Q_L(yrUrwM2 z?VnOYTkmrf{nBv7;97xyKDrRuxL$)Vmf5(wDk$Y?qtCU$=Zdkf_eGp%G#J@AT(B#h*aM}F%EdB)^5R%fEy%t*AQ^%V7N4-b+m=*LspVANAy1vxX)`?LB#hmAYQdx?+QDy};=Xa~mMWN&zaT#-fqT`T_V>!Oq zrd^uvaeMWo7R17s%zn`c&{F3$qS%MBk_WRRHIHSTQlGaT9vjE6@c)DU>oaCBj_GX{ z6U@!YVA8(z`6ksFK8fee_-oO<$j0*#e6h^MGaWTGs2{ZsDjW%)+qvAdORCs68Gjx+ z4O#AU@Wsya=O^67c6r5$n(A^Eqvjpr)G6y4)q`f4@;bBG!y7f+l`K&gaJ~wTkZBRR zy8nlIG4%nX2N;C?A3=0V7n}UN0Zyq8Bv7|2$j@jdvd3{5zL-6Zb+4Q2ZYQs|l;be7 z*ZETf@}6ZvdGA@?)!ycQv(w+)Y`*pJ{{0vZHW76$3LPX=Dyu364)Z&ti=E9> zGbczC$!9)K(UnP_Gw{zs=OD36@i20-d~q7o5eAAhYkjT+4EcPQIOSc%oQS9!5)-cN zj6eG+_nEXfd0aAI4@u+`tbC_b5In2%xn{y(&)o?s{lj0v_nD{Sd3>iaNeJur`JH4O72U(5y*}25w6}Rij;%4sFxR7byD;cxyBDg+7(HGW<^;3G z>Ys&!ITN#U!ntmNOBSY#t5dwzFpu%EN`?tKiL=ddHNZWI@9R+a)mhFUG#uIE8jUYj z%mjR`%a%g3B%5gIX$!BKvk$K*io zaeb#^-*dt(7C*-vUmg5%d@^3;pZJB>vaTVr$M;Kou_o7yQyF#pE{#`dH(%O3YTi^H zP#<|KobSKrZTQ7aOzE0^_ zw3EJ5M>w-tdS88}W)KQPar;E0`YG4FJ7gnd+(Lpy_~mOCcXUygG)UKKJqU&+%w@4?mE z)P}oNS1d2w-^aUMs!opqy-qtJl$_`@c7=cbZ%D?w41Y1IMwWXyzF21CEd^-nP)C|! zXbE-xuMKYk9vkP*H}Z}E8h|YCKzy-`$Jx7+wF+@G8RFQLhPMhHo1dS?--up8miKjh zvCPHS@AqsS-kh}XI{8-_-q=ma_*UbuMCT#Py9!?{qwzJ!ZE|pKGQ3XgYQx(IkB#qM zH*?=dCCKuQ!WYY2eAD|uo7DrR!05tN*QA_$B)Rw`kAjbD4EHLyY(D-w{%7c4$a4P& zUo4|>PD3Z$FPQ?P6X>4c=fSmuGOHr(<0WW4L}Z$!5u%Y6sFSmxqw`%7C#x=UK5 zn|$78cx;@<-@^S3%|MoS7QR?UVjmb6W1B;nQ+-SKZ5@Uv;kS}jrd|2jdNS{HX$!ehTP*xTyOXbZcD~} z5&oIz9Ax>=#TUz5+*A8mTL*lPmVnpaV0bscW8>Xt4eQ{ceURnd4__>!@lHW*6X@RQ zfv&&NaM!{mjpkbPD*ij@ePp?};EUPz`|hvbCHrBU)$OLN&Zod*JWmWsS$!1gSKVZI zi*HZzo{xV8x&~R^>+!|xJl4CPH*F+O>x|L2C1ZFL^xNEQxVOM%<2>dL-s42aAj>@i zUo3NRZq+~AI@tYFf(=Kk-f-8$Wx2n_58ui9SIBbbxPD7Tr5??HTm)61AE-x9|-pk8g7UU4f)Jl&<4s(>2OZv?|?>cyFeDm(g za(bZN$ny5Z_x}C1=iC2j?ZWC-Z#vL@l{Vx)Ey^=nqTGC&iSGhGW{!S40r7AB=>UsT68|L+!x}DWiTJ74Jb+r^NB5CcA9QCyp8bK zI1jvs`adc`mUk4sSZ3p#j+`3ilT$M@9F2Dv?p4CcchO(*-$PrF<^BX;EW>e@hId+s z%aS4X(r>)e@Rr=0EWj@Xxa%zZYr-nGO*6?nD$HuqhH(Aa&bQrR{ zN8*cRFus1nJ1x9(TZPw2++}!I!((|r#{U|9i!ARC_+pujuSHG`@4VFTIt_Ol-bwc* zd2htO6Wxm}Zv(zq2IHGj`%KTn3sQpIaF5~L3XhF%>HW-agHAz~cMiT-X5*WNoEqd) z(}D~~{k?{}0j^@c3wxk}wjk<+EO$42u`W%wdfQ#>pKY&Rk*qM6JuA0qsV#%MC=I#p zH-@(i9?Sa(egZveSrncLqRiF2IynUnHa z==nW3fJDG?5vxivagbB|u;HHxUkpBp^BVl$pa+rVe*|A__s3ZxV8@^M3w9D^mXkzG zk2%Ln)@k%Pokt93DZ7urpMw@4%ee?&%*NS{liAfcr&4ZX!OmpfNLjG6`4JP}b#TSt zlCmTGC}SW|H)OdB@x`_i-xErH;(DDet=ClevQ=^q>_A3nNL{kC{!znO21g7I$;ZFJ zPoQU!<$M8OY~*+5{`dYTX4?|q-9y=^o@4fEW}-BCc!XSdk7dk}oYe`mN0 z)+OV+0KXcYfh_mg_+lA}uRMVF#v*xj`*s7|>~kjIh`~{Ya(|cQ?1}b5mU9rk*f#U; z&c`>6vdx;PO)Zay>zPu*X?o1WwF;gXJW?(^i~llu4O!kz_+s0M>rR$ze*Zn|Hdj>7 z<3)AvJ-YZ!QV#D;c4#8^B4;e)RY*VaxZ#aGW_at^w-o;hbPck+*W-)X_s}faXtxuK3aq< z_v!dzEBx``yU4w*I47a+0Xo|d-|Z3Kt~682Ilni&>)?s=RpOlU6#bv5H?q9_@Wod7 z{k6Nu+isjEE|K-jrHrmu&zczfZNYn$R6H4lG}VLPquJ-Kg3IRT-{Zf88jm8(?DrZd^k*vH`&8+n*Lz;o8XhWM;SUe zk>yN8MfMx?1n#QDBi819|2mf$|mJJ;+5G~D}4m_m*)(x^Ni^?m;}=-{0ej`vb>e}Vz$2{ zBXLfDx4%}M@2^++w$!QDk;z#*UVFn}o#N*W_d2+w>=W+XKk&{Q+5=hcJ@Lin`tvF5 zB6r(--KwjjR$Z!AMu;=8kEIYd`Me9@vHAHi{O8d|WO-k~7t3URwolhu=j10+a&p54 z6W@aM$$UH?|8%q*S>Chp#WI$Uldvf{_^Ff}>?QBiyk$i)AwZ`mca6BYJvCygnuWCSEe}-2_h=JW>wt{XAn~(EiBs9*i%R zvHY8aP07E{waLHr8x8L&cx?Ib4*nW_vyz9AXs zEAekaw;{{D7GEr*aZZ6w3HFPr!7lxi;cbS;#`&lhcu7IG zkGA8)UN-y<@Rh+Q`8xU{b047|$ny8b7t2_@+l5bw`kzvx4oIWnUI3Sk`&0NYp+;o6 zU&j~AWZYAqwzUp;W2=BS`rNUXlJQ=O|1)$hvfRJK7t2_@)1gxW{c2jE8(uMSZiL6i zdGJPjbO^G%Fb7j0bFJ5lKlM`{(AHRvfO{d7t3V+PC}>T?6=c%cK#cNx1ceZuPgD-MzzTDo{uk< z;e4H1OHFGPHYKyP$C2>48{x9~y8kP*0ni9!xeve>%VfSzg-&MachjV?qEq)>3S3^-ekB-UQOobi}0^R*CNY( z6TVm`^K%mV7NCQM_ydpldZh<=={NhlTi~&A9`|SF|3lM}<(-8u_WvQyDYez~U~fqY zcEevyoa^B#gG-(cQ(FQrsJFfof7Pi(t{00)tiQUCR{e&>+qjNFCxqR zGQL=btsCHkMGM8DbRZiLIm zec&6kdC|eha>wz-G935xT5Ed1n^Obcu-U|W6A^vwV#9sQ%K5sodHa~xX{{ssAHOU*n7i-SobAT4!yUo4P ze=N7vYcjHCnXKfPc*n%I6rM7^OMGv_UyFW&YwnUFJ{Ld)O=;0tL*CdgLF#fDEY$7QIhA)R8r`D_S)`ePY$U$ z;<(7+DN$3{<81!G9M=lCi@8zINMtz=#24Fb;}23uZCsNN@zNZunzz$m1rQ(^+;LP&{U#Hzw9H! zTl{u1zUSayjOvi(y$WA!w~aeUAs#p^_2K<9fRiNM_$P*Q6&$jo3^n0@ioQga^FR1v zyKS67T3p+INS{8!V3kfCVG#e+aL1dnoCdhc&{g<1qczBK--R!>e#>r;Z~M@1FtR|p z8G*gXBYwnt-W~tU@HWF!43ETm{NHH@qUp%;&cYYlZO;kQ;=KKCx=VBe80JZLv(H-( z&uVytw;BH%^aHZIfp`7*ezx1=+XnU;wNkFOgmNy`d^oJEJxXFF*6_KBZz()BzBl3D zjqXR5_aS_-%)~df=OwMXs4gY6UixLthSzy78Q&xEr=wZO@}7(@wtn}=Hx)J++AGpS z8~eg=u7#rnj#BiM@V!qz0J5Cf_+q=Q9-NkMlRYizPhPL|?h|$ljF-O8I}@IIb_nk+ z`1ha&WO*OP7whlW*LLH5EeZPsRp2N&wn;fxYd)MCSjE;$)@l6GKmY$o#`PHdlhGVx zITzrIWiGBMJuB(KU7Z@-`hOYT26*b>k+}YVANhdyB9P_ngfEt%xTe6S1oygRaBIIZ zoHOCD`SpJMC(!Sa<$MlbEHiO!x9221x;Laomw0|{xbr_u#&<6M60{Uq?iKiA8HjJY z&?!;ADH&z@ss3$v6Y$tL=Wk*CC)5{N-o5d~G85gKZdsoo_csB|2~QT zJlcpX?<@FX8HjH>Y)W8nO$lt1&sp%#WLy{HpM_Q;%XuEY*lz3JNh@#Lc^H}goN0}B zm++pTdKgaqRukVwxa!#@@$LH&^T42ekmcSVUu^pBFL&F4UPA$_O_kvekC-9BeZBO{ zzA?PD@D#I0>NjuTzl%OZmiH5UvEBBZsZE?)KZH!HH>~yQ4c?Pe55vj-)^L}6oUAuo zjDID%7Fq6_@WsB`J>!^DsE4)pxHnmUxZ8^}Qz(1G+nq+AcMCkWoH*cy^a2i_dn5OWOh6Xy}{2FzSPQWBK@=%0T$Y+NTa)9;IpMV9jfe6fti zHKi7r9^7^5!4(c?hR3rOu6jHv55B<sXB7x2Y07Uyz<(9}1zFzB z_+pufb2@BFfS+j-VAt>#eU*&wa{OPQRmk%G3STT^@ojmol^)vl$V90hPl zOy9--7&Rly`8B?noww7@kF%trX83}dnyO(7%9oZ_R@ZoMp0DuU?0+{Ynt{WXRxFtd7|%e@4|q>J zUsm3x2(bE~bA>)gcjl#W-A(n39H+DOK)16l(x-(61;g2$!z@wFycXIpDUz2Cjsp2<_`MqYe68}zgFS5t|KE7E0Ip%yUTHort*t5|XXjU%= zRAb;`?QMQ#HXB{)i*`m2qE9Ne&u9#DEScP2a=4^cs=%6Dyq#~hE zFh67-*E{*$OnR~ZCeu3vKaM6Kv7eA$Y2{K$u$Ut&cD@Y+{RF!@aX+0pezS<*l=AB) z{M*sp$i}e&U#y_jx@0Zag*l?avYDskl1MG@ySqd5bjLZ;yg-@o)7i@JNIon@`QK57 zp#jLIvk$)5`c~`NwxpBH_kN0Xs+RIu)v2+Uu^_g0=Ke&|$5JBXx)aUW<2~8#CY~$! zeMw(}cMJYYs1e!pFZkZ1zjfRi`B%=!x6QVl^ygL{TDfRZFTbqfR2-MdvVniGzk5(?{)?rDK=*ZzUrKzLTv zjp>8+kkE--V!=tlF&(+)I7yY(6C8C2(e(n+sZ%TXQSsl`mUVGk!tE zoYU-S;w_M0lfedr-f3hv{=kBr(Xp?ny`Ttduh^;J=C9 zL6+lFe6cG(G}r(7Z_KuxeB)n>6gF3xp#-bgL_kPrXA5WN6wX#h_dtD|<1Evay1$ZZ z(ivhnN`5r;wpw=0z@LrgBg?S_Uu=bI;!xD9RXmc{k8p7NX9*Fx`Ts_Lt{B-e2@ckN zyDmIJzs)5wTKCr@^e+^7Y=HN>bwdqDf^_V4{8#*cqR)`!_!eJm2d?8u73FfD=Zv(y zki)GnsN{kRitslU#~uS7M-rhhKJ*!%xA=ixlR9+YxE=9Dj)Q?Y3J!o?NU<0|K# zIdIt27LZ1hHM+_*%1H?iClin5qy)oVWx}meeUSI#d{hlLJdLDR!1p-%7yfs`!b3pI z(*a+si=Qvf^XFaK9?$qil}jrmGF8hfRh8V8>XoyVkQ}T#>K*~A636JidZ_oi`Nf7~ zCh5f36XWEaj=vI}hb+ekZ8*N#YVt+N^{vXcYJxyd@A%5f(-wMj^$ci;ta0y;sD{V~ zy`1+04Sk)NhePnHRIUaE`qZnEtJLXwhK`0->Y}c}Jx1xQoEIpMq6PX_O79!!v1F6F zUIh=DpeO4^ZVA_Xmi}J%2o`n^&B>jlM-C4bXBAGSj5tkq@-(>1_j+OQ)b5?bk$EQt zPtNL?({Gf{9~c}tS?{M02!(WqF~JF0|10>LkMX}j zKOh_T;{qn`H@t1)o+xV-cdkn>ikYnH3e^t5c&E&sPi7e9pJkF zCB$3W&DBF$*{PH6>u%IJMLvl!cv!axV(7eN1KFsJh|&MzP*g_*_L{ znRl?hQ0MN~;lM5vJ9Y{DQgvFcr{|5A5oX59rkJ>F;kc!oh!cn(@H?{>Y5}rwsgSi% zu~oO4xNQBdRe2@(F(orLyFaSWUDfRVS9_bwn6^LAkn3#WmU1vRmY^;PEv3NH!vlRr zl?>0Ki9jwK9_Y~_STLF#IL9sN7%Z3=+Fxgdnzin*cW|#h-N`sXGER0l5Y7rS`;C`n zK2`b|tuNL3My*GXXGZ8HdV-$U@d%H%?pSkt8{~M{BggnY{=d*xWREWtG{?8idi?V8 zMGO4{oKU&AeBlzg;7bQjQKhG$tSLc`FTvanfkLkJs5=|cv>B>@Eh$isI?iyEl8$gl zy>v1D>1a8!92@Y(w#l(@sfn_I@H=)BT2j}}Yk?RIiV)c<6zc5ub;CM9Z6vHaN21~v z1rCUe%8rDBp?xEfh#Tmu`?xH=9&!WSbZ6I%hH^sM)nWdA&k&8XaG(Q|lxW=(p1PTa zKR;y3Es5h${E?^xS^m-ZV#R*DtZn_ee96L^g=bZmqp%&*rAJSZOQ*_7P$irowNY~$ zW6}|m?kava#_vu-ci=yc63C|eD!$kbrh9DpqGeu@U8_!A$^C4ue4HpB1Hou!l{VoZ zmU-^6p}fFJ>~_kTTg<6C-j72#+S0xljz0qJkHjd_x81%-$wwZ?D0K~ zFIG3wls6BpYE|wm_4Ct|>f_3*<)ZAH2;8Bb4yZ)n@qo9vI6&fc5$Dq2IEtOHK3rwz zhpCz$PnmPFzD)&sMEA?$D(T1qdBLn;BsWO5KTChDLe)X;L{BQ+JJ7|0;G^^u6Q6=C zQ(j7Z_QgK{jY5|DSbVYAw%VCh<<(35i@;{a200~sJQj?(N9bW(vHR(QKqQ(U&JKoM z9i-e?rTsTvYs(BrJ?Th!QiPtse-^!fEXT|EV*j%|kt-39g-of}2(Gkd|I|pCvpP=WW8m>S0r=D!en+82G2@ zmN?AomiWwcTpZ$j5U7nflcu_3LS1Pkvb1l2O|g4MIGp`&5;{kprJv%ap$F>^87@l& z_%cb}v!=NwJ_$Ht>`Cx9ui|e;e@F5UB_D^f4d+s@hz>WUQRw@*2X0sWJCCN zb$?hjgnw=}4~M*O7iqII;BDSA-`;k;Zx;mfb$%#M<*n7eGu_+&C&i&Q^l@&Yad4$u*Qn9Qu!!pgD$?IQDU=(SM-C645YEr#KW4dyO5K2Z%mKRJUTgI! z!Lo4!a@7c`L?iYIPRv!2P9eP)H6!j-ds7b>=PuHDYL*_ThP(78j@D1Gf{C6Hcu|E1 z)7n0;!^+$-{OpCQV^yd1N)PIMf$B1X9P_s7J*n3Ps{6hLqqb`o0+-Q>C2C4& z2DwW9?9HDJJ&*3b&t3-xd-mBs5)F5k^H+bSIe)PZru>R=!bagAi%vlH{GEv}mNVYu zuPwK?x(?<@gLiT{YeHAfT{v%H#azj04ekl|yQ;yx)b%zWC~wn4a$eY}a|cjC%8x#) zSqgp>fBNZs_b*E4=Y_Lb7oG>Y^!Y9)zm3v+1#eZ6ap7UyAAV+d8{wG*kBo+YA3vBI zb)v}fPQe#jdzs<X9BtL(1byM}4BJ)@|DM0G(V*f|{T zp4VLw@frHJTEFhC3VWfV2i!eQ@0)d4?nJ#;-t?Tsf&Ig0xSy%OM5p$AbDZlqjyT6r z#({0b-zzWb3`X`iufP{u^ME0MWcE?gux=K&4DOZ5Fg)eyWxd7IAub#j&Y zdX4{=_VRh(e4pt3+fU7x4&IlS%zxO^`!Z0@XD6p2I3VZul4)~8Wn3GB(E#^oKKrpJ zHx#32=|dmB&h1vjqOfH&BnHr_ldEGS7t1N4`-6X5!XA6Hz98&%*66}VGHN0O(QPoZ-jVLX8Q?WHD;PJYz8 zw=5u51^5HdAY|is5WZO4pP%2(|J>4EUQ{`!d{JtLPC|XJo>nSBSIP&{o3lPt$YnO$ zokAB?FpK*s6szUuJ!`nkaI7Mo7<*Qsd+^txCz0iN17B>-d$)SqhyH1{EgVII=5aD7 zl`omQNY<8~CdKBY>C;O&y}fGn^?@Iis^uQ{fy@&713i1a^(DNBqsO~v>WFd!IkZtJ z&u>=lVQRcCpeM*n#;Lp9@D+75aazmn!|{(rCm_o=8((Z=(B!wqP^)r49xhESUoyX9 z*c?-sdtadIo1n$`v{nh7XXvgAAEfUHIL?F0-KzBd-p@K!zcw5Vq$73nD)ch`X7qPt zIX=S|o8;H0ibiddV=+B0(&tlA4Mkz2dOYy5%n)4&MRUNpHC*T(sJ_slPQgOQIfzE+ z2$rtCQ0}Q>Wmg%Vcqelm#`udF_?Mu|k>&XkUo2;%;iy{H>N@o5Nj4Vrar{boeH>qD zZ?iGrZLT%nHklaOov&4ak>1f$ zue`;?Ye8poKjAuXF2es6x*Ul~Y~$PRZ=X}SY)K7063109WD4hj!@Mp3tC#>3J9kH- zbY63+jMe(-HTmf^u=^YQP?xBajqLdufG^f?q&YuNjBS-)c&IhMV(C$bA2o3rtvboC zjjT@nvQ~|H5((G3&br7jY66F|Nc(YAXZu8Q>D`WGht*hWmF@(W_is*8Q&n#-8K?F( z6Nf6&jj>DOunPZTbeYd0aoBcyXW3#ew=DH>N`WYK8mW);)7dH~q@;Gwb5B2=CVsO> zeh+<(|DB}KHF^ADe6cJ4eyg`_)1Ea~h`&~SVd+6ra-1!JGN-su@2~TvA=}maf5mIe zah39WHoXe`MW_ndc%OkU_P^4j>rbxAmh?uqO0UjOFTwBG^j^o`jGB;5?;rSL_Brad zj?aIHQ#yG{l;f*%j`o`Ahx@Gpe&BXWEN>5jpJ z`TeG~{_z)>a;lzvL-5C-3CQvsi!XNG$L6|eb3ZPX_OEp6OOer_Z`1c{)u8wCK0AX{ z8*u8?N%~a1kg853*e55F-I-yh%DqIAaLVpB9QC9l=b#k*0soKaRb)B-iZ9m1AJ0DW zKZd8JUa-{b#WiK7DKcC7b$QZJ#{&k*dAxY{q8hG$&d(m}4pl>){CoU3^hoxT?2msS zIs}PHIc3+&Y;*pL1}*UJtn_D#__h>Jt5qQcd)5Z3#4L=MxHUX^w^tQGHYwr|MpF zoRLCP9#0~Tz47-&CCH|K2)>w|4`2`fIetr=TcG)u_?z(4VU-B~UU{27px4qnNG1Moc{Z)w12hfUr93St(f?GGncR7UeblgS z`|DU$u3+Ucvs$baX(lf0!0%=WtgkTW9GH1EkmRI}?|JUdiizD1I>-i7eki_+r}}XR>hV z(UnZuZV1bqj{;@RQ>vRjf=AanLjHNoq_cwGk?Wpa&b9bApnCterF_|Dzh2Rx((1~Z z$~l#bCRSHhQowi{VqffhLUnMaTufNpPiKpt&MNjrdq3PZrcP#? za(&O{fuvWMwn~uZ)mz$YUJW4W`has5axPcyA6jOkr@(pK9Pa|siII*R??w2(LYMn# z%kkRsrJeG0YQ^cxSVL;yFz>*})=aLTSA_$PlWLqtBT<*OQtSzn&L)0S;woYI5B?8G z_emaa1Yc}B@ohhK@#WdT(en8fin2I(E^OM978aDF5b81Cw9I*-PI zwL&`b57bk`mr9aOUBbkriF9o~_#WTgGwMW;<;uku+mU=QwZa>!DS>Ip1rM6%S;>m> z?@hWh`P~@5TZMjxzZ%_$Y`PENi7j$0b?$BMotK3fV(O)0HDiJJX&ZASh-bSeFRNyEtaX#w)V0a^a4ex694aGki zO+%J<3BFiEz;Lef%ZZfoq2>H7Zo}KRp1bYMTGg{KkGFZ)d>ik5ISA?mwJ`S}ZfDta z(H^Xi49%xAdVzZ}0}%t<#gEtDvWgO&O70M;t5FYr&TuE-B0oBD^f&x>(TB)#e~K@* z&3byu_102XuR6Xm$6RfRpf`B#IL-4x&q|*+>6Y|MraKe=6f_svbWg<>+u?L6)K+-6 z&Sc`Rr6k_yr`y2q$~C^0gkHk$-9PH|LpI&D_+k@YG3RO0%vSY{l;b@^Iugju0}`P+ z{d7nrLhpyYO{4ktu+UX}bL@AEE|^5rs9S?}-d8ti>`C7jJ6=LS0D zI)%pT0l@?HfCx>t0q(x^cnxq*)k8ycu7)}U1_Z*Q4z%o!br+~hU3X4ktcT|`{>2>PR@c7^rO|?~c^4~K24e-f*t`xnD|2BFLS^m%Q#oo8$yV{O7YbmG3r4Bi+ zQ>S>NSC6`keqio2%DI)SHiQTVUFgba6^`e3n|zLvz0Gylz`kSfPe;p<<+uW0Y}Fnn zKXhrGFT8%wR08_d{;0fuH41a-UR&aQ9`Aj|BoDaGBa!3vr3^-Oe<9D^B8;CBz=*rB zI}e6lRPKdZ|11!bbW58|yqe&U@=?yYE~4)bf)?Y>FY zJmG{Zrzuj%m?X^wH&(CY_wxU4(l6upC2i5!_?Mu|kxhRMzSw$yyk?v6fnJ>aQ#N^_ z*SA5!mj!d(ZIHl3s-LiqVbuYtUL`c^VuR0M`!;Gcwa9yo5x)5+t1Cv*=WAic>C6|FAQr#H>W;p zF{6@uG06HTU6^}v=t@m4KTo^GdYb;VF8H;k>3ei&i7pI<-GPyz{AHoLSEmWOhi+Ex zWVfq3U3O6z?$y0tpmWEZyhwHqBR_&_j~NJkhA;ydJ(hl7JX$u~!b z_lYdl@vJ!!@mA<>RMvaJ+^!vdR5>id#(#30+RshgiU*tPUhW6S;8&nik&WBM_+pc~ znYcAXTIHXjL5s^*$f%j6Cif(VyEdtZOyR%T+z`4D6k4!bqEjeHvpBNBjFtO`ay#%i zcrm4FkkUTv(of0*wW;K*cU54jlla1LZi1r#4#`*F;s=IAoiMVT9q`3$y|+!iYRlO& z47T(bM#Q<$RXLYrdySxD{vT;)0$*iu{rzX2``jh_%>oHw$tAKREJ;{|uzJ~pA_N3M zMM;3LD3GuzD%Mz+QnebbMJz5+(ORoDwQ6xst!=Tjnp$hAwQc@tEp4r)YTdf{e$UJ^ z$t9cEcl`YBJMKf+vuF z-9L>yN8lIZIZp%_&Ds|X23DR8(2~3+Bge-Xo$9;phZ0=1hcV^_h^F%|qD7H{zAoY5x&b!z;~=&=tuWBjs<7t{>>ckmvt?`vqO(PQOq^S<89?)1JA#x1Q!`y$F$XlQHj zSL%=A)O(%z8m%AZ=+7nk^ZlUy8iBrT$=P*k>EDjEA+%q9%hn~%Qp+?K6$LYM6 z18PWU1P3;faPP%Rh!LcO2@I~Apx1b*e zj{&Ri&!8psxUZ6+uV`p}&hqQ-w>vD!qK9%GN*jZ?x!$RIn}u8#xCh`8GIB)AI^-A! zJp;@IR*qAlk5vxaT6&hHrF&Um(B{k22){Vb4d5y0KY}-bl_xO9$WuPb)Ptf)=DWN4 zEal>Rd~lWQ*V)OXG9Og>>r(TyTtCQ+yOjlL+UdOUdU5bBrczH06|qfH=FU^$$cagP z@Kco)^+PIfjT<^G=&u_plTA6UKrZS3WSqDN`buyuuyXH(mSp!29`f(m=uYmMU3DB) z!!KC2e~nvft9r={64{7a?>g6{=euu__g3p7SD9kuXoZiJBY7Sk=(c&;oTSzd6+><sXO}+@y`i8}KX2xtMk3^3MS8nArTkS_ zn+^YKp6bolCuX&JoB0~0|HXyK+K|bn++uFM!W+N;o4Nq{#7sy<4{Q7P;ri+0RnU0L3g1F36mn zL#AiCUkZFP5PivYzv&LWTjlHs&2hO9g^OR$aet2h|_C3>*(^ z{h9?WX@|eBUhdy>oG?z|&$gwwWW&a@tJj>P-COgcQ~gC?EJH<;K_910&dgoGQdpb; z2dEr{rC_|b$!YcF*dx5*Qw45^{uy`?SUKK@mUPg!lQ$H08Rrw_kRFK{t3r+x9G_R~ znd_T`24>>htmfCqn4iToJx4k(H>F>$V!Xded4Iru z?p6m%yxjp4`nF}HDaVRQrW`jQ<2vZ;z+PbG|1q?rP5%1ggYWbpzaC83I^4KU4=2s) z9KS60si(!O@}YqLJV-xYXMXLE8cktBKE>U^I41R%bw()Pi+eM9KEoXxy;XSyfk7Fm zYPw2Q;f&NkF30OJNKACk_D*GAGJ}6N)7|ZDz(;yaT7R`Rxg=>A^46Cc{fZ`=en{Hl zeCV^l8esK14_cBPFT2^lGw)|h?T#7~Sz?~?RLPH@;J>By97;$A!%&7f%{$bUL0C)j zITFB1DJfTG)Z;Lc37`5hBUdwg%XzK@Z$P(!e*!C4c#4th&5!Sp|IO**A5t_zrZK0S zw{gds4V_oy4NC6Y!%3t-Y6+L#2TGA>fE#sJC~1|H;4H?sNNzKd5`@iReuJvHU&MoKYt}4OFk8!Qu3}|07BN@|I*%dnp$vFG+-@rm@ zYdOcz%dihTBR2G6@oSCLFoRtt6Id@2@cK+o!pHT?%6r6>4q>S~FMPhcG*liO=+qx? zDF3=mJGav&@CZ2N1fp#)F=_G&5Osu?>*8TiVJ~=V z^Z7TBKOLMd!v~-r0gnUA|2gO{!e2Kh;a|0B6}2OPJ>n@}fTYN9nVeO)6=YaH!PkG9 z;V*sB1n6mCCb0Y~p+ATJw)XL6o7u5mT3frIdfR$ESFYT!DBXzhV6cEiMG9pR_psj1 zG&j#jCmK2S!6ynMk?m3FXTfv8%JFMxNp^nR%{;9`jx9AcYqy`WW#hVaQWEv*LYb-M zBSw>Qo(<+>l^m&DF%d3v&(OJ2KYcl3)6MwHJ9MT%uK;HPE5~+dNiX~H-N*a>VD00> z_HAqCW9nMJb5r#;fAN*y%;ojE_nMxvvH%a&ESe!pZj*rA&!dF#p0p zu+&?j9_1=B6=yy$$&^zw{N-I80e^&!oRI9K0W06h(2_>|%(Sy1o#&55BRa}S+W*{b zwJhc2l-aB1l-b^(K4tb-^Yp&?I$(~TRUmI`y7TYg+{iLFPNU25vavYkhcJa-6Tn8t zK+lArn1BW+@x&RY5MIdHZX_@^J2j(DrDyJUQ~GkF2GhqMDfb_;pgfh0(ZM;){yGF9-7Rw@Jl$+OmSC5uu@@No67-QY;Q3&C@@yx@|+ze zFa`gy5kCie_ouGATzx52?w%EXR3*>B3www=JmY|hOb={C*m*|&I^>i39RXKDe+^vc z%PjN99{p-o?_9UB*7S~*wR$ESN7T4;eOQ92NCcNOkD46!`LvQ}9{I(=C(z!EWG4yO za>|94G~Tbjv;BJ@+UqZ+GgDZau|e8T-TAny4yz5pwpnpt(-`>+nG24R5%mmrxavp4 zHF{Q&zvw9M>U`*Wa5=F2uZ5Om`_nG%82f%F{*|XLio&~|2#@eC#3I2oeWE*G=TqYwdbyP3z>cC)#9m+^Vi)_nfAGFSsDVlrzOngc1iz^@17ZTWo-x&hqq#rSQM zgVoiW7Hr(iwmo|<;tS%x7sI(eQs%^ceuv51&Wk>S&YF$S7qI193@yo?H|SFDibfc- z6J3M0I(3~JousEme>XFvR4I1sK9TVZXvK76=~RZn9!E=SbS|d%4=}KON2EyOA-}$=MywQsf(3Xyo`9J{3GiV4OCGcMl4Hm18=z zq?P`@Te*MlaC`fxuH$g)TI=9k?>(OWSwPi$GbxA04Cg12xse6zWzUm!#p0anRCq{a zQh0FW@}z9NO@Z^lU(+Q2=4D2nJ@AY1d<5JL{TuKau=4bqYvfts$7hJSUHWq~zutD# zz8u5Yvu4D4h*_&X1p1PB?x#n!yFLANulX$v;oDN24

      {(ci5I6}aD4UMzGLCheKD z?+P!U|M25TK2A2!A~fo)?l3ivutXzMQdM@p^dxj@P~QCk{;qc8S8)Q}e~~+jP3yGD z(XAQ%ou)I4zPr#z+Jn@EZ$RG)?gUof??FrIr5$#R+ol;x9}wGFpkmTHHY(>Cwmt_& zrg+4k3c_l(yOR7WSNiXt{DprT$e)LACzu2*|BcX+DvC_Mxbl)t{bS<1LH9xJLKL87Se!e4PYj)X%? zQf{!nBG6;JF#R2K^WC4zPOURv0}tEj02soYF}T z_OiEC8`;}W6cx6)Pln!gRht__==w0Hsu|wcULWorPi!pn{h{KgSv(ANPe|$$<-|{b zVXqorP;7kO>8z4t$ke#T$hiSI(=G|@NJJis%ekZOg&J*9I z_97qJmXY^vhHA_BZ(sfOa!`N$Aq(-_N}ataqv@3*UWylBf>N8FgDEadxu@o@$&KV^ z40zB@AIA2nyO(uSa({1H^7_c#7;Vz?@ImR9=MhEYG^{*xqN^FuLL4m1b5~@YJvEbn zFKo<3La)2&tHlg6-i>pTVRBlbJoJkMlfr?K;qZ*%9{+Mea9r3e92+eCrb?BGVs0c9Uq@K04O0V$Zbb=JPHw;SVAA+x zLK#S)luZ7q$_=JSg$Xa?@BKIv>JsiC=mj}i8|JSg(}Vpz%s>1uX@QX^1l?dJ5fIa` zz*Kpyu6tKz=qVMv$xFq1B%V^1{Ju(>?kx!3;YF^dA_htWGt~E$Hz#n3>uymex{oR~ zS3FQnYPit6--;8>`<4CGGokCj<-oq*XP_nRy2ZTTgP(P(hue2l@7THB_wZnIb+4

      %r8A-DgN4BA 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 %yGfxk2 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 #dB_c{CAfziPFGaua-lW)@J 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

      (_nN?*A|oMa*5Dbidio^+7#(eY^IUd593TN!+RQ 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

      e`SO9Kt4bGG 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

      tq1@jW<(v?%r}0BN4~)2f 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%0D-=MyM>eV8w7c^bRxW0Z%*TZ|F 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 z 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

      E{2&Gvli)F8fg872hvxjW>>`NAKr& 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 z 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
    1. diQtkd_~gCbuiB;-C~nVzadoj4eal0;wWT3Sy}gdzak*Gw z{IKNwd7E*LSZ_pC)5iI@hWMe(`Q^{2VZl+((3M-=#rfTZnck!gtH}5bDAs7$Jq1f8RpXp z=FECr^*L zL)>0{<|`J_3LbN3rE!A#ZK3Fh&-^Z0i*o8>Pz#}s$5Ged>_~I!l9(1WS|B`IBAtoi ziYe*T^>e-5#qLFHT=dU`aw2igfM(7>-8Rc*!&6s>k>u0`2`697OyHR?+>}(Ot|zta z<4ovANpkA?(6j%iE(Y%}J9Rmtxs5X-%BhR7+H0trErw^K(imw@T`# zm@MbPWGN3OwLF+q^I&rPbLaNd3zv>Mcz!}-TXyrvP97G!oQK6Os~{wLC1gmWRdG@~~Lk{&eydu$G6# z*7C5}S{@c#%fn);d01>U4~wnlVX=5j^W=6?&BJ1=d01>U4~wnlVX@UbEVi15#a8mL z*h(H2Tgk&>D|uLKB@c_OcpbI#=N4VX*isxZXOov=3%jJ9v187 zVXm( zmh!OJQXUpt%EMwyd01>I4~s43;bTklSu8zBNLvP@eTQbNUaP)?`|gXS1GPI>-8o{m zEGKsA((`1C&34BZo-K>;4iXxvm(3zlp6n)VzZE8=* zlZrtPjM?`v#OblunC@uX9gX_xmwKZSht_uJ2K%BrDbNuDBTc5ck_z;*%B9k>lq_yp z_d2`5aD?|s1>RvWJ?tnMV|}69jz>3?hIYsmAI0p(>^59lbi9Cxl@J zctxj(w?Y>C^hDiw+-@w=uVR8)-A0eAGLtwz6njuDtt|HU+IW#K{#xIzH{4BP;R}9! zznctWet1+jD+IYg(6Lsn)G>-q(9#>2k2`2mv8~FsjMvTQOJ8`KVurQZbo$5Lx34%v zwla_K-j3f{dATHOa<$ui|-^U0iVZig~wLy$3-zLGr^jQQd9@ z{k;}^&*ELL+j*1g@8cO{=}=SnX4JyF-u;sOMWi+t)6>a@_^tb`-JmfXs?>@o-%jir z7CTThLYrWSmp(iGb)Oz4RS!;G4LXhZJcmv04eRap40Jv{bw#Y!+pXPEHa{i&Wv{-A zyKT}pvfC-|+{Ifgc!4ze_Rq9fw8uXcDe`7^5|+=wt(|t2YtZ{CgGK&>vR)kHwbY^g z7_?K0d8wIV#7^>L#Sn`yi+I?6r>|<)EYk0^d*0yg4P3%$d8jPTxbC>!^*ThYZlkP} zmYa45!Jv4z#cIk8TDI=FLzj*2(_I|#eIbi|Z!q+WPoCezYw%AM(M$(800WzeQCW9O zI=B+YW?%>5Fkw~@P8nqdB_y&K3Wwk5C5Y^PPBM2EFI=FIAplOfW`y7_#EcL$O0g^Y zV|1+3%*d6@kZj#fP;I(%hn}E1B52t{?4#)L_=*tY_xK@`Wd3#6o{o)Xga`%AafEW# zA-JVWW+s%Gjv*A84iHLA2UN4t5DG)b5XwRaWamARM�*s*@C;hu7E=^d0imaaEwZ znuJ zjwJoFn>Vg!#lT3elZrrH;UtsDJ;&9yGj4ZA?cOk<&6kJQh8unOQjANItX>{o!llT= z4!ZEESkK_*$C!n_JiMNS@XlSDD7bUG*G|C~`$1YVI_{OA+#Pj0siX-^Ck{~HO0*Zd zeSxiz8ZO+%+yQ!SXc6!*c`p$Q!8e0Rp|Qx+Yf3QSGPyeL)%EBMcT5-KY8~wBliz6D z6ORmXM)9srpJ*xSeBtEyary8#5hl&hsq3pbl8MYO)0)N3}}{Xa`d=x zQQL^i(vvMJ>dtybNA#WkAV_z3=(PlWF9iLK{ZMDowDI1~9eTn5Zzu6ypvS+_jC+&r_~S5=!ZWR4tNipPfCdH8DniwO@yd8IP3|oHlIUNH^Lgr0)`foah(UlSj6+9VdqCrl21r&_U z%PZnvQIrWaLpDE@v|5NsimktkX(-E3_M|?=V+t|I_Vkhf>8KKZWK|VRU5s3zvxH%t zfxG?)(_Cs0c$s?R@pnTERpi$UQs3uJ(IvG8)C?Jh0qb}vW(dD!#Za`mFBK0^v{C?d zL_2C!VX;xY!8ij2QNK@s(Zh?QL?Y&W>k?9!3+x{2jK!3`Pk)$-=g&33q(lHdtzhVrCrJ{)~oe)Fk z1HZ!$h##etj-lv;axihyG03UvA43!WE5)Ep#Hs9W2|H5@;g;PwX^v2&;MOCa=@=|B zH6*I@OB#ZWfRT2yb)DhJ0foCOhHR2sD?YTj58wgQM>D-GrDSB?v&(Gttl?o*7hs#n%D_| z0fUVU?+(MSJ9Q7&8WIQXjMRoO>uUwR+WJacUaYUQdByrl+fb~pv}rH;sx~uOUuj#C z^_4alSzl?ZkoA=|{a9b)QmKtNR%l!*=eWid@;B$#4y@W%S{lLoJtrG@Gy3( zg$+pR?n;$u62^qov3`e5Na-jbqVCxiWb0*S!d2?Ca~rRPz4k#^4To4SP1i$AL`Nh< z8?QrlJx+((l~`sE3f$h)E4G88o}h@cw;3q*q#xw?X0S1?@7};G19%`~I708%A2dCB zY{mB4ws2wc#6f#qwS}F&c6i?v&E#VTjM6n`!O#Wlpo2<_;ZGGCff~Z(D+tbu{G*UG+BWBb^%=ukTQnM0~U!$j>dM6m-{VA-cLwAZp z)TFwVs&07O{ry@%GHVKat?*Yn6%Xub(oa=%@TTvIBbRC$vr8R3H`>5F73*!>?_;8V zNG&U-iFEQ2rkmYi1Ym*QHUg`ih}i)0s4Q+1s}sE*22AQ**CM4~JWrWkqZRI?CZc>h z%S&Y?OC4)J!^=0k;a>FhQvB=1@GEJaHlNW$v5K64aSu-k*J;yF!(f$I@hEw$jF5Vg zxx5LHNo*BHAD~b!(y)5afJvpm%C7H*K?Y7nQ5!h20u@d>cl1b%Ql+hwy4=*KHSAPX zR7Urk!L>DGL+o%i160&B2FKHfmcWxuE+R)zYv6UofD{NH!q{kV_sY0`xfA#YzV@h3 zl4h@7c}iEg+dX*NJthu=sM5(H*j|CLX_6k(tIDvr5wI!`DE`buvelJbnx z@8hW-awI-VupQ<<1mZ=HZp@8g>sjth;oF89$fk&}h&!a°*akUfj+@)p!=Ug%lH z>DJeE1_oQ*y;RC6YEGK6{N#Ds$NZFbG0;k^?q0dMfy&uz^QJ^-E6~mY>vxSScpMiE zfF@vlk{7;?Fbyh^_AA$V&)3#aQ0)T+)SFt_QB^Kg@a_`{6N3^%0N5xw3Zp&x92eF! zWyA=Y%O*fuM=b?!NDKW9GP`PxFJg)SZ?hj*31Gg&C7`z2gv7~6re0tyG9K$%l&`eR zcCOQ*&l0?NNDNu%+dN8FlPFug9MeDTTW2WElU+mzm>9}~e`V&(i5mX9b z?vY#ML~gW)RIMnO8WN*T3ccOi>jiguH~rC_V6Z1~ZwHHQ4B&9Dc*Ebni8rUMb~p;t zy$ChZiNI*Q6JfLmu@L{-9b3Ro2Y&9^pyRIvK_?V7im-E6+z^LWF51m#_6rxK;dpX5 z9UqWSob;wiH|%3H>=$3g5Ry5f&3DBmkk>H2qD^p#UL239Ovd(#;S4k>KGDA1x`x@dY zZ`{R$We0Yn5D(AVdmX1rZMlvIr*>M$gM|&&ae^uPs*}W0>k+4kIzCQm#Z453BK2vp zZ3FL7QrFeArsqR0JD7RFT|k<25H^>puWJoeLVDGVXlmtC!x#~9oOY-xrz+Ug{;IS% ziv8^#j|%+;HAi-tPLH8dnjy*!j1qS}938T0CDB)V)PSTtA6A(3+(i~alQyf>Djb|B z8x>e~ll)4X4hmu6A5Jo&~twV{X};RM#YO|KuE^gBmj)+*+i=llDr% zoE#wK3*cnN7pa~n_0I64I%(Wh<1gGR#OPpgJV0+BqwYq4t=6HZGa-8Gv8@gv6}2vi zbnF$!NxfC-m`bZ&CQiD;sWZY|TV0Su+NNwq`ptpL^#(oAI&$^EJuTZfx0h36l>m6K2f45h^tEYsmwjoXY zg(AXJ!_W?qaN3#ppiIKtcn8hZUz}) zIJ%~Cy?RjZj2pgA{K^=+v8m09#(SZts{No7vk8JOM%jy9njy!=>`^i_tnS=!CnDFe zPlm?Li>-II`^mVJH2G~c$D9m|7^{Oy!f-UI9rhZr`3&0~!FB1ah2^D}Og@W4JU!aP zKp^paEYvPXx

      BKa9m| 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�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 z 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�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

      M>`&6q^qbNLMZK^b*O7q%(b)TAjm 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 z zUDjVrveLj;hO6zmPS3-w?fdOr)BZI;ILWEbeXXvgz?$nfMnS(`ss&)RjTboSw`e{A|5` zUXgd@3n_yc7s}3@n|lCc(5WRFV8+$RURtgrHME_a?;vNf+Mr8zdo*d6{3h*k)~

      T+B0Fx$Z!)gBV*7p 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 z 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

      FU&N?qOfd8==4Z< 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ⅇIIwe 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

      B0uPT{U^VtTRUdeCpLq|At_YK0Ik&Dj zvL#d52ei*x^xqvXxRD3Vfvx3sz~@QFO4Yn7YzQ5Wb_&Xw!bFk3bcgR=+u3=Ae%G

      Xj48r~Vt({6vgGAM9+p_bH$6SdHJy=# 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

      ?GmNA|KTD(CY zP%p(;AuZmh52=@3Qh*kyk|e!yY%=~tWYk459HbWm^Jsk`ZyA;yJFa~R><%t5?xO+;(^?KMjlNh z)=3RIK@TsAd-419HHxGTnO7)MW{GIfS!kzF9-6+ZRk`eF>rn|o{#(I?-RPxF>Z%itiyiq z`}G~cVU+~easo~c%>Oz(;_sM0U2qWVpzm8f<&Zx4HhoBgDaQ)$Gd`dXZ!p`W5G|;E zqyhGA^oc;`dbBbgE9}b()qIAn^86W9rsKSl*lzPLaI(c0s_meseL)2pKjEk7Y$fP1|6gMvFJDt-VBHdgoGLGY9h z>%+`p5Jk_EL-$-qSy>53iS9Qh*k;k|etbqRX6Z(Ls~!gk0l z&2LPgAjB+oYhz@pm257#HecCL8mg4oGV8^~Ck2XaJ5xFM`VNX-fdKOq?+DtSNQVPb$4U?kz%C;l@`c?LS@`$C7o-p6Zw@qhj6l3*1O};zB0scxI;Hz5G)`QelWQhc8&wb8KmzV>-EvQ=Fh;VLvm6 zc~YtXTQEFB{JZDVZoFp{YkS~=S%^I>c+9!sGZ;J|_*7vzeAfKkEnAiDj!xec}_DUF|PgMGsnOj(kP>_ez+9dnZ8tOt-q9h-8fu~mMbWv!incN>{mkx_tI zO-!Wn{5xsuFsAsI6-wJ-Zm=TDlJ-7Xl^Grztk>NCMKm&q|M2(WO<%&l)6m6gOUy-Czu+4>TvV&%Oo!{1FNA2{kbdK#_*w^yeZ(x&6 zc4;D;=Q~pId+=bYG7fe2JDVd0OWB>-oIM6lsm_`|-E!j^6qzf=xlj;(JkWB+H<9r2 zR53M@&1EZDo|znKc{%>l{A#YtIwkHJ^Ii1Nd8aQX|Cj4g6hdpxcD1_4Okm#W_meG1 z29~_wbaDJ8w*3J;a8DFZL=piee<@2S!5*c_>wt#+kdRI9Xy}nwSZ}m}0v_Ke zTku>lUqoj9W34Rr~vrXTae@DJyT5cH3kY|oJX)a z1`93Ybp{NhUMP7LZg1Dl`(nrYr>*T6UjXB3!keeJ6l+SleGs;6=*>|AOWEEyK^8RmQQ zUUVUj3+(NWLbc-jl%St<)a+G@Mie2D=AF^DauPnlgJ^HR4@FzEY180pnaGVt^E?V=PR9rD=Ac#QHf3p<^*Fa-be3&#@@a(LvBfoOsm{ z9kD{`A4a%zr1w&USq6p)XO=-Tj8{Gv56|)a2ZkmL-tI{t9TiE_MJ3I}v`c8x(%lQ@qefIb7L)9SMg<(DqCxVBLFZhE2b${%>KBUQy!eo<{{b>h|eAemDZYGfope9i?uPs-IA~BUu^HE#Q@)F(>up%UXDP=szDF8s zK8^FTL4p}m6$-T-CDMtm9!`<2b%u^R2T>$WjcLHau!sN}Q-VXF^jTTQ~jUA@W$=7$=GTJCf8vi4Mx1^uK~V9cYNwK8=0RJxMki zUIfRi*ie4O|6$;3KFWzFGLc~7%HQNx;n!d60;se_H>3bqE+-Ezr20?5C1>oT76(qMMQh?J*0y1RfAT?;Sx#7g(O>6;Rl`rP zdpHs78%^-_Y8VHf_hT~8<}(NEh@{kJ8M%X{cfK!yeNu%KXYo%^i7O|Hm0jXf@tg}E z!otrR^S$!0+h}B{Lioq_v^x_`P+v@kTLTHR#W>V6r2snG2<(HH*U<`L@6pQ5XaqW12_&pHwgi$SUUih< zvvrR)?m(m0(MEI+u!1R^=OsZ6YuU$OjPRKON^Qt4<*1Xho-RzD&l ztd6n*$U?%gDODQd9~Z5bpX*2QfIf(IjZsZZV>a5OcFBh6fCX&(W~a=ZXd34@5IUJN z#}HGvPC141Olk*QrV>80Gi?<8ka)bHeL&k<$@J+H)^*+HBwWy-X+#P zAt@akPnFqc1nc8}K)Z%4EW;;oZ0WB3X@A6G=kl`!()8GrA1XPU_gMiq&o#0R^u|jz z%aXPEQMM{G{({Ghs03vp1#65}z^YJ39m$_oIDw9&mLa6Pk8~_^-7}ZQzf|a90%o8% zleb3xgovYJ!3e&IMLMCy(;Z2y2bR=fy$Oa!cd{?kN*yMmvGX02#l7V4h_HiG$$8$1 zKBM9OAc=V72PGLlqmBI+4+39)9IyV=K<9#-;J{0GI~g8yY)rC(qM6o~ZNYw|%Tf>>;Z6gHnfv}s>Z<1RqHN9={(@z$ zgF;ZZ{lUMAa%1w@7NMmxNPARflWen42%TX&m~uN?>PnUQ@}SIN%;`iGz?^C}BE0edTjvi<|5HD-lGLO@jR96N3rUr0PmYXsY$#Rs z;7AkIu`t;$X-GFmh1B#CoVGZ#-B~!JtGNuTY0qK^2US&b734Ovgf06_8prq1t3m6mu+tJh#;dOp@N-qiNRv%2`5qO@O5Eq)d%S z1b7oUzBuu?)(W9(XC4f_axog0J$ARyvWFoBaJr;aLl(*h$-~DOwNg%VrVf@B92(o? zVA<7X)J#1Z)g5riSYhBf8@{+}k=G3$J6qpKB(bAns1cWI?=#}YQgTwG zj{g#|vh|KR=0GW1N5l6LzQW3ee>q>F+q&;+`Qvf=p6u7HmJbs-aNvKU133W>5xb(o;CC%%^L3Pi~Vch(ran&5( z{7hQljP@Bwj)GAWQ<*t@tD6ruot!bMJF;#ay$90gdC$Tp#}rnMvYhe_?J>m=kCwGY zra%y3nEK7}7!u{Tw@Dl;VjZUN3<{U8c%r@)x#=A}bCFM;q;EHF$w`x1mVyMqHl9Hn zxTI~+In`KggEnE5xpln$WPR)KQapO>uEx2Cy71S^7Yg84aK8UjhvOAS!0uFILpND4@rvy zobskU*szsZzIMe%mZ8d79i1TM-@@lnu})Sb*&u!CiX`&4K5;-x-Ghd^h}z#X55a-2 zzs@eA#Bpbs!3V$V6Wkj6IhcB$n9V+{C#emH7;i*fI1%H!%TQ@C-d*YCf*qoOn*_iF zt&cthJ4fBZ{D`*uw5?0Q6~SyrIXe*!l@s(Kl>f4+l*+@A6*%gHKljO><+s;CGUYbs z+v_lu>6*8rRnn=ZK}dr2ZYa{3Rxm_F{`XXYfX>G)N$ltu-0ygrnV8_wq1L$0ln4%b z2MsVX?c0U}ZLC8&y*2%^r|X*o(SqxRXZ*HV7!G07^Q>{=s^?vD)l(-@kS<8CIzEdr z@>$Q&hX_frODSf|#%AV9w9P_(Ig;bT0L5)rEO+9&4Ee(!P6+MD_rT%yil;mi%W&t; zByH2tK~_OSBlrtSC_)`uJN0dd9?zaY#pnco!97ug42@@z&VG}K*T64$mcT_H2I`&! z(u%}FB6%_+u=$<0HjlP|q_K6dRSG;;S2Y#TH78hB6A%>b!D!^WSW z*TCVL^qp`WgSG0}LQgt|Ehw=Ud}d6D^lXAi$W0Jy^E(T=cME zg2J%p6yi-1a0aUN^yi?Jt;&|l73V^xS@RFI$}F3u=kA}5I6sMP+UzAG6M@!?sK!K+ z6GAcJ3l3bDTrA|7wZKF~>IP4hNUzd~P3{g!r#iu#)8iTEbDnF+zB#p{CvD%pK*+6) zibsoDqlGZfd8dEb-s1i0U{SFksT6-Kk0B=c3dkjGgWDic8x?S$5OqJo>v%kXDpHD$vM4ImIVp zY0Y7s$Cq!SwscZah2+sQUMD(DMg)&O4fz=O*QcMS`d1Nyc&tlqbP$8TV{bioiK;l-*QrqzVUcfLQ2*MiyneJ=)HK`JR-@ruV^LIeHF~+s{ zmC=bG zp|AmwU|E|M4@5B2Ua_0l5ojW8#y-V}R+8ew6? z(0SNxInSW6A7umC`tuE1wnv*#bv=c9M(zPU&?z3^rqNXva zWQpDBHdFS*;x&h#_OocI*Sm`{goBufWe$S_IvnJa^+~W}uPl||nB1{*T2EZ)`VlHD&lbnw{hHY;Xpjs18MZ{g#n$Y@?N#gW8RHCp z8+ac5-8wC>A<^}8>*49ExVr}ZQY=<<@#;S8Eqqsqu0J1IU32qJH$*_ zD3upvwqz|hJ(=Mfavw^C7STEN^$Y(^gwL*c&(JIYgdJ+6Mpk?mWD!rm@sUE*R8peJVZZHxyJQ5~(5qv9#ZnPM3 z^NmF-bM^$=EO9%MnQC>SWV>~0bR$@6%oVO=?0tI>4Szg4#$H*OLj?hB1@;@D9Z$XA zk)|N8tHNNpq!+R?>crW^b?=S+5mplN>i}eNmx=B z!|J=pHI{Y5s{B-oQrN=^TdB|^pGuurkO=yOIE|Q;RmLY$=_LEZI^#bS0i2(PLDwS8 zI!Cd2@WoQ+S~kD~1amx9L|>1m^2gH#{<0g*G@FNK@Sx2gl}Hht{)sgLrPlv^ zWAF~J^_mM6jk|vwKckI5lJsY+)W$vqpYmV^xy!S^Zl-95Q=nn8Y00gdB6T-+%VO@h zJ>AyZQP@+QP;;y`cT0CCuh3_aGF-o(0uun?u*o&4^0@WzsNm={icoesQ%)uMpZwmw z3VX>H;uAX(Wj(k7`5%J6&7r?Zb_3fEt`llH6*7`S1}@_4NR{(l$x3Q$X+E>g&X2)s zQt@>-QB(G*h4v$Bz`XmRo0)7J}i4$9y!wI9p~GhOL4)DLhE z%Y2x~5-X=cLY{^_*ClvfwqulC*_h0+$ehaI(YNPsLgSE2s6V^}OKK(Qr$!KlL<;+1 z7C1zZj+X2lP;kuaRtuX3Im4MF*0?jO_3WK?j$fGmWfWjqnHni{W&KltGxit87qQU> zX+mX{!dqM3o~$e%PnGb8IkGnUVK9}YALhD#urquwM>`ey1(b{s<095TC=$l_c2xh= z9LJ?tppg}b^nm+!h`M0{Z$yI3x|KREOW1*G1m_H`G*p1As!J@d;;)f^3*7?r$&;*k zJ1!NL;}_cL2x-j1s^S44kbF@B{x~Z!QYhr$B_Hw?=ReLHBa7j0`zS4_XK;Zib0>}? zRe>C?Mh>AaBvLT!C$3JcN-*~-R!Zk3mUbp0`%1jjy7V#%8%+{`1bG!RQ(9+lTiOYK zoij~pK3;)pd7)IQ7AtG>#cGAt`r1xWDy_`}hLC`Y3U!@)Qz*cpxeVq%n@PgZDv;W3 z7#$Tkr}d%>1&D3XoJn%VdIB}HeIIt=GhVM8-zHuA>iLy&yHgh~F&OVim(o2l)*pX4 ziRAwc@PN7u5gPW|Dk--7IA`A)62#w%C>{@{!!_Sg-c?Sga=GMcyRtEr&lDzjzBzl& z-1do7af?XXc_0T%vz84DhU9|~WDPF6OeI94^AaOup=mb>%_Iv89Ua|h;#+o%#xooPuEt9IG zfby^2CF@IT$)z%xQUc&hZdyLL6#o0X#7;KzWa0X}L}5FR*kEjbxm(K3;w6{}UklA{EjAwo3(J%hi(R!csL~f%__w+bZmei(6JoR_F&dW-wPBxLHgpff3n=wJcfd zZ<4<>Y)c91Pu zKlzLNZA&+g3tdaBU;b1U(kGKjuACGLi9=Pe73=A5kyS8nWi|Mu0*(v+<4r-omf5Qc zY;^{fQTS5M8{e!}n_S#^4tR2QGP$$@K#x=_HuE%SAeJO9d8KT@hw8HuEGCMDq1$)M z-;gDT8a#Xp@g}p}hS!8g*}{iy{oEn7uw;+HGD)&BUV`DrdhLiLM8Cj#<#;x4TW=r6 zpXP4a3QLdY3v29`rwylCEZ3K7s08+WK91}Ca;zP2S2}g1n%-_#kkles`CyCHRw)#j zd~8&>hSFSIfu8fV7mL{4#$ATpzPY>Q_f%HcX_rRX0zI1xynLIe35FTc*XdN_RMwr3 zl}@WIjSuxW>#>mVkFhD>964UGwk(O5D@&Ha-`Z(SuT6B$+RP)C+Q1H-;)E>D+dEU~ zDuW3=yU?Hn{0Fjk6LH@NC=ENJ99WdE%>5R<3pCvpfvds(4$h9Yj1e5w-Ij4JmANfr z_PbIcmSK?F-(t^}$F>Z1VBR`OEsAx@;(Dyppn%p0qJ;L{JZbk1o_@>OZsWFF3|ftM z0?PpW9g^h5)f5bwm0kVT%M)U-n)ZZ6ig=B8LAjkbFk& zn@HYc8Q}u@t2M%{81s8$mdf1TIQyD$)fw zk)B~*gFhyeG)K7Zqo7Q5J@Rljw{?ZUsKCe-1rYt67v$oqYxo620iaiHZPEcMN> z!#f|RCZ1_gczyHsL`*C8B#R^M3CDnNeorqnfwXIhT6EBF(W@I#5C=4?h3#X=o_mh` zv?Ai|!FC!;eb@%{dLh;IfErNIJetI&f~G942h9LRe?=0Z!@|mJzgz?IV1o0;EnBy2 zrA0%w{+4vDx%FF1zaiGof+vJPd{K1!JvF9jpWWeg+1B}MendqsRCl+&t%T+&*Tiaf z{uF8>(5>CTJj%qjg0d`*M7cJ@Um*z_3D1oWEwE!PmE5>;Xwy)-P=qjk(Uz0>ZU%3f zMwwL8>89FEE#vuM*=sy(MfICh=CPjdR8;FR#$+dv_Jk099hl|i%)OrjyUP~X$qx=Te}PeI7w2zJ)gyeC9x^HxT} zD^@0pBdyGco-QZ#H)Wk{+A7zJM{M@dwBI`QEMw~m9{Oe2p`4xUw|@JaAo>)W8qnKT>a91>q8S_J;6J3L{F`! zDmk6IE=&4debjP=7rfl>3Rh5>#}$tMFVz*?Jt^J36-i)v7g5u^*$|_O*~sEZvx(re zj}(Al(jwo3cq}jw#)TEw65j#)6N~JKK}RuFD%;6TB^wqFZaY)#)AP*n2vVG4Ml`Rr zNI3Tueq_6-%!TZMmY-s&Z;>#f_i<|DndwI~Fpo2_t>7$+BXO>+-!uLj?H%EU-E^bu zJ>?`cWe3?|XJ5ay`wwFA$Ackya2<2K0Gc>I+8q7m>#xWh&EH_szUY;Rx5O5X2~T68 z18sPh>#44QG`ymDD2Yu4N?9BUWjLe1B8i%X5TZeirIw)1d8=B>E}y;D>z;t%Z>dNk z$mSjYwHxhFF7}4@*-ll%5#+p?2auRl0F=cI1IYQjo#a9M$OCbum|A{}rA~wl=&nMQgkuHNFb@(jrhp`i>jKH8 zve~aidu$w|+F{V65njDkdh%6dhej|K>i$G!E`$!WyyA6Xj>M(oy`4^V0s!I_%;Q6B zDfr0Z`tjim?js2hp1QJ2IDOJFUa1t58)1igW)obpQnX=(?}mGW98O6sL)zAwFkJC^ zzh}LH%3QeZZax1EDsGr`h4lOdq=KO^y0UpZiCqOxSsaOHG_yI<1Y*0;(SW_93YVMo zTiXu^$bu3{aag3^%3T#uhfwv=Ttv-kWg@HgcoBt6q)J6@KwD0DqY7wvr*B6J2xfRi z^FR`t3Xrlm63B2y&mrj%M2R7MjmX2i0B+VBFJ#~rq<(9`n=mn%<^I3}?bs)v^mr6HnKR2d6KPb_q+rvEWWZxjH=-d1?^uy-R101dO7P$@(eu(5 zzr}A&Z=*7|H68Q9x7Ke>q3qm;q-&Z|CbKrFgdd$C`A@dGMYF8Xk7DqbZzg{8VB&&Hs{5g7>L8*tXR;ZU+8~V%d@cY&`sm$YB zZ-1w0-y$CWg#8)v5F%^d&PfG|oy+1_?VQAVlyE?6rSiy+BAVtDHbjxN3Lcsr3TP;S z6Yn)ff@i+lXRR%(sLW%nt8Y-P)fA66VSi|?rpTJNR#Jgtt+F`MT1|=d9>O6$Yvrey zhWII~AsZ(4t%oBf?M=z0_E2l3?Hf1x&GZjc<}%X*Ex*}o#7tEN*ArQ>IA=s?^9D-7 zD+Vfy%^Rqyrt^JF(-r7Jmk5Oxr*OM=}j{no1! z#_cq;uUZB>_h!Grc2SwfVEb<|Vz8l{z7>1bvD{GN=1rK?p_s5NHgCeA!ref~G-Asz zaEaRvi-k<0;T#t3y2_lFfSa1Y!tqwR1Z)EPx7XI*1r$riwbF(|xBAWe?^Nb8^8+ns zzCXmwW8pi$4}0@u6R~t<-UUciiVMi%dR!nDQQu4$HsTNs`v6`;1y|OomvsE~<-!eO zxuheYzgm}c{QNe*_5a%ku*_}!v*&*>#QF_tXD9Zo6X@9vc5w?xfI7@uI;lmmbXi=F zr5n`XwS*Gdck`sZmvsEwj-bQ@I$@vs8ta5l2iC@aNk^AVuh?674K9=TnXI)+^5qZt zJt=v+-;;LFn)tBlNsTGcZQ*W|v_*UkvWxCFL%1^Uvjl+Rv$8nSXJ?33+V-OnH8PO# ztrE^R)FB(*ASf+L&{Y8Zc2M|0zldG;DmL7#f$NELcIm8s>o4nSCvZCSYvpMl{fOVc zzDH&5k*eiuA65MeQVF<^R_qjMQv41g>Pg3R1U7HiB)DSNvN+PN4e01ucX(~GVOL4A z%Yq?i%4FFaIc(b-EG%xo8=O4X6XM9z_%+eqF8Zzh#Svz0K!8R1EY|XjRUh+v#y%?Z zc*esYS3P59NZ3Yv3Fn#_OqusjQorJ#vN+N|XNFQ<_6e_xyEnx4Tai1U8*~I((|~Pc zYy?`FuascRS-vyEUERzy>UY82)pEVF?()0dCMxr|-pD6a*E7%U$FXP3-^_Pl-X%ya zic84iNS81#;Fk!cMzSjJX|UubJOtNo-TUkaW5w|{v~exdn{&6{^qx;;9@G0FmO5Mi zYxO-sEj-CiD68hpf>>6}LKa7wMJ?%`{Aq6t6ZLD{56>6qYaD4+z9)H`)LL%;HFD)X zC~(qe5MvjfyIVhmr9M2p9bQ1yJRo}Om`9G7Q;?Iz^&scv@jE0(BR(08sR9&Lt_9J% zKI?b6&r_KPqQTFhiOIukXq42|&#;+yrHg(w4+}A*z#@z5frYr+LBb&*?3$sT?tb|u zP9k78cpb-|1Q|T|)EB+~_W9FZB;Wypi>XKg0S|m1!a~PYJiV0ZXW=5A-^^n`%qSSh z;z$fUygf#IH{udow!#ZM6_^LZsx(adm$7%!`>iGa6?OYNT5WLCgEcN+`tawyAhDxQ z-=Q*z5RE8BK}h(B0}R% zJ!{N+sOqKc)uevwpF5&ZqRMRTV-fuYTh;>XnP2n+wvNg?fc+Lrov=t<1@${gwS~Jy zRW%PIv8uo*iz8u-V)f}?LZd}Q8=GIlQ4+cjk8Px6Ul_!#V?TOn(1qjB2m4U6WviiH zZn`EaY8&Zfkuhd#9nE?ak#J?ct(V=4w(PdsS+^cg1I-K*^)twJQ(sU*Y|0!E=Dm{u zQM^+YM|$TB!*0!&4F&`e46b=eL@=P>RM)tmr7OmaZ!oE`>HCV~|o5$B@PGIYy9tHxfiR){*InFD#L(wp;MMPxugn z&*}xImu`{}v1qPXD`)x9*L-Hy@}#e0ncK`---o3kIg8U}7gY>MSDaerAtweE$YpUP z1L#dumMVuK$J~v@cMZ3$)#>n-8k7A+W);- z5pxP)vbcW0NO3PCaS<*m55@p0v82!~ZP-+T;1UF7EEoP;3$E4Q^uv{-G7ns{zoo(z z*x_~9*gKi?%3&TZVorfe7DvJrC`XdSZ3HgY`wL!)|F%mE_MWRP6yQP|*IMoW6SC^c zkz1bnZN$}u;enQ~VyP1+QTEWj2dRZ;IVH5Zd1Q%Y1zA~K53-@`{+*<21Z1%=(bsR? zx*}?}j+&LVYmIi_|Bl}it@{H|Y<(k^`cRa8J%?&|P?JBKM~PTcP?E*gx1NEIuX+!r0dbZYG)&yN1V6UtU3E6qdM+1t zIII;l&-!1#ue_4V+`iI!@BgX3(y$6+kna)XwW--Fi(+n*X>gNoh|10TC#hcXPgxx4 zpECiat=~u6iEl)rJw~hTyj@CF3UJ`>_*u9@Dsy?* z?$)y&P(2KDAZ;FBiT&w}MFFClw{%j0V(GFt($Y<7>%RyGwAO{}Llxw!pIaGl3?GLR znS4bjwSv?lS-Y0;eB?)7%Jq8C@JIcHAmDFSst{p6`E)}Xpcw_q?0;J&Q zgt70?*0qih{U7q`p5*_N%3P@Jp0)ocsw>s%doi`}pcu-kc~psI1yxyG52~T;{+Og| z1m%U*d^QaS4w4&F`7wJ_Y9t4r=YRdz+HM%wu$D_sKje4GpHi6%+ygDI`)LTcf&HFF zwLQQFRy7YQv8sS7i|YY3kkz|Ms)hg^&85bYtKg4*>%|Y(fupN_E%@H}Ge3NvpfV49 z>mCk+&(+-n*wmZtEOgZ{4;C?|fF+CT0?WnW4idB>jPSaRj$*M?7)=hsA6e#i$N#hr zyh7U6!fx>+e(YXIWghJ2{5%Z1kgi{kWE31jYMaND7*;Tq#dTpC!tFVv48+&FEjRL2 zACWv3D=@>k{wD(1;LnC`nu}bmYOM*J`U^j#161aQwDtQ~>LfT}eU4L25lO?UnFo|$4TY!Sz<0VV&}@q zO@-|?Tb5?435eGX3%&2&?lPJD~`O z^xdoG0VImir%kfI_u3V#aJ`<2q*=AjA7AyTYE|`he*lSLAgiyDdD|fN72A--k+xAE z)7OxO5Pm>Ez&?SAAQ*Nwk5C3Oi>PL;ATj<2ADFMAA_*oON@+Rek1Cq(Zf?gm zh?)B*^T-e@3No@d5*asZ-6T^ZLxQ{vWTHBW$FSe}9Zc#yPm&QFIC2HFtF;yU5#-OE z5T5WS1k8=jtdC-;lh%j#dI8n-1m*zt%p*r^D#*#=NaVsLdVnNC9HlI9c}9My0#9)E zTOasVz-Zi%TluNh?PdEP^Lg1}Dsto4a@FIimqqk?EL9djM$|NqBr&QWDT^bKjNtTE zlBp4wboQ}E@5)6Ba;n#wkzM^~FK+C^!CoqJ1J~N~7Zo^PXZK=5Z-VYHV;&@8N&!h0 zM}p+z|56gMF=!S}WGC#7iE6Hrg?Df($yKnxwZ=|m*afezx(@?q)c0O^10{k)EeKEh zs~^JisLTW5pRm*yL!%kkNzFXrG`fv>sEK(6YFS(#)KQ9l{@>8T8-rh%QLX**K4Yr| z>#2YD!+I{2d0-`LpXqKlBh;GvhhMF4q_U$~t?nhVux;gl086s%JbVU&gW(-llnePJ z|C?P}Rn4dSt#^JgAc>$n&)vnM2qLvo@-O_;kHK%K%#A_IK5L%E|B?49)XT$})KUbk zdYhS#TO@)Kw`6f-+^U!DACt7`WA1h}jA6@GFOs8KJD2IVj=3jl;BwPn6hfg^fIHYC zuyyDDf2A_F6|~;o8fgU~{dZ8S01F6dVjg{BT|r+K*N=V(%MX#1h&;^xqG#Cr3d6J5 zjgQGpzPxcGEaQDP!spp+(%+=kI`LO#d9j5RlwVSj3)ODWp{%n)2SS+Dt_>j5l(=~o2z=QA?{mke!V_MWp2H;yk@rQ0sh|po$7dW?_$Ti z@`)`)`LZ}z`QA?%E(SkL5;Ou2I8aC|ql^Sq#Vgi=WXBvoByXcK46scjnt*?=J_=$Z5epws|e|=1UkTldtj3&}` zZQjB0QfX6bruMcc`!T+o%3K&9Xx-edVjSG#!`Rw8hAM2C$B@`lFqFlS7zRr+MG`lH z84RX&DbCA9|KmhIVg)L5A?CVV)H7Ck88i=sydJcW%Zyah+wDr(YMq~u#lb){sA6`? z;zbum40ulV$g0*#R6cZ)U#H)qGM7#dw7mG_5HA3=vY!5kt-RKcKbluIv7=bMEDlz- zHk9g^I1>GOSk9Af5Ofh;!?yD1!;aD0{mg(L_gX=H(>yQm zEPdTYWfFO|J*ejl75cguxC4oKV|_H!=D{cC74T(oB=~j7c?GEl(T78H`SYKjD3p^! z*|B^IR@e^Q7U08V28D7*nb%9=`w#ur$sY))Pjgk3{Gx>c6l%>^H$KG+KFn9AsK|x= z?v{?Hs@T`n|9&Kf*w@v@Jod!Ag1sz`#J(<`M@c<|eQ8&*Qs`jMOQql`4_L(epX}WR4H=_Oh-4TvU!6dA4t$_XHfA_-40(LKzx$r#D@(`B# zmJ2=4pSc;&QVY*qD73nHbctmJU0EE7ZY|B8_6)S|MnK-O6;ii;>x8s`G&niOp0RGN z?E4SMhkL>Ij0B?UMzHl3EcGFXJzYS*d+@>^&7(u?DCo%IdeA}UzC@fif`i-XnOh(as&WTm5m&-Zk|-o3J& z%);NrY_@u3?JyQip;j#W=Ck~|{}YwDbbp{_|9tdaxeo?mj%J{fnt2?gMjNXwU8v?O zupVO_i!Thcv8fnap_%h`oWixSBTJo7GFO%?gTJ-Y6tB%$)tN`kD?rNPU?9Eg579h7 zOma4Y{#c>7eJq*H^Hqp5yX~Fb$&y_zyu}O7(yLf^io@oHPq42yl;~jib=@gdQ74Y#{WnXYuW{0qFS3DE%aN1 zSsV#xxfd~%;2Xf2i^RcWq8B3`evaQ7<~|q8T-I>ln0v9*H$X-+u$r3HG6WaX=52tO zS8PBQH)#Vdp8r8|BK+<(tQx!G76xb#LT)u(5oU1tc*Nk~?g!ST{Bg!jsn#Izvju)@ zIPt6iYiPM2OC4)yh>?q^xv+|c+M2fx5=OBOSsZB{4e@@;LZM6xtwhXBl51dq>$e^r z4Zs;HvL#D-V^;M2S}uBvWRqHH+1%%OQHQ08#Z=@90lQnjf~5}Vf&FcvY69HAI_5zn z<`fWRaU_U=JpPX4K(HwNjNCm}$75uePKlbemJ~j@$O{gegFZlIE_C*^j6WZ3OU>Iv zM;86;3B=+z^Jow=3L3IF5)F@nt|h)3L17G5gxI-pIz9gA>HydB4W!;SwSwgfl73_| zROUkFKuc>!2r??0d$758+N0Joj}|egpe2jzK}+TGc_j3y@b`AQv9XP*2I??1%p2N(>PhDcWjg~!Pm}X1R<8n|J)tmh3tsmK zfaEiiP_jUhFWKpxtfbRZvI9!`?jSX=R;*-=EMboVLc!)auNDY`Vxk3pW*7R+|L;xc z)M!N=6_)X*Zgdc78M4G@87{y}skB|X=g#gpBE_e^cQQZm(`GbMI2^m^6$$pRE`L6-c8{Tc5wgYZmJ7M}=V=K_MK&kR_ppp$fVlRY#`5Mol!Ft~d`NvKJ9#Y`gwGKg5i#C+pn z!o{xJ6gHfvm;x0@Q*bpLKi6vquAhzyBRHLel|~>-LXE&B<92LL76A6qFN=JpI%qIm%OOYj^mJ%rKYmgAx z3_C|AbYHYfu$Sw_X`xep#o_)Aw+KI_gYR!wWGpzE+-{O z+mR)qwi6=n7m*ZMk6kLUmp2BCuW6jI-t?LVvg)>uyO%1~K?Tw}u+a|&yw-rlM+N%V zl8_R8SrUpq%J*$-`u{rGxY}?up;}#zrYljEB|cQ0#RU=QuPnhvjE(jVc#0U-Bi9)B z-L|)xj`VVj^0MysO1wq#2B{Oer zs4aOb9u<~!KT6}Qf60=1EXgbJO4KUZk@%!LVBBbKTw^)~6`Z;)Cy0k>`5e|j3Tlm6ni1wCTvMSv+tV8P4Gt?6OQa=(`dWcbwl2{UV)qnB;>A! zYcKNR?)vGdaQw%Sf5-7a*D(<)745Hz$>OWj~lHqSoYvF@~ zihWRlV;`YfxNw7FE}=D#3hOzJR3Sr$ED5)sP?4`jYGh2XkP6!~;+O!@hADI3)QDGi zAlbE1F%&A0hC;2bzu0RQRDM(#!3q*n8i6bcH3AawP1u$ohx0bByCKY{3}3`O?dE{@ za_okGa+3l-6-fBKZC^9wH3ILiM};X|Kq5<1kR_p};1%;aq(BDY8f(m3!wg6%aAnIS z=1~Fkg_ojjOQ2{LKlU1A;;QTOv(R3v3d7?Q3lD9nFH_$6Ly zVJ@3;vFlEyzOWSqr>)FXAX6zU?UuAhzylwU)_%CS|J_)w;A1TGnW zgw3s4_|j>zylbM88nKR>bzuBfK4ad#Of03e%o-~s+5OhZh45?N*WauB$~Io}pQe`X zQhp-Z_E>tPT!z-eU%ENG3fmG6b+&yav`z$%va3=7=^VIFm4oWYD%OC?adO@APT*m| zoLfA6TC?zn^|RF1OS8aH|1dD`cf3Jyuwd1l^n!*xXY~aGiv3)RpXG z_5DnS*6(8V{anD|hjKcV&)X$9e2tr4dnF-)26HmRH!s~6fcU@U1 zWf7%jfXSf{PkI?ZWCLUM}ji;c6|f11A!^e7(olADmh7L2Qyk` z`MMF!HkDFEsNc6nsF)u4YnjE4i+7|;>7H)5ZyUrI%NKIlp`n$%-N{sGth^11(%a$g zTWCD|bJay$jmJ|v?cip`sAe7|?DHwX)j_#z_jUsp@5f4%TQkhya)8uGlE2o8vShMj z@PQicEGm!l1A{%RY2Q*Iy%S?x^w+LjxspFU4;81-cJ@j1TooNPQT5%v>gmD+oVbCD z^x%Ac1@10zn|azNr4&GEma(az0IpIooir1Hzypd?5XYmtDLSL_WXcpD1X9XR|M9y^Y^h%`l_D*Y}mluFei z96c^pD_)7OUs(g5(d?w1VIl&bD*$NG^VGE>B0Yd7mRe>hJ*tXgC%s%^PSz3=&Vj*J zJ$u=%iVcD$ekTOI7^9a$}3(#IaK;qh@@_1@-S9fxR*^Vy; z_8nH8K%9o{z*Zhl-x=ZQp1#y*1zPDl*JnSTChP4hg<&7^Bd%8L>FX7B4~EsvlvCay zb%l?0l#|s2MXt@mLF;U0usV{r3Q%ZgghE*pP-u5I>S;PmPZOENA#yzaqY%SjO*}&6eL|p~SWR%VO3q%H zhfUad&}jkd1`KwtMgh{!3zOE)<+4S{Rg%fEf>`NJcCoQ>?{aU7*& zx35?J*W71D!&cULQuC|(7^I_i*f}uU^(*eGL_Hp2IoNisg?EDXM^P76>5FT z-=!Y<;Gf<;A#>25jLKhY>A^KRFFBiMjCY7o{9-(DD&0qF!qbFdw)rN}dFp z8J_H+2#&A;juL~KKQ2hWJCro5I|yf6A=9afWYoIvvwYP2Ok|rE+NDAdgMM#aU2KF% z!v-v9um%JEgAw$*dl~(U3YozrOsT{73Nh{&q=8(umLfPkHQ{WvpCrQARd~gc9|zWC z5QtCt5Tj@m)XDN{X{Vi3f8nJ-YdUN+a$(cm3l$Ul1$z%jiKtTwfwBdsJ(wz`Cd#4x z&Z!wE7|zh|04o388%dMYuPPF25H0M1n!+->U7E{@QS17!Xe=dV)eHGFGm;|ChG!2E zpDKS&;5!@Y@4ZU(_wf2m4^{*97eoEwUG>vv_*ysCU!Pg{zzwzP7Z-OkciT+q(#5qE z!-C6zFA#<+hS8s1T9XmTL!jbdO-3eC#C2lQS>hBc>l-hTs?;m1`f@^QXYIp5_S;ji`uT5si2>Pk2$Q?c`vGP}_Z$R}|I4;=3fU{^1&=3nN#+RTgVApcc5X)aDQ4fNI)nbmJNoyE~ajA3jMK;@u(Y0qyfA zIgqW+8uUT+Vo1E+H~jbwDc#ox{}2Co4T%26kp72iNaR*&UlP9EXGfm8?9vaIDEQbC zl{{-woh%etw~4{AfwH`_&V#<8 z&u2ss#CRkFOG=aBxc?2xKk};(BO0GbrM;|9gtAJCWNY@o znm{Bd=Ij+Khn8oHjL%d!`4Uvg^MD^=}s&6lEEU;B%I3592htXH> z45}(9^SbPDVAh5;!F2R{K~2ib3X|M#51;y3o}@n(RBP0RGn{q~>H@uig3#j9ODJ4a z*f*EtSXu#ZEIk3$i~pf2-DlZ-X<^y;FW-0zRQCK`wd?>YY@F`}VlZk4kNJmvRJ`Wq z8Z*xBj)a=OLLpsI{ZK?kpz%oEG)B^}ddU`gf}I+sVr-N;)BwGe z`P%dj`{*GCDeyddvIyTwz`>W6{Zamru!S1d64t+&C_EemH9ip47_zX)#)qOB!v}_- z4yS*t`h!bOk5!I_StXOJ3P-NW%uC0xmVZIXMK>@s?)Qa8)h<~_@W05=d@u^PV&O^_ zcm~5_AM2nzQkdki^idH@ot0~s0F?}LYHLh{y7#M2&FEAzEPz|-$SSNOYg=R$wkBn5 ziKxODW|b1Zk7C&73||>Rr@}`Zs~lN{GhB$Q!WrJKR*|9I_1e$sLIEzIgAY#C%piQm z8ZDG2I<{9w>=Nv`vdbNCNpnZ4nC;lvHDXs%J<0W{2{_#c7f^w)M+Viwz(5BZ;S%U- zDQ#a|g@N*ta%znA)V@jWsd)*Ul>vsvbdNfrH6X*voiM!t;`=XA*)~9YSr*of^J74M zPaNdgw;!NA{GvEmhnvkag9Y|Yd+*j5=mY6Zc%M9Uapcl?yBI3kY-h&QuwUGiJRcZ99YJ04pNNo8R}h>#3APK<|kyrs`kchz)v8m?P{B;X_&S;%W+(yDIGaY3IeNPMT-WlNr9YPY`zRtHK>K&G#?_ znX+A32lK^bV?<~^C+q>_Uho2++_a&mG0N^_M6jpJS|SeCWS(R~d3W{4pghT;uKxDN z!5z7tlLqRtIWG>FJ#3nQx;=I-b@ezW4i494C$g1|sr;C2LWz6hut}o#gE82P zRxH4-^0EolO~uNguKp(D;0`BjCbZWVhsPp+y1MI(gSU-#5w@4|4_$0>S1RP-)mjsp zw4N|-b@lpq95%TOSKG?E+!f>GPM5jyIGA%SB$_b9BQf)+tHVcP;?KQ=smtTxn0PdS z@1dA@)79BQKVR9(cy$EM{7iH-_~D10(!A%+&dzhhcFn%~)lZhqFXIsDK0&%O2GVB> z(hFiBy+n{s#6WtnAU!Vz(s%}$$CO=nyAEIU@$a!4 zjW4)&Cuxv}CDy}Re5-^_db^wTv)TEI#A7P!&3e0sG&_p1_h%|;?T-ZAN%@6o&t5_5pAD?JDO+^vy!m;anV~m#|PWd~qzKStyzQR4k;qv&W+le&)uv1H}Z4 zOPI4y55}Sp=Iqnmv5@8)q++q_CCu5UuT{A=;Ow;9XYZ)$>IR5=J}}XCsDr+ZS4K>R z1(kt#aAiSd?{-!BCb(YAf{GOnX`Y`vpmI&$w&hV#wnLfD7K#7nXGdx0GNZEpcX6bZ zi&oX%?CiK2J2lHP1s{84K5Zx8QUvq*-X%`%o;Txley97Shb8 zhp&i(bT6NCWMd)Cym8=vVxV;Ey#wbQ*)2T~M$J4;P?XD^E> zhyPnukP*@>rVRMPl>yxcew~2M_NG-NjL5+P*zh-FQ4!Crd=sg-RKzTbZD8E9J{CRk zsoTL=^u$8u@SkHL%|hltEaP<#51GDv-vGLl? zx!cFXHA|I-|4ZfCqyw=Ie8-cFm>|!^4yn8wA!K4mmA<{8hh~vblmAXJlH&EPDt%y!D+ff zRfG}JEI1|Nu?)WSb!RMG^LgB!SV;4n8v$eQ~fRPb`|)vw7qKpV83XDU16fZ4;Q38)vu&ZREgi8wfP4lJ5b;(<6U(qU0ogVXV_r+=pMU>uCmu21cw z9lPV?P8ZXuIGFRT`k8Q!>z@!#b@jO?4la!y@4YKF?sWCGKMu~s7Ykk1Hpek=7&_iN z8Hd^~f&{&gH{r8~<6}=3)kATxX7q^fzF0Zb)!*JYxHEXfcQ6jGHFU&xO&q)hZlW>a z#PgmoZgp+4Jq}J`Z-xmiS~2k^#yFVI!4-D8`kNCIkGgz4?vFK1pSr?>kbQW9?ufy& zXha%M&^;0ZX*@x9I0n+4;&9#}^Q6zl^Q;GBAdTl)55zzk&$CX)KpM}p-W3CBJkPp6 z2GV$*bzcmm(Hr;1KpMUA`WQ%~H|~jnGg>J7XY?-nbwJ(&&we7)YZx z&WnLGdSiPGq|qDa#6TLo(TagIdgJ5YHg|FkliX3s(%tBd z12K?BZ|sYKG$C7dgGiJNTWAeF_1=YeEeI+ z^Xo43#v?J1MsIv12GZz_hhrd(-gqbm(&&u`V<3&*cpwJS=#A4ckVbF3D+bc&jr(ID zjo!G=JZZYIb8if!(HpOifi!yKo)}1@H|~ysG=MS?k0B@nYxpWu%B2t;_8F=5%GExUI9Nj~&U`^TE$q z?4Pg67f5SR@SHLEnh*1utt&&>G4_aX=W|5If2g z?6`p1g`?G>A2tY_WZ;q_bYH~oD-Cu}Big~HyI)bblF2{VECV?19cYSy<#sj)owqmJ zIgX6eO?AF1S17PhG0<%9IC$RGXzyq`oj5}7Z?ru(9mFlsEb};q?(=m{akhryi+g zyE@aU^f=o%dL%mr$F8B}@P|dqKi1olYkB2lD$QDV`s|@jJj>}+j=eO@ z3Dp9YS7yHRh!5AgyUW!yAKA+hUoUBR+6c~)v-g_$4gN(K9G_pUW-}e^kEDBG>S8C9 z6GwcHQAbl)zjhro8@yFu@$}2cdTMmcPQ4AWH=(y}Ufjt#K8O@v);NU?YuV%WdzvS3 z$wnr_{^kj+SU&^=ij5NR4C*7kLA{0wUD$Q;tGouDBa)gA)~mwFovB>aX5PQ&M1h~< z{WUr-rScgbunxXkwESwlomR3F_WHt=&}rg&(ezQ-l=;L&u~4cMA$0?@?Wh#C+xaBa zo&K4cHpuh9Te=o+g11ca2Tl|E|I7=?34&^}^l$oDag9xKOH@skY7Wd6HEp$KzEH6*N|m;A z5E2OAgUc0qu4^Qi9(>RDB7+`7e6Pucd0}D#9xC5B3FH#*5cs_-oHt%?L0G+F z$%gbimx6z-f~P{5Ar4^OJ2Q$TuDWm>3v3@A9)tRQb<}6azFFNhVRZ}DO0ilQEZL)3 zCRb+|zxhIbX|6b)T3?;ug3Sx72s4lLIIq&~t5PV#71&;1Z;hwvWjKl zjJ2DE%AG0Lc&a?^rFE;1R^S8+W>-;j(1X4Smg6&BOSba46%576e2R^x#Z9V{$O=xF z>gwxZCVlu`F~mG5Ar7v~Muo#46Lr70iOeCeF25}6;7y`#iyd8;U21Wl&bsU}iRn7) z^1E=JC-7M~=;L7|TY;H$GF2KYZv&mAx5EuKK)wAJYG9KRX0kgkyYw<&i$r*fR3>v# zHFs%1qr*QB?Pj8yTbjvSdYQkcf$*NnJ+w%ZS(UEh1v@vb!CB>ZX%Ra#$HycX0OOE>0MVSY_Bpy-;W(TDTZW@YWH-@ z+Izxl^O?i4RN3YU>)~$*CKmX;K4+&!hq70(S;k%H!^f`k`Q`Q$$PK2D+B9CW%j1O{ zi?jQ~>x1|k@{Hy|pC&1#Unp{ra&n`cf-H$Gx^1lH?V)rkpXcNCG%GdjB6)9Od#}(Vk$dVq#f~WhIB-D+1_txvUjjm)-m_cuReC zS-{^LUDsp6Q$7>+@Blo+nM9yjJWXiFu_jMFXVB^F>|^|9z9RH+#wcmiQ(w^CFuE?w zq1_Wr9cm9%QYALva#&55b`>jyj>*NH=X4aeXD5@(?NTK>%AUF8 zuhT;7L`Jon@Yb7_4fzRJm#YMVj0#>MVA(;iaRD3cKBEezf^-Bm_9uVxfEz4O+4~38 zGy|xld)az>Tb4Ypwc!fLP$bG*ZMh(J-Vu35mCuHD8HG|y&AK^KS2ybluy&QL?1Bcxx*BXKjln&)SMhgtQ@Km>SswBlZVLVFsjJa) znC4VUjK4u&eHrOoH$Mu*l51cNv~|nYDvX`XRomCq6XYPmvF}vD<0nu#Qai|nw-;dw zs7(JgBs`VMDc1@@=h;2X!FK;x)V@qkchDw%wb|rox>c+mz|u%%B5teCep8B zR4q2?7Rv>EVPOjEemsapn805Ok%y1ta18}8FnEIqBhDn=#Q;=a0k^$DDj+I%Mpd>; zrNQb5yo9}SvRKH&y39J5Ub1*F+)%5biByh#UMfbl0>&Hx&x&3jD^?fv5(O zxr+*!YOc!S+LW)pcf}7J*e>N$xsLKKwx*q9D}U_|hj|bzL3&p{ilGL;s+O6QgC$sg zEJNhXzY;JM_wC%>rkG7{hrkZm+t@BKsoH;Elqdxe z%BR+f=+!%QOT08XQEoTgM8;?PFjk9lKH!xih=%o!vK%ThNL1PbRD$teLU2Q`Ho&Dy!l+WaUBb%~TJmsAvc*}$(E zcjJS;cTj!ezyJtp>FZ6045Yxqv~RnIWFescVyYyzrR=S3C@_A*|A~2uqe17tpz|ZY z4mFg3&Y6L1vnR3@2twOfa7g$bh6rfoEU~k(ZR&q&+5lsj{ljt(@dp#b!}q~oHq1AC zk*KnjdCvGj-;)G>=r{{Zhxk8KCUL@K@ht&IiqA6l;jh#dpUneF_C7~!`Y)ier*`Uv z$TK0}A8PCT8>k%qt*S(y5=3kGlk?++fze{T_@D?-f-}SH?e1bJXyOVn#9uf@kvX81 zTzp|ERObiO1(m24ELpt5w_Fp|f*I$=hFUN@FRrJBCkea7HwM#y8o=Fldl$cprVO9_ z*k$T=i47vq@AN7)a0Rr601tyWL~&>^iQieCJRYHN9^Ih+Mb(Nu360wEq22XYL^N8k zkgpWkrL!OlfG!I-m>>F{YF;Gh!T?@3!8a(#pbDiVq(yu;P3O`&L|m{?L`0?U!vTH! zM!9hq{Bx{RG4wxDPaDUOVV@emSA3^%=m(-yw4@8g9513*1wu3J%BWm1_ytMx%W# z;o5jogW6caw2^3-L#Bct zC16-yee23sxWqyNB7 zjrFy73F~X>eT}v1J%_cLes3MEM&J|klFrdwf$wk`_if~f3{YLYOPD!X^>pMUCtbaZ z*}yRHo;uibwd(0(hVjzI5}~dhK6JgL(N;VWy1LO;oh+HWs=-#>T!*_FZMB#g{A7cz zy7=CtD;sUq#qiqMXsb?!SGBQLo#${jsWjY*4GrZ+TlF&QEj8Gxk2}|nMqBYG>5C1w zVwx{B+KO+~%s1HT>~ok!O*Gt!X+GCzEAGqN8*atI+7*qq>SnF7jke;6&3MDDSh6wJ zXsb@9`O$h>W$e)u+!?zKR-dj8%^t%?QtZCA4Mhug&J1g=dy zUeDTun??yf!FWj=@sX$imaxGoo!oMc^R2?YKO7c;DjfiNF$dphI`~3M5?JF#z`s@u z)?NV@ONX$C8nBXyD~N-?Q4tU7*jYwqk$3t`kpVbcv4Ykv*^(_=O@;sSm>K^IP}%b< zwdVjTxE9M+fdga!D#H!(&nFdi^3RrD@qc_R*2Mwz+*uF*d@)&Q!PT1d!T9%m1PIVV z*cS{z2dE_KT1`egsCVzbfy(~+Bogb<-q!Rtpt9FTCEB8RqI|h(QPMwx8gGIPDU9EG zJU~JipnpDr8}RY#M?jb{`;!@>{6De^;KlmAYRy56JMpP&F`wT~-!BCJ_D%}_LCrjC z*x&Q5VFxu?u#hR~tYooJyc9lLiB;CeA83%tF88J1#+xx) zuzbzr(sfX`m%-)a>)9!=pdZ-}Kyq@We>EF|X=)_7FsRkq(OLu-G)B=ma zlFVmnQQX}a#UcJ7ycW^TjS*c2r<`h$eB8I+I1=#n*_ssl8e?)rp;C*-_Qoh~g2B5M z$#EaaAX{W*s|qF9jShA^!TodrLt zCrCiURqR{72rP+u+lUR0qR~LTjh2t6khk`Fn?TIVvc+4`Q?cG=8&g-Zw*;}-V7<*; zZPwJ=hMzAL>`^zPef73ujw8AnudB&|gnDuaqTL){I~GlxbDQbH1f13@mpevTV$Jq&6@Q`{ zytf70PuGzyrF*(rZOgagD>z-?xdP7^SPBV=1Eokj>FE5O+Nb1e?5b?hyK^k#8}YrB z;VbI$aXY=eTxHvZ4&#|u*9JZknUjXvYiN(y5no*ydPl0G>|}^IBO?0~N)x5f*W@|s zz#5=>Nr$fnrF$YJdCkPN75ZBLr^QXh|M^}!0 z=D%Bd_ykcr;=b3nWs1qm27zL!PysuKP0$(GYkAL|!0@Mi9C#}`30r4(FPj$)A1^+Q zoiRy6hPMG&0c%Pj1otQ69P zoVWF)?+yZ=!HV5|Y~ty7toy$}QPY1{z1K%2ddkMle(Ik!sfd{)qs91v|5J{d05ALh zQU?__jTO^2{wEM}N;of;Zzs4Ne1)ZnhPgQc~~ zu0c3oIZ+0g?m~b9f?1)QT$P0zN|g4j3>yA_#JvfCo6A)uD##l!FDzmU&Vw)-2rs?{ zx+V4EHkU$xU>KHu8DJbph=APSkT3`W zi6JD2K)}Ey5ik%39-xFQ20|hr5VOx&s{XD1rPM9WYu{^kx4t@c>eQ)Ir%qL!N`|gp zeE22?ed{%3mGhg05WKH2E*pN7Z$tVUdjgjRx0RXLF6H9`n%tsGpVA4i6iGXiPe^c* zgQXmXyci^B_wA8+x&Yt(8lEkjlRU_m2S=M+-GAb7_?#PZ5+a;u^!-fW=$k>du~)D^ z(?a_`z2G5(wAKLDJYT;~1GA^}J5^`nP_*F?xU;m4m5HNzckOd%HqRHJLLObIz|l1$ z4%YXTgY`_J3HcS^-m_VA7C}c8LUw;SE(UH7&(EB$L8cUT9R@G-_mdwilQoGngjavt zKe6}=KxP_O1-He4{O__m&X4!>;VYCr^urtG4!B4c&FfXT_u7P=05+vPQO|Q$exnwrD-{Tb!!~4`5X-)OMw;$S? z+CA70Z7qr1ADc3rd(xE2-ThJ5luBQv9&C3h>{7MBRCmNqwzJp|v6)x!J|h%3Bpi^c zGUJ6=Te(HACH^~V6Cjd6yCWftVeT?5(mjQ=nA_I3;2t_&yo2UFc-nipB=IZ>D&nQc zdJ!=96-;pp?~B8gs79lPx5@G%DT>^bwyD4-J7hO%&ZKu?@`WkbnSA}<0hK*v9So_i z;94W3NpXi(emQjhlkh9G?Hc4{y#htgmAuZ!o)UwQMk}DOx9Cl!s4TL+j1&9%CT#k# zyLtEz{SJW*1<|c96tn3~V`J&Kkv2qGyw+N;;Q}u$q}{P|t;Chxxd9hCt?Q7raUIvJ zN5&>V#cRe66;DMMx7EX^0!yOUf3e3&9NQ=^VE3?IeJ-6{5heaExGI}NTY+<;!1ugd zdaVNEQcvPj3yuWJGENJ>!Yry-%l#4iI8Rs^U~}ILy~xfWCAtM z6&vRyFU_*CkfC6=^&x+Kfdxu%g&oKblt8+@F!5Z;g>dB~<_)rLDRCzm^UBPec7FeG z3UXqkY=_=x)-p4fHlBhdw6k5s%TFb@o13C@ZUxUYc3$#^1b#^q@oCQr=Fg~Z)jf=1 zdTOK9ZkQ+F6F4AL-qGRj;gr?hfQDir4>`USFFq0EaFaoRC}RXQZukQV(KgAGVrU$5os5c7wk zJ*8unwnN=8R%oR7hKkKVUpZdmZnC2uc#v=Sq+dOPv2=c46N|?{yWi}m8z=-&7RZ$V zS|P4S50t=)A^;2wb5n!=hRQ9CAhRt*DNK~BLlHaUQ+_+3@IqY?(IG%3uR}$ylHDA5 zcuD<(pO@fnqLXSz{Gt!WVt_dj-c4}+a%Q7l+%{WJfO0}ImnWV)>L%n$WrZTw&+ro5 zNE)?6C6?~4(a~ItcK6RjHPZ=oXBHRmq~Q_8Jq@@?d*FDEr~BL5aI~^HZ8+V! zN&E{_|D9N7=5dgAS{z3FWIPp=Q&RD$krYBPhqn$w44uMU4hn>zOD@D~{Vj@VwM zJV#x9INFVZJ{)^>_^ZrJyhhGpucoxAQ6jSQ!yt`9ksy9;=^< zX9jzgOP*bA>KQcHU2Wbeze7PE zj=h@Fhm)K1E?q(LZF_w<_G&>Nj=fq)vWhuJQ^V|ghUWNfhJVoCO^`fc26h^jD^^*a zgS+`ml%L_AB{Qf2od%Qs^luH)0M8O_S6QODnZ{&*a=o#-aw3BVTraPlfGS2`#8o^V zH=gF{VoLXe>=nHK+k!ZPYEHCUWn9jX$9bYygi}%Hn$5P^XhLPZFN~@zxb&rB69Zg> zxT3JoGvP!UBrI1sz;=rAP9{ScAaTp-N{w%6l$N0hkcYHtQ?;{L96sY;#7IKkmE|hs z>VRu4zSAN{l=95L46fE_n{gywRe17dkc=UCbEVd7Tli>zS+W<8DBZwZV;FODH<5D5 zHw^GOAFb5kxEe^z8xKl^Fi3e?XW(3Cqup$sY~cRkYW|UPP_ZRs>zog8SLj-q!@Cuz z273bk72grmKu`nBK3Q6BH?Eoa1-h|iMQW+H#M}~&`TQ6qvUGnkJx+-%){%@WVK2&0 z(OBDy#5d&twfppx*)l3slvq+Ehx|EPVp8ecl3s!MCxp9vX;%&@{TWnN#1`$r%{ zN$xHp_V&BV-o6J^DcQPrdYQGa7pZ+PhU^kNGYk~G>;<2pyHRTyyWSp5D?RAu?i`#T zXyARtywPl4uQ#~kKv;d%f;k%>XX#BJIOftW<-u-k!U6W#?KY&gg)@3)6B0;o&0fb> zC}y8()@$DQ%kkvc|@o6&}Ow-f#jc5!6>EyLX9Ar3`QI4C}`Dz z$USY+8HDGx2+tPDyTbxKJ^DMP1w(p7Q|22;V)Cj3XlretJDP`wCjA_&{R|t{< zbFT=ALHe+`($Bp>4@=AZ@6yBKYB2S<9+npPFW1B3+O99&E^{`Hg5`h#o17k9biE2s zXt99dw_I2t+bdmK&W8nqy-*6fJx_}P?6UqVwV>EzeZAhL4pXQGSu@x}hpQ4RF!$6i zH1WhhPqAZx{ej)wS5zXX24OFq8Vc+E1b#U^9#jn0XPXsdX!y7v9$^xlA?E%Ls1!7Y zK971F{$vFBUxyA*$txQN0le_wcglIc11cls{R>+FuH6uq*x}vDMoVNCFK`=&QG638u&S4Q9Ma|k)xQwMhns88uKHO&j+0#C_^p6Z>j_z3R~F7LTg3D8 z87Kfz(b9Qi!^HO*24BD;Q6h~rW}4NxRhrgiu?xml1yB0@Ct-WHE(Gwz^Rz4?Ys$yf zv`ot(SxnZ{iK@vZt9c3_cD{fep!WyK%7Z3l6X+*i9ngwx^9DSLVNe8IwqY--4Lb&e zsT!;B@`eeIXs~#DQ3b5VW+uzZb|gxF5SM0LDh4+vaOp6dSm(ps&IIpHciy1eX_GC7cd{l0<+$rD&V9?aw39FXwhRU>>6M3ge^V7js)mcq8ssq3tEI- zE67uDJwBf7Z7s5{9=mM56do&Y#Z%5}QD%n667d}^!Y-EMd0o*WSu?M%!qzctPOibD zLZ*fcZfVhVby_^DJuQ-~xiXRvGiuABBwfaXmc90d^SvmecFlmtvnUNDuVYu+c>Nm8 zLsFy{|7`m)RaXV*h6NPkIW`$K8KWT*yH9&zyHALrOVD;0BlQ+`*`D)*0bYy9$jDp0 z@EW<ojD2>yt!aV%)~>_`nL=`cLHkc^!2x}IIL6&o+pgb(^mcW+ z3D6zNO6cMh#2KFJ{^#&et`dlAgOmacYBF5kHzB1Li1fti6Q?AD zEWH{06;QgV(^RNFlfH0bS&~3@V2Ooq*y8~LATtVUB1~Hu$2Gg7xMq@XmwHk|BWt@K zRvR)#!v~Z8VC%&8>-!zm7;8rnQlM;@ls3k@>G%ICYZq*XvyC_2{-R-3X0K&7V7s4g zNOV8!k6lfM_#AChDa3<PhBGZdJNJdWfcwD2WB>`nS~N%w(snt@I#X&oPD|KhvAPk)3Icb#g20jXiGkfl7Him&$i#5k81W@XYrnmb4 ztW>Y;DbaqE>(-EWM~SYqq?K)U*O=d;BSW)AqjaqTFRnr6w@oly?wgITRhaNjwf2FT zvbl@1=Sbt&1|D#TW;zjn+JVmngwp|DlJT!^;(ng{%TEueSn6|s&NwL<*UaKpd3LM* zwN1;sK8t_35^mv+QddMgogF1t2gH=eF#c-}P#&$*#RHT_Px{sY%42l=!v4$C?TSYZ zP#&XYFFZhb+$uVEfbtmFS~);@+)g@sfbyt+xi|QCAGP%_i~4t1?6mGff9X<3PT42b zuOIa1+{_doPd z^IpGy=27v^@4qZ2di!h4iP~1x?$Y$wO+iLHB=Y04QE3$0Rioh%NpfsXAc1(Xf*~T=FMD4;u2dnH zVG-iml0bTF0(Q`Tc$DjY`RfCg{DDqACDq3Fp=9mW*h&F;ZBM_nZB(0*#NhuLR{~YD z<|WY^p9>u1lt^E)F&eusi*lIR(#VfZ0|pr!Xuvd;l8?dIZIQ!FCcKoC_QqE73g58u z43)jH`6*rjHCbw5+R@9WVGilPnmhP4Pw1*y5$->~O2XBKJEj;32&TQ_qX83s%U=g&;$|+%CU9xbk5O&~K@v3cO1|mqU znRD1~v~xgsS0x9LE$onyQ7U~IlA9l7MaH+FHfqLAPk+(+R#j>eXI5icg@jasG1d+g=+ zMFgh<6`ZCN4;Q_$rxZ9ISSiXzB*&M-=4R7`Eeivj9njyxaUNECc0#{6|G7>C@+~ZV zQ~3Y-r7_d+Ie0h&OPBXm`tl_xy1TEUk5p=<)pBLC1r)B1-Ed?bDLhswwaP%j8k+|e z-5H<*#XD+oY0$t3mS-DqT5JY#T*3<_tF-wDd-d|y{0ksbP;%%xAkR|kr&Il!%IeZ#0;2F3u`6)6LM&;~w#6CT2u+vIy=fVe46 z%ejJLY73$A@aB~xnzxDD8pAJPZoldcl3RMf6duFaSTc?k=Z`IxvKuDG7>+1s7Wk=C z6?yBo{KJ}%bDO2?@nczI$=EC|X6Ww7ih^`xwN30jm)U#NX3qg!C%~8b`F8-DI6Ea? z&VZNM6a?EDvv|C895^yJHt2=r+fmFAsc&IYZ@eO9<QW^;i@y_f#-l(LjO-lh!oTS3 z+W1ri@X&h^+dfA>gWBsU?UYpxM5y~-$T+>w5}b_>IZ*iDw{)G8${3XDUgq6ir)d)) zOo96En*w1BWaSq9go6Z%+hBOX4X?Jsx8AKpppsOw^eR+reIhDe3iuOH$t%xJO+b2C zAlzT@H!@b^r20`-Wgs!oQrdm`zrmJxy#Dr3Xvzn`(u&J6VP3hWG3N{quiRRYYhK~? z01|-yx=^XLo0n?n<)%fX?dFe5H~0XgH!53e<$AlxOQNJ8{Q>*9{sBmPZ2Zkh*?5n* zxx+9S3|!G4(-;I0qBz~n37lcHwI6!UCEnbmd(Olq*r7>w8t6j?-pmBD55$@EiRc2z zL6A*NQU-Eh2--&`NgE{bxY@w>Hx5r&z&wl-lcWlQ5X`PjQU*YlnO&MBRoGKH0SQm8 zQ@AFtP>z|jF4x<&(iIE(rSk@MwOWr(zz~9uXumYs6USB$a@N!~0kD?{kC3-+Fm*C9 zeL+8cdoql})8l4?u9$Zx$vKFGH!vP+)af~!y-8AUTGdMH@@eSDt3m}_`)`mEw6--< zZ*0x(&Z74Mo*O>8>peC5bikYvQf}FR+ecMWxIOiaqOjUdL3RV&qk{|DrdguWUH#G< z;010qP+PH$BG~wh-Q|JnM~(`>!oEmhBZ?Ndp5^N(>&IX!P+pA-@Na+@E1n7vKqlaS zPN%~^I5dGuUx_jByV{;NxW53I2tzI`7BlvLcvkL#5|bCTXN`2m`45nz5F}hw^&#(v zAU8K0jfd}mW-^4&(!yNHTr@Vb$Ck{ZiHGv;hS4+^v*vtu6S$aTnwCNn7ilx+>s#3w zqp{UohwCcEZ3qVg)xn?pmunKMK>H!k5-bB1a4#5qrV#}027@8-heCHR7z@GaHXx<4M8k@&53$Ww0klk2<0RqUI!L>tkjk)9I=G?-u<8#GhINa`q z(UilD`2}P-nCAQ(4oBw;kfUH^I-PM~@Ess~p|$&wSA%K#HUg05o`M{IEzY;RGOmP{ zw9_{Hq(6#35z@Fp(EYHVv>l__e;s(Brj3VHtj7)(tHU*afJ`X7`b&Ps_s_2rxLv}Y z?7VXtq>tgb_5SOoLK^p*bN@CK(m00?zjrF6(GKKp`AJVe|4zc83Gvjpcn;m!mQ)<@ zV!)T%3Sh4(SLP<6YqX=^`S1SrD-3dCUFq7>P9*F`IxGT%oxk?G853a;r`>|-XK*5< zX$N-3Pg<+(?tNHbaBn(oN8?yH?`Lo#ZAbIf|JtdLM)TGGfoYKDB$eOhCq04Ddioli zK1XS9`X`GC5Wg(;HD1)Nw+#7;aWC($Nl)>-gRVan#iI-rxv6Le<@oHE{9UNkcE{Y1 z165Gku0_RtfC75lQm9N~d@bULYj<;B9)1dMq`qDt6$&~A&thQ^Q}`MO~&MF{G=zK;FnF< zVSpp=aC8%Hl(ic5Dr^!%I|{|A=nPt}#BclCF%jw0`czpnO-n>{eFYk0DX%n7q%mkK zQ_oC8PMjg>!Bj|-G5hkVkVa$Ed7+>51bP=tSFedqs^K|VD6a5)e%jLQ&WcPq5wCzkec^edt!(_hm>)Ro-WpPNd@qiOE0OoKFK zSxZcZG@0g~^>b~j)Bc5SE6)YEv9<|$@<2Rk#ft8qz7r;%!#SW(@V8+iq$w);TT>y8 zma}knDx}eJCZ5G_I5@%9i3o&3g;O!gbL0amsyGp@aoBgiWh%19VV_V)PjUc31*s>e zVGSvFS@#d7Ar^W?zVp{pA&tYmzdjYxINTH8GZoSltorAvkjCMjQx<`f7&w$&t?=Db z;hG$!w@igJ4)^|dO@%bg@_#xF(v-<9_tjG&OgHPh?7t;)n+k72j=8A8SEez)$R$`aHF(!M`KW*HR+RwulksY6*#FSx z{KuxTshpwU*mQadEoJ{9e^2edR~(0*&Ti;zS7Q=i{6li}Y}4Zo8t`ZR-87_%gEp}_ zjh3V9lX%-SD5E7DPA32hl)KXjJ^|(a-%djsXu!JD@dJ_P=}huKIic)^+Lm$u18)s3 zh2u3{pg)>mR9hxbABcMq2T7hT<4B(KuHUJgca`sy935L2kUm~!vVi02aCa8YfK!t8 zTMzrK`~EAjPuGbT{lN7Z^ID#qSuSln1t~9}!>)eA)Yv;HYx!xghEq1BibB8fpd22W zDu)N&)PGRk_NE~=CvbRB_8v@+y@Rqgng(m9YbDAG z+?>Qyc@23g_4+ zA;rT};>~>k?x6BZsXUIAD$Kp?5V^vRi(Fw>e|K_$Vo5qJ(!5Qc^wM#WoM8&2mzZ>J z3Z#!Q>7^-H zF&ZEI&ctoVF)>$(qc5;ngVGt8uz9^T8ze^%(qc3gCQo`njK-@|AT35?ehQ?;Xk3{BX)zjeQy?uy%hZkQSry@Dxak(U_b(>3K044^4rz7>$W3kQSry!C>P4^}HC3qbZOUqw)R} zNQ==poC0Yv8t+Ykv>1(pDUcSU@$M8zi_zGhJn11)ODUcSU@%9u*i_zGf z0%#>VAT37Y%_)!;qp>pu(qc63PJy%-jn?EzXT@kNPl2=;jfE+Y7Nha% z6iAEFn4bb^F&bB`#HT7>#$PKw6B(-V{iS(Rh0bq{V3LPJy%-jkl&iT8zfMDUcSU@#X|b zw;%-u95C=c!tou-6%~1dW2?9UPmIr`cf|!ZdIXiP2)eUB(k-^sU;7QSg>MCKR;ng` znlq$X8DzJ$+N4h`6FG^grbHRIYtJ@Xp6g}izc-sH=gck`HW=XC1-fTT$;#-S@ui$R`@lzT_mdyq#MNcVX-*!^#yKYokXWUAcY5HRNIcGDLRKPtnQun^0JQoFEtNzQ@-|`yrK>`_-)p~`scH{h z9Cho9_j{nS_rO2!R4Smrq{AV42vmBC4hB$Ja;3ZV@_|1AmE-~67rl2mGNXlwGm7~nZVN{L-J1`&$oM)=-W*J^OdMVKC z3RYhGQ&K(M76G5iq7<^JA7*nqd`!Z;#&WA}ZEwL$G<(C?seV<~yp~04GE_{PeYM?{ z^rHeV-8D9iqM3U6E1z`#02KC=S7n1J$k)H|*1Pu!^sREe)NB%0GoX`IOrgA|yvObu zKkfpaQTIbpqt0Eb3A0euJa3}wd>Y}?ZqFN-9Vkyj3FNWGlFPFOj8Yi5H-Q{qqkE{&(C>-8y4tMq(-zLQWmC0M(v4M)B1KBf)F zHAg}ljwySF3NOk!r-;jbShTzP!SgLp>HV9oyePG4PPajrS|_KCrs4Zdy0p=E%PDB@ ztmo-Qp)gP^r9ABC0lL%!Z9)GVby=c3p4w(h>qhr8EjtSP47tHm$PUMLu2!v`B7aoj+48(XdEW@ zbit_KySiXh?`yhX)b5rp7?t~f>Vi>82fAR?(e#gL3L5+Hh%OkrFckxKrt8cCW%C<- z8V>*`#^%+Kn#mS@_$Sd8sw(`bdvQ2HZxDmq67+g;I6?1r49gE^!;;fkC#FzcOnajkT&*rzbw37JHEUa~GIM-42K&NQ53jp1aCo@R+-1slVxTK) z7tVWVTQLP76kBD|dof5@voY{y4AA4#{ej}sd6jjeU8%MzH4J~c@8spd&|bGKQApcIfF9AWZKoY_zQEQXdxv9R+9OFtA%t!(Zhu@^+6 zo@$=2U%yhT05lmx@TukrtBzvo#WILVlK_~Hp;&J1RH@1N7>Wp_Mq3&{!DU(px`QE_ zqy|#7XfFl|$=T@5a8cOR8+yl;oo=R|m4i)udiU~zO z)2hY8@TZEQ#hu`;;0bWhmc$|U*S=91>VeuKU;Bw`T0!9>QoAX; zS{%*1Letx}BS+C2h0uW|x3>jrej*>c-inUI4v_j z)8A`eQGlVj?2eyw6+w9MFEbl;P==%kKfbUb?U%A1g?(B|6pVlSzAz?%OP8dwRm+tb z8`m2Zh){Zzke;f4Tr75;yD969DuDucaa;+{d4yjLSxng+*=Stz9DYOaYRX1?e8}Gq z@gZ$=dBbP`RV&2Xa&WZQ>fRm;=`_2G{sWjYFw$BdA-Wp8+&mqU1Qb< zak!STLC&9w1NQ@>FU6TBvd5m}2n2{Ct-P_Ws2OM(JaX+0ba=l9DoHiQC2Mo-b;x)} z)woe_>&9S6f*-V zqj9JN(V#vWj77y3u$vxAcRVUm7Vdw6N-v%YcCou|HwFA2d5J%!Bc)5t0b3tHrK9i? z6pNHY@LHu=X+617uUCPA!b{?ZARZHn_#dbwKcS-}5tfC7k_}4GWuIZF6L$x@?X1VM2KwWJ%AI`EF&(l(6D8v7|bQX9+o?vl~j(Mqh5%B)SuY zCaSz?FSz@@wTLJZbb`_(AuTJ{`QelrSnPNMQrC^)hUBZJAMqky)=%Lckp`iAU zKPRc#3pI&t&_p7C5@O0JYcUDfhPX84xd#T4U&#jT(+iTiMEMLF_xK&9FD1Ox-r2!Z z!O1ryx*a9C@{T;GBt#^UXp(bL}bM&en*z^@l!=N=9uLybcP zQ{2Ln%GGsP>eX=Vw(@#E6?^7=B9lSqyIpLhUY;^iT=B!HjT9(c>XlHo@VI~K#Z$wu zC}QXgZTo&5>(KT|yUpAbaPP$_eCZ#1r!eOysL9gsGJp(kaKlsYOg zdkBi}=AvSKOhdZRB5IWMwq<$#7kKIaWjqyb5&Ug`12i20?(7yStA-Hc{Wl=2km=oDGK{HUkUNT<-+)iqC{2RemTFL_&Z zU#C!h?Xst)p-!QTtDZ9Vbjn<~>M1kOD&yttu2z|I7tdVsRMpoicGgqoj#inIo-#e1 zGAjyuw{(gvUp#T)w5PT`r5GCP^<^~I>zi;y7>8Y@wpnMhZ3T{M7o1_cfv|>IHI1gZ zR&D@Q2BzO1G*_&{s%yQtIk&lBZfwkF<`+Z-TWkf89T;j4#8RwdFO`V(u(C#F9oAWN zHUJ8b#wzR<1QfnMc3~(q3@;=XqH-k&=%iSQe%T%ObWMVhXlL1aNPzn<4+s0!u?bP+ z?#q-SUcqNc6TFhYr!>W5gW)jKu5G!Sa05Y@?!2OF;l;2#h#?nK#5F~T5{IKpxaS8K zYxkp#J(Plnm})bX`V8*{0&5Rqi`X&{tld*epu&PiUaYUSi{%S#I0c7}7KFR(3nqyl zW4FyYyX2LbO8~Cs98rXTH3=6iEBuc{;Ml8*8_jrZ4(?PzL-XQ*&c8eFRuwn~0GBtb z5H7>pI1kN~>KLo$QC$uh{zgf>`XOS@M*=yh*NSkP!@&XzqjNle&#$^!zzfusqKU5S zJ#mHXY0q$x0CKAVl~aU?Vrh{*qPN`ub6-&U^q-eiNr5Alx7IJ!sy95kaOkYxzd$gW zxqPGEsu$~3(c)BOi)}FQF$`?7CCLHeuHf|jo;Jgct7US z5k@KhvcTQkbG2(mwNgU;^)5|DXM!4Btk#<*s-fH^?*uA>vx3RaFVLo#2zve0+ojBr z%Zhbvu?~`Eok|8u4lwf$=8%GMH^zWg;4u-J|DXc=8AOx_>^m_kE3oHYHL?9>6ZI?^ zs6oxPWx=_+Ca$JnQyg$_Rieh&A{P!9Zf}bN;W3Y&Q$Vs!N4YDJIckz?{PzMa4Rj${ zuQfRASgS$k-K1-SKN@La@_1dy+xde)>Q1j_mX6H<=lw6`b?FB??p`gzQQYS81*5qQ zkmOHr-FJRbft(bOkyvtm+B+1;2>}@dLQu)^`uzqNGN`JjC_0Pp+G4xb#1qAELlDI= z2*b^b5z|xFz+oMNJp&ZGinx3QE`n+xGXmdN@c1Dpq!^9%^nihPyA*MuUi1Hy+AD9_qBHj{sVWnvqONKVj2(}TdAMoPrumH)AT7MY{BE(>Cb1x z2kcB4SYGP=TK!y}ZADJ816~}gEE$-O1A-@Gf&NH)R?xt2HH;mmepLg_$Z`x4{0|1M z1Oi))8>iaVanNtt(BzC0+TOQj?wGe2gZ9L9nZg@T# z4tF78Cr9ub4Ta-WWRXrm4R~6L3tf#a1nq1JWG)a1Lj^J5o`5X6fm$hjXsceWqEBMN z;Ht=N-9Qt3>Y|^;-wDZ_fTj_5xb|5T;yPdpg*F}A-zJdGD4r7Ld_#Z z{+@cE*S!A3t!O;(B{M5ggX9m;qGU94xDb_Sf6;bbywo0^M57IJN@I ziB%I$beh(QYULXG8$&Vjui2pk56_n@FUNP|IksI`V9a2rX(f#53za5Xb}Yg!`A{+N zk{xX3mD+Z#e!X_lY+bK6w!NG^P!=(=WTie^fsK<&b*5xqyV!;^SnieoKq=?o#Tr|l zOgN+%FbC?1g}+&eg*(6o99agr-xrWq+un~CveO1oPAt}M*ehiRv{40DW~E*=^L!LA zEj-!2VnkeJZUpD6xU0wjW#{CnlG&XwT}n?$zNGA-;wGVYchLJ2P_h2XA1bs@UIZLO z#*-_MGDWg6`xIOSvoobwN~V-pQsVSv{*r&)t26};GT;uZ{fBnlX&2PqJWVTSsue1- z6z;@2Uye{fAbar#D}V}}I|lLM4lEMlJy~(l9jPcM7QHfaj;G*GZ0;eRp0@3nt1UTi zF>nImvd=2s6oF#NtFdB}j3Jw}y-z#Go(89soojEMp6dnQrCdhvf<2?}Ta?k~1>@Ql zVp1g$1@FGcuZjV1vNh~uq;&9LW*B`_56$2`9q<`L#-Gxfa3;Q_hh}h22fWs3Gy1k3 zn!!CC@M*D&o6rKULh3*!enRxLA42VqD77DRt7^stD0=se3xobd|c)IK5-FM6hw7Q+SOl zx}(G1MaJIJ!Jc94iVl_Wx~<*!B91!>{wtWZp6nS<>#r2rn=>T1=UsoM?M7 zWjv|S3gSrcqrdE5<9OQ@ngJ~r=muI?_*Oz0*QzH zx+4jATL9bCVBtx{1MbV`6@f`AKJ~p)6Z;Kj3B6&j9*=YzVz&RUv9d z1?I9&fjgqWjgSI(@4&;-Af_R(FTwjtqW|1NxEE8_U)Qae=Hv3l!(1n(04~$lAg`R$ zuWt6Nn9_LfIJo59n1*>uqFO^&^Z4fkr^06yzE0>gKI0t@VR4at-EzS-BNZcrk%i>|)(6ey~FweI>jAZJKR2 zAfg5vE~Pr|`9RmaLv4phg(Kmd(aPHs9cAl2fJKga;NMO#!TApAE+}CIg$f>H4?4mw z=>V0Sl8!;80>Ky|FAC}SLta3o_YeNnCAQyw1A;=5jz^T9qXXcb9qy^Wf44LkY9SCD zW?NpX*U#5!U(9;FzkAg}?wS@c{F4yZ*52`Wcuk&u{RO38)mrMc)4P=xC2u|t`}No( z;#yvCgxM#K9b@-AL}zNn8+<*rrgKS?dEMdizunoQN_Myl?2~T@DE5emy+-sQKdRfK7Iyx~gEMQa6PocwFSYV*d z6y+htB146#0CR%jT*th5zvpZNpYofN7EGX=DrD~1lTzk0&01x16GCE8acbQ^Vw8%# z6{(gLs91_kwd-;PlxZh(6dF;6br4^sY{98WGldRxX7S-v&GQrP;MW_lI!GNwz|zAh zJlEj;4J3tx@=q){*E8WH@{V1g2=8y$KUhVo2=2^2oWk3!cD2pA{ViNuHeoNUWZ9*} z(Pq5HL#W01)GJ+_7>x#2FFCY%7Qg3~(H+rr-Y@gLG+Ea97pM#s z3#3rt$xrAuG2~Fd?|@2Qnfz2L&?Nc_7#o60=dbo7+@1FB=^C5|1UB}<_0Fvwt`bRQ z@=+M)oHroAaK=+wyW1#18q;3!@lU{gN?ACnya0uu((-peC9m)jp`kF)d=BLqIFQC( zEYKQ-pBk{oC-{k0y++Z7o^n*cuG)s<$i$vfph{aoRyYl#$O798R60t6dW8xeN(fSY zm&9^fLxAXz_5(q2v;bJMd#|VPVz=4x|Iqy#2HnADTjv1L-5FlGa(y5Lj`~E_8!V5-ta+TC(l>v;kPWj8V|U(jiSvEanFo&N2^Ym36k4U%W9<5ZLdH z!QwLqT}>>H!`rHAbb;_Uu!#$d5+d4c_x7%a*x zr-#j;`#by^t+r*bZ7FeH_j$iA;}y1E+UJar=9`ssMk)^JH8gOP07!WXLZyfLx+ijC zI47}G9Lh4Oyd|RIP^UXeI#v~j@*Zg5Q0FX-_M*Pk6r8;WU^9U6PhaGB+3z&kU}t2{ zr59+VBzfjIGVu50tQ~7o!lS*=aMz5A4LUvRA2PrUim#LW@dSRY+sw%3+6}WOS_V5Q7`z{p=ia_Dc6PvK1 zMe%EB@<>k^(r6PBU&ORO@S_oYWRFvxnY?J<(PtvCb7^|aEhpOdA(dy}`Q+O=vnfzT z3sDf`Xlpl`1Cm-e*0VowN7)~c>S*_LtJ=gjn&I@owkdX}ytbemt|bt+&6_pc2|fic zhM+fM!xL6|xOSy`f{iAc6Z6_Em|y$3yKH-YC65 zmr0X?kB3#k10Sv(JB1Ouae$+9EO?GQp}=f@ZTtpo5}dwt1_mZZ;swX{Ntm=^0#phZ zEcZ6pE^aBs?Rsl@?BMfwQ@bR4ml}~$8ex|}RcjcqAZ|eWp?7kM8re|1piwoKFP}$TQfy>7oBTGe zRByxWYJuAex;(`b;OR!=N(~`t@6@HFX5 z@sQv}aDV`r7iQ1NZ{QZijQhHph_ohxE)Cf#gtG`}uVXLwl&RXTz%^p4A|ZzV0@+^p z$aN%?IZlg-Ubuvgz$8(t!HzTDFXbifZoC<46)pQpR@e=R zMQg*o6S~&xpM(pcgzWA`SLCa>E=6S50&ja4x1kGa(40E2%$!r)D5@ihvP&Ay z2*x|EtJf>7V%aQm z6s|muGZUaVR=7h00$iy)g?TSmpf>WZn&D69wiuY6Q%tg?Vs}8QJnO>zmxC*e2mRezR>wY)UuE0emdmMv<-Ecnw`s^Z{?JF8MmjiUD^qS2A zK*cR;+}{FhU%}eaunWN%IeOQ@J1p$3y`|`>N@4?Ydhz8o&mDl2Qq->0K1JoRG=orE z;M`lT)iECmI8{=aY)5iyq@LS6CMO-bzRsRaA!$467oBc|2 zt-fv6)-Wp;Acud*iJ5=M9!56O(ew1uF^mNcKacf1AbiA=;YEHiYppIP=Y$aZx7%?K zDH&HScvVJNH~Y8O(Fn7uQOolh)|Yu$g`f-QjPgq6Zn&u31;yOfQxw!@4Nr)jf%Dz) z7AN+7{AzEe2zmv6plqaja#&Hgk^(vkFnq|+J6cH?E8+PVM4^DmBCiy(TZ94J%ccdn z1&I8i&`vvAZ_c2>ar{tWehx=l%XV{(7lbQ#lJPjS|5ey?FhJJxkf zD}_dl%iPr}L)R{xLWA%^MH)}Iv{^O45wAf0FzE6JV%ef9wc~^o4M;yz#f2*QWr_(~ zQu^I7e5WdK+OdK2w)LYNKKqeZ$nkD#2MPnv;Ct=K1BI+Dw30}igDw`_gLggL(p3cD zKVrMuDxDVhae%9?xF+>oHzZefCCzfZT`fuSd5Fhy$MlsdLW-P~dAjZ>C6vPgv;$5X z;kd2sY-7vsfYpI=yh)*go8nMjWP6ZhoqvH!LD>{nsBn3wl>a-Vq8vF0+FBp)@`qtn zX5ncTV+KjVVK(E*eU`s`dUb8JSf|TagDd{&bsCI}JGaHqn ztYg2to|BiAfyv#k_uCd^(zzky;e3<#^R+5TZs-g#9mgi-Gy z!2TEU=?SNyA;oqoH5|WrxcbW#c16l^Cy8}7UbzO7qSZqAE$ zHu~@LcWVgGvYg1$aYwh@>6fyDbz8TciObPF^rnd$$oZV}Wo*sMo)8J9?d+dLP& z*q$^dPyPt&p_63bfGx(97RARJ23~{MXS0`)1a`C9YR}A3Z_c(c z6b8`beZ{7Dppfz(VY!d`VN%rgGlX!PBH+vMQh~}K{?R`4lI8M=PZ#46V-J<|gIHc9 z1*e?%JK)6%UqHFW?N+^NK-vh{7u}kH==mls+0*ftWzg4`Um>f>j&k%oq+ZuxTcG{* z;RdUo6FCbhxe7aFXfuaJ9tB<7W`-2-7`E4_hRvDN-GYJi&aiKFz7B_~8sc4pTT#~C zU6vE;Qb}O6r)1R)Vbp5WF|RiEqZo%y4)Bm7sPST7IjQ6g4=DrPj%`BD#I)mFzm6;d zCGNkPd+EJ)dd?sN;g&`7My+^~l*~Q;-3Yow4r!>sPGHgusHLk8_25pzyy<{X=r;X= zvAYU$E)>}l?#l1w1C~;5?wTe{V3pPlsdinFxa5!S#T4}UD_vdXf$pJH0q`~v@Hr9! zAH6BAI#>);AsK!PcC=`C2t{`ismApbn*xl(*&T6HwCpsaFGwxC;9wp>`V(U-HK(J@V^6|bGUzWq;a33_}( z3l_xa9HwG?$noTkQD_()^+pqE7e4HF_RuyvK@f2;Z+9Vm9N~LPnv6g=p1cKgM?ovY z051sBO-t}X@w%|znFA_AB6X`mz0y6detc%GXQh;6+G0dUy@ar;DQr33Yar zrrVYt93_XhpJ2=1*^>4pCpf(9AGUU3I&XibQQv{f3YA8^wgcmg!)dpa-AB8S=r6ls zkZoR^w)iFcqTzUh(IwkQIi7?)mVR!8ldzw0l%A*Tt6lN%h;yZOJZ_conVvs})}tV< zLfvyTOm-I^4HF9`aKZUZ1C-f2?(ZD0@gI{jCmsj12Ql-|c(k#P}p2IxMmi&%ClSbuZ7`%9b*6H zbs`Z{pNoWsUg4}qb8%83Yfp$zWRH)zRV!y2I}SK{%&!^}V#lv+4^{fA;xK@8SW$X^ zn($JEP(Z%I5+Kh z2AXnn@*AK46uD_qaUy>}C9gxpsaN)!`(L2aRWchKKxU_}fsqHI$?#JLc#$kI2d_$6k!lU_Cp{){ANFS zMFtqHm+f^r*ZB@*pakp>#w3vJmSP!0&BJcyKMq6kJD}22PR!Vhsg`*BAqckyS_WJ& zX|PnG!orrHZM#B9o-~mfOn(hZkD*kgSm;~I;(~Hw(Ey+Q_?Qfg93(=cNQ%_`FHq@x z%svMOH-3bmv{6jdt^i{Q zREA2@9$PQQ=gwBU3vfVm6IMr1!ALwU>CC{Br~nA3;wJ)ZyjwH;-y;1fNIAFj8`SXOY{?i1*SCF9# zc7 zP@b$w@&pefT6NfOhGZEBHx;)h$r;C04l1m|Bqtz@k4{2PqlwCiL!*=6>*^$hg%^A) zd?E}@Spy#3FyTR=gKJr8l3YTdhZT*(q6byCq{`d4yWV)i7ET_T)lJA>vyJf+v5E^k zFs8Yv5!VKA@7YCpB$n>4dBR=|?~B_?)n1AvC%`O3EHEtsTpg98Et&UrdG*$hW2 z0GLxAwUUt;*N!;^?IQxjK&2DD+v?hf2|`{wZ85){uXPb>%~W+B63 zeRGBqdz{1vp19XKf9#JfdhwJy!-dbs;9cQWIA912rNs5zdv=J?TLCe&VjAL=x4jtp z#E%0n#K1l5;K_oaNX~xh&tz`~u(MW%`x&KWx}?Ekx)oCov85I(#}xChN9l=l*gIQq z)ZraNeE1R5SKW-Mgn09nxf)Z(!x$y^k73Lc7kwr)F6tHb#*O70#VVX;QL@GKlzcPZ zlBmm&2!-zwuo$eECKWAAYiGTgPjy}YknDdM8}(Koeu5)vSkH|2Lz1ZY2x#^dW`k+s z3*c9p^ssh7lkbF5Im#x68|%G75qN$JBfVaRp*xG)T5Y)-%0&c4rTYd76>sTw4K0vh zNL+y0jp@G)R}m2UK%CQj_J?IhdaHn>LYVs(dk@gV7<7w9JYUOlO5KkswIv6lQ{-Mu z5sO(uuPgBeR>iOJw_wJeFYqWeY$?*0fBO!91}cSr@f&b^3vY9$HDg&QE9F#-kd;%D ztc9yn>jElW<@y~~uxddb5pitukTSUPM;5#o%%G8qsSC4JBx35KE{&#xvuZjT zLgzuFIYuFWUo-?|{IX~Wia4RXx8V^DO87xE6%_DGq9G{XQ8Wa_yB7$N3kl%^rUKPe zf{0YacDX8d!a^d8T0^cYES?sFUlInjx~q3=XqteD+7AamP<<^%f#}6<@I%D6HbwDNJ^Gy zib4r8dK4vS6i5iNJw8?`ae_5krUx=bS#~8MD-Z$;2MHJi0yZFp+Ztzl7%K12d&Fv4 ztY|{X@`1vHCjzX8W+2#Ag!CV^N|8J4{(Jm06-tY-4LG_}Z_r6U$Xsy-ZcGZVU=&!$ zLaMS!dw~*$oB%~QYI+j($0V>92&`4A5|9oAz>wK7Nbw{tA1cJAgrox@P#4??F>4Z) z53CSSCouiLL?r`JmL-Og6*!bCkT}FuQ%~XN`J0RLGjKnD)BOcXoqHyzn>LNA@LX~5 zD#WlS;jLktnd0F8BJTj-jfvcm>!qcYLclcOVAr@`_nSpRKRhU8+hRGauYy0~PSV zJK;)gIO7AEQRv>W_pa_?Wz0Gw9C_HR){WNUd_c)mOi8x60R@+03gTQCSYuA3U_gB< zF~!2FI}%?mptf{ODV$CNiY1j|)N>+t5@u@$HMlGK`HDRg+U`9|8@7%)k(Mii=s>wX zp$r*0UdZ34)0$qNAF3`WktkWCN0Z;HXtJ$N5|0^LY9-1A6R?QwD9KAD0a>2^6-9%7 zc@9Hi!14oS-6=^OmBlt-s#~Sphf-j$K*3aq&6$RigLN;ICl8@05;x;X_<&qoKH%$*nTg?}&!tq9*YQ zHo-p`4aLPx;T_CC`p+Yqg^Qg2hZ%Y~8cLZD-^0+KwbRo|VR;7Vdz1lc!!YM>9@p=4 zSoPrl<_3NCB|n!HcpFKgk}qJ?%g(DPNiKs@@5PZL)F>3yELS#ZZIt_0Zf9E+$&OL2 z=Hao7M)T6<8ePHfe1yT@uYfHNF7+1f=+s~Mo57P2pfk7_PkoT-omX%;P~gP`P8gIH zQmH90N5|8d1?;xrVgG?+*&i_b(|Ilc?TO%!v&$(#b$DxLhx5!lCRV#n4#4Mc&Shtc z8=0l!i%ZD;t>5$yJwnVoh&=@%DqEhJJKo4lU^m3?0vsR~>&#8GE7`O}la*P%zmO zOkP!RKnWtOz+GPS`R5d6JKVDk8PkjH9a?I0Q~=796(cOG2b`T0rK|KlyzGSO&RoH? z^aAzp;EgQIx7&OMLFtBYUk{37H~BM6_2kE7(*miY#q2(x2hwjuL(vQlf0?O%G8&45 zWc1To4A2OjVFsS}S5dq!VodCT2vwbnhLZKt64oM41S37FXkzbcK^KtM?ys=~8j-v% zAg^7)>uaK+*jsyA&7uc|hCj~?ygQmIHmj@Ev6M!l^MI*-Jd)}>204fS2Y2jqKOWVw z^XLq8KEPB@MMF_og?kLGM?S!pccJA*P`m51URPElI8TyGx=p3aP`5lHH z{zQ}n=1>C3w=nc%G?aXt4>5E*8j8*8YUu!Z4;?Mdn!^#BIKv(L1Cb2SqqLpRW#}(Q zLs0_5_j9v877e9Qpw$Igl)yk3qUVgFx*&@k+xdNF;G3eMD1o7XmLs9`^4+k{R9_Vh zMG2&YoB8%=C`uspY?kYXqMoI^5C}=UTNIRKYRLOhOq>2*AYjpv6Zv`!NSwsosw0dh1Rr{`%T*=WZ{0rL}8XO9JG*4RoybJb7;^J*qfj(^?%nk0Umz zM-}xosYR7tGOdm!mn@;hz#KMf)Z>ngTyM?KVaMwA7P(}j+e|fbT`)g~637i0T8h*x za>)i-yk=?M(y{}54O@%XELm+ms;C3QJKU_uHOxGnO3{;R76ZG^wJxq{PDHZPC1<1S}n4%`M?1 zug3s+Zv{Q7+HTI=5)K$kOISM}=;US5w^eRZKQT~3QC`Gk6MJw_9!9#!obW{!Nq zp3aD!qt`2MYeC7;>*_(t(GwjTxjdgEM=!0V1B?1Pu!tsO^d_ERBBMET^a?>i#}v=8 zPpD;G$R|t*s*wx6Ira&)B%tpT&hdW6+cf6bIST&i>FYeY4k;~tCHGO2D*0JI#k@xP zS#$IHqhKD5=0J}ss*4`4bgVo)!ah`bLvH&7|mpNH=GWJk8W^uv|Y7Ic?0L zB}`~_EP0xG!ph?6cuz|LSUEkgm||H%wQh7{+> z)6`>t_FTJKBQ~S$YxA3jJ)H&L9C?~OE!8GZv+x%zfoK11L@Z;DgMVUm92wc4qu`&V zr7q-Y>dBRYe|bHs+IAp&oFXKGYUC6rUN`>(7S>{vu(BK{)R|$j1k<<0yfYPTVES?xPq5kxprr+{eBa154VLFiY;^o{qj|mvAmf>gYh0+(#|V%2J$g z^gp;`Bj>Fw=YiJhSc(%SbgWC3+{c2>=1TS$I!^;VUQt+@62NJrr$v=QiajlTUBqdl zptEra3Ge~Br(?C@>;gc$zsJ25t*_)h7IbzYvJ_J6-({+ib8ME}$DFVOk#WK-hZJ?9 zk6Cgb3p&$AmfXkApEIwKRy#}XW9QEpx*ln>^xelSxsP2fJD~4AX6H!kE*smke$O(Fnl*RD>CenCHGO!4v_n} zC+tAsLMWn`A!M=gC#YrBsbavyVoYNYp;q3B~rrwcL^eN5|7MLV#oM-`hjd@B!_$Z&mz z9;?#otqkoQ=XF#&Lq1_nXXeh3Pngn@0Q-bG>XOm-2{YsqCUw*$Lq4IU)dlPmYBh`Y zjz{8%WTaD?A)ipMw~nC>bcGH?#^9)+IwB#o;~HtjNPbnMGU|l(mG# zd&hcEa&`5Zh30LTBo43i;8ub*HE%tu4X*oRn5?k)yY#6ZXiEqc{8` zZq|Q|WPlvKf{wGFq2QmT#Q<$H_y3U@h&-EmktftLw9VYr zah@_1{2LCLYUGj53^{tazhvkKB6+3YU;nFF2R{4nqcn>gy~JY-Jr)hcDK4+o1$;>G zvzTh+!MhAOdWjb>v=qqz2md~Uq2C(~B|DI2=r2S=$=TQHElSNkd^J=3NHkTlgikZ{ z3qBRq1?1=re~vpg+8^LNLmy}8H$_rqNAFq8z*EssoC~^N$I$POhN30Bsx=poqc_l+ zILXn=>rhRTqu140Z>9M>p@2r7El*RNaP$V2Yoz;_<~ZT+iY|ycQ<|ppgxvu{BhNgg zDfpMx8Cz-IJN^dltzVAntu#4$iDiaw^e=P1D|SVu`6PM?=xgNqhlA z>(Nm12R@UbKNtxmM{oE%hQ2!*igqCNUl}@zhN2x9zKNmF`-dnAkWW~6J40U>1zjNj zwJRQFza9;xnOi`AEgDJ=i5`^ZErV~eMN=hDv#SRsUiF~lW#~aEm{QQOqOut6IFGx^%Umyu+slCAsQBX@+@Bhu34os}__u*z};ps~(hieN=~6+F+qUI>(bSOqSY>FL1{Kj=|NGohcD9Ml{SL9dQg(9 z9+c#&1?3Yzc^zK)x}P4D^i>BsN3&r{4@zU}k_NAHygJr{k`8D=X&WS^2PK}gQBofdQh4RF6i*eTMJrHTJRP0pfnfgK}oLXb$F$_W?elf%>`P}1>#i? zO1wUz!z<5QT2Q(bR?vfz4(LHi2VSVd>pTjps|Do~6nan;R!R>_yq?qHm80caP(JIR z2PIzhpya=<=aG@)jv5s7pyX@oL5bJ1I=oU?xvK@`+e>;-(pNnw>A)EsUelzn zI?yafb@ia61A0*6^|S`B+2h2k7L+EVf*zE3)q@hRr*wE-B3`wie9J};O8Tk?r8W1m z4zGM4Mh}WgSkQwKuX<1#TPJmRrA1U%4~mmfK@Ungpa&&hPw4PUlTlX>N^;eMl3ewm z#A{BASJGE4C~XKA^q|D69+dR;n{;@kt=q01l;o-hC0_NQ#OpWe@Jc(UT|FrAss}|w zl+uF|uiv1S(`gpnV(e-`c^Rn(B|D%4 zP4lT*EeTK(jA2%%S-j%Zfu=cKg&vfer3a-MMrXjJ=@Mc0tGQYIfASw-*sNDe7iyq< zSCb6+t6&Hq@{x%1ZGfyqLs|gIMMJ6pS&D`LA{~i1S1X&1O3N$(@<=4|EWENpkEKSU zFx7k=Wpp?c_NdXQ&_e?2@KCB*ghx66e(&?bM0%xG*{qnQ^>Zb&zSS_SGNA8efz|qEYyDIm-Yq2&gyWDhP#S!07^9bs61>KXoze@%yr^A= zXHMV|h4l+Y1EqO86q+~iE!EokV`b=`b$a9i@GG%+JL7LI6nd!zoxXkrQs_5|MiaB# zr(&=dj26oMa187`=ED{wlQGzHr`F+Jp$6*tp?D}ha9pXik=H~F`YbAmxnr)M-fh7f z0Z`(>=Y;hcOX8HdX|$`Y^>bUbdc)YLn(NDu7d1uTX&!;V`3#1LMK}8791wl~Tpk8!Mr} z6RXST&aJPw+L;K&!sCF@53P#Xgt`yEmg|1@Kl>-F7gylpQK(6K43x39wZ06p0NgMX z_o#uN4?;h`vIQ2M<=GDftyLQ3cFO=lcOo#Gpfy`4&|U-xhMIxeayuMTCYHNV0HcP? z+zN-7)%F%n)HlN+#!dz8eJ293-9X{(Mj$Hdk5)|7Ybz48RyJ_{DMy0Nqk$|$K(;oF z+I4LB)hLirt#1J)ABtJM0dqK-yOjt?wce_%Uo^Hz0=aNt8{Q(XnKTbAg`>bSudl*W zb7l=$ONRq1wX#vgCLW5wlu9hlL^!5wT&s|tJqVp}R?(i9u&z-!#(;N|n&nmlVZ#X6 z4vr$K@Lnhi-o+!k)elFQPr;yus_ukB@=c?Pb5PzWSD}r7?S^B^brY&&L3AQu@Vpuj z1X{b{AfpY&rnbIf;IPU^;Ho<};Gr?p<)sKz*|=U_UjcWM6e1LOskvj+Zjkgt=h;h* z3L)-=BdVqKwRU5h=gLUfDm7d77wRep@@sy_)f=e_~+*J^);iiex7_hD;#HR7%ju3aZ(6H zUASS?U_7Ckg<~4X(MtH|pjh=PcTFw=RYK7$g##)E)^j8r058+w^trxLf$z4e;0)ok zo{BH9+y>RThQs7=1Q?!|Tz{mpZEUxZ(PRYDY?&2UnB1Us-l1?HK9yJ&eJ~1jFE1Ei z*GakVM_{VP4Y1ZYvkfCqWdp1lvVSiWbpA$347Uh~Fl7C3NTo@0egvRq8jbanz)L3% zbOJ!T5ulw7tjh{Vm=?$xAcb%UtR^WW5rIJU%ZGmcNV#1yuEW|DP`L7YCa zT?w44HDOsn%QFl=z{Z#uf{* z7Y;Hv8s>HAkZw4{sI_Z#)U{htn5{N?%zIG~`21$*=V#lEo%S|qP$vTP6gXzItl13* ziGY?BfiP+<8i3_+Oda|eJ>fz)1fIZziG){3SHoduy|KkAlMe-*hT7^DRhSD0RX2<# z&5%psn5`S2s|I*uo>?ayTHsXMs@7pzg{B?}2bnNZK%L0tPe#C+W`ou|he9#Su*9gc zb218D2ru7&x8m!#c)A~sfj6Wpn{|rb4Z~6R2paa|AOa#{n|H$@Fg)wq^c?k_2oTM8 zy(qx;4%+?O;Si$@aW?F~6VTZ;R-3&DIM@U1x|3j|RC-wDN^Y?mt- z8?hn~K!w+J<#15LfNrK`YZRuugVVv)a7f*%@6_onxO_MW){fxs0-;5F|$HD2ecovDFVkUAYVJ=^*q|+L*f&{xME8z3`7wX5H|QQHgJb ze@uQ+_{UIqCG^u})Y5yQBYPP+>xX}gYS<0`7)5$3{A2iZH}q4_yRq+I;#Y)Tzw8fH z5*Ey%CxBR zqe_*QJ0hR@B;SF5(M5kV?;QeIHTXRMnur(fDOs%K!Q`(1BCt;j3Sr83OZt%lOAf>xukRJEjYF>S&Y$|9U3FlzWy!F9DC}I5HcJ`SQ((;6CTtJC>uu?D zhGAEgW(LW0XTR53dyn!c**+=NM_~rvSdaQ2s0=^lXC{z}XcCa)wz<^>sO;%cfx`U7FZVi9 zf$xCINTuRwz4M@P<~SY38j2J#PL_19xI6{$P}+);)F!;ZBC?dLvyplDUu+?VvP7rQ z^CH8}lR5=3TWU^Z8hV9J0XztL>uEL{KcrItPmGOzf(!hDPJsm|kQ#@jc_^?q4oh=T z;O005@MKPBoC4U>yW))*{Ne&GH%HDFJt$51{>E1Vdkr|I%_ za-15lr_19s8v7xY<`L)Q#t9#yoUm?;8tAZPFGj5R+%;Vn8$F*{F<%lq_z8$|ALsya zWc74_G@^8XC|^qlh$ASk1H_S&)&XKqB{hKfjKJut;>C#LW1xXU1K88RVXy3I;ILCH z4IK7KUIT|+lGeask0dp4*de2@jFz9r$@+E0$qF4li=2l2Gv7ctmA|MTtmLjabdg`@ z{){mSkY)XRF0i#5Ujy5{F=`;Il>19|VqQ?wr~yUV9is-a8R;=2~0%T!_Np00w_+Pwz34C2e_5VOoiwhRlDvD77)1Ya# zrl}Q3_ZFHqBrOywCNJ;ZB(HfJ@4nZhC>RyBY!X?t2xvq$Wh%)N7OUUJ`kfArHN=bZV@nKN_GY-eUD@R&`#(DUddNOy^PNM`K9 z+T5C0nu4Te9l}4gy}M;Yk`pfXSA-E!Fbgs!U8BaNOU_DguGC38KTs310%Nhbh>h(b zPzIrvK*(8NHopY~X9fxiAs+w53^8|+AoyV&1~mfW&V9KRRN%5(G?%rLLvk82b|Dvj zIAC-MdrT=QyTg!`=B;t(uq>a?=4&nd7xd{~YRq(8gK$v216J=ra(f9^DJ0iq3u{(` z_%2ht2mz#NtF|u=iwt&c-B3JEGbk}{kw4VT1yIJ{C_ROW!_228j?*XjMWvR%0m{UG zy(lo}i`TlG`!2(|JN6^td@2ib$9ygQv6_Ad#%i4=gilH8rUBvq{ZttU z#>$hqkR~bIae~hnHx|?UP@2I1#-+3M{sfe<39skzLlMhYY8;)bjx$l`w3q=WGpz6s5aPMz%LFuk$c<+9gl0u;mB(&S5l5{7aC4+xjzp}?`+`F z#Sg5s96iHY73X+rY;WMv&4I1I=HJ;KcR5_W+is(GBdn9M8Ea`WkNwZY9G;sRo{>a6 zm9?#2u@pVxZ`qz$Vkzv|0?4c@IEY$ccCtxkXluAtNIdE6KX?n(OlRxr_U47ky%A*0 zX1W2aN=&h~x#h6%&`HNqy!##XR~)+ZSZZx1j>DU(BYEH2}m^wCNh2C2qDKh z{|0FH!=BF4*cLe#+3w%iQxI>eN#*>d2&@fG_w-A+UH*ay=aL&S2l?&5$Tkfqbw(fLha%@O z^i!smKLKUf*!D9~L=WgUok1i(>3-h(!5ApGAtdwl`w1vj#;%aP#fFxZs_lpkc8XuU zQ;9KSa3K?k6wSsEdAw8-BgL-36h+%Iq=$Uw3%nfZB1EB!fkBqea2Mk&#W^UT81q>ZZwtJ*i?Qq%7R z-uG6@Pen@G$vy}f!w-6FkgGOhZ~4JW8TcTx)ZdSt%|#A&L>ylvWij_yB9UK}u?o#F z@#VIXRLEwhG5ehqqsb3rZ0Z>6Gsz)8i1DdItg#9Z@-^UxvU}<%s{sO{=4my+Fv>0i zM_SIxYzDuLky0a_+#l`SMP2_<5=BoD;MM9%>w@b=SgIfo z3A)r7i$;cBT3`jM$!EV^>IEp<4PBDi+UlD2WX|erU>k|IuszEjgxm?awhRe~uOIY}7D;6yLlHgZtkQ9Kk-DtLeDjk%-Tjlr) zt`=#Yz_Nf^%uvh86r|H|x;x2BTnm5l-yqe7Mc$qnxZtFYh2zLHF7GyAxxm`qUYp{V z+YJFd-_^-9-mT!1bE{v@VC(f62nvBq5v;_rwaY=nRs$at0J%;aQ!XyTa30rDs|+dB zBAo-79Gu**g>%#W#h%)DHa&Mm7LFKMV7p?XJryfpT=d+5Vh`Lj0H=TKxw(P9xv^Yw zZW_+m)&g?wuiiFMF7oDjJil9y;3H9kBWQaGP^j4~4%z2!va=aXz)B>5)>@cbu+lj& zmxT#t{e8+dv2(rkjdSDKo;+D|^ly`=%v-&9BL-&D`po2_)2q^AA(@&x7)ug#T?s9w z4~+^}MT)u^PetyTp^&40pbb+QN1vYDd|7)gAFxmhC}C^-hUWGrQx<(p zP}aH<38rVkcq(aSNb9i@O>ug-vc~|0SqU3zz%e{*?8^X3u0)O+3LL9}{kpk*a9o}8FOd>;E#|3M<0Vw(AAPK65HH8c zBRHG-S3d5K)7&*Y;%FY(^aSNBHPMCJ`xiHM6 zYSpq8%3F(RV)WJciC)+eW#)CO{|hL)j54bawZ3thK1EhKe%9cc za_|-6e6c&`ZIN0Y_0i-}b$oCh^P|Zk*Y3^2FnAn4DrcEPH#Pqqj4|?tw~;x9P4<<;sS|ueH^HReeO*k%KdX zgnfRxf1p36Y|E`Xg*{#Lw(kFlhUc`pSw=T& z7*iw(Y%x%xc4H(li9g^IA4s*`@C4x(|0$}w1W8#7db!IODVD$f^l>o-GlcVCsA9X@60*!I~Y@`yv=#5y0 zSKe2nY!x_+9#3dQ#*o2b>0MeAkgodHim0B0+b!ua81Yy~3SwTuYVU)k7ht#o z1;B;<2$nv00CY*|k;m+E4hvnm~B2ae7R5nyaIR?OZ=l%=$xbX5aXA79Yevq^D`CQUp*)Uo5HhIhb{4tmV}hL6^Ej?VrMbl54nW1 zpGUnq({^QlJGA`T{Z8J*)rt1$`Aq5BG&eG@8NZ#;k6pp=zg-FE+bLJ$CVGl=R`mX< ziC!nK5~ir$<%~Sa*0_b%FCXJJ{+Jb=DOxC+t`er8khT4|#!;GrE=FK?aNy%3G{t;c z%B_UP)CZi;H9jXojp=TR?h0t!jLmKQyb5THrs)0z|D=B#p(*NdDVP0&Yy4zybRS3^ zZb?*Cz{dQU=XY?8qx?X~ACE>4+_-?8&;qXU{JzL++_(UpP`A)H8znF~q30rR6^%5) z9Twxo+alDs1s99ED*=qAXsrM+4V_yn&?@HBy7{xwR~}svR8;{}G*i@GC4kWszKf#{I9DH(JtC}cpnmC54*Y6#pIu!uO)R4~aNRr_R8#_( z1{DjC@=n~{pDk8fyMfOlGteXs(+b zD`5&A&%j65Hz`wuYr}Z{f%X+vC*j!l^qjYhnsN?i>{w`gS>?9XmU+}GemLs&frj}D z$c}I2HLrysm0?G@_ff7LM@>71*9sPgqNv-MpNYI~=hI5>_`bXfu1-6CC|qG>XR)#f zuT|rPA$aMMIBGM^8j(*={cVg=*sB^_==9aVOt{Mr39vST#CB_cOK^|7^+~ zoheitkBnt_g++%-vlio!LdfNFS$FH;F!1`!KqfnwS%-JbmFe5|L{#x~UYXj{N_gEF z0v31!=>B6g4L~ieHq2{64>uvMR?~K9%KN;~uB3kyR-K5*s-)18q764`qgvdi{n6VL z;+(;z4F#i1pLLkw_RDKNNesY(Ywn_G zJyoq<<=j^jt;ZMa?S6TSZ1=sBd}I2hEuM6_hm3ULY#^=M8Sl67BK>JK8kv}nwjkgM zF8F~PuEx!@^~lVnvGRd&R9;mH zjd1`u{&qgxk8by>zM0n0$7gcjC~Zv5ZL|^^lZ{2V;l2gulP|mSXmNgA07tj#U*9~B zmdq-kF<+4p_xnd_3f_A>$y-HVgf?!ZrJfIX*O2HE^!4kOKqKE{+{0exuml3T;YF(+ zxF(ctCY+vQ_vx)?l{q-Ub-v}oqCUWTNyBh(6D!7uZ`~l}KbHG`iwjyjWlzytjSU9Zl zsI`mEmWHmoqt+D`3Zk*%n5kL0J!De@E)Nc0%)5nEnaly>0v?DhMi6fv zEYI*cc(T2~58>f<1lPLGTv-X%T8D}q>V~<6I zZQ-#!Wsh8x_8^tHx;rCM7Z9L-|e<^atdH8-f>W$>~JZwQ`5=ne! zz@633z=GAtowZtg+eU2nsVz)FfX|^naPQJVfOmXV%H;spxy6PzlOXXdbDvPuemKK+ z!lPCg8%G0AyViz3YxR)hZBdyP7oBm8G|ja`V`u)EDB~f;IHFvg+Rb>#aGxX^$+zDt zSHsy+YRIEboNa@0b=wnVjYbgW#7tXFYuyDakw^8w_qux@lH~2Dn@Th)fsZ~ zLwBy8mzUtWuMbs!fpNP_Up&jNvBry+RZ9IgK&dk>vo6SYh%qb$w#aJNq31Ookda&! z5{vQaV?%rqS%t)!au|@?eT-MmFah193ip==-GvG$t*?65dJPn5NdNnL>G0@CX+xqe zL3*G;K^JD|;uzAr6?x+c(kx4|utT^w#a~lx?-*nFpJqK-8Gab}D0Cxk$LEZ6h8>^; z0oPjd1_3C;#^u&hNma*~+t_Ekb!W;^AoD?9IYTRcU9R3F-M^z&rSGW4x^<^>{{<*@ zrRzATg-9$wX)ld}cbt=|s{BiF`Lfie-HS3aTjC<-VWW}@6!%ui6(Xn;ra7L8Mh&kf#f@ONS$e#PWB9!5p z>>%fFHL{WI$91vM9|A8ns>#~5I4*RBw=rY4h@ORoJtBCjaz#E{%+0A?n$5`fX^mYX zsY5(h#H9m>qy!^x6;BHPZi&3yLW*ILP-3@`cxhPf3P}kOj8@meOWhinBSOKucmTFb zfL(l@H#^|xitv6mX1OC!skD`b=XG%e4q{gZiuIto5rQ2&9w8@$-}aO@XI&Uj`vXzI za4PeWKryAG$a;HV)`gB@25#@?_FEZfKYVg5m9kQx-B=)2e{8e0Y$ygVuUfb-_J9%A zA**d2@Acqi1KU~-9X3jDxa1e)m-Uy*Ar!c}rsG(8fp|{s`V87sw14A+b2dGDwm>$u z{H1G85t*@OQ+eB^>)|j=|YRo@qD+sUTTpvD5)8dekTkw@-b8rT70Jx!bT0 zNj6&bAY0dCwKOy*nipWq=lE}fEU6-C>+1OPac|*0=6g={wG=vR0NfyyG`&Q1&Eh$= zsThQ*i_P$sz>mf;J-(X_p#7_O1KiRnjp|s*O5;Wcnx}P;3;oE^4=t|-&b)bpV7s_t zM>S`+AkDNvZluJ(T6Sq5I#yZ~RxLK;aD96TQFtB}-fgD$ms^dOSo2mNyi{209`Xzi zmK@564xW#)e8$_L*Z6bZmS7Bs_%Ka?_@it$hG~8%{N{f?5}_f9=liZQhO0)wX$<#q zZ^4~Wt&mU0;{EW(3KVbjIIj;g6vGjbu88+{K$$RJ6EIQ0KHil2--uFr)CjhrF|nRf z{~asrd2fZen=v=RTo`B+Vnf*Sx{a16tfW5m`gwG*%!bQ&mvZ5M<@e?%N!Ka-h)e!# zQb`sIML{4g6)PtpB#dnH{Xu-_OXFo74#Z0RUwtToxfn$>{T{&7Cdw?5}7 zi;x46x*!qFFm?4fyO?;E+uS^+Co+fTV0FTE$Cl4}ov}VB(qyu0;N^8d&j~$FTJ2Fa zlf@EK~)u%b=9Y~5A7w5t1YGAXRhSnV+zUFG%*xztsTO`J|jh$k~Jq7&ydbP^

      m$9Buns2}8Z8TuK1giqDrQA_YjIOFopA^~OMMXv|&L*dSr z{?$DQ{~fJR`jtS)9{v~fWDO0;T8*cneRT=OAe3EGKq1d)mPAs}x`t;obio3VSp(jV z0hCeW-9rOqGMk`2fRZ!2vLZb9jOPmDJ@^oO-4Lom+643)fHGn{J|^am&KSQ#ALLIM zgM1y6 zBr~AE@MP&pVuKj8y7H$DoP05d?w(!7y)e5{4_aK0GSjv4PcH4<~M$* z0aVwZzH@5hs>$w|w0L-e!o7W1Wt0p$2@vVTDK5>|AoIFQco9FaM$24{66zY7=~crI z@p0*&hczi^x67fP?0yT2SS9LS0m-_r0YU6_=!fV;>32vqbD>Y=AC*ku>cXtR)`Tlk z?Xg1tG6Z&ZW~+_zf*6Rj!zM=3wqbh}KqoHsDK7}Y;A$C0QiI5NfQaOe8GKn%**X9( zlXdi(>DRzPWL0K!ER{st?J^Ltrq(V;eZ?sgAG1Q~L>mJz%?@>j9qj&OFU=@NpK;GG z*o<_{rh_-5PkTACZ%D(NjD+HvOXeaHX|mhJs9w!`YxxuwaH{U)&QkK&3=fXTHrtzs zZ4r52Y2c-jnc@+`u00)+?qb`4ltaQHcPpEon5$nafW9+{{YjwhX1w2C+g?mp7} z6|fm9dG~p@GW<{FdtxyO2Fvt8!U^FS6;mY~{+>6XG(ghM!MQ8EGHccXcgA};MoQX) z$xI?Uh^3o7iIkm9#qvp%dQYH~g_BsYBrbgH?Dt*`Bel|nj`9j6diplUu$%0D)N4&u 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

      o$K4abKg^t@=z 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

      6xV6ekm!-2|5;3|Enys8dvCjS<1#~>8~W^ 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 diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/kubectl_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/kubectl_test.go deleted file mode 100644 index 6381b4920..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/kubectl_test.go +++ /dev/null @@ -1,194 +0,0 @@ -/* -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 kubectl - -import ( - "testing" -) - -func TestParseFileSource(t *testing.T) { - cases := []struct { - name string - input string - key string - filepath string - err bool - }{ - { - name: "success 1", - input: "boo=zoo", - key: "boo", - filepath: "zoo", - err: false, - }, - { - name: "success 2", - input: "boo=/path/to/zoo", - key: "boo", - filepath: "/path/to/zoo", - err: false, - }, - { - name: "success 3", - input: "boo-2=/1/2/3/4/5/zab.txt", - key: "boo-2", - filepath: "/1/2/3/4/5/zab.txt", - err: false, - }, - { - name: "success 4", - input: "boo-=this/seems/weird.txt", - key: "boo-", - filepath: "this/seems/weird.txt", - err: false, - }, - { - name: "success 5", - input: "-key=some/path", - key: "-key", - filepath: "some/path", - err: false, - }, - { - name: "invalid 1", - input: "key==some/path", - err: true, - }, - { - name: "invalid 2", - input: "=key=some/path", - err: true, - }, - { - name: "invalid 3", - input: "==key=/some/other/path", - err: true, - }, - { - name: "invalid 4", - input: "=key", - err: true, - }, - { - name: "invalid 5", - input: "key=", - err: true, - }, - } - - for _, tc := range cases { - key, filepath, err := parseFileSource(tc.input) - if err != nil { - if tc.err { - continue - } - - t.Errorf("%v: unexpected error: %v", tc.name, err) - continue - } - - if tc.err { - t.Errorf("%v: unexpected success", tc.name) - continue - } - - if e, a := tc.key, key; e != a { - t.Errorf("%v: expected key %v; got %v", tc.name, e, a) - continue - } - - if e, a := tc.filepath, filepath; e != a { - t.Errorf("%v: expected filepath %v; got %v", tc.name, e, a) - } - } -} - -func TestParseLiteralSource(t *testing.T) { - cases := []struct { - name string - input string - key string - value string - err bool - }{ - { - name: "success 1", - input: "key=value", - key: "key", - value: "value", - err: false, - }, - { - name: "success 2", - input: "key=value/with/slashes", - key: "key", - value: "value/with/slashes", - err: false, - }, - { - name: "err 1", - input: "key==value", - err: true, - }, - { - name: "err 2", - input: "key=value=", - err: true, - }, - { - name: "err 3", - input: "key2=value==", - err: true, - }, - { - name: "err 4", - input: "==key", - err: true, - }, - { - name: "err 5", - input: "=key=", - err: true, - }, - } - - for _, tc := range cases { - key, value, err := parseLiteralSource(tc.input) - if err != nil { - if tc.err { - continue - } - - t.Errorf("%v: unexpected error: %v", tc.name, err) - continue - } - - if tc.err { - t.Errorf("%v: unexpected success", tc.name) - continue - } - - if e, a := tc.key, key; e != a { - t.Errorf("%v: expected key %v; got %v", tc.name, e, a) - continue - } - - if e, a := tc.value, value; e != a { - t.Errorf("%v: expected value %v; got %v", tc.name, e, a) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/namespace_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/namespace_test.go deleted file mode 100644 index b0445bd29..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/namespace_test.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright 2015 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 kubectl - -import ( - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api" -) - -func TestNamespaceGenerate(t *testing.T) { - tests := []struct { - params map[string]interface{} - expected *api.Namespace - expectErr bool - }{ - { - params: map[string]interface{}{ - "name": "foo", - }, - expected: &api.Namespace{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - }, - expectErr: false, - }, - { - params: map[string]interface{}{}, - expectErr: true, - }, - } - generator := NamespaceGeneratorV1{} - for _, test := range tests { - obj, err := generator.Generate(test.params) - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v", err) - } - if test.expectErr && err != nil { - continue - } - if !reflect.DeepEqual(obj.(*api.Namespace), test.expected) { - t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*api.Namespace)) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/proxy_server.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/proxy_server.go index bfbb9b6a0..082b542fc 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/proxy_server.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/proxy_server.go @@ -28,7 +28,7 @@ import ( "time" "github.com/golang/glog" - client "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/util" ) @@ -146,7 +146,7 @@ type ProxyServer struct { // NewProxyServer creates and installs a new ProxyServer. // It automatically registers the created ProxyServer to http.DefaultServeMux. // 'filter', if non-nil, protects requests to the api only. -func NewProxyServer(filebase string, apiProxyPrefix string, staticPrefix string, filter *FilterServer, cfg *client.Config) (*ProxyServer, error) { +func NewProxyServer(filebase string, apiProxyPrefix string, staticPrefix string, filter *FilterServer, cfg *restclient.Config) (*ProxyServer, error) { host := cfg.Host if !strings.HasSuffix(host, "/") { host = host + "/" @@ -156,7 +156,7 @@ func NewProxyServer(filebase string, apiProxyPrefix string, staticPrefix string, return nil, err } proxy := newProxy(target) - if proxy.Transport, err = client.TransportFor(cfg); err != nil { + if proxy.Transport, err = restclient.TransportFor(cfg); err != nil { return nil, err } proxyServer := http.Handler(proxy) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/proxy_server_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/proxy_server_test.go deleted file mode 100644 index 494b926cc..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/proxy_server_test.go +++ /dev/null @@ -1,337 +0,0 @@ -/* -Copyright 2014 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 kubectl - -import ( - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - "net/url" - "path/filepath" - "strings" - "testing" - - client "k8s.io/kubernetes/pkg/client/unversioned" -) - -func TestAccept(t *testing.T) { - tests := []struct { - acceptPaths string - rejectPaths string - acceptHosts string - path string - host string - method string - expectAccept bool - }{ - - { - acceptPaths: DefaultPathAcceptRE, - rejectPaths: DefaultPathRejectRE, - acceptHosts: DefaultHostAcceptRE, - path: "/api/v1/pods", - host: "127.0.0.1", - method: "GET", - expectAccept: true, - }, - { - acceptPaths: DefaultPathAcceptRE, - rejectPaths: DefaultPathRejectRE, - acceptHosts: DefaultHostAcceptRE, - path: "/api/v1/pods", - host: "localhost", - method: "GET", - expectAccept: true, - }, - { - acceptPaths: DefaultPathAcceptRE, - rejectPaths: DefaultPathRejectRE, - acceptHosts: DefaultHostAcceptRE, - path: "/api/v1/pods/foo/exec", - host: "127.0.0.1", - method: "GET", - expectAccept: false, - }, - { - acceptPaths: DefaultPathAcceptRE, - rejectPaths: DefaultPathRejectRE, - acceptHosts: DefaultHostAcceptRE, - path: "/api/v1/pods/foo/attach", - host: "127.0.0.1", - method: "GET", - expectAccept: false, - }, - { - acceptPaths: DefaultPathAcceptRE, - rejectPaths: DefaultPathRejectRE, - acceptHosts: DefaultHostAcceptRE, - path: "/api/v1/pods", - host: "evil.com", - method: "GET", - expectAccept: false, - }, - { - acceptPaths: DefaultPathAcceptRE, - rejectPaths: DefaultPathRejectRE, - acceptHosts: DefaultHostAcceptRE, - path: "/api/v1/pods", - host: "localhost.evil.com", - method: "GET", - expectAccept: false, - }, - { - acceptPaths: DefaultPathAcceptRE, - rejectPaths: DefaultPathRejectRE, - acceptHosts: DefaultHostAcceptRE, - path: "/api/v1/pods", - host: "127a0b0c1", - method: "GET", - expectAccept: false, - }, - { - acceptPaths: DefaultPathAcceptRE, - rejectPaths: DefaultPathRejectRE, - acceptHosts: DefaultHostAcceptRE, - path: "/ui", - host: "localhost", - method: "GET", - expectAccept: true, - }, - { - acceptPaths: DefaultPathAcceptRE, - rejectPaths: DefaultPathRejectRE, - acceptHosts: DefaultHostAcceptRE, - path: "/api/v1/pods", - host: "localhost", - method: "POST", - expectAccept: false, - }, - { - acceptPaths: DefaultPathAcceptRE, - rejectPaths: DefaultPathRejectRE, - acceptHosts: DefaultHostAcceptRE, - path: "/api/v1/pods/somepod", - host: "localhost", - method: "PUT", - expectAccept: false, - }, - { - acceptPaths: DefaultPathAcceptRE, - rejectPaths: DefaultPathRejectRE, - acceptHosts: DefaultHostAcceptRE, - path: "/api/v1/pods/somepod", - host: "localhost", - method: "PATCH", - expectAccept: false, - }, - } - for _, test := range tests { - filter := &FilterServer{ - AcceptPaths: MakeRegexpArrayOrDie(test.acceptPaths), - RejectPaths: MakeRegexpArrayOrDie(test.rejectPaths), - AcceptHosts: MakeRegexpArrayOrDie(test.acceptHosts), - RejectMethods: MakeRegexpArrayOrDie(DefaultMethodRejectRE), - } - accept := filter.accept(test.method, test.path, test.host) - if accept != test.expectAccept { - t.Errorf("expected: %v, got %v for %#v", test.expectAccept, accept, test) - } - } -} - -func TestRegexpMatch(t *testing.T) { - tests := []struct { - str string - regexps string - expectMatch bool - }{ - { - str: "foo", - regexps: "bar,.*", - expectMatch: true, - }, - { - str: "foo", - regexps: "bar,fo.*", - expectMatch: true, - }, - { - str: "bar", - regexps: "bar,fo.*", - expectMatch: true, - }, - { - str: "baz", - regexps: "bar,fo.*", - expectMatch: false, - }, - } - for _, test := range tests { - match := matchesRegexp(test.str, MakeRegexpArrayOrDie(test.regexps)) - if test.expectMatch != match { - t.Errorf("expected: %v, found: %v, for %s and %v", test.expectMatch, match, test.str, test.regexps) - } - } -} - -func TestFileServing(t *testing.T) { - const ( - fname = "test.txt" - data = "This is test data" - ) - dir, err := ioutil.TempDir("", "data") - if err != nil { - t.Fatalf("error creating tmp dir: %v", err) - } - if err := ioutil.WriteFile(filepath.Join(dir, fname), []byte(data), 0755); err != nil { - t.Fatalf("error writing tmp file: %v", err) - } - - const prefix = "/foo/" - handler := newFileHandler(prefix, dir) - server := httptest.NewServer(handler) - // TODO: Uncomment when fix #19254 - // defer server.Close() - - url := server.URL + prefix + fname - res, err := http.Get(url) - if err != nil { - t.Fatalf("http.Get(%q) error: %v", url, err) - } - defer res.Body.Close() - - if res.StatusCode != http.StatusOK { - t.Errorf("res.StatusCode = %d; want %d", res.StatusCode, http.StatusOK) - } - b, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatalf("error reading resp body: %v", err) - } - if string(b) != data { - t.Errorf("have %q; want %q", string(b), data) - } -} - -func TestAPIRequests(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - fmt.Fprintf(w, "%s %s %s", r.Method, r.RequestURI, string(b)) - })) - // TODO: Uncomment when fix #19254 - // defer ts.Close() - - // httptest.NewServer should always generate a valid URL. - target, _ := url.Parse(ts.URL) - proxy := newProxy(target) - - tests := []struct{ method, body string }{ - {"GET", ""}, - {"DELETE", ""}, - {"POST", "test payload"}, - {"PUT", "test payload"}, - } - - const path = "/api/test?fields=ID%3Dfoo&labels=key%3Dvalue" - for i, tt := range tests { - r, err := http.NewRequest(tt.method, path, strings.NewReader(tt.body)) - if err != nil { - t.Errorf("error creating request: %v", err) - continue - } - w := httptest.NewRecorder() - proxy.ServeHTTP(w, r) - if w.Code != http.StatusOK { - t.Errorf("%d: proxy.ServeHTTP w.Code = %d; want %d", i, w.Code, http.StatusOK) - } - want := strings.Join([]string{tt.method, path, tt.body}, " ") - if w.Body.String() != want { - t.Errorf("%d: response body = %q; want %q", i, w.Body.String(), want) - } - } -} - -func TestPathHandling(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprint(w, r.URL.Path) - })) - // TODO: Uncomment when fix #19254 - // defer ts.Close() - - table := []struct { - prefix string - reqPath string - expectPath string - }{ - {"/api/", "/metrics", "404 page not found\n"}, - {"/api/", "/api/metrics", "/api/metrics"}, - {"/api/", "/api/v1/pods/", "/api/v1/pods/"}, - {"/", "/metrics", "/metrics"}, - {"/", "/api/v1/pods/", "/api/v1/pods/"}, - {"/custom/", "/metrics", "404 page not found\n"}, - {"/custom/", "/api/metrics", "404 page not found\n"}, - {"/custom/", "/api/v1/pods/", "404 page not found\n"}, - {"/custom/", "/custom/api/metrics", "/api/metrics"}, - {"/custom/", "/custom/api/v1/pods/", "/api/v1/pods/"}, - } - - cc := &client.Config{ - Host: ts.URL, - } - - for _, item := range table { - func() { - p, err := NewProxyServer("", item.prefix, "/not/used/for/this/test", nil, cc) - if err != nil { - t.Fatalf("%#v: %v", item, err) - } - pts := httptest.NewServer(p.handler) - // TODO: Uncomment when fix #19254 - // defer pts.Close() - - r, err := http.Get(pts.URL + item.reqPath) - if err != nil { - t.Fatalf("%#v: %v", item, err) - } - body, err := ioutil.ReadAll(r.Body) - r.Body.Close() - if err != nil { - t.Fatalf("%#v: %v", item, err) - } - if e, a := item.expectPath, string(body); e != a { - t.Errorf("%#v: Wanted %q, got %q", item, e, a) - } - }() - } -} - -func TestExtractHost(t *testing.T) { - fixtures := map[string]string{ - "localhost:8085": "localhost", - "marmalade": "marmalade", - } - for header, expected := range fixtures { - host := extractHost(header) - if host != expected { - t.Fatalf("%s != %s", host, expected) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/builder.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/builder.go index 61b0f1131..7b7bc1504 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/builder.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/builder.go @@ -126,9 +126,8 @@ func (b *Builder) FilenameParam(enforceNamespace bool, paths ...string) *Builder func (b *Builder) URL(urls ...*url.URL) *Builder { for _, u := range urls { b.paths = append(b.paths, &URLVisitor{ - Mapper: b.mapper, - URL: u, - Schema: b.schema, + URL: u, + StreamVisitor: NewStreamVisitor(nil, b.mapper, u.String(), b.schema), }) } return b @@ -434,20 +433,36 @@ func (b *Builder) SingleResourceType() *Builder { return b } +// mappingFor returns the RESTMapping for the Kind referenced by the resource. +// prefers a fully specified GroupVersionResource match. If we don't have one match on GroupResource +func (b *Builder) mappingFor(resourceArg string) (*meta.RESTMapping, error) { + fullySpecifiedGVR, groupResource := unversioned.ParseResourceArg(resourceArg) + gvk := unversioned.GroupVersionKind{} + if fullySpecifiedGVR != nil { + gvk, _ = b.mapper.KindFor(*fullySpecifiedGVR) + } + if gvk.IsEmpty() { + var err error + gvk, err = b.mapper.KindFor(groupResource.WithVersion("")) + if err != nil { + return nil, err + } + } + + return b.mapper.RESTMapping(gvk.GroupKind(), gvk.Version) +} + func (b *Builder) resourceMappings() ([]*meta.RESTMapping, error) { if len(b.resources) > 1 && b.singleResourceType { return nil, fmt.Errorf("you may only specify a single resource type") } mappings := []*meta.RESTMapping{} for _, r := range b.resources { - gvk, err := b.mapper.KindFor(unversioned.GroupVersionResource{Resource: r}) - if err != nil { - return nil, err - } - mapping, err := b.mapper.RESTMapping(gvk.GroupKind(), gvk.Version) + mapping, err := b.mappingFor(r) if err != nil { return nil, err } + mappings = append(mappings, mapping) } return mappings, nil @@ -460,14 +475,11 @@ func (b *Builder) resourceTupleMappings() (map[string]*meta.RESTMapping, error) if _, ok := mappings[r.Resource]; ok { continue } - gvk, err := b.mapper.KindFor(unversioned.GroupVersionResource{Resource: r.Resource}) - if err != nil { - return nil, err - } - mapping, err := b.mapper.RESTMapping(gvk.GroupKind(), gvk.Version) + mapping, err := b.mappingFor(r.Resource) if err != nil { return nil, err } + mappings[mapping.Resource] = mapping mappings[r.Resource] = mapping canonical[mapping.Resource] = struct{}{} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/builder_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/builder_test.go deleted file mode 100644 index 1fad662b6..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/builder_test.go +++ /dev/null @@ -1,1072 +0,0 @@ -/* -Copyright 2014 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 resource - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/http/httptest" - "reflect" - "testing" - - "github.com/ghodss/yaml" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/testapi" - apitesting "k8s.io/kubernetes/pkg/api/testing" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/v1" - "k8s.io/kubernetes/pkg/client/unversioned/fake" - "k8s.io/kubernetes/pkg/runtime" - utilerrors "k8s.io/kubernetes/pkg/util/errors" - "k8s.io/kubernetes/pkg/watch" - watchjson "k8s.io/kubernetes/pkg/watch/json" -) - -func stringBody(body string) io.ReadCloser { - return ioutil.NopCloser(bytes.NewReader([]byte(body))) -} - -func watchBody(events ...watch.Event) string { - buf := &bytes.Buffer{} - enc := watchjson.NewEncoder(buf, testapi.Default.Codec()) - for _, e := range events { - enc.Encode(&e) - } - return buf.String() -} - -func fakeClient() ClientMapper { - return ClientMapperFunc(func(*meta.RESTMapping) (RESTClient, error) { - return &fake.RESTClient{}, nil - }) -} - -func fakeClientWith(testName string, t *testing.T, data map[string]string) ClientMapper { - return ClientMapperFunc(func(*meta.RESTMapping) (RESTClient, error) { - return &fake.RESTClient{ - Codec: testapi.Default.Codec(), - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - p := req.URL.Path - q := req.URL.RawQuery - if len(q) != 0 { - p = p + "?" + q - } - body, ok := data[p] - if !ok { - t.Fatalf("%s: unexpected request: %s (%s)\n%#v", testName, p, req.URL, req) - } - return &http.Response{ - StatusCode: http.StatusOK, - Body: stringBody(body), - }, nil - }), - }, nil - }) -} - -func testData() (*api.PodList, *api.ServiceList) { - pods := &api.PodList{ - ListMeta: unversioned.ListMeta{ - ResourceVersion: "15", - }, - Items: []api.Pod{ - { - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"}, - Spec: apitesting.DeepEqualSafePodSpec(), - }, - { - ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"}, - Spec: apitesting.DeepEqualSafePodSpec(), - }, - }, - } - svc := &api.ServiceList{ - ListMeta: unversioned.ListMeta{ - ResourceVersion: "16", - }, - Items: []api.Service{ - { - ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, - Spec: api.ServiceSpec{ - Type: "ClusterIP", - SessionAffinity: "None", - }, - }, - }, - } - return pods, svc -} - -func streamTestData() (io.Reader, *api.PodList, *api.ServiceList) { - pods, svc := testData() - r, w := io.Pipe() - go func() { - defer w.Close() - w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), pods))) - w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), svc))) - }() - return r, pods, svc -} - -func JSONToYAMLOrDie(in []byte) []byte { - data, err := yaml.JSONToYAML(in) - if err != nil { - panic(err) - } - return data -} - -func streamYAMLTestData() (io.Reader, *api.PodList, *api.ServiceList) { - pods, svc := testData() - r, w := io.Pipe() - go func() { - defer w.Close() - w.Write(JSONToYAMLOrDie([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), pods)))) - w.Write([]byte("\n---\n")) - w.Write(JSONToYAMLOrDie([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), svc)))) - }() - return r, pods, svc -} - -func streamTestObject(obj runtime.Object) io.Reader { - r, w := io.Pipe() - go func() { - defer w.Close() - w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), obj))) - }() - return r -} - -type testVisitor struct { - InjectErr error - Infos []*Info -} - -func (v *testVisitor) Handle(info *Info, err error) error { - if err != nil { - return err - } - v.Infos = append(v.Infos, info) - return v.InjectErr -} - -func (v *testVisitor) Objects() []runtime.Object { - objects := []runtime.Object{} - for i := range v.Infos { - objects = append(objects, v.Infos[i].Object) - } - return objects -} - -func TestPathBuilderAndVersionedObjectNotDefaulted(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - FilenameParam(false, "../../../docs/user-guide/update-demo/kitten-rc.yaml") - - test := &testVisitor{} - singular := false - - err := b.Do().IntoSingular(&singular).Visit(test.Handle) - if err != nil || !singular || len(test.Infos) != 1 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos) - } - - info := test.Infos[0] - if info.Name != "update-demo-kitten" || info.Namespace != "" || info.Object == nil { - t.Errorf("unexpected info: %#v", info) - } - version, ok := info.VersionedObject.(*v1.ReplicationController) - // versioned object does not have defaulting applied - if info.VersionedObject == nil || !ok || version.Spec.Replicas != nil { - t.Errorf("unexpected versioned object: %#v", info.VersionedObject) - } -} - -func TestNodeBuilder(t *testing.T) { - node := &api.Node{ - ObjectMeta: api.ObjectMeta{Name: "node1", Namespace: "should-not-have", ResourceVersion: "10"}, - Spec: api.NodeSpec{}, - Status: api.NodeStatus{ - Capacity: api.ResourceList{ - api.ResourceCPU: resource.MustParse("1000m"), - api.ResourceMemory: resource.MustParse("1Mi"), - }, - }, - } - r, w := io.Pipe() - go func() { - defer w.Close() - w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), node))) - }() - - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - NamespaceParam("test").Stream(r, "STDIN") - - test := &testVisitor{} - - err := b.Do().Visit(test.Handle) - if err != nil || len(test.Infos) != 1 { - t.Fatalf("unexpected response: %v %#v", err, test.Infos) - } - info := test.Infos[0] - if info.Name != "node1" || info.Namespace != "" || info.Object == nil { - t.Errorf("unexpected info: %#v", info) - } -} - -func TestPathBuilderWithMultiple(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml"). - FilenameParam(false, "../../../examples/pod"). - NamespaceParam("test").DefaultNamespace() - - test := &testVisitor{} - singular := false - - err := b.Do().IntoSingular(&singular).Visit(test.Handle) - if err != nil || singular || len(test.Infos) != 2 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos) - } - - info := test.Infos[0] - if _, ok := info.Object.(*api.ReplicationController); !ok || info.Name != "redis-master" || info.Namespace != "test" { - t.Errorf("unexpected info: %#v", info) - } - info = test.Infos[1] - if _, ok := info.Object.(*api.Pod); !ok || info.Name != "nginx" || info.Namespace != "test" { - t.Errorf("unexpected info: %#v", info) - } -} - -func TestDirectoryBuilder(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - FilenameParam(false, "../../../examples/guestbook"). - NamespaceParam("test").DefaultNamespace() - - test := &testVisitor{} - singular := false - - err := b.Do().IntoSingular(&singular).Visit(test.Handle) - if err != nil || singular || len(test.Infos) < 4 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos) - } - - found := false - for _, info := range test.Infos { - if info.Name == "redis-master" && info.Namespace == "test" && info.Object != nil { - found = true - } - } - if !found { - t.Errorf("unexpected responses: %#v", test.Infos) - } -} - -func TestNamespaceOverride(t *testing.T) { - s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusOK) - w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), &api.Pod{ObjectMeta: api.ObjectMeta{Namespace: "foo", Name: "test"}}))) - })) - // TODO: Uncomment when fix #19254 - // defer s.Close() - - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - FilenameParam(false, s.URL). - NamespaceParam("test") - - test := &testVisitor{} - - err := b.Do().Visit(test.Handle) - if err != nil || len(test.Infos) != 1 && test.Infos[0].Namespace != "foo" { - t.Fatalf("unexpected response: %v %#v", err, test.Infos) - } - - b = NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - FilenameParam(true, s.URL). - NamespaceParam("test") - - test = &testVisitor{} - - err = b.Do().Visit(test.Handle) - if err == nil { - t.Fatalf("expected namespace error. got: %#v", test.Infos) - } -} - -func TestURLBuilder(t *testing.T) { - s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusOK) - w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), &api.Pod{ObjectMeta: api.ObjectMeta{Namespace: "foo", Name: "test"}}))) - })) - // TODO: Uncomment when fix #19254 - // defer s.Close() - - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - FilenameParam(false, s.URL). - NamespaceParam("test") - - test := &testVisitor{} - singular := false - - err := b.Do().IntoSingular(&singular).Visit(test.Handle) - if err != nil || !singular || len(test.Infos) != 1 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos) - } - info := test.Infos[0] - if info.Name != "test" || info.Namespace != "foo" || info.Object == nil { - t.Errorf("unexpected info: %#v", info) - } -} - -func TestURLBuilderRequireNamespace(t *testing.T) { - s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusOK) - w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), &api.Pod{ObjectMeta: api.ObjectMeta{Namespace: "foo", Name: "test"}}))) - })) - // TODO: Uncomment when fix #19254 - // defer s.Close() - - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - FilenameParam(false, s.URL). - NamespaceParam("test").RequireNamespace() - - test := &testVisitor{} - singular := false - - err := b.Do().IntoSingular(&singular).Visit(test.Handle) - if err == nil || !singular || len(test.Infos) != 0 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos) - } -} - -func TestResourceByName(t *testing.T) { - pods, _ := testData() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ - "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]), - }), testapi.Default.Codec()). - NamespaceParam("test") - - test := &testVisitor{} - singular := false - - if b.Do().Err() == nil { - t.Errorf("unexpected non-error") - } - - b.ResourceTypeOrNameArgs(true, "pods", "foo") - - err := b.Do().IntoSingular(&singular).Visit(test.Handle) - if err != nil || !singular || len(test.Infos) != 1 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos) - } - if !reflect.DeepEqual(&pods.Items[0], test.Objects()[0]) { - t.Errorf("unexpected object: %#v", test.Objects()[0]) - } - - mapping, err := b.Do().ResourceMapping() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if mapping.Resource != "pods" { - t.Errorf("unexpected resource mapping: %#v", mapping) - } -} - -func TestMultipleResourceByTheSameName(t *testing.T) { - pods, svcs := testData() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ - "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]), - "/namespaces/test/pods/baz": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[1]), - "/namespaces/test/services/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &svcs.Items[0]), - "/namespaces/test/services/baz": runtime.EncodeOrDie(testapi.Default.Codec(), &svcs.Items[0]), - }), testapi.Default.Codec()). - NamespaceParam("test") - - test := &testVisitor{} - singular := false - - if b.Do().Err() == nil { - t.Errorf("unexpected non-error") - } - - b.ResourceTypeOrNameArgs(true, "pods,services", "foo", "baz") - - err := b.Do().IntoSingular(&singular).Visit(test.Handle) - if err != nil || singular || len(test.Infos) != 4 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos) - } - if !api.Semantic.DeepDerivative([]runtime.Object{&pods.Items[0], &pods.Items[1], &svcs.Items[0], &svcs.Items[0]}, test.Objects()) { - t.Errorf("unexpected visited objects: %#v", test.Objects()) - } - - if _, err := b.Do().ResourceMapping(); err == nil { - t.Errorf("unexpected non-error") - } -} - -func TestResourceNames(t *testing.T) { - pods, svc := testData() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ - "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]), - "/namespaces/test/services/baz": runtime.EncodeOrDie(testapi.Default.Codec(), &svc.Items[0]), - }), testapi.Default.Codec()). - NamespaceParam("test") - - test := &testVisitor{} - - if b.Do().Err() == nil { - t.Errorf("unexpected non-error") - } - - b.ResourceNames("pods", "foo", "services/baz") - - err := b.Do().Visit(test.Handle) - if err != nil || len(test.Infos) != 2 { - t.Fatalf("unexpected response: %v %#v", err, test.Infos) - } - if !reflect.DeepEqual(&pods.Items[0], test.Objects()[0]) { - t.Errorf("unexpected object: \n%#v, expected: \n%#v", test.Objects()[0], &pods.Items[0]) - } - if !reflect.DeepEqual(&svc.Items[0], test.Objects()[1]) { - t.Errorf("unexpected object: \n%#v, expected: \n%#v", test.Objects()[1], &svc.Items[0]) - } -} - -func TestResourceByNameWithoutRequireObject(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{}), testapi.Default.Codec()). - NamespaceParam("test") - - test := &testVisitor{} - singular := false - - if b.Do().Err() == nil { - t.Errorf("unexpected non-error") - } - - b.ResourceTypeOrNameArgs(true, "pods", "foo").RequireObject(false) - - err := b.Do().IntoSingular(&singular).Visit(test.Handle) - if err != nil || !singular || len(test.Infos) != 1 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos) - } - if test.Infos[0].Name != "foo" { - t.Errorf("unexpected name: %#v", test.Infos[0].Name) - } - if test.Infos[0].Object != nil { - t.Errorf("unexpected object: %#v", test.Infos[0].Object) - } - - mapping, err := b.Do().ResourceMapping() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if mapping.GroupVersionKind.Kind != "Pod" || mapping.Resource != "pods" { - t.Errorf("unexpected resource mapping: %#v", mapping) - } -} - -func TestResourceByNameAndEmptySelector(t *testing.T) { - pods, _ := testData() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ - "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]), - }), testapi.Default.Codec()). - NamespaceParam("test"). - SelectorParam(""). - ResourceTypeOrNameArgs(true, "pods", "foo") - - singular := false - infos, err := b.Do().IntoSingular(&singular).Infos() - if err != nil || !singular || len(infos) != 1 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, infos) - } - if !reflect.DeepEqual(&pods.Items[0], infos[0].Object) { - t.Errorf("unexpected object: %#v", infos[0]) - } - - mapping, err := b.Do().ResourceMapping() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if mapping.Resource != "pods" { - t.Errorf("unexpected resource mapping: %#v", mapping) - } -} - -func TestSelector(t *testing.T) { - pods, svc := testData() - labelKey := unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String()) - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ - "/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), pods), - "/namespaces/test/services?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), svc), - }), testapi.Default.Codec()). - SelectorParam("a=b"). - NamespaceParam("test"). - Flatten() - - test := &testVisitor{} - singular := false - - if b.Do().Err() == nil { - t.Errorf("unexpected non-error") - } - - b.ResourceTypeOrNameArgs(true, "pods,service") - - err := b.Do().IntoSingular(&singular).Visit(test.Handle) - if err != nil || singular || len(test.Infos) != 3 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos) - } - if !api.Semantic.DeepDerivative([]runtime.Object{&pods.Items[0], &pods.Items[1], &svc.Items[0]}, test.Objects()) { - t.Errorf("unexpected visited objects: %#v", test.Objects()) - } - - if _, err := b.Do().ResourceMapping(); err == nil { - t.Errorf("unexpected non-error") - } -} - -func TestSelectorRequiresKnownTypes(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - SelectorParam("a=b"). - NamespaceParam("test"). - ResourceTypes("unknown") - - if b.Do().Err() == nil { - t.Errorf("unexpected non-error") - } -} - -func TestSingleResourceType(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - SelectorParam("a=b"). - SingleResourceType(). - ResourceTypeOrNameArgs(true, "pods,services") - - if b.Do().Err() == nil { - t.Errorf("unexpected non-error") - } -} - -func TestResourceTuple(t *testing.T) { - expectNoErr := func(err error) bool { return err == nil } - expectErr := func(err error) bool { return err != nil } - testCases := map[string]struct { - args []string - errFn func(error) bool - }{ - "valid": { - args: []string{"pods/foo"}, - errFn: expectNoErr, - }, - "valid multiple with name indirection": { - args: []string{"pods/foo", "pod/bar"}, - errFn: expectNoErr, - }, - "valid multiple with namespaced and non-namespaced types": { - args: []string{"nodes/foo", "pod/bar"}, - errFn: expectNoErr, - }, - "mixed arg types": { - args: []string{"pods/foo", "bar"}, - errFn: expectErr, - }, - /*"missing resource": { - args: []string{"pods/foo2"}, - errFn: expectNoErr, // not an error because resources are lazily visited - },*/ - "comma in resource": { - args: []string{",pods/foo"}, - errFn: expectErr, - }, - "multiple types in resource": { - args: []string{"pods,services/foo"}, - errFn: expectErr, - }, - "unknown resource type": { - args: []string{"unknown/foo"}, - errFn: expectErr, - }, - "leading slash": { - args: []string{"/bar"}, - errFn: expectErr, - }, - "trailing slash": { - args: []string{"bar/"}, - errFn: expectErr, - }, - } - for k, testCase := range testCases { - for _, requireObject := range []bool{true, false} { - expectedRequests := map[string]string{} - if requireObject { - pods, _ := testData() - expectedRequests = map[string]string{ - "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]), - "/namespaces/test/pods/bar": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]), - "/nodes/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &api.Node{ObjectMeta: api.ObjectMeta{Name: "foo"}}), - } - } - - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith(k, t, expectedRequests), testapi.Default.Codec()). - NamespaceParam("test").DefaultNamespace(). - ResourceTypeOrNameArgs(true, testCase.args...).RequireObject(requireObject) - - r := b.Do() - - if !testCase.errFn(r.Err()) { - t.Errorf("%s: unexpected error: %v", k, r.Err()) - } - if r.Err() != nil { - continue - } - switch { - case (r.singular && len(testCase.args) != 1), - (!r.singular && len(testCase.args) == 1): - t.Errorf("%s: result had unexpected singular value", k) - } - info, err := r.Infos() - if err != nil { - // test error - continue - } - if len(info) != len(testCase.args) { - t.Errorf("%s: unexpected number of infos returned: %#v", k, info) - } - } - } -} - -func TestStream(t *testing.T) { - r, pods, rc := streamTestData() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - NamespaceParam("test").Stream(r, "STDIN").Flatten() - - test := &testVisitor{} - singular := false - - err := b.Do().IntoSingular(&singular).Visit(test.Handle) - if err != nil || singular || len(test.Infos) != 3 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos) - } - if !api.Semantic.DeepDerivative([]runtime.Object{&pods.Items[0], &pods.Items[1], &rc.Items[0]}, test.Objects()) { - t.Errorf("unexpected visited objects: %#v", test.Objects()) - } -} - -func TestYAMLStream(t *testing.T) { - r, pods, rc := streamYAMLTestData() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - NamespaceParam("test").Stream(r, "STDIN").Flatten() - - test := &testVisitor{} - singular := false - - err := b.Do().IntoSingular(&singular).Visit(test.Handle) - if err != nil || singular || len(test.Infos) != 3 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos) - } - if !api.Semantic.DeepDerivative([]runtime.Object{&pods.Items[0], &pods.Items[1], &rc.Items[0]}, test.Objects()) { - t.Errorf("unexpected visited objects: %#v", test.Objects()) - } -} - -func TestMultipleObject(t *testing.T) { - r, pods, svc := streamTestData() - obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - NamespaceParam("test").Stream(r, "STDIN").Flatten(). - Do().Object() - - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - expected := &api.List{ - Items: []runtime.Object{ - &pods.Items[0], - &pods.Items[1], - &svc.Items[0], - }, - } - if !api.Semantic.DeepDerivative(expected, obj) { - t.Errorf("unexpected visited objects: %#v", obj) - } -} - -func TestContinueOnErrorVisitor(t *testing.T) { - r, _, _ := streamTestData() - req := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - ContinueOnError(). - NamespaceParam("test").Stream(r, "STDIN").Flatten(). - Do() - count := 0 - testErr := fmt.Errorf("test error") - err := req.Visit(func(_ *Info, _ error) error { - count++ - if count > 1 { - return testErr - } - return nil - }) - if err == nil { - t.Fatalf("unexpected error: %v", err) - } - if count != 3 { - t.Fatalf("did not visit all infos: %d", count) - } - agg, ok := err.(utilerrors.Aggregate) - if !ok { - t.Fatalf("unexpected error: %v", err) - } - if len(agg.Errors()) != 2 || agg.Errors()[0] != testErr || agg.Errors()[1] != testErr { - t.Fatalf("unexpected error: %v", err) - } -} - -func TestSingularObject(t *testing.T) { - obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - NamespaceParam("test").DefaultNamespace(). - FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml"). - Flatten(). - Do().Object() - - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - rc, ok := obj.(*api.ReplicationController) - if !ok { - t.Fatalf("unexpected object: %#v", obj) - } - if rc.Name != "redis-master" || rc.Namespace != "test" { - t.Errorf("unexpected controller: %#v", rc) - } -} - -func TestSingularObjectNoExtension(t *testing.T) { - obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - NamespaceParam("test").DefaultNamespace(). - FilenameParam(false, "../../../examples/pod"). - Flatten(). - Do().Object() - - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - pod, ok := obj.(*api.Pod) - if !ok { - t.Fatalf("unexpected object: %#v", obj) - } - if pod.Name != "nginx" || pod.Namespace != "test" { - t.Errorf("unexpected pod: %#v", pod) - } -} - -func TestSingularRootScopedObject(t *testing.T) { - node := &api.Node{ObjectMeta: api.ObjectMeta{Name: "test"}, Spec: api.NodeSpec{ExternalID: "test"}} - r := streamTestObject(node) - infos, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - NamespaceParam("test").DefaultNamespace(). - Stream(r, "STDIN"). - Flatten(). - Do().Infos() - - if err != nil || len(infos) != 1 { - t.Fatalf("unexpected error: %v", err) - } - - if infos[0].Namespace != "" { - t.Errorf("namespace should be empty: %#v", infos[0]) - } - n, ok := infos[0].Object.(*api.Node) - if !ok { - t.Fatalf("unexpected object: %#v", infos[0].Object) - } - if n.Name != "test" || n.Namespace != "" { - t.Errorf("unexpected object: %#v", n) - } -} - -func TestListObject(t *testing.T) { - pods, _ := testData() - labelKey := unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String()) - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ - "/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), pods), - }), testapi.Default.Codec()). - SelectorParam("a=b"). - NamespaceParam("test"). - ResourceTypeOrNameArgs(true, "pods"). - Flatten() - - obj, err := b.Do().Object() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - list, ok := obj.(*api.List) - if !ok { - t.Fatalf("unexpected object: %#v", obj) - } - if list.ResourceVersion != pods.ResourceVersion || len(list.Items) != 2 { - t.Errorf("unexpected list: %#v", list) - } - - mapping, err := b.Do().ResourceMapping() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if mapping.Resource != "pods" { - t.Errorf("unexpected resource mapping: %#v", mapping) - } -} - -func TestListObjectWithDifferentVersions(t *testing.T) { - pods, svc := testData() - labelKey := unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String()) - obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ - "/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), pods), - "/namespaces/test/services?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), svc), - }), testapi.Default.Codec()). - SelectorParam("a=b"). - NamespaceParam("test"). - ResourceTypeOrNameArgs(true, "pods,services"). - Flatten(). - Do().Object() - - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - list, ok := obj.(*api.List) - if !ok { - t.Fatalf("unexpected object: %#v", obj) - } - // resource version differs between type lists, so it's not possible to get a single version. - if list.ResourceVersion != "" || len(list.Items) != 3 { - t.Errorf("unexpected list: %#v", list) - } -} - -func TestWatch(t *testing.T) { - _, svc := testData() - w, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ - "/watch/namespaces/test/services/redis-master?resourceVersion=12": watchBody(watch.Event{ - Type: watch.Added, - Object: &svc.Items[0], - }), - }), testapi.Default.Codec()). - NamespaceParam("test").DefaultNamespace(). - FilenameParam(false, "../../../examples/guestbook/redis-master-service.yaml").Flatten(). - Do().Watch("12") - - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - defer w.Stop() - ch := w.ResultChan() - select { - case obj := <-ch: - if obj.Type != watch.Added { - t.Fatalf("unexpected watch event %#v", obj) - } - service, ok := obj.Object.(*api.Service) - if !ok { - t.Fatalf("unexpected object: %#v", obj) - } - if service.Name != "baz" || service.ResourceVersion != "12" { - t.Errorf("unexpected service: %#v", service) - } - } -} - -func TestWatchMultipleError(t *testing.T) { - _, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - NamespaceParam("test").DefaultNamespace(). - FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten(). - FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten(). - Do().Watch("") - - if err == nil { - t.Fatalf("unexpected non-error") - } -} - -func TestLatest(t *testing.T) { - r, _, _ := streamTestData() - newPod := &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "13"}, - } - newPod2 := &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "14"}, - } - newSvc := &api.Service{ - ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "15"}, - } - - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ - "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), newPod), - "/namespaces/test/pods/bar": runtime.EncodeOrDie(testapi.Default.Codec(), newPod2), - "/namespaces/test/services/baz": runtime.EncodeOrDie(testapi.Default.Codec(), newSvc), - }), testapi.Default.Codec()). - NamespaceParam("other").Stream(r, "STDIN").Flatten().Latest() - - test := &testVisitor{} - singular := false - - err := b.Do().IntoSingular(&singular).Visit(test.Handle) - if err != nil || singular || len(test.Infos) != 3 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos) - } - if !api.Semantic.DeepDerivative([]runtime.Object{newPod, newPod2, newSvc}, test.Objects()) { - t.Errorf("unexpected visited objects: %#v", test.Objects()) - } -} - -func TestReceiveMultipleErrors(t *testing.T) { - pods, svc := testData() - - r, w := io.Pipe() - go func() { - defer w.Close() - w.Write([]byte(`{}`)) - w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]))) - }() - - r2, w2 := io.Pipe() - go func() { - defer w2.Close() - w2.Write([]byte(`{}`)) - w2.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), &svc.Items[0]))) - }() - - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). - Stream(r, "1").Stream(r2, "2"). - ContinueOnError() - - test := &testVisitor{} - singular := false - - err := b.Do().IntoSingular(&singular).Visit(test.Handle) - if err == nil || singular || len(test.Infos) != 2 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos) - } - - errs, ok := err.(utilerrors.Aggregate) - if !ok { - t.Fatalf("unexpected error: %v", reflect.TypeOf(err)) - } - if len(errs.Errors()) != 2 { - t.Errorf("unexpected errors %v", errs) - } -} - -func TestReplaceAliases(t *testing.T) { - tests := []struct { - name string - arg string - expected string - }{ - { - name: "no-replacement", - arg: "service", - expected: "service", - }, - { - name: "all-replacement", - arg: "all", - expected: "rc,svc,pods,pvc", - }, - { - name: "alias-in-comma-separated-arg", - arg: "all,secrets", - expected: "rc,svc,pods,pvc,secrets", - }, - } - - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()) - - for _, test := range tests { - replaced := b.replaceAliases(test.arg) - if replaced != test.expected { - t.Errorf("%s: unexpected argument: expected %s, got %s", test.name, test.expected, replaced) - } - } -} - -func TestHasNames(t *testing.T) { - tests := []struct { - args []string - expectedHasName bool - expectedError error - }{ - { - args: []string{""}, - expectedHasName: false, - expectedError: nil, - }, - { - args: []string{"rc"}, - expectedHasName: false, - expectedError: nil, - }, - { - args: []string{"rc,pod,svc"}, - expectedHasName: false, - expectedError: nil, - }, - { - args: []string{"rc/foo"}, - expectedHasName: true, - expectedError: nil, - }, - { - args: []string{"rc", "foo"}, - expectedHasName: true, - expectedError: nil, - }, - { - args: []string{"rc,pod,svc", "foo"}, - expectedHasName: true, - expectedError: nil, - }, - { - args: []string{"rc/foo", "rc/bar", "rc/zee"}, - expectedHasName: true, - expectedError: nil, - }, - { - args: []string{"rc/foo", "bar"}, - expectedHasName: false, - expectedError: fmt.Errorf("when passing arguments in resource/name form, all arguments must include the resource"), - }, - } - for _, test := range tests { - hasNames, err := HasNames(test.args) - if !reflect.DeepEqual(test.expectedError, err) { - t.Errorf("expected HasName to error %v, got %s", test.expectedError, err) - } - if hasNames != test.expectedHasName { - t.Errorf("expected HasName to return %v for %s", test.expectedHasName, test.args) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/helper_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/helper_test.go deleted file mode 100644 index bfb05f7f7..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/helper_test.go +++ /dev/null @@ -1,498 +0,0 @@ -/* -Copyright 2014 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 resource - -import ( - "bytes" - "errors" - "io" - "io/ioutil" - "net/http" - "reflect" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - apitesting "k8s.io/kubernetes/pkg/api/testing" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/fake" - "k8s.io/kubernetes/pkg/labels" - "k8s.io/kubernetes/pkg/runtime" -) - -func objBody(obj runtime.Object) io.ReadCloser { - return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), obj)))) -} - -// splitPath returns the segments for a URL path. -func splitPath(path string) []string { - path = strings.Trim(path, "/") - if path == "" { - return []string{} - } - return strings.Split(path, "/") -} - -func TestHelperDelete(t *testing.T) { - tests := []struct { - Err bool - Req func(*http.Request) bool - Resp *http.Response - HttpErr error - }{ - { - HttpErr: errors.New("failure"), - Err: true, - }, - { - Resp: &http.Response{ - StatusCode: http.StatusNotFound, - Body: objBody(&unversioned.Status{Status: unversioned.StatusFailure}), - }, - Err: true, - }, - { - Resp: &http.Response{ - StatusCode: http.StatusOK, - Body: objBody(&unversioned.Status{Status: unversioned.StatusSuccess}), - }, - Req: func(req *http.Request) bool { - if req.Method != "DELETE" { - t.Errorf("unexpected method: %#v", req) - return false - } - parts := splitPath(req.URL.Path) - if len(parts) < 3 { - t.Errorf("expected URL path to have 3 parts: %s", req.URL.Path) - return false - } - if parts[1] != "bar" { - t.Errorf("url doesn't contain namespace: %#v", req) - return false - } - if parts[2] != "foo" { - t.Errorf("url doesn't contain name: %#v", req) - return false - } - return true - }, - }, - } - for _, test := range tests { - client := &fake.RESTClient{ - Codec: testapi.Default.Codec(), - Resp: test.Resp, - Err: test.HttpErr, - } - modifier := &Helper{ - RESTClient: client, - NamespaceScoped: true, - } - err := modifier.Delete("bar", "foo") - if (err != nil) != test.Err { - t.Errorf("unexpected error: %t %v", test.Err, err) - } - if err != nil { - continue - } - if test.Req != nil && !test.Req(client.Req) { - t.Errorf("unexpected request: %#v", client.Req) - } - } -} - -func TestHelperCreate(t *testing.T) { - expectPost := func(req *http.Request) bool { - if req.Method != "POST" { - t.Errorf("unexpected method: %#v", req) - return false - } - parts := splitPath(req.URL.Path) - if parts[1] != "bar" { - t.Errorf("url doesn't contain namespace: %#v", req) - return false - } - return true - } - - tests := []struct { - Resp *http.Response - HttpErr error - Modify bool - Object runtime.Object - - ExpectObject runtime.Object - Err bool - Req func(*http.Request) bool - }{ - { - HttpErr: errors.New("failure"), - Err: true, - }, - { - Resp: &http.Response{ - StatusCode: http.StatusNotFound, - Body: objBody(&unversioned.Status{Status: unversioned.StatusFailure}), - }, - Err: true, - }, - { - Resp: &http.Response{ - StatusCode: http.StatusOK, - Body: objBody(&unversioned.Status{Status: unversioned.StatusSuccess}), - }, - Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, - ExpectObject: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, - Req: expectPost, - }, - { - Modify: false, - Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}}, - ExpectObject: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}}, - Resp: &http.Response{StatusCode: http.StatusOK, Body: objBody(&unversioned.Status{Status: unversioned.StatusSuccess})}, - Req: expectPost, - }, - { - Modify: true, - Object: &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}, - Spec: apitesting.DeepEqualSafePodSpec(), - }, - ExpectObject: &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: apitesting.DeepEqualSafePodSpec(), - }, - Resp: &http.Response{StatusCode: http.StatusOK, Body: objBody(&unversioned.Status{Status: unversioned.StatusSuccess})}, - Req: expectPost, - }, - } - for i, test := range tests { - client := &fake.RESTClient{ - Codec: testapi.Default.Codec(), - Resp: test.Resp, - Err: test.HttpErr, - } - modifier := &Helper{ - RESTClient: client, - Versioner: testapi.Default.MetadataAccessor(), - NamespaceScoped: true, - } - _, err := modifier.Create("bar", test.Modify, test.Object) - if (err != nil) != test.Err { - t.Errorf("%d: unexpected error: %t %v", i, test.Err, err) - } - if err != nil { - continue - } - if test.Req != nil && !test.Req(client.Req) { - t.Errorf("%d: unexpected request: %#v", i, client.Req) - } - body, err := ioutil.ReadAll(client.Req.Body) - if err != nil { - t.Fatalf("%d: unexpected error: %#v", i, err) - } - t.Logf("got body: %s", string(body)) - expect := []byte{} - if test.ExpectObject != nil { - expect = []byte(runtime.EncodeOrDie(testapi.Default.Codec(), test.ExpectObject)) - } - if !reflect.DeepEqual(expect, body) { - t.Errorf("%d: unexpected body: %s (expected %s)", i, string(body), string(expect)) - } - - } -} - -func TestHelperGet(t *testing.T) { - tests := []struct { - Err bool - Req func(*http.Request) bool - Resp *http.Response - HttpErr error - }{ - { - HttpErr: errors.New("failure"), - Err: true, - }, - { - Resp: &http.Response{ - StatusCode: http.StatusNotFound, - Body: objBody(&unversioned.Status{Status: unversioned.StatusFailure}), - }, - Err: true, - }, - { - Resp: &http.Response{ - StatusCode: http.StatusOK, - Body: objBody(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}), - }, - Req: func(req *http.Request) bool { - if req.Method != "GET" { - t.Errorf("unexpected method: %#v", req) - return false - } - parts := splitPath(req.URL.Path) - if parts[1] != "bar" { - t.Errorf("url doesn't contain namespace: %#v", req) - return false - } - if parts[2] != "foo" { - t.Errorf("url doesn't contain name: %#v", req) - return false - } - return true - }, - }, - } - for _, test := range tests { - client := &fake.RESTClient{ - Codec: testapi.Default.Codec(), - Resp: test.Resp, - Err: test.HttpErr, - } - modifier := &Helper{ - RESTClient: client, - NamespaceScoped: true, - } - obj, err := modifier.Get("bar", "foo", false) - if (err != nil) != test.Err { - t.Errorf("unexpected error: %t %v", test.Err, err) - } - if err != nil { - continue - } - if obj.(*api.Pod).Name != "foo" { - t.Errorf("unexpected object: %#v", obj) - } - if test.Req != nil && !test.Req(client.Req) { - t.Errorf("unexpected request: %#v", client.Req) - } - } -} - -func TestHelperList(t *testing.T) { - tests := []struct { - Err bool - Req func(*http.Request) bool - Resp *http.Response - HttpErr error - }{ - { - HttpErr: errors.New("failure"), - Err: true, - }, - { - Resp: &http.Response{ - StatusCode: http.StatusNotFound, - Body: objBody(&unversioned.Status{Status: unversioned.StatusFailure}), - }, - Err: true, - }, - { - Resp: &http.Response{ - StatusCode: http.StatusOK, - Body: objBody(&api.PodList{ - Items: []api.Pod{{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - }, - }, - }), - }, - Req: func(req *http.Request) bool { - if req.Method != "GET" { - t.Errorf("unexpected method: %#v", req) - return false - } - if req.URL.Path != "/namespaces/bar" { - t.Errorf("url doesn't contain name: %#v", req.URL) - return false - } - if req.URL.Query().Get(unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String())) != labels.SelectorFromSet(labels.Set{"foo": "baz"}).String() { - t.Errorf("url doesn't contain query parameters: %#v", req.URL) - return false - } - return true - }, - }, - } - for _, test := range tests { - client := &fake.RESTClient{ - Codec: testapi.Default.Codec(), - Resp: test.Resp, - Err: test.HttpErr, - } - modifier := &Helper{ - RESTClient: client, - NamespaceScoped: true, - } - obj, err := modifier.List("bar", testapi.Default.GroupVersion().String(), labels.SelectorFromSet(labels.Set{"foo": "baz"}), false) - if (err != nil) != test.Err { - t.Errorf("unexpected error: %t %v", test.Err, err) - } - if err != nil { - continue - } - if obj.(*api.PodList).Items[0].Name != "foo" { - t.Errorf("unexpected object: %#v", obj) - } - if test.Req != nil && !test.Req(client.Req) { - t.Errorf("unexpected request: %#v", client.Req) - } - } -} - -func TestHelperReplace(t *testing.T) { - expectPut := func(path string, req *http.Request) bool { - if req.Method != "PUT" { - t.Errorf("unexpected method: %#v", req) - return false - } - if req.URL.Path != path { - t.Errorf("unexpected url: %v", req.URL) - return false - } - return true - } - - tests := []struct { - Resp *http.Response - HTTPClient *http.Client - HttpErr error - Overwrite bool - Object runtime.Object - Namespace string - NamespaceScoped bool - - ExpectPath string - ExpectObject runtime.Object - Err bool - Req func(string, *http.Request) bool - }{ - { - Namespace: "bar", - NamespaceScoped: true, - HttpErr: errors.New("failure"), - Err: true, - }, - { - Namespace: "bar", - NamespaceScoped: true, - Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, - Resp: &http.Response{ - StatusCode: http.StatusNotFound, - Body: objBody(&unversioned.Status{Status: unversioned.StatusFailure}), - }, - Err: true, - }, - { - Namespace: "bar", - NamespaceScoped: true, - Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, - ExpectPath: "/namespaces/bar/foo", - ExpectObject: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}, - Resp: &http.Response{ - StatusCode: http.StatusOK, - Body: objBody(&unversioned.Status{Status: unversioned.StatusSuccess}), - }, - Req: expectPut, - }, - // namespace scoped resource - { - Namespace: "bar", - NamespaceScoped: true, - Object: &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - Spec: apitesting.DeepEqualSafePodSpec(), - }, - ExpectPath: "/namespaces/bar/foo", - ExpectObject: &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}, - Spec: apitesting.DeepEqualSafePodSpec(), - }, - Overwrite: true, - HTTPClient: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - if req.Method == "PUT" { - return &http.Response{StatusCode: http.StatusOK, Body: objBody(&unversioned.Status{Status: unversioned.StatusSuccess})}, nil - } - return &http.Response{StatusCode: http.StatusOK, Body: objBody(&api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}})}, nil - }), - Req: expectPut, - }, - // cluster scoped resource - { - Object: &api.Node{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - }, - ExpectObject: &api.Node{ - ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}, - }, - Overwrite: true, - ExpectPath: "/foo", - HTTPClient: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - if req.Method == "PUT" { - return &http.Response{StatusCode: http.StatusOK, Body: objBody(&unversioned.Status{Status: unversioned.StatusSuccess})}, nil - } - return &http.Response{StatusCode: http.StatusOK, Body: objBody(&api.Node{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}})}, nil - }), - Req: expectPut, - }, - { - Namespace: "bar", - NamespaceScoped: true, - Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}}, - ExpectPath: "/namespaces/bar/foo", - ExpectObject: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}}, - Resp: &http.Response{StatusCode: http.StatusOK, Body: objBody(&unversioned.Status{Status: unversioned.StatusSuccess})}, - Req: expectPut, - }, - } - for i, test := range tests { - client := &fake.RESTClient{ - Client: test.HTTPClient, - Codec: testapi.Default.Codec(), - Resp: test.Resp, - Err: test.HttpErr, - } - modifier := &Helper{ - RESTClient: client, - Versioner: testapi.Default.MetadataAccessor(), - NamespaceScoped: test.NamespaceScoped, - } - _, err := modifier.Replace(test.Namespace, "foo", test.Overwrite, test.Object) - if (err != nil) != test.Err { - t.Errorf("%d: unexpected error: %t %v", i, test.Err, err) - } - if err != nil { - continue - } - if test.Req != nil && !test.Req(test.ExpectPath, client.Req) { - t.Errorf("%d: unexpected request: %#v", i, client.Req) - } - body, err := ioutil.ReadAll(client.Req.Body) - if err != nil { - t.Fatalf("%d: unexpected error: %#v", i, err) - } - expect := []byte{} - if test.ExpectObject != nil { - expect = []byte(runtime.EncodeOrDie(testapi.Default.Codec(), test.ExpectObject)) - } - if !reflect.DeepEqual(expect, body) { - t.Errorf("%d: unexpected body: %s", i, string(body)) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/interfaces.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/interfaces.go index 54d20dfbf..2639a61ec 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/interfaces.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/interfaces.go @@ -19,7 +19,7 @@ package resource import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" - client "k8s.io/kubernetes/pkg/client/unversioned" + client "k8s.io/kubernetes/pkg/client/restclient" ) // RESTClient is a client helper for dealing with RESTful resources diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/mapper.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/mapper.go index 0839ca4a8..25fd97d90 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/mapper.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/mapper.go @@ -21,6 +21,7 @@ import ( "reflect" "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" ) @@ -85,11 +86,17 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) { // InfoForObject creates an Info object for the given Object. An error is returned // if the object cannot be introspected. Name and namespace will be set into Info // if the mapping's MetadataAccessor can retrieve them. -func (m *Mapper) InfoForObject(obj runtime.Object) (*Info, error) { - groupVersionKind, err := m.ObjectKind(obj) +func (m *Mapper) InfoForObject(obj runtime.Object, preferredGVKs []unversioned.GroupVersionKind) (*Info, error) { + groupVersionKinds, err := m.ObjectKinds(obj) if err != nil { return nil, fmt.Errorf("unable to get type info from the object %q: %v", reflect.TypeOf(obj), err) } + + groupVersionKind := groupVersionKinds[0] + if len(groupVersionKinds) > 1 && len(preferredGVKs) > 0 { + groupVersionKind = preferredObjectKind(groupVersionKinds, preferredGVKs) + } + mapping, err := m.RESTMapping(groupVersionKind.GroupKind(), groupVersionKind.Version) if err != nil { return nil, fmt.Errorf("unable to recognize %v: %v", groupVersionKind, err) @@ -111,3 +118,39 @@ func (m *Mapper) InfoForObject(obj runtime.Object) (*Info, error) { ResourceVersion: resourceVersion, }, nil } + +// preferredObjectKind picks the possibility that most closely matches the priority list in this order: +// GroupVersionKind matches (exact match) +// GroupKind matches +// Group matches +func preferredObjectKind(possibilities []unversioned.GroupVersionKind, preferences []unversioned.GroupVersionKind) unversioned.GroupVersionKind { + // Exact match + for _, priority := range preferences { + for _, possibility := range possibilities { + if possibility == priority { + return possibility + } + } + } + + // GroupKind match + for _, priority := range preferences { + for _, possibility := range possibilities { + if possibility.GroupKind() == priority.GroupKind() { + return possibility + } + } + } + + // Group match + for _, priority := range preferences { + for _, possibility := range possibilities { + if possibility.Group == priority.Group { + return possibility + } + } + } + + // Just pick the first + return possibilities[0] +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/visitor.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/visitor.go index a187919a4..76569389c 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/visitor.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource/visitor.go @@ -20,13 +20,13 @@ import ( "bytes" "fmt" "io" - "io/ioutil" "net/http" "net/url" "os" "path/filepath" "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/runtime" utilerrors "k8s.io/kubernetes/pkg/util/errors" @@ -219,9 +219,8 @@ func ValidateSchema(data []byte, schema validation.Schema) error { // URLVisitor downloads the contents of a URL, and if successful, returns // an info object representing the downloaded object. type URLVisitor struct { - *Mapper - URL *url.URL - Schema validation.Schema + URL *url.URL + *StreamVisitor } func (v *URLVisitor) Visit(fn VisitorFunc) error { @@ -233,18 +232,9 @@ func (v *URLVisitor) Visit(fn VisitorFunc) error { if res.StatusCode != 200 { return fmt.Errorf("unable to read URL %q, server reported %d %s", v.URL, res.StatusCode, res.Status) } - data, err := ioutil.ReadAll(res.Body) - if err != nil { - return fmt.Errorf("unable to read URL %q: %v\n", v.URL, err) - } - if err := ValidateSchema(data, v.Schema); err != nil { - return fmt.Errorf("error validating %q: %v", v.URL, err) - } - info, err := v.Mapper.InfoForData(data, v.URL.String()) - if err != nil { - return err - } - return fn(info, nil) + + v.StreamVisitor.Reader = res.Body + return v.StreamVisitor.Visit(fn) } // DecoratedVisitor will invoke the decorators in order prior to invoking the visitor function @@ -348,8 +338,15 @@ func (v FlattenListVisitor) Visit(fn VisitorFunc) error { }{v.Mapper, v.Mapper.Decoder}); len(errs) > 0 { return utilerrors.NewAggregate(errs) } + + // If we have a GroupVersionKind on the list, prioritize that when asking for info on the objects contained in the list + var preferredGVKs []unversioned.GroupVersionKind + if info.Mapping != nil && !info.Mapping.GroupVersionKind.IsEmpty() { + preferredGVKs = append(preferredGVKs, info.Mapping.GroupVersionKind) + } + for i := range items { - item, err := v.InfoForObject(items[i]) + item, err := v.InfoForObject(items[i], preferredGVKs) if err != nil { return err } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource_printer.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource_printer.go index d33bb92f0..7f9d4ab0e 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource_printer.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource_printer.go @@ -396,14 +396,14 @@ func (h *HumanReadablePrinter) HandledResources() []string { // pkg/kubectl/cmd/get.go to reflect the new resource type. var podColumns = []string{"NAME", "READY", "STATUS", "RESTARTS", "AGE"} var podTemplateColumns = []string{"TEMPLATE", "CONTAINER(S)", "IMAGE(S)", "PODLABELS"} -var replicationControllerColumns = []string{"CONTROLLER", "REPLICAS", "AGE"} -var replicaSetColumns = []string{"CONTROLLER", "REPLICAS", "AGE"} -var jobColumns = []string{"JOB", "SUCCESSFUL"} +var replicationControllerColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"} +var replicaSetColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"} +var jobColumns = []string{"NAME", "DESIRED", "SUCCESSFUL", "AGE"} var serviceColumns = []string{"NAME", "CLUSTER-IP", "EXTERNAL-IP", "PORT(S)", "AGE"} -var ingressColumns = []string{"NAME", "RULE", "BACKEND", "ADDRESS"} +var ingressColumns = []string{"NAME", "RULE", "BACKEND", "ADDRESS", "AGE"} var endpointColumns = []string{"NAME", "ENDPOINTS", "AGE"} var nodeColumns = []string{"NAME", "STATUS", "AGE"} -var daemonSetColumns = []string{"NAME", "NODE-SELECTOR"} +var daemonSetColumns = []string{"NAME", "DESIRED", "CURRENT", "NODE-SELECTOR", "AGE"} var eventColumns = []string{"FIRSTSEEN", "LASTSEEN", "COUNT", "NAME", "KIND", "SUBOBJECT", "TYPE", "REASON", "SOURCE", "MESSAGE"} var limitRangeColumns = []string{"NAME", "AGE"} var resourceQuotaColumns = []string{"NAME", "AGE"} @@ -638,22 +638,19 @@ func printPodTemplate(pod *api.PodTemplate, w io.Writer, options PrintOptions) e namespace := pod.Namespace containers := pod.Template.Spec.Containers - var firstContainer api.Container - if len(containers) > 0 { - firstContainer, containers = containers[0], containers[1:] - } if options.WithNamespace { if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil { return err } } - if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s", - name, - firstContainer.Name, - firstContainer.Image, - labels.FormatLabels(pod.Template.Labels), - ); err != nil { + if _, err := fmt.Fprintf(w, "%s", name); err != nil { + return err + } + if err := layoutContainers(containers, w); err != nil { + return err + } + if _, err := fmt.Fprintf(w, "\t%s", labels.FormatLabels(pod.Template.Labels)); err != nil { return err } if _, err := fmt.Fprint(w, appendLabels(pod.Labels, options.ColumnLabels)); err != nil { @@ -663,20 +660,6 @@ func printPodTemplate(pod *api.PodTemplate, w io.Writer, options PrintOptions) e return err } - // Lay out all the other containers on separate lines. - extraLinePrefix := "\t" - if options.WithNamespace { - extraLinePrefix = "\t\t" - } - for _, container := range containers { - _, err := fmt.Fprintf(w, "%s%s\t%s\t%s", extraLinePrefix, container.Name, container.Image, "") - if err != nil { - return err - } - if _, err := fmt.Fprint(w, appendLabelTabs(options.ColumnLabels)); err != nil { - return err - } - } return nil } @@ -689,33 +672,34 @@ func printPodTemplateList(podList *api.PodTemplateList, w io.Writer, options Pri return nil } +// TODO(AdoHe): try to put wide output in a single method func printReplicationController(controller *api.ReplicationController, w io.Writer, options PrintOptions) error { name := controller.Name namespace := controller.Namespace containers := controller.Spec.Template.Spec.Containers - var firstContainer api.Container - if len(containers) > 0 { - firstContainer, containers = containers[0], containers[1:] - } if options.WithNamespace { if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil { return err } } - if _, err := fmt.Fprintf(w, "%s\t%d\t%s", + + desiredReplicas := controller.Spec.Replicas + currentReplicas := controller.Status.Replicas + if _, err := fmt.Fprintf(w, "%s\t%d\t%d\t%s", name, - controller.Spec.Replicas, + desiredReplicas, + currentReplicas, translateTimestamp(controller.CreationTimestamp), ); err != nil { return err } + if options.Wide { - if _, err := fmt.Fprintf(w, "\t%s\t%s\t%s", - firstContainer.Name, - firstContainer.Image, - labels.FormatLabels(controller.Spec.Selector), - ); err != nil { + if err := layoutContainers(containers, w); err != nil { + return err + } + if _, err := fmt.Fprintf(w, "\t%s", labels.FormatLabels(controller.Spec.Selector)); err != nil { return err } } @@ -726,20 +710,6 @@ func printReplicationController(controller *api.ReplicationController, w io.Writ return err } - // Lay out all the other containers on separate lines. - extraLinePrefix := "\t" - if options.WithNamespace { - extraLinePrefix = "\t\t" - } - for _, container := range containers { - _, err := fmt.Fprintf(w, "%s%s\t%s\t%s\t%s", extraLinePrefix, container.Name, container.Image, "", "") - if err != nil { - return err - } - if _, err := fmt.Fprint(w, appendLabelTabs(options.ColumnLabels)); err != nil { - return err - } - } return nil } @@ -756,29 +726,28 @@ func printReplicaSet(rs *extensions.ReplicaSet, w io.Writer, options PrintOption name := rs.Name namespace := rs.Namespace containers := rs.Spec.Template.Spec.Containers - var firstContainer api.Container - if len(containers) > 0 { - firstContainer, containers = containers[0], containers[1:] - } if options.WithNamespace { if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil { return err } } - if _, err := fmt.Fprintf(w, "%s\t%d\t%s", + + desiredReplicas := rs.Spec.Replicas + currentReplicas := rs.Status.Replicas + if _, err := fmt.Fprintf(w, "%s\t%d\t%d\t%s", name, - rs.Spec.Replicas, + desiredReplicas, + currentReplicas, translateTimestamp(rs.CreationTimestamp), ); err != nil { return err } if options.Wide { - if _, err := fmt.Fprintf(w, "\t%s\t%s\t%s", - firstContainer.Name, - firstContainer.Image, - unversioned.FormatLabelSelector(rs.Spec.Selector), - ); err != nil { + if err := layoutContainers(containers, w); err != nil { + return err + } + if _, err := fmt.Fprintf(w, "\t%s", unversioned.FormatLabelSelector(rs.Spec.Selector)); err != nil { return err } } @@ -789,20 +758,6 @@ func printReplicaSet(rs *extensions.ReplicaSet, w io.Writer, options PrintOption return err } - // Lay out all the other containers on separate lines. - extraLinePrefix := "\t" - if options.WithNamespace { - extraLinePrefix = "\t\t" - } - for _, container := range containers { - _, err := fmt.Fprintf(w, "%s%s\t%s\t%s\t%s", extraLinePrefix, container.Name, container.Image, "", "") - if err != nil { - return err - } - if _, err := fmt.Fprint(w, appendLabelTabs(options.ColumnLabels)); err != nil { - return err - } - } return nil } @@ -819,31 +774,44 @@ func printJob(job *extensions.Job, w io.Writer, options PrintOptions) error { name := job.Name namespace := job.Namespace containers := job.Spec.Template.Spec.Containers - var firstContainer api.Container - if len(containers) > 0 { - firstContainer, containers = containers[0], containers[1:] - } + if options.WithNamespace { if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil { return err } } - selector, _ := unversioned.LabelSelectorAsSelector(job.Spec.Selector) - _, err := fmt.Fprintf(w, "%s\t%d", - name, - job.Status.Succeeded) + selector, err := unversioned.LabelSelectorAsSelector(job.Spec.Selector) if err != nil { + // this shouldn't happen if LabelSelector passed validation return err } - if options.Wide { - if _, err := fmt.Fprintf(w, "\t%s\t%s\t%s", - firstContainer.Name, - firstContainer.Image, - selector.String(), + if job.Spec.Completions != nil { + if _, err := fmt.Fprintf(w, "%s\t%d\t%d\t%s", + name, + *job.Spec.Completions, + job.Status.Succeeded, + translateTimestamp(job.CreationTimestamp), ); err != nil { return err } + } else { + if _, err := fmt.Fprintf(w, "%s\t%s\t%d\t%s", + name, + "", + job.Status.Succeeded, + translateTimestamp(job.CreationTimestamp), + ); err != nil { + return err + } + } + if options.Wide { + if err := layoutContainers(containers, w); err != nil { + return err + } + if _, err := fmt.Fprintf(w, "\t%s", selector.String()); err != nil { + return err + } } if _, err := fmt.Fprint(w, appendLabels(job.Labels, options.ColumnLabels)); err != nil { return err @@ -852,20 +820,6 @@ func printJob(job *extensions.Job, w io.Writer, options PrintOptions) error { return err } - // Lay out all the other containers on separate lines. - extraLinePrefix := "\t" - if options.WithNamespace { - extraLinePrefix = "\t\t" - } - for _, container := range containers { - _, err := fmt.Fprintf(w, "%s%s\t%s\t%s\t%s", extraLinePrefix, container.Name, container.Image, "", "") - if err != nil { - return err - } - if _, err := fmt.Fprint(w, appendLabelTabs(options.ColumnLabels)); err != nil { - return err - } - } return nil } @@ -983,11 +937,13 @@ func printIngress(ingress *extensions.Ingress, w io.Writer, options PrintOptions } } - if _, err := fmt.Fprintf(w, "%s\t%v\t%v\t%v", + if _, err := fmt.Fprintf(w, "%s\t%v\t%v\t%v\t%s", name, "-", backendStringer(ingress.Spec.Backend), - loadBalancerStatusStringer(ingress.Status.LoadBalancer)); err != nil { + loadBalancerStatusStringer(ingress.Status.LoadBalancer), + translateTimestamp(ingress.CreationTimestamp), + ); err != nil { return err } if _, err := fmt.Fprint(w, appendLabels(ingress.Labels, options.ColumnLabels)); err != nil { @@ -998,7 +954,8 @@ func printIngress(ingress *extensions.Ingress, w io.Writer, options PrintOptions return err } - // Lay out all the rules on separate lines. + // Lay out all the rules on separate lines if use wide output. + // TODO(AdoHe): improve ingress output extraLinePrefix := "" if options.WithNamespace { extraLinePrefix = "\t" @@ -1024,6 +981,7 @@ func printIngress(ingress *extensions.Ingress, w io.Writer, options PrintOptions } } } + return nil } @@ -1041,33 +999,34 @@ func printDaemonSet(ds *extensions.DaemonSet, w io.Writer, options PrintOptions) namespace := ds.Namespace containers := ds.Spec.Template.Spec.Containers - var firstContainer api.Container - if len(containers) > 0 { - firstContainer, containers = containers[0], containers[1:] - } if options.WithNamespace { if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil { return err } } + + desiredScheduled := ds.Status.DesiredNumberScheduled + currentScheduled := ds.Status.CurrentNumberScheduled selector, err := unversioned.LabelSelectorAsSelector(ds.Spec.Selector) if err != nil { // this shouldn't happen if LabelSelector passed validation return err } - if _, err := fmt.Fprintf(w, "%s\t%s", + if _, err := fmt.Fprintf(w, "%s\t%d\t%d\t%s\t%s", name, + desiredScheduled, + currentScheduled, labels.FormatLabels(ds.Spec.Template.Spec.NodeSelector), + translateTimestamp(ds.CreationTimestamp), ); err != nil { return err } if options.Wide { - if _, err := fmt.Fprintf(w, "\t%s\t%s\t%s", - firstContainer.Name, - firstContainer.Image, - selector, - ); err != nil { + if err := layoutContainers(containers, w); err != nil { + return err + } + if _, err := fmt.Fprintf(w, "\t%s", selector.String()); err != nil { return err } } @@ -1078,20 +1037,6 @@ func printDaemonSet(ds *extensions.DaemonSet, w io.Writer, options PrintOptions) return err } - // Lay out all the other containers on separate lines. - extraLinePrefix := "\t" - if options.WithNamespace { - extraLinePrefix = "\t\t" - } - for _, container := range containers { - _, err := fmt.Fprintf(w, "%s%s\t%s\t%s\t%s", extraLinePrefix, container.Name, container.Image, "", "") - if err != nil { - return err - } - if _, err := fmt.Fprint(w, appendLabelTabs(options.ColumnLabels)); err != nil { - return err - } - } return nil } @@ -1640,8 +1585,8 @@ func printConfigMapList(list *api.ConfigMapList, w io.Writer, options PrintOptio func printPodSecurityPolicy(item *extensions.PodSecurityPolicy, w io.Writer, options PrintOptions) error { _, err := fmt.Fprintf(w, "%s\t%t\t%v\t%t\t%s\t%s\n", item.Name, item.Spec.Privileged, - item.Spec.Capabilities, item.Spec.Volumes, item.Spec.SELinuxContext.Type, - item.Spec.RunAsUser.Type) + item.Spec.Capabilities, item.Spec.Volumes, item.Spec.SELinux.Rule, + item.Spec.RunAsUser.Rule) return err } @@ -1700,6 +1645,26 @@ func appendLabelTabs(columnLabels []string) string { return buffer.String() } +// Lay out all the containers on one line if use wide output. +func layoutContainers(containers []api.Container, w io.Writer) error { + var namesBuffer bytes.Buffer + var imagesBuffer bytes.Buffer + + for i, container := range containers { + namesBuffer.WriteString(container.Name) + imagesBuffer.WriteString(container.Image) + if i != len(containers)-1 { + namesBuffer.WriteString(",") + imagesBuffer.WriteString(",") + } + } + _, err := fmt.Fprintf(w, "\t%s\t%s", namesBuffer.String(), imagesBuffer.String()) + if err != nil { + return err + } + return nil +} + func formatLabelHeaders(columnLabels []string) []string { formHead := make([]string, len(columnLabels)) for i, l := range columnLabels { diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource_printer_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource_printer_test.go deleted file mode 100644 index 52e286db1..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/resource_printer_test.go +++ /dev/null @@ -1,1366 +0,0 @@ -/* -Copyright 2014 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 kubectl - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "reflect" - "strings" - "testing" - "time" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/v1" - "k8s.io/kubernetes/pkg/apis/extensions" - kubectltesting "k8s.io/kubernetes/pkg/kubectl/testing" - "k8s.io/kubernetes/pkg/runtime" - yamlserializer "k8s.io/kubernetes/pkg/runtime/serializer/yaml" - "k8s.io/kubernetes/pkg/util" - "k8s.io/kubernetes/pkg/util/intstr" - "k8s.io/kubernetes/pkg/util/sets" - - "github.com/ghodss/yaml" -) - -func init() { - api.Scheme.AddKnownTypes(testapi.Default.InternalGroupVersion(), &kubectltesting.TestStruct{}) - api.Scheme.AddKnownTypes(*testapi.Default.GroupVersion(), &kubectltesting.TestStruct{}) -} - -var testData = kubectltesting.TestStruct{ - Key: "testValue", - Map: map[string]int{"TestSubkey": 1}, - StringList: []string{"a", "b", "c"}, - IntList: []int{1, 2, 3}, -} - -func TestVersionedPrinter(t *testing.T) { - original := &kubectltesting.TestStruct{Key: "value"} - p := NewVersionedPrinter( - ResourcePrinterFunc(func(obj runtime.Object, w io.Writer) error { - if obj == original { - t.Fatalf("object should not be identical: %#v", obj) - } - if obj.(*kubectltesting.TestStruct).Key != "value" { - t.Fatalf("object was not converted: %#v", obj) - } - return nil - }), - api.Scheme, - *testapi.Default.GroupVersion(), - ) - if err := p.PrintObj(original, nil); err != nil { - t.Errorf("unexpected error: %v", err) - } -} - -func TestPrintDefault(t *testing.T) { - printer, found, err := GetPrinter("", "") - if err != nil { - t.Fatalf("unexpected error: %#v", err) - } - if found { - t.Errorf("no printer should have been found: %#v / %v", printer, err) - } -} - -type TestPrintType struct { - Data string -} - -func (obj *TestPrintType) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind } - -type TestUnknownType struct{} - -func (obj *TestUnknownType) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind } - -func TestPrinter(t *testing.T) { - //test inputs - simpleTest := &TestPrintType{"foo"} - podTest := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - podListTest := &api.PodList{ - Items: []api.Pod{ - {ObjectMeta: api.ObjectMeta{Name: "foo"}}, - {ObjectMeta: api.ObjectMeta{Name: "bar"}}, - }, - } - emptyListTest := &api.PodList{} - testapi, err := api.Scheme.ConvertToVersion(podTest, testapi.Default.GroupVersion().String()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - printerTests := []struct { - Name string - Format string - FormatArgument string - Input runtime.Object - Expect string - }{ - {"test json", "json", "", simpleTest, "{\n \"Data\": \"foo\"\n}\n"}, - {"test yaml", "yaml", "", simpleTest, "Data: foo\n"}, - {"test template", "template", "{{if .id}}{{.id}}{{end}}{{if .metadata.name}}{{.metadata.name}}{{end}}", - podTest, "foo"}, - {"test jsonpath", "jsonpath", "{.metadata.name}", podTest, "foo"}, - {"test jsonpath list", "jsonpath", "{.items[*].metadata.name}", podListTest, "foo bar"}, - {"test jsonpath empty list", "jsonpath", "{.items[*].metadata.name}", emptyListTest, ""}, - {"test name", "name", "", podTest, "pod/foo\n"}, - {"emits versioned objects", "template", "{{.kind}}", testapi, "Pod"}, - } - for _, test := range printerTests { - buf := bytes.NewBuffer([]byte{}) - printer, found, err := GetPrinter(test.Format, test.FormatArgument) - if err != nil || !found { - t.Errorf("in %s, unexpected error: %#v", test.Name, err) - } - if err := printer.PrintObj(test.Input, buf); err != nil { - t.Errorf("in %s, unexpected error: %#v", test.Name, err) - } - if buf.String() != test.Expect { - t.Errorf("in %s, expect %q, got %q", test.Name, test.Expect, buf.String()) - } - } - -} - -func TestBadPrinter(t *testing.T) { - badPrinterTests := []struct { - Name string - Format string - FormatArgument string - Error error - }{ - {"empty template", "template", "", fmt.Errorf("template format specified but no template given")}, - {"bad template", "template", "{{ .Name", fmt.Errorf("error parsing template {{ .Name, template: output:1: unclosed action\n")}, - {"bad templatefile", "templatefile", "", fmt.Errorf("templatefile format specified but no template file given")}, - {"bad jsonpath", "jsonpath", "{.Name", fmt.Errorf("error parsing jsonpath {.Name, unclosed action\n")}, - } - for _, test := range badPrinterTests { - _, _, err := GetPrinter(test.Format, test.FormatArgument) - if err == nil || err.Error() != test.Error.Error() { - t.Errorf("in %s, expect %s, got %s", test.Name, test.Error, err) - } - } -} - -func testPrinter(t *testing.T, printer ResourcePrinter, unmarshalFunc func(data []byte, v interface{}) error) { - buf := bytes.NewBuffer([]byte{}) - - err := printer.PrintObj(&testData, buf) - if err != nil { - t.Fatal(err) - } - var poutput kubectltesting.TestStruct - // Verify that given function runs without error. - err = unmarshalFunc(buf.Bytes(), &poutput) - if err != nil { - t.Fatal(err) - } - // Use real decode function to undo the versioning process. - poutput = kubectltesting.TestStruct{} - s := yamlserializer.NewDecodingSerializer(testapi.Default.Codec()) - if err := runtime.DecodeInto(s, buf.Bytes(), &poutput); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(testData, poutput) { - t.Errorf("Test data and unmarshaled data are not equal: %v", util.ObjectDiff(poutput, testData)) - } - - obj := &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "foo"}, - } - buf.Reset() - printer.PrintObj(obj, buf) - var objOut api.Pod - // Verify that given function runs without error. - err = unmarshalFunc(buf.Bytes(), &objOut) - if err != nil { - t.Fatalf("unexpected error: %#v", err) - } - // Use real decode function to undo the versioning process. - objOut = api.Pod{} - if err := runtime.DecodeInto(s, buf.Bytes(), &objOut); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(obj, &objOut) { - t.Errorf("Unexpected inequality:\n%v", util.ObjectDiff(obj, &objOut)) - } -} - -func TestYAMLPrinter(t *testing.T) { - testPrinter(t, &YAMLPrinter{}, yaml.Unmarshal) -} - -func TestJSONPrinter(t *testing.T) { - testPrinter(t, &JSONPrinter{}, json.Unmarshal) -} - -func PrintCustomType(obj *TestPrintType, w io.Writer, options PrintOptions) error { - _, err := fmt.Fprintf(w, "%s", obj.Data) - return err -} - -func ErrorPrintHandler(obj *TestPrintType, w io.Writer, options PrintOptions) error { - return fmt.Errorf("ErrorPrintHandler error") -} - -func TestCustomTypePrinting(t *testing.T) { - columns := []string{"Data"} - printer := NewHumanReadablePrinter(false, false, false, false, false, false, []string{}) - printer.Handler(columns, PrintCustomType) - - obj := TestPrintType{"test object"} - buffer := &bytes.Buffer{} - err := printer.PrintObj(&obj, buffer) - if err != nil { - t.Fatalf("An error occurred printing the custom type: %#v", err) - } - expectedOutput := "Data\ntest object" - if buffer.String() != expectedOutput { - t.Errorf("The data was not printed as expected. Expected:\n%s\nGot:\n%s", expectedOutput, buffer.String()) - } -} - -func TestPrintHandlerError(t *testing.T) { - columns := []string{"Data"} - printer := NewHumanReadablePrinter(false, false, false, false, false, false, []string{}) - printer.Handler(columns, ErrorPrintHandler) - obj := TestPrintType{"test object"} - buffer := &bytes.Buffer{} - err := printer.PrintObj(&obj, buffer) - if err == nil || err.Error() != "ErrorPrintHandler error" { - t.Errorf("Did not get the expected error: %#v", err) - } -} - -func TestUnknownTypePrinting(t *testing.T) { - printer := NewHumanReadablePrinter(false, false, false, false, false, false, []string{}) - buffer := &bytes.Buffer{} - err := printer.PrintObj(&TestUnknownType{}, buffer) - if err == nil { - t.Errorf("An error was expected from printing unknown type") - } -} - -func TestTemplatePanic(t *testing.T) { - tmpl := `{{and ((index .currentState.info "foo").state.running.startedAt) .currentState.info.net.state.running.startedAt}}` - printer, err := NewTemplatePrinter([]byte(tmpl)) - if err != nil { - t.Fatalf("tmpl fail: %v", err) - } - buffer := &bytes.Buffer{} - err = printer.PrintObj(&api.Pod{}, buffer) - if err == nil { - t.Fatalf("expected that template to crash") - } - if buffer.String() == "" { - t.Errorf("no debugging info was printed") - } -} - -func TestNamePrinter(t *testing.T) { - tests := map[string]struct { - obj runtime.Object - expect string - }{ - "singleObject": { - &api.Pod{ - TypeMeta: unversioned.TypeMeta{ - Kind: "Pod", - }, - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - }, - "pod/foo\n"}, - "List": { - &v1.List{ - TypeMeta: unversioned.TypeMeta{ - Kind: "List", - }, - Items: []runtime.RawExtension{ - { - RawJSON: []byte(`{"kind": "Pod", "apiVersion": "v1", "metadata": { "name": "foo"}}`), - }, - { - RawJSON: []byte(`{"kind": "Pod", "apiVersion": "v1", "metadata": { "name": "bar"}}`), - }, - }, - }, - "pod/foo\npod/bar\n"}, - } - printer, _, _ := GetPrinter("name", "") - for name, item := range tests { - buff := &bytes.Buffer{} - err := printer.PrintObj(item.obj, buff) - if err != nil { - t.Errorf("%v: unexpected err: %v", name, err) - continue - } - got := buff.String() - if item.expect != got { - t.Errorf("%v: expected %v, got %v", name, item.expect, got) - } - } -} - -func TestTemplateStrings(t *testing.T) { - // This unit tests the "exists" function as well as the template from update.sh - table := map[string]struct { - pod api.Pod - expect string - }{ - "nilInfo": {api.Pod{}, "false"}, - "emptyInfo": {api.Pod{Status: api.PodStatus{ContainerStatuses: []api.ContainerStatus{}}}, "false"}, - "fooExists": { - api.Pod{ - Status: api.PodStatus{ - ContainerStatuses: []api.ContainerStatus{ - { - Name: "foo", - }, - }, - }, - }, - "false", - }, - "barExists": { - api.Pod{ - Status: api.PodStatus{ - ContainerStatuses: []api.ContainerStatus{ - { - Name: "bar", - }, - }, - }, - }, - "false", - }, - "bothExist": { - api.Pod{ - Status: api.PodStatus{ - ContainerStatuses: []api.ContainerStatus{ - { - Name: "foo", - }, - { - Name: "bar", - }, - }, - }, - }, - "false", - }, - "barValid": { - api.Pod{ - Status: api.PodStatus{ - ContainerStatuses: []api.ContainerStatus{ - { - Name: "foo", - }, - { - Name: "bar", - State: api.ContainerState{ - Running: &api.ContainerStateRunning{ - StartedAt: unversioned.Time{}, - }, - }, - }, - }, - }, - }, - "false", - }, - "bothValid": { - api.Pod{ - Status: api.PodStatus{ - ContainerStatuses: []api.ContainerStatus{ - { - Name: "foo", - State: api.ContainerState{ - Running: &api.ContainerStateRunning{ - StartedAt: unversioned.Time{}, - }, - }, - }, - { - Name: "bar", - State: api.ContainerState{ - Running: &api.ContainerStateRunning{ - StartedAt: unversioned.Time{}, - }, - }, - }, - }, - }, - }, - "true", - }, - } - // The point of this test is to verify that the below template works. - tmpl := `{{if (exists . "status" "containerStatuses")}}{{range .status.containerStatuses}}{{if (and (eq .name "foo") (exists . "state" "running"))}}true{{end}}{{end}}{{end}}` - p, err := NewTemplatePrinter([]byte(tmpl)) - if err != nil { - t.Fatalf("tmpl fail: %v", err) - } - - printer := NewVersionedPrinter(p, api.Scheme, *testapi.Default.GroupVersion()) - - for name, item := range table { - buffer := &bytes.Buffer{} - err = printer.PrintObj(&item.pod, buffer) - if err != nil { - t.Errorf("%v: unexpected err: %v", name, err) - continue - } - actual := buffer.String() - if len(actual) == 0 { - actual = "false" - } - if e := item.expect; e != actual { - t.Errorf("%v: expected %v, got %v", name, e, actual) - } - } -} - -func TestPrinters(t *testing.T) { - om := func(name string) api.ObjectMeta { return api.ObjectMeta{Name: name} } - templatePrinter, err := NewTemplatePrinter([]byte("{{.name}}")) - if err != nil { - t.Fatal(err) - } - templatePrinter2, err := NewTemplatePrinter([]byte("{{len .items}}")) - if err != nil { - t.Fatal(err) - } - jsonpathPrinter, err := NewJSONPathPrinter("{.metadata.name}") - if err != nil { - t.Fatal(err) - } - printers := map[string]ResourcePrinter{ - "humanReadable": NewHumanReadablePrinter(true, false, false, false, false, false, []string{}), - "humanReadableHeaders": NewHumanReadablePrinter(false, false, false, false, false, false, []string{}), - "json": &JSONPrinter{}, - "yaml": &YAMLPrinter{}, - "template": templatePrinter, - "template2": templatePrinter2, - "jsonpath": jsonpathPrinter, - "name": &NamePrinter{ - Typer: runtime.ObjectTyperToTyper(api.Scheme), - Decoder: api.Codecs.UniversalDecoder(), - }, - } - objects := map[string]runtime.Object{ - "pod": &api.Pod{ObjectMeta: om("pod")}, - "emptyPodList": &api.PodList{}, - "nonEmptyPodList": &api.PodList{Items: []api.Pod{{}}}, - "endpoints": &api.Endpoints{ - Subsets: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "127.0.0.1"}, {IP: "localhost"}}, - Ports: []api.EndpointPort{{Port: 8080}}, - }}}, - } - // map of printer name to set of objects it should fail on. - expectedErrors := map[string]sets.String{ - "template2": sets.NewString("pod", "emptyPodList", "endpoints"), - "jsonpath": sets.NewString("emptyPodList", "nonEmptyPodList", "endpoints"), - } - - for pName, p := range printers { - for oName, obj := range objects { - b := &bytes.Buffer{} - if err := p.PrintObj(obj, b); err != nil { - if set, found := expectedErrors[pName]; found && set.Has(oName) { - // expected error - continue - } - t.Errorf("printer '%v', object '%v'; error: '%v'", pName, oName, err) - } - } - } -} - -func TestPrintEventsResultSorted(t *testing.T) { - // Arrange - printer := NewHumanReadablePrinter(false /* noHeaders */, false, false, false, false, false, []string{}) - - obj := api.EventList{ - Items: []api.Event{ - { - Source: api.EventSource{Component: "kubelet"}, - Message: "Item 1", - FirstTimestamp: unversioned.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), - LastTimestamp: unversioned.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), - Count: 1, - Type: api.EventTypeNormal, - }, - { - Source: api.EventSource{Component: "scheduler"}, - Message: "Item 2", - FirstTimestamp: unversioned.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)), - LastTimestamp: unversioned.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)), - Count: 1, - Type: api.EventTypeNormal, - }, - { - Source: api.EventSource{Component: "kubelet"}, - Message: "Item 3", - FirstTimestamp: unversioned.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)), - LastTimestamp: unversioned.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)), - Count: 1, - Type: api.EventTypeNormal, - }, - }, - } - buffer := &bytes.Buffer{} - - // Act - err := printer.PrintObj(&obj, buffer) - - // Assert - if err != nil { - t.Fatalf("An error occurred printing the EventList: %#v", err) - } - out := buffer.String() - VerifyDatesInOrder(out, "\n" /* rowDelimiter */, " " /* columnDelimiter */, t) -} - -func TestPrintNodeStatus(t *testing.T) { - printer := NewHumanReadablePrinter(false, false, false, false, false, false, []string{}) - table := []struct { - node api.Node - status string - }{ - { - node: api.Node{ - ObjectMeta: api.ObjectMeta{Name: "foo1"}, - Status: api.NodeStatus{Conditions: []api.NodeCondition{{Type: api.NodeReady, Status: api.ConditionTrue}}}, - }, - status: "Ready", - }, - { - node: api.Node{ - ObjectMeta: api.ObjectMeta{Name: "foo2"}, - Spec: api.NodeSpec{Unschedulable: true}, - Status: api.NodeStatus{Conditions: []api.NodeCondition{{Type: api.NodeReady, Status: api.ConditionTrue}}}, - }, - status: "Ready,SchedulingDisabled", - }, - { - node: api.Node{ - ObjectMeta: api.ObjectMeta{Name: "foo3"}, - Status: api.NodeStatus{Conditions: []api.NodeCondition{ - {Type: api.NodeReady, Status: api.ConditionTrue}, - {Type: api.NodeReady, Status: api.ConditionTrue}}}, - }, - status: "Ready", - }, - { - node: api.Node{ - ObjectMeta: api.ObjectMeta{Name: "foo4"}, - Status: api.NodeStatus{Conditions: []api.NodeCondition{{Type: api.NodeReady, Status: api.ConditionFalse}}}, - }, - status: "NotReady", - }, - { - node: api.Node{ - ObjectMeta: api.ObjectMeta{Name: "foo5"}, - Spec: api.NodeSpec{Unschedulable: true}, - Status: api.NodeStatus{Conditions: []api.NodeCondition{{Type: api.NodeReady, Status: api.ConditionFalse}}}, - }, - status: "NotReady,SchedulingDisabled", - }, - { - node: api.Node{ - ObjectMeta: api.ObjectMeta{Name: "foo6"}, - Status: api.NodeStatus{Conditions: []api.NodeCondition{{Type: "InvalidValue", Status: api.ConditionTrue}}}, - }, - status: "Unknown", - }, - { - node: api.Node{ - ObjectMeta: api.ObjectMeta{Name: "foo7"}, - Status: api.NodeStatus{Conditions: []api.NodeCondition{{}}}, - }, - status: "Unknown", - }, - { - node: api.Node{ - ObjectMeta: api.ObjectMeta{Name: "foo8"}, - Spec: api.NodeSpec{Unschedulable: true}, - Status: api.NodeStatus{Conditions: []api.NodeCondition{{Type: "InvalidValue", Status: api.ConditionTrue}}}, - }, - status: "Unknown,SchedulingDisabled", - }, - { - node: api.Node{ - ObjectMeta: api.ObjectMeta{Name: "foo9"}, - Spec: api.NodeSpec{Unschedulable: true}, - Status: api.NodeStatus{Conditions: []api.NodeCondition{{}}}, - }, - status: "Unknown,SchedulingDisabled", - }, - } - - for _, test := range table { - buffer := &bytes.Buffer{} - err := printer.PrintObj(&test.node, buffer) - if err != nil { - t.Fatalf("An error occurred printing Node: %#v", err) - } - if !contains(strings.Fields(buffer.String()), test.status) { - t.Fatalf("Expect printing node %s with status %#v, got: %#v", test.node.Name, test.status, buffer.String()) - } - } -} - -func contains(fields []string, field string) bool { - for _, v := range fields { - if v == field { - return true - } - } - return false -} - -func TestPrintHunmanReadableIngressWithColumnLabels(t *testing.T) { - ingress := extensions.Ingress{ - ObjectMeta: api.ObjectMeta{ - Name: "test1", - CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)}, - Labels: map[string]string{ - "app_name": "kubectl_test_ingress", - }, - }, - Spec: extensions.IngressSpec{ - Backend: &extensions.IngressBackend{ - ServiceName: "svc", - ServicePort: intstr.FromInt(93), - }, - }, - Status: extensions.IngressStatus{ - LoadBalancer: api.LoadBalancerStatus{ - Ingress: []api.LoadBalancerIngress{ - { - IP: "2.3.4.5", - Hostname: "localhost.localdomain", - }, - }, - }, - }, - } - buff := bytes.Buffer{} - printIngress(&ingress, &buff, PrintOptions{false, false, false, false, false, false, []string{"app_name"}}) - output := string(buff.Bytes()) - appName := ingress.ObjectMeta.Labels["app_name"] - if !strings.Contains(output, appName) { - t.Errorf("expected to container app_name label value %s, but doesn't %s", appName, output) - } -} - -func TestPrintHumanReadableService(t *testing.T) { - tests := []api.Service{ - { - Spec: api.ServiceSpec{ - ClusterIP: "1.2.3.4", - Type: "LoadBalancer", - Ports: []api.ServicePort{ - { - Port: 80, - Protocol: "TCP", - }, - }, - }, - Status: api.ServiceStatus{ - LoadBalancer: api.LoadBalancerStatus{ - Ingress: []api.LoadBalancerIngress{ - { - IP: "2.3.4.5", - }, - { - IP: "3.4.5.6", - }, - }, - }, - }, - }, - { - Spec: api.ServiceSpec{ - ClusterIP: "1.2.3.4", - Ports: []api.ServicePort{ - { - Port: 80, - Protocol: "TCP", - }, - { - Port: 8090, - Protocol: "UDP", - }, - { - Port: 8000, - Protocol: "TCP", - }, - }, - }, - }, - { - Spec: api.ServiceSpec{ - ClusterIP: "1.2.3.4", - Type: "LoadBalancer", - Ports: []api.ServicePort{ - { - Port: 80, - Protocol: "TCP", - }, - { - Port: 8090, - Protocol: "UDP", - }, - { - Port: 8000, - Protocol: "TCP", - }, - }, - }, - Status: api.ServiceStatus{ - LoadBalancer: api.LoadBalancerStatus{ - Ingress: []api.LoadBalancerIngress{ - { - IP: "2.3.4.5", - }, - }, - }, - }, - }, - { - Spec: api.ServiceSpec{ - ClusterIP: "1.2.3.4", - Type: "LoadBalancer", - Ports: []api.ServicePort{ - { - Port: 80, - Protocol: "TCP", - }, - { - Port: 8090, - Protocol: "UDP", - }, - { - Port: 8000, - Protocol: "TCP", - }, - }, - }, - Status: api.ServiceStatus{ - LoadBalancer: api.LoadBalancerStatus{ - Ingress: []api.LoadBalancerIngress{ - { - IP: "2.3.4.5", - }, - { - IP: "3.4.5.6", - }, - { - IP: "5.6.7.8", - Hostname: "host5678", - }, - }, - }, - }, - }, - } - - for _, svc := range tests { - buff := bytes.Buffer{} - printService(&svc, &buff, PrintOptions{false, false, false, false, false, false, []string{}}) - output := string(buff.Bytes()) - ip := svc.Spec.ClusterIP - if !strings.Contains(output, ip) { - t.Errorf("expected to contain ClusterIP %s, but doesn't: %s", ip, output) - } - - for _, ingress := range svc.Status.LoadBalancer.Ingress { - ip = ingress.IP - if !strings.Contains(output, ip) { - t.Errorf("expected to contain ingress ip %s, but doesn't: %s", ip, output) - } - } - - for _, port := range svc.Spec.Ports { - portSpec := fmt.Sprintf("%d/%s", port.Port, port.Protocol) - if !strings.Contains(output, portSpec) { - t.Errorf("expected to contain port: %s, but doesn't: %s", portSpec, output) - } - } - // Each service should print on one line - if 1 != strings.Count(output, "\n") { - t.Errorf("expected a single newline, found %d", strings.Count(output, "\n")) - } - } -} - -func TestPrintHumanReadableWithNamespace(t *testing.T) { - namespaceName := "testnamespace" - name := "test" - table := []struct { - obj runtime.Object - isNamespaced bool - }{ - { - obj: &api.Pod{ - ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, - }, - isNamespaced: true, - }, - { - obj: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, - Spec: api.ReplicationControllerSpec{ - Replicas: 2, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "name": "foo", - "type": "production", - }, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Image: "foo/bar", - TerminationMessagePath: api.TerminationMessagePathDefault, - ImagePullPolicy: api.PullIfNotPresent, - }, - }, - RestartPolicy: api.RestartPolicyAlways, - DNSPolicy: api.DNSDefault, - NodeSelector: map[string]string{ - "baz": "blah", - }, - }, - }, - }, - }, - isNamespaced: true, - }, - { - obj: &api.Service{ - ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, - Spec: api.ServiceSpec{ - ClusterIP: "1.2.3.4", - Ports: []api.ServicePort{ - { - Port: 80, - Protocol: "TCP", - }, - }, - }, - Status: api.ServiceStatus{ - LoadBalancer: api.LoadBalancerStatus{ - Ingress: []api.LoadBalancerIngress{ - { - IP: "2.3.4.5", - }, - }, - }, - }, - }, - isNamespaced: true, - }, - { - obj: &api.Endpoints{ - ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, - Subsets: []api.EndpointSubset{{ - Addresses: []api.EndpointAddress{{IP: "127.0.0.1"}, {IP: "localhost"}}, - Ports: []api.EndpointPort{{Port: 8080}}, - }, - }}, - isNamespaced: true, - }, - { - obj: &api.Namespace{ - ObjectMeta: api.ObjectMeta{Name: name}, - }, - isNamespaced: false, - }, - { - obj: &api.Secret{ - ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, - }, - isNamespaced: true, - }, - { - obj: &api.ServiceAccount{ - ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, - Secrets: []api.ObjectReference{}, - }, - isNamespaced: true, - }, - { - obj: &api.Node{ - ObjectMeta: api.ObjectMeta{Name: name}, - Status: api.NodeStatus{}, - }, - isNamespaced: false, - }, - { - obj: &api.PersistentVolume{ - ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, - Spec: api.PersistentVolumeSpec{}, - }, - isNamespaced: false, - }, - { - obj: &api.PersistentVolumeClaim{ - ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, - Spec: api.PersistentVolumeClaimSpec{}, - }, - isNamespaced: true, - }, - { - obj: &api.Event{ - ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, - Source: api.EventSource{Component: "kubelet"}, - Message: "Item 1", - FirstTimestamp: unversioned.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), - LastTimestamp: unversioned.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), - Count: 1, - Type: api.EventTypeNormal, - }, - isNamespaced: true, - }, - { - obj: &api.LimitRange{ - ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, - }, - isNamespaced: true, - }, - { - obj: &api.ResourceQuota{ - ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, - }, - isNamespaced: true, - }, - { - obj: &api.ComponentStatus{ - Conditions: []api.ComponentCondition{ - {Type: api.ComponentHealthy, Status: api.ConditionTrue, Message: "ok", Error: ""}, - }, - }, - isNamespaced: false, - }, - } - - for _, test := range table { - if test.isNamespaced { - // Expect output to include namespace when requested. - printer := NewHumanReadablePrinter(false, true, false, false, false, false, []string{}) - buffer := &bytes.Buffer{} - err := printer.PrintObj(test.obj, buffer) - if err != nil { - t.Fatalf("An error occurred printing object: %#v", err) - } - matched := contains(strings.Fields(buffer.String()), fmt.Sprintf("%s", namespaceName)) - if !matched { - t.Errorf("Expect printing object to contain namespace: %#v", test.obj) - } - } else { - // Expect error when trying to get all namespaces for un-namespaced object. - printer := NewHumanReadablePrinter(false, true, false, false, false, false, []string{}) - buffer := &bytes.Buffer{} - err := printer.PrintObj(test.obj, buffer) - if err == nil { - t.Errorf("Expected error when printing un-namespaced type") - } - } - } -} - -func TestPrintPod(t *testing.T) { - tests := []struct { - pod api.Pod - expect string - }{ - { - // Test name, num of containers, restarts, container ready status - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "test1"}, - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, - Status: api.PodStatus{ - Phase: "podPhase", - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - }, - "test1\t1/2\tpodPhase\t6\t", - }, - { - // Test container error overwrites pod phase - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "test2"}, - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, - Status: api.PodStatus{ - Phase: "podPhase", - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {State: api.ContainerState{Waiting: &api.ContainerStateWaiting{Reason: "ContainerWaitingReason"}}, RestartCount: 3}, - }, - }, - }, - "test2\t1/2\tContainerWaitingReason\t6\t", - }, - { - // Test the same as the above but with Terminated state and the first container overwrites the rest - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "test3"}, - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, - Status: api.PodStatus{ - Phase: "podPhase", - ContainerStatuses: []api.ContainerStatus{ - {State: api.ContainerState{Waiting: &api.ContainerStateWaiting{Reason: "ContainerWaitingReason"}}, RestartCount: 3}, - {State: api.ContainerState{Terminated: &api.ContainerStateTerminated{Reason: "ContainerTerminatedReason"}}, RestartCount: 3}, - }, - }, - }, - "test3\t0/2\tContainerWaitingReason\t6\t", - }, - { - // Test ready is not enough for reporting running - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "test4"}, - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, - Status: api.PodStatus{ - Phase: "podPhase", - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {Ready: true, RestartCount: 3}, - }, - }, - }, - "test4\t1/2\tpodPhase\t6\t", - }, - { - // Test ready is not enough for reporting running - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "test5"}, - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, - Status: api.PodStatus{ - Reason: "OutOfDisk", - Phase: "podPhase", - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {Ready: true, RestartCount: 3}, - }, - }, - }, - "test5\t1/2\tOutOfDisk\t6\t", - }, - } - - buf := bytes.NewBuffer([]byte{}) - for _, test := range tests { - printPod(&test.pod, buf, PrintOptions{false, false, false, true, false, false, []string{}}) - // We ignore time - if !strings.HasPrefix(buf.String(), test.expect) { - t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) - } - buf.Reset() - } -} - -func TestPrintNonTerminatedPod(t *testing.T) { - tests := []struct { - pod api.Pod - expect string - }{ - { - // Test pod phase Running should be printed - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "test1"}, - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, - Status: api.PodStatus{ - Phase: api.PodRunning, - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - }, - "test1\t1/2\tRunning\t6\t", - }, - { - // Test pod phase Pending should be printed - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "test2"}, - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, - Status: api.PodStatus{ - Phase: api.PodPending, - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - }, - "test2\t1/2\tPending\t6\t", - }, - { - // Test pod phase Unknown should be printed - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "test3"}, - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, - Status: api.PodStatus{ - Phase: api.PodUnknown, - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - }, - "test3\t1/2\tUnknown\t6\t", - }, - { - // Test pod phase Succeeded shouldn't be printed - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "test4"}, - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, - Status: api.PodStatus{ - Phase: api.PodSucceeded, - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - }, - "", - }, - { - // Test pod phase Failed shouldn't be printed - api.Pod{ - ObjectMeta: api.ObjectMeta{Name: "test5"}, - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, - Status: api.PodStatus{ - Phase: api.PodFailed, - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {Ready: true, RestartCount: 3}, - }, - }, - }, - "", - }, - } - - buf := bytes.NewBuffer([]byte{}) - for _, test := range tests { - printPod(&test.pod, buf, PrintOptions{false, false, false, false, false, false, []string{}}) - // We ignore time - if !strings.HasPrefix(buf.String(), test.expect) { - t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) - } - buf.Reset() - } -} - -func TestPrintPodWithLabels(t *testing.T) { - tests := []struct { - pod api.Pod - labelColumns []string - startsWith string - endsWith string - }{ - { - // Test name, num of containers, restarts, container ready status - api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "test1", - Labels: map[string]string{"col1": "asd", "COL2": "zxc"}, - }, - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, - Status: api.PodStatus{ - Phase: "podPhase", - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - }, - []string{"col1", "COL2"}, - "test1\t1/2\tpodPhase\t6\t", - "\tasd\tzxc\n", - }, - { - // Test name, num of containers, restarts, container ready status - api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "test1", - Labels: map[string]string{"col1": "asd", "COL2": "zxc"}, - }, - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, - Status: api.PodStatus{ - Phase: "podPhase", - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - }, - []string{}, - "test1\t1/2\tpodPhase\t6\t", - "\n", - }, - } - - buf := bytes.NewBuffer([]byte{}) - for _, test := range tests { - printPod(&test.pod, buf, PrintOptions{false, false, false, false, false, false, test.labelColumns}) - // We ignore time - if !strings.HasPrefix(buf.String(), test.startsWith) || !strings.HasSuffix(buf.String(), test.endsWith) { - t.Fatalf("Expected to start with: %s and end with: %s, but got: %s", test.startsWith, test.endsWith, buf.String()) - } - buf.Reset() - } -} - -type stringTestList []struct { - name, got, exp string -} - -func TestTranslateTimestamp(t *testing.T) { - tl := stringTestList{ - {"a while from now", translateTimestamp(unversioned.Time{Time: time.Now().Add(2.1e9)}), ""}, - {"almost now", translateTimestamp(unversioned.Time{Time: time.Now().Add(1.9e9)}), "0s"}, - {"now", translateTimestamp(unversioned.Time{Time: time.Now()}), "0s"}, - {"unknown", translateTimestamp(unversioned.Time{}), ""}, - {"30 seconds ago", translateTimestamp(unversioned.Time{Time: time.Now().Add(-3e10)}), "30s"}, - {"5 minutes ago", translateTimestamp(unversioned.Time{Time: time.Now().Add(-3e11)}), "5m"}, - {"an hour ago", translateTimestamp(unversioned.Time{Time: time.Now().Add(-6e12)}), "1h"}, - {"2 days ago", translateTimestamp(unversioned.Time{Time: time.Now().AddDate(0, 0, -2)}), "2d"}, - {"months ago", translateTimestamp(unversioned.Time{Time: time.Now().AddDate(0, 0, -90)}), "90d"}, - {"10 years ago", translateTimestamp(unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)}), "10y"}, - } - for _, test := range tl { - if test.got != test.exp { - t.Errorf("On %v, expected '%v', but got '%v'", - test.name, test.exp, test.got) - } - } -} - -func TestPrintDeployment(t *testing.T) { - tests := []struct { - deployment extensions.Deployment - expect string - }{ - { - extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - Name: "test1", - CreationTimestamp: unversioned.Time{Time: time.Now().Add(1.9e9)}, - }, - Spec: extensions.DeploymentSpec{ - Replicas: 5, - Template: api.PodTemplateSpec{ - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, - }, - }, - Status: extensions.DeploymentStatus{ - Replicas: 10, - UpdatedReplicas: 2, - AvailableReplicas: 1, - UnavailableReplicas: 4, - }, - }, - "test1\t5\t10\t2\t1\t0s\n", - }, - } - - buf := bytes.NewBuffer([]byte{}) - for _, test := range tests { - printDeployment(&test.deployment, buf, PrintOptions{false, false, false, true, false, false, []string{}}) - if buf.String() != test.expect { - t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) - } - buf.Reset() - } -} - -func TestPrintPodShowLabels(t *testing.T) { - tests := []struct { - pod api.Pod - startsWith string - endsWith string - showLabels bool - }{ - { - // Test name, num of containers, restarts, container ready status - api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "test1", - Labels: map[string]string{"col1": "asd", "COL2": "zxc"}, - }, - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, - Status: api.PodStatus{ - Phase: "podPhase", - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - }, - "test1\t1/2\tpodPhase\t6\t", - "\tCOL2=zxc,col1=asd\n", - true, - }, - { - // Test name, num of containers, restarts, container ready status - api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "test1", - Labels: map[string]string{"col3": "asd", "COL4": "zxc"}, - }, - Spec: api.PodSpec{Containers: make([]api.Container, 2)}, - Status: api.PodStatus{ - Phase: "podPhase", - ContainerStatuses: []api.ContainerStatus{ - {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}}, - {RestartCount: 3}, - }, - }, - }, - "test1\t1/2\tpodPhase\t6\t", - "\n", - false, - }, - } - - buf := bytes.NewBuffer([]byte{}) - for _, test := range tests { - printPod(&test.pod, buf, PrintOptions{false, false, false, false, test.showLabels, false, []string{}}) - // We ignore time - if !strings.HasPrefix(buf.String(), test.startsWith) || !strings.HasSuffix(buf.String(), test.endsWith) { - t.Fatalf("Expected to start with: %s and end with: %s, but got: %s", test.startsWith, test.endsWith, buf.String()) - } - buf.Reset() - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/rolling_updater.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/rolling_updater.go index d734b153d..e69d889a7 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/rolling_updater.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/rolling_updater.go @@ -20,7 +20,6 @@ import ( goerrors "errors" "fmt" "io" - "math" "strconv" "strings" "time" @@ -30,6 +29,7 @@ import ( client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/deployment" "k8s.io/kubernetes/pkg/util/integer" "k8s.io/kubernetes/pkg/util/intstr" "k8s.io/kubernetes/pkg/util/wait" @@ -113,10 +113,8 @@ type RollingUpdater struct { getOrCreateTargetController func(controller *api.ReplicationController, sourceId string) (*api.ReplicationController, bool, error) // cleanup performs post deployment cleanup tasks for newRc and oldRc. cleanup func(oldRc, newRc *api.ReplicationController, config *RollingUpdaterConfig) error - // waitForReadyPods should block until there are >0 total pods ready amongst - // the old and new controllers, and should return the amount of old and new - // ready. - waitForReadyPods func(interval, timeout time.Duration, oldRc, newRc *api.ReplicationController) (int, int, error) + // getReadyPods returns the amount of old and new ready pods. + getReadyPods func(oldRc, newRc *api.ReplicationController) (int, int, error) } // NewRollingUpdater creates a RollingUpdater from a client. @@ -128,7 +126,7 @@ func NewRollingUpdater(namespace string, client client.Interface) *RollingUpdate // Inject real implementations. updater.scaleAndWait = updater.scaleAndWaitWithScaler updater.getOrCreateTargetController = updater.getOrCreateTargetControllerWithClient - updater.waitForReadyPods = updater.pollForReadyPods + updater.getReadyPods = updater.readyPods updater.cleanup = updater.cleanupWithClients return updater } @@ -194,18 +192,9 @@ func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error { } oldRc = updated } - original, err := strconv.Atoi(oldRc.Annotations[originalReplicasAnnotation]) - if err != nil { - return fmt.Errorf("Unable to parse annotation for %s: %s=%s\n", - oldRc.Name, originalReplicasAnnotation, oldRc.Annotations[originalReplicasAnnotation]) - } - // The maximum pods which can go unavailable during the update. - maxUnavailable, err := extractMaxValue(config.MaxUnavailable, "maxUnavailable", desired) - if err != nil { - return err - } - // The maximum scaling increment. - maxSurge, err := extractMaxValue(config.MaxSurge, "maxSurge", desired) + // maxSurge is the maximum scaling increment and maxUnavailable are the maximum pods + // that can be unavailable during a rollout. + maxSurge, maxUnavailable, err := deployment.ResolveFenceposts(&config.MaxSurge, &config.MaxUnavailable, desired) if err != nil { return err } @@ -220,12 +209,12 @@ func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error { // the effective scale of the old RC regardless of the configuration // (equivalent to 100% maxUnavailable). if desired == 0 { - maxUnavailable = original + maxUnavailable = oldRc.Spec.Replicas minAvailable = 0 } fmt.Fprintf(out, "Scaling up %s from %d to %d, scaling down %s from %d to 0 (keep %d pods available, don't exceed %d pods)\n", - newRc.Name, newRc.Spec.Replicas, desired, oldRc.Name, oldRc.Spec.Replicas, minAvailable, original+maxSurge) + newRc.Name, newRc.Spec.Replicas, desired, oldRc.Name, oldRc.Spec.Replicas, minAvailable, desired+maxSurge) // Scale newRc and oldRc until newRc has the desired number of replicas and // oldRc has 0 replicas. @@ -236,7 +225,7 @@ func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error { oldReplicas := oldRc.Spec.Replicas // Scale up as much as possible. - scaledRc, err := r.scaleUp(newRc, oldRc, original, desired, maxSurge, maxUnavailable, scaleRetryParams, config) + scaledRc, err := r.scaleUp(newRc, oldRc, desired, maxSurge, maxUnavailable, scaleRetryParams, config) if err != nil { return err } @@ -269,14 +258,14 @@ func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error { // scaleUp scales up newRc to desired by whatever increment is possible given // the configured surge threshold. scaleUp will safely no-op as necessary when // it detects redundancy or other relevant conditions. -func (r *RollingUpdater) scaleUp(newRc, oldRc *api.ReplicationController, original, desired, maxSurge, maxUnavailable int, scaleRetryParams *RetryParams, config *RollingUpdaterConfig) (*api.ReplicationController, error) { +func (r *RollingUpdater) scaleUp(newRc, oldRc *api.ReplicationController, desired, maxSurge, maxUnavailable int, scaleRetryParams *RetryParams, config *RollingUpdaterConfig) (*api.ReplicationController, error) { // If we're already at the desired, do nothing. if newRc.Spec.Replicas == desired { return newRc, nil } // Scale up as far as we can based on the surge limit. - increment := (original + maxSurge) - (oldRc.Spec.Replicas + newRc.Spec.Replicas) + increment := (desired + maxSurge) - (oldRc.Spec.Replicas + newRc.Spec.Replicas) // If the old is already scaled down, go ahead and scale all the way up. if oldRc.Spec.Replicas == 0 { increment = desired - newRc.Spec.Replicas @@ -299,7 +288,7 @@ func (r *RollingUpdater) scaleUp(newRc, oldRc *api.ReplicationController, origin return scaledRc, nil } -// scaleDown scales down oldRc to 0 at whatever increment possible given the +// scaleDown scales down oldRc to 0 at whatever decrement possible given the // thresholds defined on the config. scaleDown will safely no-op as necessary // when it detects redundancy or other relevant conditions. func (r *RollingUpdater) scaleDown(newRc, oldRc *api.ReplicationController, desired, minAvailable, maxUnavailable, maxSurge int, config *RollingUpdaterConfig) (*api.ReplicationController, error) { @@ -307,15 +296,19 @@ func (r *RollingUpdater) scaleDown(newRc, oldRc *api.ReplicationController, desi if oldRc.Spec.Replicas == 0 { return oldRc, nil } - // Block until there are any pods ready. - _, newAvailable, err := r.waitForReadyPods(config.Interval, config.Timeout, oldRc, newRc) + // Get ready pods. We shouldn't block, otherwise in case both old and new + // pods are unavailable then the rolling update process blocks. + // Timeout-wise we are already covered by the progress check. + _, newAvailable, err := r.getReadyPods(oldRc, newRc) if err != nil { return nil, err } // The old controller is considered as part of the total because we want to // maintain minimum availability even with a volatile old controller. // Scale down as much as possible while maintaining minimum availability - decrement := oldRc.Spec.Replicas + newAvailable - minAvailable + allPods := oldRc.Spec.Replicas + newRc.Spec.Replicas + newUnavailable := newRc.Spec.Replicas - newAvailable + decrement := allPods - minAvailable - newUnavailable // The decrement normally shouldn't drop below 0 because the available count // always starts below the old replica count, but the old replica count can // decrement due to externalities like pods death in the replica set. This @@ -360,40 +353,34 @@ func (r *RollingUpdater) scaleAndWaitWithScaler(rc *api.ReplicationController, r return r.c.ReplicationControllers(rc.Namespace).Get(rc.Name) } -// pollForReadyPods polls oldRc and newRc each interval and returns the old -// and new ready counts for their pods. If a pod is observed as being ready, -// it's considered ready even if it later becomes notReady. -func (r *RollingUpdater) pollForReadyPods(interval, timeout time.Duration, oldRc, newRc *api.ReplicationController) (int, int, error) { +// readyPods returns the old and new ready counts for their pods. +// If a pod is observed as being ready, it's considered ready even +// if it later becomes notReady. +func (r *RollingUpdater) readyPods(oldRc, newRc *api.ReplicationController) (int, int, error) { controllers := []*api.ReplicationController{oldRc, newRc} oldReady := 0 newReady := 0 - err := wait.Poll(interval, timeout, func() (done bool, err error) { - anyReady := false - for _, controller := range controllers { - selector := labels.Set(controller.Spec.Selector).AsSelector() - options := api.ListOptions{LabelSelector: selector} - pods, err := r.c.Pods(controller.Namespace).List(options) - if err != nil { - return false, err - } - for _, pod := range pods.Items { - if api.IsPodReady(&pod) { - switch controller.Name { - case oldRc.Name: - oldReady++ - case newRc.Name: - newReady++ - } - anyReady = true + + for i := range controllers { + controller := controllers[i] + selector := labels.Set(controller.Spec.Selector).AsSelector() + options := api.ListOptions{LabelSelector: selector} + pods, err := r.c.Pods(controller.Namespace).List(options) + if err != nil { + return 0, 0, err + } + for _, pod := range pods.Items { + if api.IsPodReady(&pod) { + switch controller.Name { + case oldRc.Name: + oldReady++ + case newRc.Name: + newReady++ } } } - if anyReady { - return true, nil - } - return false, nil - }) - return oldReady, newReady, err + } + return oldReady, newReady, nil } // getOrCreateTargetControllerWithClient looks for an existing controller with @@ -490,29 +477,6 @@ func (r *RollingUpdater) cleanupWithClients(oldRc, newRc *api.ReplicationControl } } -// func extractMaxValue is a helper to extract config max values as either -// absolute numbers or based on percentages of the given value. -func extractMaxValue(field intstr.IntOrString, name string, value int) (int, error) { - switch field.Type { - case intstr.Int: - if field.IntVal < 0 { - return 0, fmt.Errorf("%s must be >= 0", name) - } - return field.IntValue(), nil - case intstr.String: - s := strings.Replace(field.StrVal, "%", "", -1) - v, err := strconv.Atoi(s) - if err != nil { - return 0, fmt.Errorf("invalid %s value %q: %v", name, field.StrVal, err) - } - if v < 0 { - return 0, fmt.Errorf("%s must be >= 0", name) - } - return int(math.Ceil(float64(value) * (float64(v)) / 100)), nil - } - return 0, fmt.Errorf("invalid kind %q for %s", field.Type, name) -} - func Rename(c client.ReplicationControllersNamespacer, rc *api.ReplicationController, newName string) error { oldName := rc.Name rc.Name = newName diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/rolling_updater_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/rolling_updater_test.go deleted file mode 100644 index 4ba556847..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/rolling_updater_test.go +++ /dev/null @@ -1,1622 +0,0 @@ -/* -Copyright 2014 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 kubectl - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "net/http" - "reflect" - "testing" - "time" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - apitesting "k8s.io/kubernetes/pkg/api/testing" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/fake" - "k8s.io/kubernetes/pkg/client/unversioned/testclient" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/intstr" - "k8s.io/kubernetes/pkg/util/sets" -) - -func oldRc(replicas int, original int) *api.ReplicationController { - return &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo-v1", - UID: "7764ae47-9092-11e4-8393-42010af018ff", - Annotations: map[string]string{ - originalReplicasAnnotation: fmt.Sprintf("%d", original), - }, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: replicas, - Selector: map[string]string{"version": "v1"}, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Name: "foo-v1", - Labels: map[string]string{"version": "v1"}, - }, - }, - }, - Status: api.ReplicationControllerStatus{ - Replicas: replicas, - }, - } -} - -func newRc(replicas int, desired int) *api.ReplicationController { - rc := oldRc(replicas, replicas) - rc.Spec.Template = &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Name: "foo-v2", - Labels: map[string]string{"version": "v2"}, - }, - } - rc.Spec.Selector = map[string]string{"version": "v2"} - rc.ObjectMeta = api.ObjectMeta{ - Name: "foo-v2", - Annotations: map[string]string{ - desiredReplicasAnnotation: fmt.Sprintf("%d", desired), - sourceIdAnnotation: "foo-v1:7764ae47-9092-11e4-8393-42010af018ff", - }, - } - return rc -} - -// TestUpdate performs complex scenario testing for rolling updates. It -// provides fine grained control over the states for each update interval to -// allow the expression of as many edge cases as possible. -func TestUpdate(t *testing.T) { - // up represents a simulated scale up event and expectation - type up struct { - // to is the expected replica count for a scale-up - to int - } - // down represents a simulated scale down event and expectation - type down struct { - // oldReady is the number of oldRc replicas which will be seen - // as ready during the scale down attempt - oldReady int - // newReady is the number of newRc replicas which will be seen - // as ready during the scale up attempt - newReady int - // to is the expected replica count for the scale down - to int - // noop and to are mutually exclusive; if noop is true, that means for - // this down event, no scaling attempt should be made (for example, if - // by scaling down, the readiness minimum would be crossed.) - noop bool - } - - tests := []struct { - name string - // oldRc is the "from" deployment - oldRc *api.ReplicationController - // newRc is the "to" deployment - newRc *api.ReplicationController - // whether newRc existed (false means it was created) - newRcExists bool - maxUnavail intstr.IntOrString - maxSurge intstr.IntOrString - // expected is the sequence of up/down events that will be simulated and - // verified - expected []interface{} - // output is the expected textual output written - output string - }{ - { - name: "10->10 30/0 fast readiness", - oldRc: oldRc(10, 10), - newRc: newRc(0, 10), - newRcExists: false, - maxUnavail: intstr.FromString("30%"), - maxSurge: intstr.FromString("0%"), - expected: []interface{}{ - down{oldReady: 10, newReady: 0, to: 7}, - up{3}, - down{oldReady: 7, newReady: 3, to: 4}, - up{6}, - down{oldReady: 4, newReady: 6, to: 1}, - up{9}, - down{oldReady: 1, newReady: 9, to: 0}, - up{10}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 7 pods available, don't exceed 10 pods) -Scaling foo-v1 down to 7 -Scaling foo-v2 up to 3 -Scaling foo-v1 down to 4 -Scaling foo-v2 up to 6 -Scaling foo-v1 down to 1 -Scaling foo-v2 up to 9 -Scaling foo-v1 down to 0 -Scaling foo-v2 up to 10 -`, - }, - { - name: "10->10 30/0 delayed readiness", - oldRc: oldRc(10, 10), - newRc: newRc(0, 10), - newRcExists: false, - maxUnavail: intstr.FromString("30%"), - maxSurge: intstr.FromString("0%"), - expected: []interface{}{ - down{oldReady: 10, newReady: 0, to: 7}, - up{3}, - down{oldReady: 7, newReady: 0, noop: true}, - down{oldReady: 7, newReady: 1, to: 6}, - up{4}, - down{oldReady: 6, newReady: 4, to: 3}, - up{7}, - down{oldReady: 3, newReady: 7, to: 0}, - up{10}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 7 pods available, don't exceed 10 pods) -Scaling foo-v1 down to 7 -Scaling foo-v2 up to 3 -Scaling foo-v1 down to 6 -Scaling foo-v2 up to 4 -Scaling foo-v1 down to 3 -Scaling foo-v2 up to 7 -Scaling foo-v1 down to 0 -Scaling foo-v2 up to 10 -`, - }, { - name: "10->10 30/0 fast readiness, continuation", - oldRc: oldRc(7, 10), - newRc: newRc(3, 10), - newRcExists: false, - maxUnavail: intstr.FromString("30%"), - maxSurge: intstr.FromString("0%"), - expected: []interface{}{ - down{oldReady: 7, newReady: 3, to: 4}, - up{6}, - down{oldReady: 4, newReady: 6, to: 1}, - up{9}, - down{oldReady: 1, newReady: 9, to: 0}, - up{10}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 3 to 10, scaling down foo-v1 from 7 to 0 (keep 7 pods available, don't exceed 10 pods) -Scaling foo-v1 down to 4 -Scaling foo-v2 up to 6 -Scaling foo-v1 down to 1 -Scaling foo-v2 up to 9 -Scaling foo-v1 down to 0 -Scaling foo-v2 up to 10 -`, - }, { - name: "10->10 30/0 fast readiness, continued after restart which prevented first scale-up", - oldRc: oldRc(7, 10), - newRc: newRc(0, 10), - newRcExists: false, - maxUnavail: intstr.FromString("30%"), - maxSurge: intstr.FromString("0%"), - expected: []interface{}{ - down{oldReady: 7, newReady: 0, noop: true}, - up{3}, - down{oldReady: 7, newReady: 3, to: 4}, - up{6}, - down{oldReady: 4, newReady: 6, to: 1}, - up{9}, - down{oldReady: 1, newReady: 9, to: 0}, - up{10}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 7 to 0 (keep 7 pods available, don't exceed 10 pods) -Scaling foo-v2 up to 3 -Scaling foo-v1 down to 4 -Scaling foo-v2 up to 6 -Scaling foo-v1 down to 1 -Scaling foo-v2 up to 9 -Scaling foo-v1 down to 0 -Scaling foo-v2 up to 10 -`, - }, { - name: "10->10 0/30 fast readiness", - oldRc: oldRc(10, 10), - newRc: newRc(0, 10), - newRcExists: false, - maxUnavail: intstr.FromString("0%"), - maxSurge: intstr.FromString("30%"), - expected: []interface{}{ - up{3}, - down{oldReady: 10, newReady: 3, to: 7}, - up{6}, - down{oldReady: 7, newReady: 6, to: 4}, - up{9}, - down{oldReady: 4, newReady: 9, to: 1}, - up{10}, - down{oldReady: 1, newReady: 10, to: 0}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 10 pods available, don't exceed 13 pods) -Scaling foo-v2 up to 3 -Scaling foo-v1 down to 7 -Scaling foo-v2 up to 6 -Scaling foo-v1 down to 4 -Scaling foo-v2 up to 9 -Scaling foo-v1 down to 1 -Scaling foo-v2 up to 10 -Scaling foo-v1 down to 0 -`, - }, { - name: "10->10 0/30 delayed readiness", - oldRc: oldRc(10, 10), - newRc: newRc(0, 10), - newRcExists: false, - maxUnavail: intstr.FromString("0%"), - maxSurge: intstr.FromString("30%"), - expected: []interface{}{ - up{3}, - down{oldReady: 10, newReady: 0, noop: true}, - down{oldReady: 10, newReady: 1, to: 9}, - up{4}, - down{oldReady: 9, newReady: 3, to: 7}, - up{6}, - down{oldReady: 7, newReady: 6, to: 4}, - up{9}, - down{oldReady: 4, newReady: 9, to: 1}, - up{10}, - down{oldReady: 1, newReady: 9, noop: true}, - down{oldReady: 1, newReady: 10, to: 0}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 10 pods available, don't exceed 13 pods) -Scaling foo-v2 up to 3 -Scaling foo-v1 down to 9 -Scaling foo-v2 up to 4 -Scaling foo-v1 down to 7 -Scaling foo-v2 up to 6 -Scaling foo-v1 down to 4 -Scaling foo-v2 up to 9 -Scaling foo-v1 down to 1 -Scaling foo-v2 up to 10 -Scaling foo-v1 down to 0 -`, - }, { - name: "10->10 10/20 fast readiness", - oldRc: oldRc(10, 10), - newRc: newRc(0, 10), - newRcExists: false, - maxUnavail: intstr.FromString("10%"), - maxSurge: intstr.FromString("20%"), - expected: []interface{}{ - up{2}, - down{oldReady: 10, newReady: 2, to: 7}, - up{5}, - down{oldReady: 7, newReady: 5, to: 4}, - up{8}, - down{oldReady: 4, newReady: 8, to: 1}, - up{10}, - down{oldReady: 1, newReady: 10, to: 0}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 9 pods available, don't exceed 12 pods) -Scaling foo-v2 up to 2 -Scaling foo-v1 down to 7 -Scaling foo-v2 up to 5 -Scaling foo-v1 down to 4 -Scaling foo-v2 up to 8 -Scaling foo-v1 down to 1 -Scaling foo-v2 up to 10 -Scaling foo-v1 down to 0 -`, - }, { - name: "10->10 10/20 delayed readiness", - oldRc: oldRc(10, 10), - newRc: newRc(0, 10), - newRcExists: false, - maxUnavail: intstr.FromString("10%"), - maxSurge: intstr.FromString("20%"), - expected: []interface{}{ - up{2}, - down{oldReady: 10, newReady: 2, to: 7}, - up{5}, - down{oldReady: 7, newReady: 4, to: 5}, - up{7}, - down{oldReady: 5, newReady: 4, noop: true}, - down{oldReady: 5, newReady: 7, to: 2}, - up{10}, - down{oldReady: 2, newReady: 9, to: 0}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 9 pods available, don't exceed 12 pods) -Scaling foo-v2 up to 2 -Scaling foo-v1 down to 7 -Scaling foo-v2 up to 5 -Scaling foo-v1 down to 5 -Scaling foo-v2 up to 7 -Scaling foo-v1 down to 2 -Scaling foo-v2 up to 10 -Scaling foo-v1 down to 0 -`, - }, { - name: "10->10 10/20 fast readiness continued after restart which prevented first scale-down", - oldRc: oldRc(10, 10), - newRc: newRc(2, 10), - newRcExists: false, - maxUnavail: intstr.FromString("10%"), - maxSurge: intstr.FromString("20%"), - expected: []interface{}{ - down{oldReady: 10, newReady: 2, to: 7}, - up{5}, - down{oldReady: 7, newReady: 5, to: 4}, - up{8}, - down{oldReady: 4, newReady: 8, to: 1}, - up{10}, - down{oldReady: 1, newReady: 10, to: 0}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 2 to 10, scaling down foo-v1 from 10 to 0 (keep 9 pods available, don't exceed 12 pods) -Scaling foo-v1 down to 7 -Scaling foo-v2 up to 5 -Scaling foo-v1 down to 4 -Scaling foo-v2 up to 8 -Scaling foo-v1 down to 1 -Scaling foo-v2 up to 10 -Scaling foo-v1 down to 0 -`, - }, { - name: "10->10 0/100 fast readiness", - oldRc: oldRc(10, 10), - newRc: newRc(0, 10), - newRcExists: false, - maxUnavail: intstr.FromString("0%"), - maxSurge: intstr.FromString("100%"), - expected: []interface{}{ - up{10}, - down{oldReady: 10, newReady: 10, to: 0}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 10 pods available, don't exceed 20 pods) -Scaling foo-v2 up to 10 -Scaling foo-v1 down to 0 -`, - }, { - name: "10->10 0/100 delayed readiness", - oldRc: oldRc(10, 10), - newRc: newRc(0, 10), - newRcExists: false, - maxUnavail: intstr.FromString("0%"), - maxSurge: intstr.FromString("100%"), - expected: []interface{}{ - up{10}, - down{oldReady: 10, newReady: 0, noop: true}, - down{oldReady: 10, newReady: 2, to: 8}, - down{oldReady: 8, newReady: 7, to: 3}, - down{oldReady: 3, newReady: 10, to: 0}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 10 pods available, don't exceed 20 pods) -Scaling foo-v2 up to 10 -Scaling foo-v1 down to 8 -Scaling foo-v1 down to 3 -Scaling foo-v1 down to 0 -`, - }, { - name: "10->10 100/0 fast readiness", - oldRc: oldRc(10, 10), - newRc: newRc(0, 10), - newRcExists: false, - maxUnavail: intstr.FromString("100%"), - maxSurge: intstr.FromString("0%"), - expected: []interface{}{ - down{oldReady: 10, newReady: 0, to: 0}, - up{10}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 10, scaling down foo-v1 from 10 to 0 (keep 0 pods available, don't exceed 10 pods) -Scaling foo-v1 down to 0 -Scaling foo-v2 up to 10 -`, - }, { - name: "1->1 10/0 fast readiness", - oldRc: oldRc(1, 1), - newRc: newRc(0, 1), - newRcExists: false, - maxUnavail: intstr.FromString("10%"), - maxSurge: intstr.FromString("0%"), - expected: []interface{}{ - down{oldReady: 1, newReady: 0, to: 0}, - up{1}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 1, scaling down foo-v1 from 1 to 0 (keep 0 pods available, don't exceed 1 pods) -Scaling foo-v1 down to 0 -Scaling foo-v2 up to 1 -`, - }, { - name: "1->1 0/10 delayed readiness", - oldRc: oldRc(1, 1), - newRc: newRc(0, 1), - newRcExists: false, - maxUnavail: intstr.FromString("0%"), - maxSurge: intstr.FromString("10%"), - expected: []interface{}{ - up{1}, - down{oldReady: 1, newReady: 0, noop: true}, - down{oldReady: 1, newReady: 1, to: 0}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 1, scaling down foo-v1 from 1 to 0 (keep 1 pods available, don't exceed 2 pods) -Scaling foo-v2 up to 1 -Scaling foo-v1 down to 0 -`, - }, { - name: "1->1 10/10 delayed readiness", - oldRc: oldRc(1, 1), - newRc: newRc(0, 1), - newRcExists: false, - maxUnavail: intstr.FromString("10%"), - maxSurge: intstr.FromString("10%"), - expected: []interface{}{ - up{1}, - down{oldReady: 1, newReady: 0, noop: true}, - down{oldReady: 1, newReady: 1, to: 0}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 1, scaling down foo-v1 from 1 to 0 (keep 0 pods available, don't exceed 2 pods) -Scaling foo-v2 up to 1 -Scaling foo-v1 down to 0 -`, - }, { - name: "3->3 1/1 fast readiness (absolute values)", - oldRc: oldRc(3, 3), - newRc: newRc(0, 3), - newRcExists: false, - maxUnavail: intstr.FromInt(0), - maxSurge: intstr.FromInt(1), - expected: []interface{}{ - up{1}, - down{oldReady: 3, newReady: 1, to: 2}, - up{2}, - down{oldReady: 2, newReady: 2, to: 1}, - up{3}, - down{oldReady: 1, newReady: 3, to: 0}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 3, scaling down foo-v1 from 3 to 0 (keep 3 pods available, don't exceed 4 pods) -Scaling foo-v2 up to 1 -Scaling foo-v1 down to 2 -Scaling foo-v2 up to 2 -Scaling foo-v1 down to 1 -Scaling foo-v2 up to 3 -Scaling foo-v1 down to 0 -`, - }, { - name: "10->10 0/20 fast readiness, continued after restart which resulted in partial first scale-up", - oldRc: oldRc(6, 10), - newRc: newRc(5, 10), - newRcExists: false, - maxUnavail: intstr.FromString("0%"), - maxSurge: intstr.FromString("20%"), - expected: []interface{}{ - up{6}, - down{oldReady: 6, newReady: 6, to: 4}, - up{8}, - down{oldReady: 4, newReady: 8, to: 2}, - up{10}, - down{oldReady: 1, newReady: 10, to: 0}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 5 to 10, scaling down foo-v1 from 6 to 0 (keep 10 pods available, don't exceed 12 pods) -Scaling foo-v2 up to 6 -Scaling foo-v1 down to 4 -Scaling foo-v2 up to 8 -Scaling foo-v1 down to 2 -Scaling foo-v2 up to 10 -Scaling foo-v1 down to 0 -`, - }, { - name: "10->20 0/300 fast readiness", - oldRc: oldRc(10, 10), - newRc: newRc(0, 20), - newRcExists: false, - maxUnavail: intstr.FromString("0%"), - maxSurge: intstr.FromString("300%"), - expected: []interface{}{ - up{20}, - down{oldReady: 10, newReady: 20, to: 0}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 20, scaling down foo-v1 from 10 to 0 (keep 20 pods available, don't exceed 70 pods) -Scaling foo-v2 up to 20 -Scaling foo-v1 down to 0 -`, - }, { - name: "1->1 0/1 scale down unavailable rc to a ready rc (rollback)", - oldRc: oldRc(1, 1), - newRc: newRc(1, 1), - newRcExists: true, - maxUnavail: intstr.FromInt(0), - maxSurge: intstr.FromInt(1), - expected: []interface{}{ - up{1}, - down{oldReady: 0, newReady: 1, to: 0}, - }, - output: `Continuing update with existing controller foo-v2. -Scaling up foo-v2 from 1 to 1, scaling down foo-v1 from 1 to 0 (keep 1 pods available, don't exceed 2 pods) -Scaling foo-v1 down to 0 -`, - }, - { - name: "3->0 1/1 desired 0 (absolute values)", - oldRc: oldRc(3, 3), - newRc: newRc(0, 0), - newRcExists: true, - maxUnavail: intstr.FromInt(1), - maxSurge: intstr.FromInt(1), - expected: []interface{}{ - down{oldReady: 3, newReady: 0, to: 0}, - }, - output: `Continuing update with existing controller foo-v2. -Scaling up foo-v2 from 0 to 0, scaling down foo-v1 from 3 to 0 (keep 0 pods available, don't exceed 4 pods) -Scaling foo-v1 down to 0 -`, - }, - { - name: "3->0 10/10 desired 0 (percentages)", - oldRc: oldRc(3, 3), - newRc: newRc(0, 0), - newRcExists: true, - maxUnavail: intstr.FromString("10%"), - maxSurge: intstr.FromString("10%"), - expected: []interface{}{ - down{oldReady: 3, newReady: 0, to: 0}, - }, - output: `Continuing update with existing controller foo-v2. -Scaling up foo-v2 from 0 to 0, scaling down foo-v1 from 3 to 0 (keep 0 pods available, don't exceed 3 pods) -Scaling foo-v1 down to 0 -`, - }, - { - name: "3->0 10/10 desired 0 (create new RC)", - oldRc: oldRc(3, 3), - newRc: newRc(0, 0), - newRcExists: false, - maxUnavail: intstr.FromString("10%"), - maxSurge: intstr.FromString("10%"), - expected: []interface{}{ - down{oldReady: 3, newReady: 0, to: 0}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 0, scaling down foo-v1 from 3 to 0 (keep 0 pods available, don't exceed 3 pods) -Scaling foo-v1 down to 0 -`, - }, - { - name: "0->0 1/1 desired 0 (absolute values)", - oldRc: oldRc(0, 0), - newRc: newRc(0, 0), - newRcExists: true, - maxUnavail: intstr.FromInt(1), - maxSurge: intstr.FromInt(1), - expected: []interface{}{ - down{oldReady: 0, newReady: 0, to: 0}, - }, - output: `Continuing update with existing controller foo-v2. -Scaling up foo-v2 from 0 to 0, scaling down foo-v1 from 0 to 0 (keep 0 pods available, don't exceed 1 pods) -`, - }, { - name: "30->2 50%/0", - oldRc: oldRc(30, 30), - newRc: newRc(0, 2), - newRcExists: false, - maxUnavail: intstr.FromString("50%"), - maxSurge: intstr.FromInt(0), - expected: []interface{}{ - down{oldReady: 30, newReady: 0, to: 1}, - up{2}, - down{oldReady: 1, newReady: 2, to: 0}, - }, - output: `Created foo-v2 -Scaling up foo-v2 from 0 to 2, scaling down foo-v1 from 30 to 0 (keep 1 pods available, don't exceed 30 pods) -Scaling foo-v1 down to 1 -Scaling foo-v2 up to 2 -Scaling foo-v1 down to 0 -`, - }, - } - - for i, test := range tests { - // Extract expectations into some makeshift FIFOs so they can be returned - // in the correct order from the right places. This lets scale downs be - // expressed a single event even though the data is used from multiple - // interface calls. - oldReady := []int{} - newReady := []int{} - upTo := []int{} - downTo := []int{} - for _, event := range test.expected { - switch e := event.(type) { - case down: - oldReady = append(oldReady, e.oldReady) - newReady = append(newReady, e.newReady) - if !e.noop { - downTo = append(downTo, e.to) - } - case up: - upTo = append(upTo, e.to) - } - } - - // Make a way to get the next item from our FIFOs. Returns -1 if the array - // is empty. - next := func(s *[]int) int { - slice := *s - v := -1 - if len(slice) > 0 { - v = slice[0] - if len(slice) > 1 { - *s = slice[1:] - } else { - *s = []int{} - } - } - return v - } - t.Logf("running test %d (%s) (up: %v, down: %v, oldReady: %v, newReady: %v)", i, test.name, upTo, downTo, oldReady, newReady) - updater := &RollingUpdater{ - ns: "default", - scaleAndWait: func(rc *api.ReplicationController, retry *RetryParams, wait *RetryParams) (*api.ReplicationController, error) { - // Return a scale up or scale down expectation depending on the rc, - // and throw errors if there is no expectation expressed for this - // call. - expected := -1 - switch { - case rc == test.newRc: - t.Logf("scaling up %s:%d", rc.Name, rc.Spec.Replicas) - expected = next(&upTo) - case rc == test.oldRc: - t.Logf("scaling down %s:%d", rc.Name, rc.Spec.Replicas) - expected = next(&downTo) - } - if expected == -1 { - t.Fatalf("unexpected scale of %s to %d", rc.Name, rc.Spec.Replicas) - } else if e, a := expected, rc.Spec.Replicas; e != a { - t.Fatalf("expected scale of %s to %d, got %d", rc.Name, e, a) - } - // Simulate the scale. - rc.Status.Replicas = rc.Spec.Replicas - return rc, nil - }, - getOrCreateTargetController: func(controller *api.ReplicationController, sourceId string) (*api.ReplicationController, bool, error) { - // Simulate a create vs. update of an existing controller. - return test.newRc, test.newRcExists, nil - }, - cleanup: func(oldRc, newRc *api.ReplicationController, config *RollingUpdaterConfig) error { - return nil - }, - } - // Set up a mock readiness check which handles the test assertions. - updater.waitForReadyPods = func(interval, timeout time.Duration, oldRc, newRc *api.ReplicationController) (int, int, error) { - // Return simulated readiness, and throw an error if this call has no - // expectations defined. - oldReady := next(&oldReady) - newReady := next(&newReady) - if oldReady == -1 || newReady == -1 { - t.Fatalf("unexpected waitForReadyPods call for:\noldRc: %+v\nnewRc: %+v", oldRc, newRc) - } - return oldReady, newReady, nil - } - var buffer bytes.Buffer - config := &RollingUpdaterConfig{ - Out: &buffer, - OldRc: test.oldRc, - NewRc: test.newRc, - UpdatePeriod: 0, - Interval: time.Millisecond, - Timeout: time.Millisecond, - CleanupPolicy: DeleteRollingUpdateCleanupPolicy, - MaxUnavailable: test.maxUnavail, - MaxSurge: test.maxSurge, - } - err := updater.Update(config) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if buffer.String() != test.output { - t.Errorf("Bad output. expected:\n%s\ngot:\n%s", test.output, buffer.String()) - } - } -} - -// TestUpdate_progressTimeout ensures that an update which isn't making any -// progress will eventually time out with a specified error. -func TestUpdate_progressTimeout(t *testing.T) { - oldRc := oldRc(2, 2) - newRc := newRc(0, 2) - updater := &RollingUpdater{ - ns: "default", - scaleAndWait: func(rc *api.ReplicationController, retry *RetryParams, wait *RetryParams) (*api.ReplicationController, error) { - // Do nothing. - return rc, nil - }, - getOrCreateTargetController: func(controller *api.ReplicationController, sourceId string) (*api.ReplicationController, bool, error) { - return newRc, false, nil - }, - cleanup: func(oldRc, newRc *api.ReplicationController, config *RollingUpdaterConfig) error { - return nil - }, - } - updater.waitForReadyPods = func(interval, timeout time.Duration, oldRc, newRc *api.ReplicationController) (int, int, error) { - // Coerce a timeout by pods never becoming ready. - return 0, 0, nil - } - var buffer bytes.Buffer - config := &RollingUpdaterConfig{ - Out: &buffer, - OldRc: oldRc, - NewRc: newRc, - UpdatePeriod: 0, - Interval: time.Millisecond, - Timeout: time.Millisecond, - CleanupPolicy: DeleteRollingUpdateCleanupPolicy, - MaxUnavailable: intstr.FromInt(0), - MaxSurge: intstr.FromInt(1), - } - err := updater.Update(config) - if err == nil { - t.Fatalf("expected an error") - } - if e, a := "timed out waiting for any update progress to be made", err.Error(); e != a { - t.Fatalf("expected error message: %s, got: %s", e, a) - } -} - -func TestUpdate_assignOriginalAnnotation(t *testing.T) { - oldRc := oldRc(1, 1) - delete(oldRc.Annotations, originalReplicasAnnotation) - newRc := newRc(1, 1) - var updatedOldRc *api.ReplicationController - fake := &testclient.Fake{} - fake.AddReactor("*", "*", func(action testclient.Action) (handled bool, ret runtime.Object, err error) { - switch a := action.(type) { - case testclient.GetAction: - return true, oldRc, nil - case testclient.UpdateAction: - updatedOldRc = a.GetObject().(*api.ReplicationController) - return true, updatedOldRc, nil - } - return false, nil, nil - }) - updater := &RollingUpdater{ - c: fake, - ns: "default", - scaleAndWait: func(rc *api.ReplicationController, retry *RetryParams, wait *RetryParams) (*api.ReplicationController, error) { - return rc, nil - }, - getOrCreateTargetController: func(controller *api.ReplicationController, sourceId string) (*api.ReplicationController, bool, error) { - return newRc, false, nil - }, - cleanup: func(oldRc, newRc *api.ReplicationController, config *RollingUpdaterConfig) error { - return nil - }, - waitForReadyPods: func(interval, timeout time.Duration, oldRc, newRc *api.ReplicationController) (int, int, error) { - return 1, 1, nil - }, - } - var buffer bytes.Buffer - config := &RollingUpdaterConfig{ - Out: &buffer, - OldRc: oldRc, - NewRc: newRc, - UpdatePeriod: 0, - Interval: time.Millisecond, - Timeout: time.Millisecond, - CleanupPolicy: DeleteRollingUpdateCleanupPolicy, - MaxUnavailable: intstr.FromString("100%"), - } - err := updater.Update(config) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if updatedOldRc == nil { - t.Fatalf("expected rc to be updated") - } - if e, a := "1", updatedOldRc.Annotations[originalReplicasAnnotation]; e != a { - t.Fatalf("expected annotation value %s, got %s", e, a) - } -} - -func TestRollingUpdater_multipleContainersInPod(t *testing.T) { - tests := []struct { - oldRc *api.ReplicationController - newRc *api.ReplicationController - - container string - image string - deploymentKey string - }{ - { - oldRc: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.ReplicationControllerSpec{ - Selector: map[string]string{ - "dk": "old", - }, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "dk": "old", - }, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "container1", - Image: "image1", - }, - { - Name: "container2", - Image: "image2", - }, - }, - }, - }, - }, - }, - newRc: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.ReplicationControllerSpec{ - Selector: map[string]string{ - "dk": "old", - }, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "dk": "old", - }, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "container1", - Image: "newimage", - }, - { - Name: "container2", - Image: "image2", - }, - }, - }, - }, - }, - }, - container: "container1", - image: "newimage", - deploymentKey: "dk", - }, - { - oldRc: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "bar", - }, - Spec: api.ReplicationControllerSpec{ - Selector: map[string]string{ - "dk": "old", - }, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "dk": "old", - }, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "container1", - Image: "image1", - }, - }, - }, - }, - }, - }, - newRc: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "bar", - }, - Spec: api.ReplicationControllerSpec{ - Selector: map[string]string{ - "dk": "old", - }, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "dk": "old", - }, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "container1", - Image: "newimage", - }, - }, - }, - }, - }, - }, - container: "container1", - image: "newimage", - deploymentKey: "dk", - }, - } - - for _, test := range tests { - fake := &testclient.Fake{} - fake.AddReactor("*", "*", func(action testclient.Action) (handled bool, ret runtime.Object, err error) { - switch action.(type) { - case testclient.GetAction: - return true, test.oldRc, nil - } - return false, nil, nil - }) - - codec := testapi.Default.Codec() - - deploymentHash, err := api.HashObject(test.newRc, codec) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - test.newRc.Spec.Selector[test.deploymentKey] = deploymentHash - test.newRc.Spec.Template.Labels[test.deploymentKey] = deploymentHash - test.newRc.Name = fmt.Sprintf("%s-%s", test.newRc.Name, deploymentHash) - - updatedRc, err := CreateNewControllerFromCurrentController(fake, codec, "", test.oldRc.ObjectMeta.Name, test.newRc.ObjectMeta.Name, test.image, test.container, test.deploymentKey) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !reflect.DeepEqual(updatedRc, test.newRc) { - t.Errorf("expected:\n%v\ngot:\n%v\n", test.newRc, updatedRc) - } - } -} - -// TestRollingUpdater_cleanupWithClients ensures that the cleanup policy is -// correctly implemented. -func TestRollingUpdater_cleanupWithClients(t *testing.T) { - rc := oldRc(2, 2) - rcExisting := newRc(1, 3) - - tests := []struct { - name string - policy RollingUpdaterCleanupPolicy - responses []runtime.Object - expected []string - }{ - { - name: "preserve", - policy: PreserveRollingUpdateCleanupPolicy, - responses: []runtime.Object{rcExisting}, - expected: []string{ - "get", - "update", - "get", - "get", - }, - }, - { - name: "delete", - policy: DeleteRollingUpdateCleanupPolicy, - responses: []runtime.Object{rcExisting}, - expected: []string{ - "get", - "update", - "get", - "get", - "delete", - }, - }, - { - name: "rename", - policy: RenameRollingUpdateCleanupPolicy, - responses: []runtime.Object{rcExisting}, - expected: []string{ - "get", - "update", - "get", - "get", - "delete", - "create", - "delete", - }, - }, - } - - for _, test := range tests { - fake := testclient.NewSimpleFake(test.responses...) - updater := &RollingUpdater{ - ns: "default", - c: fake, - } - config := &RollingUpdaterConfig{ - Out: ioutil.Discard, - OldRc: rc, - NewRc: rcExisting, - UpdatePeriod: 0, - Interval: time.Millisecond, - Timeout: time.Millisecond, - CleanupPolicy: test.policy, - } - err := updater.cleanupWithClients(rc, rcExisting, config) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if len(fake.Actions()) != len(test.expected) { - t.Fatalf("%s: unexpected actions: %v, expected %v", test.name, fake.Actions(), test.expected) - } - for j, action := range fake.Actions() { - if e, a := test.expected[j], action.GetVerb(); e != a { - t.Errorf("%s: unexpected action: expected %s, got %s", test.name, e, a) - } - } - } -} - -func TestFindSourceController(t *testing.T) { - ctrl1 := api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Annotations: map[string]string{ - sourceIdAnnotation: "bar:1234", - }, - }, - } - ctrl2 := api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "bar", - Annotations: map[string]string{ - sourceIdAnnotation: "foo:12345", - }, - }, - } - ctrl3 := api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{ - sourceIdAnnotation: "baz:45667", - }, - }, - } - tests := []struct { - list *api.ReplicationControllerList - expectedController *api.ReplicationController - err error - name string - expectError bool - }{ - { - list: &api.ReplicationControllerList{}, - expectError: true, - }, - { - list: &api.ReplicationControllerList{ - Items: []api.ReplicationController{ctrl1}, - }, - name: "foo", - expectError: true, - }, - { - list: &api.ReplicationControllerList{ - Items: []api.ReplicationController{ctrl1}, - }, - name: "bar", - expectedController: &ctrl1, - }, - { - list: &api.ReplicationControllerList{ - Items: []api.ReplicationController{ctrl1, ctrl2}, - }, - name: "bar", - expectedController: &ctrl1, - }, - { - list: &api.ReplicationControllerList{ - Items: []api.ReplicationController{ctrl1, ctrl2}, - }, - name: "foo", - expectedController: &ctrl2, - }, - { - list: &api.ReplicationControllerList{ - Items: []api.ReplicationController{ctrl1, ctrl2, ctrl3}, - }, - name: "baz", - expectedController: &ctrl3, - }, - } - for _, test := range tests { - fakeClient := testclient.NewSimpleFake(test.list) - ctrl, err := FindSourceController(fakeClient, "default", test.name) - if test.expectError && err == nil { - t.Errorf("unexpected non-error") - } - if !test.expectError && err != nil { - t.Errorf("unexpected error") - } - if !reflect.DeepEqual(ctrl, test.expectedController) { - t.Errorf("expected:\n%v\ngot:\n%v\n", test.expectedController, ctrl) - } - } -} - -func TestUpdateExistingReplicationController(t *testing.T) { - tests := []struct { - rc *api.ReplicationController - name string - deploymentKey string - deploymentValue string - - expectedRc *api.ReplicationController - expectErr bool - }{ - { - rc: &api.ReplicationController{ - Spec: api.ReplicationControllerSpec{ - Template: &api.PodTemplateSpec{}, - }, - }, - name: "foo", - deploymentKey: "dk", - deploymentValue: "some-hash", - - expectedRc: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{ - "kubectl.kubernetes.io/next-controller-id": "foo", - }, - }, - Spec: api.ReplicationControllerSpec{ - Selector: map[string]string{ - "dk": "some-hash", - }, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "dk": "some-hash", - }, - }, - }, - }, - }, - }, - { - rc: &api.ReplicationController{ - Spec: api.ReplicationControllerSpec{ - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "dk": "some-other-hash", - }, - }, - }, - Selector: map[string]string{ - "dk": "some-other-hash", - }, - }, - }, - name: "foo", - deploymentKey: "dk", - deploymentValue: "some-hash", - - expectedRc: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Annotations: map[string]string{ - "kubectl.kubernetes.io/next-controller-id": "foo", - }, - }, - Spec: api.ReplicationControllerSpec{ - Selector: map[string]string{ - "dk": "some-other-hash", - }, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "dk": "some-other-hash", - }, - }, - }, - }, - }, - }, - } - for _, test := range tests { - buffer := &bytes.Buffer{} - fakeClient := testclient.NewSimpleFake(test.expectedRc) - rc, err := UpdateExistingReplicationController(fakeClient, test.rc, "default", test.name, test.deploymentKey, test.deploymentValue, buffer) - if !reflect.DeepEqual(rc, test.expectedRc) { - t.Errorf("expected:\n%#v\ngot:\n%#v\n", test.expectedRc, rc) - } - if test.expectErr && err == nil { - t.Errorf("unexpected non-error") - } - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v", err) - } - } -} - -func TestUpdateWithRetries(t *testing.T) { - codec := testapi.Default.Codec() - rc := &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "rc", - Labels: map[string]string{ - "foo": "bar", - }, - }, - Spec: api.ReplicationControllerSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - Spec: apitesting.DeepEqualSafePodSpec(), - }, - }, - } - - // Test end to end updating of the rc with retries. Essentially make sure the update handler - // sees the right updates, failures in update/get are handled properly, and that the updated - // rc with new resource version is returned to the caller. Without any of these rollingupdate - // will fail cryptically. - newRc := *rc - newRc.ResourceVersion = "2" - newRc.Spec.Selector["baz"] = "foobar" - updates := []*http.Response{ - {StatusCode: 500, Body: objBody(codec, &api.ReplicationController{})}, - {StatusCode: 500, Body: objBody(codec, &api.ReplicationController{})}, - {StatusCode: 200, Body: objBody(codec, &newRc)}, - } - gets := []*http.Response{ - {StatusCode: 500, Body: objBody(codec, &api.ReplicationController{})}, - {StatusCode: 200, Body: objBody(codec, rc)}, - } - fakeClient := &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == testapi.Default.ResourcePath("replicationcontrollers", "default", "rc") && m == "PUT": - update := updates[0] - updates = updates[1:] - // We should always get an update with a valid rc even when the get fails. The rc should always - // contain the update. - if c, ok := readOrDie(t, req, codec).(*api.ReplicationController); !ok || !reflect.DeepEqual(rc, c) { - t.Errorf("Unexpected update body, got %+v expected %+v", c, rc) - } else if sel, ok := c.Spec.Selector["baz"]; !ok || sel != "foobar" { - t.Errorf("Expected selector label update, got %+v", c.Spec.Selector) - } else { - delete(c.Spec.Selector, "baz") - } - return update, nil - case p == testapi.Default.ResourcePath("replicationcontrollers", "default", "rc") && m == "GET": - get := gets[0] - gets = gets[1:] - return get, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - clientConfig := &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} - client := client.NewOrDie(clientConfig) - client.Client = fakeClient.Client - - if rc, err := updateWithRetries( - client.ReplicationControllers("default"), rc, func(c *api.ReplicationController) { - c.Spec.Selector["baz"] = "foobar" - }); err != nil { - t.Errorf("unexpected error: %v", err) - } else if sel, ok := rc.Spec.Selector["baz"]; !ok || sel != "foobar" || rc.ResourceVersion != "2" { - t.Errorf("Expected updated rc, got %+v", rc) - } - if len(updates) != 0 || len(gets) != 0 { - t.Errorf("Remaining updates %+v gets %+v", updates, gets) - } -} - -func readOrDie(t *testing.T, req *http.Request, codec runtime.Codec) runtime.Object { - data, err := ioutil.ReadAll(req.Body) - if err != nil { - t.Errorf("Error reading: %v", err) - t.FailNow() - } - obj, err := runtime.Decode(codec, data) - if err != nil { - t.Errorf("error decoding: %v", err) - t.FailNow() - } - return obj -} - -func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser { - return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj)))) -} - -func TestAddDeploymentHash(t *testing.T) { - buf := &bytes.Buffer{} - codec := testapi.Default.Codec() - rc := &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{Name: "rc"}, - Spec: api.ReplicationControllerSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{ - "foo": "bar", - }, - }, - }, - }, - } - - podList := &api.PodList{ - Items: []api.Pod{ - {ObjectMeta: api.ObjectMeta{Name: "foo"}}, - {ObjectMeta: api.ObjectMeta{Name: "bar"}}, - {ObjectMeta: api.ObjectMeta{Name: "baz"}}, - }, - } - - seen := sets.String{} - updatedRc := false - fakeClient := &fake.RESTClient{ - Codec: codec, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - switch p, m := req.URL.Path, req.Method; { - case p == testapi.Default.ResourcePath("pods", "default", "") && m == "GET": - if req.URL.RawQuery != "labelSelector=foo%3Dbar" { - t.Errorf("Unexpected query string: %s", req.URL.RawQuery) - } - return &http.Response{StatusCode: 200, Body: objBody(codec, podList)}, nil - case p == testapi.Default.ResourcePath("pods", "default", "foo") && m == "PUT": - seen.Insert("foo") - obj := readOrDie(t, req, codec) - podList.Items[0] = *(obj.(*api.Pod)) - return &http.Response{StatusCode: 200, Body: objBody(codec, &podList.Items[0])}, nil - case p == testapi.Default.ResourcePath("pods", "default", "bar") && m == "PUT": - seen.Insert("bar") - obj := readOrDie(t, req, codec) - podList.Items[1] = *(obj.(*api.Pod)) - return &http.Response{StatusCode: 200, Body: objBody(codec, &podList.Items[1])}, nil - case p == testapi.Default.ResourcePath("pods", "default", "baz") && m == "PUT": - seen.Insert("baz") - obj := readOrDie(t, req, codec) - podList.Items[2] = *(obj.(*api.Pod)) - return &http.Response{StatusCode: 200, Body: objBody(codec, &podList.Items[2])}, nil - case p == testapi.Default.ResourcePath("replicationcontrollers", "default", "rc") && m == "PUT": - updatedRc = true - return &http.Response{StatusCode: 200, Body: objBody(codec, rc)}, nil - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } - clientConfig := &client.Config{ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} - client := client.NewOrDie(clientConfig) - client.Client = fakeClient.Client - - if _, err := AddDeploymentKeyToReplicationController(rc, client, "dk", "hash", api.NamespaceDefault, buf); err != nil { - t.Errorf("unexpected error: %v", err) - } - for _, pod := range podList.Items { - if !seen.Has(pod.Name) { - t.Errorf("Missing update for pod: %s", pod.Name) - } - } - if !updatedRc { - t.Errorf("Failed to update replication controller with new labels") - } -} - -func TestRollingUpdater_pollForReadyPods(t *testing.T) { - mkpod := func(owner *api.ReplicationController, ready bool) *api.Pod { - labels := map[string]string{} - for k, v := range owner.Spec.Selector { - labels[k] = v - } - status := api.ConditionTrue - if !ready { - status = api.ConditionFalse - } - return &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "pod", - Labels: labels, - }, - Status: api.PodStatus{ - Conditions: []api.PodCondition{ - { - Type: api.PodReady, - Status: status, - }, - }, - }, - } - } - - tests := []struct { - oldRc *api.ReplicationController - newRc *api.ReplicationController - // expectated old/new ready counts - oldReady int - newReady int - // pods owned by the rcs; indicate whether they're ready - oldPods []bool - newPods []bool - }{ - { - oldRc: oldRc(4, 4), - newRc: newRc(4, 4), - oldReady: 4, - newReady: 2, - oldPods: []bool{ - true, - true, - true, - true, - }, - newPods: []bool{ - true, - false, - true, - false, - }, - }, - { - oldRc: oldRc(4, 4), - newRc: newRc(4, 4), - oldReady: 0, - newReady: 1, - oldPods: []bool{ - false, - }, - newPods: []bool{ - true, - }, - }, - { - oldRc: oldRc(4, 4), - newRc: newRc(4, 4), - oldReady: 1, - newReady: 0, - oldPods: []bool{ - true, - }, - newPods: []bool{ - false, - }, - }, - } - - for i, test := range tests { - t.Logf("evaluating test %d", i) - // Populate the fake client with pods associated with their owners. - pods := []runtime.Object{} - for _, ready := range test.oldPods { - pods = append(pods, mkpod(test.oldRc, ready)) - } - for _, ready := range test.newPods { - pods = append(pods, mkpod(test.newRc, ready)) - } - client := testclient.NewSimpleFake(pods...) - - updater := &RollingUpdater{ - ns: "default", - c: client, - } - oldReady, newReady, err := updater.pollForReadyPods(time.Millisecond, time.Second, test.oldRc, test.newRc) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if e, a := test.oldReady, oldReady; e != a { - t.Errorf("expected old ready %d, got %d", e, a) - } - if e, a := test.newReady, newReady; e != a { - t.Errorf("expected new ready %d, got %d", e, a) - } - } -} - -func TestRollingUpdater_extractMaxValue(t *testing.T) { - tests := []struct { - field intstr.IntOrString - original int - expected int - valid bool - }{ - { - field: intstr.FromInt(1), - original: 100, - expected: 1, - valid: true, - }, - { - field: intstr.FromInt(0), - original: 100, - expected: 0, - valid: true, - }, - { - field: intstr.FromInt(-1), - original: 100, - valid: false, - }, - { - field: intstr.FromString("10%"), - original: 100, - expected: 10, - valid: true, - }, - { - field: intstr.FromString("100%"), - original: 100, - expected: 100, - valid: true, - }, - { - field: intstr.FromString("200%"), - original: 100, - expected: 200, - valid: true, - }, - { - field: intstr.FromString("0%"), - original: 100, - expected: 0, - valid: true, - }, - { - field: intstr.FromString("-1%"), - original: 100, - valid: false, - }, - } - - for i, test := range tests { - t.Logf("evaluating test %d", i) - max, err := extractMaxValue(test.field, "field", test.original) - if test.valid && err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !test.valid && err == nil { - t.Fatalf("expected an error") - } - if e, a := test.expected, max; e != a { - t.Fatalf("expected max %d, got %d", e, a) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/run.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/run.go index e0ed8ada8..688b570e7 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/run.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/run.go @@ -24,6 +24,8 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" + batchv1 "k8s.io/kubernetes/pkg/apis/batch/v1" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/validation" @@ -187,6 +189,24 @@ func getEnvs(genericParams map[string]interface{}) ([]api.EnvVar, error) { return envs, nil } +func getV1Envs(genericParams map[string]interface{}) ([]v1.EnvVar, error) { + var envs []v1.EnvVar + envStrings, found := genericParams["env"] + if found { + if envStringArray, isArray := envStrings.([]string); isArray { + var err error + envs, err = parseV1Envs(envStringArray) + if err != nil { + return nil, err + } + delete(genericParams, "env") + } else { + return nil, fmt.Errorf("expected []string, found: %v", envStrings) + } + } + return envs, nil +} + type JobV1Beta1 struct{} func (JobV1Beta1) ParamNames() []GeneratorParam { @@ -256,7 +276,7 @@ func (JobV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object restartPolicy := api.RestartPolicy(params["restart"]) if len(restartPolicy) == 0 { - restartPolicy = api.RestartPolicyAlways + restartPolicy = api.RestartPolicyNever } podSpec.RestartPolicy = restartPolicy @@ -269,6 +289,7 @@ func (JobV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object Selector: &unversioned.LabelSelector{ MatchLabels: labels, }, + ManualSelector: newBool(true), Template: api.PodTemplateSpec{ ObjectMeta: api.ObjectMeta{ Labels: labels, @@ -281,6 +302,97 @@ func (JobV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object return &job, nil } +type JobV1 struct{} + +func (JobV1) ParamNames() []GeneratorParam { + return []GeneratorParam{ + {"labels", false}, + {"default-name", false}, + {"name", true}, + {"image", true}, + {"port", false}, + {"hostport", false}, + {"stdin", false}, + {"leave-stdin-open", false}, + {"tty", false}, + {"command", false}, + {"args", false}, + {"env", false}, + {"requests", false}, + {"limits", false}, + {"restart", false}, + } +} + +func (JobV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) { + args, err := getArgs(genericParams) + if err != nil { + return nil, err + } + + envs, err := getV1Envs(genericParams) + if err != nil { + return nil, err + } + + params, err := getParams(genericParams) + if err != nil { + return nil, err + } + + name, err := getName(params) + if err != nil { + return nil, err + } + + labels, err := getLabels(params, true, name) + if err != nil { + return nil, err + } + + podSpec, err := makeV1PodSpec(params, name) + if err != nil { + return nil, err + } + + if err = updateV1PodContainers(params, args, envs, podSpec); err != nil { + return nil, err + } + + leaveStdinOpen, err := GetBool(params, "leave-stdin-open", false) + if err != nil { + return nil, err + } + podSpec.Containers[0].StdinOnce = !leaveStdinOpen && podSpec.Containers[0].Stdin + + if err := updateV1PodPorts(params, podSpec); err != nil { + return nil, err + } + + restartPolicy := v1.RestartPolicy(params["restart"]) + if len(restartPolicy) == 0 { + restartPolicy = v1.RestartPolicyNever + } + podSpec.RestartPolicy = restartPolicy + + job := batchv1.Job{ + ObjectMeta: v1.ObjectMeta{ + Name: name, + Labels: labels, + }, + Spec: batchv1.JobSpec{ + Template: v1.PodTemplateSpec{ + ObjectMeta: v1.ObjectMeta{ + Labels: labels, + }, + Spec: *podSpec, + }, + }, + } + + return &job, nil +} + type BasicReplicationController struct{} func (BasicReplicationController) ParamNames() []GeneratorParam { @@ -326,6 +438,30 @@ func populateResourceList(spec string) (api.ResourceList, error) { return result, nil } +// populateResourceList takes strings of form =,= +func populateV1ResourceList(spec string) (v1.ResourceList, error) { + // empty input gets a nil response to preserve generator test expected behaviors + if spec == "" { + return nil, nil + } + + result := v1.ResourceList{} + resourceStatements := strings.Split(spec, ",") + for _, resourceStatement := range resourceStatements { + parts := strings.Split(resourceStatement, "=") + if len(parts) != 2 { + return nil, fmt.Errorf("Invalid argument syntax %v, expected =", resourceStatement) + } + resourceName := v1.ResourceName(parts[0]) + resourceQuantity, err := resource.ParseQuantity(parts[1]) + if err != nil { + return nil, err + } + result[resourceName] = *resourceQuantity + } + return result, nil +} + // HandleResourceRequirements parses the limits and requests parameters if specified func HandleResourceRequirements(params map[string]string) (api.ResourceRequirements, error) { result := api.ResourceRequirements{} @@ -342,6 +478,22 @@ func HandleResourceRequirements(params map[string]string) (api.ResourceRequireme return result, nil } +// HandleResourceRequirements parses the limits and requests parameters if specified +func handleV1ResourceRequirements(params map[string]string) (v1.ResourceRequirements, error) { + result := v1.ResourceRequirements{} + limits, err := populateV1ResourceList(params["limits"]) + if err != nil { + return result, err + } + result.Limits = limits + requests, err := populateV1ResourceList(params["requests"]) + if err != nil { + return result, err + } + result.Requests = requests + return result, nil +} + func makePodSpec(params map[string]string, name string) (*api.PodSpec, error) { stdin, err := GetBool(params, "stdin", false) if err != nil { @@ -372,6 +524,36 @@ func makePodSpec(params map[string]string, name string) (*api.PodSpec, error) { return &spec, nil } +func makeV1PodSpec(params map[string]string, name string) (*v1.PodSpec, error) { + stdin, err := GetBool(params, "stdin", false) + if err != nil { + return nil, err + } + + tty, err := GetBool(params, "tty", false) + if err != nil { + return nil, err + } + + resourceRequirements, err := handleV1ResourceRequirements(params) + if err != nil { + return nil, err + } + + spec := v1.PodSpec{ + Containers: []v1.Container{ + { + Name: name, + Image: params["image"], + Stdin: stdin, + TTY: tty, + Resources: resourceRequirements, + }, + }, + } + return &spec, nil +} + func (BasicReplicationController) Generate(genericParams map[string]interface{}) (runtime.Object, error) { args, err := getArgs(genericParams) if err != nil { @@ -454,6 +636,25 @@ func updatePodContainers(params map[string]string, args []string, envs []api.Env return nil } +func updateV1PodContainers(params map[string]string, args []string, envs []v1.EnvVar, podSpec *v1.PodSpec) error { + if len(args) > 0 { + command, err := GetBool(params, "command", false) + if err != nil { + return err + } + if command { + podSpec.Containers[0].Command = args + } else { + podSpec.Containers[0].Args = args + } + } + + if len(envs) > 0 { + podSpec.Containers[0].Env = envs + } + return nil +} + func updatePodPorts(params map[string]string, podSpec *api.PodSpec) (err error) { port := -1 hostPort := -1 @@ -488,6 +689,40 @@ func updatePodPorts(params map[string]string, podSpec *api.PodSpec) (err error) return nil } +func updateV1PodPorts(params map[string]string, podSpec *v1.PodSpec) (err error) { + port := -1 + hostPort := -1 + if len(params["port"]) > 0 { + port, err = strconv.Atoi(params["port"]) + if err != nil { + return err + } + } + + if len(params["hostport"]) > 0 { + hostPort, err = strconv.Atoi(params["hostport"]) + if err != nil { + return err + } + if hostPort > 0 && port < 0 { + return fmt.Errorf("--hostport requires --port to be specified") + } + } + + // Don't include the port if it was not specified. + if port > 0 { + podSpec.Containers[0].Ports = []v1.ContainerPort{ + { + ContainerPort: int32(port), + }, + } + if hostPort > 0 { + podSpec.Containers[0].Ports[0].HostPort = int32(hostPort) + } + } + return nil +} + type BasicPod struct{} func (BasicPod) ParamNames() []GeneratorParam { @@ -607,3 +842,27 @@ func parseEnvs(envArray []string) ([]api.EnvVar, error) { } return envs, nil } + +func parseV1Envs(envArray []string) ([]v1.EnvVar, error) { + envs := []v1.EnvVar{} + for _, env := range envArray { + pos := strings.Index(env, "=") + if pos == -1 { + return nil, fmt.Errorf("invalid env: %v", env) + } + name := env[:pos] + value := env[pos+1:] + if len(name) == 0 || !validation.IsCIdentifier(name) || len(value) == 0 { + return nil, fmt.Errorf("invalid env: %v", env) + } + envVar := v1.EnvVar{Name: name, Value: value} + envs = append(envs, envVar) + } + return envs, nil +} + +func newBool(val bool) *bool { + p := new(bool) + *p = val + return p +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/run_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/run_test.go deleted file mode 100644 index 27a366c41..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/run_test.go +++ /dev/null @@ -1,883 +0,0 @@ -/* -Copyright 2014 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 kubectl - -import ( - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" -) - -func TestGenerate(t *testing.T) { - tests := []struct { - params map[string]interface{} - expected *api.ReplicationController - expectErr bool - }{ - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "port": "-1", - }, - expected: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"run": "foo"}, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 1, - Selector: map[string]string{"run": "foo"}, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"run": "foo"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - }, - }, - }, - }, - }, - }, - }, - - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "port": "-1", - "env": []string{"a=b", "c=d"}, - }, - expected: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"run": "foo"}, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 1, - Selector: map[string]string{"run": "foo"}, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"run": "foo"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - Env: []api.EnvVar{ - { - Name: "a", - Value: "b", - }, - { - Name: "c", - Value: "d", - }, - }, - }, - }, - }, - }, - }, - }, - }, - - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "port": "-1", - "args": []string{"bar", "baz", "blah"}, - }, - expected: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"run": "foo"}, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 1, - Selector: map[string]string{"run": "foo"}, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"run": "foo"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - Args: []string{"bar", "baz", "blah"}, - }, - }, - }, - }, - }, - }, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "port": "-1", - "args": []string{"bar", "baz", "blah"}, - "command": "true", - }, - expected: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"run": "foo"}, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 1, - Selector: map[string]string{"run": "foo"}, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"run": "foo"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - Command: []string{"bar", "baz", "blah"}, - }, - }, - }, - }, - }, - }, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "port": "80", - }, - expected: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"run": "foo"}, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 1, - Selector: map[string]string{"run": "foo"}, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"run": "foo"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - Ports: []api.ContainerPort{ - { - ContainerPort: 80, - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "port": "80", - "hostport": "80", - }, - expected: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"run": "foo"}, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 1, - Selector: map[string]string{"run": "foo"}, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"run": "foo"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - Ports: []api.ContainerPort{ - { - ContainerPort: 80, - HostPort: 80, - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "hostport": "80", - }, - expected: nil, - expectErr: true, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "labels": "foo=bar,baz=blah", - }, - expected: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "bar", "baz": "blah"}, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 1, - Selector: map[string]string{"foo": "bar", "baz": "blah"}, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"foo": "bar", "baz": "blah"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - }, - }, - }, - }, - }, - }, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "hostport": "80", - }, - expected: nil, - expectErr: true, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "labels": "foo=bar,baz=blah", - "requests": "cpu100m,memory=100Mi", - }, - expected: nil, - expectErr: true, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "labels": "foo=bar,baz=blah", - "requests": "cpu=100m&memory=100Mi", - }, - expected: nil, - expectErr: true, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "labels": "foo=bar,baz=blah", - "requests": "cpu=", - }, - expected: nil, - expectErr: true, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "labels": "foo=bar,baz=blah", - "requests": "cpu=100m,memory=100Mi", - "limits": "cpu=400m,memory=200Mi", - }, - expected: &api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "bar", "baz": "blah"}, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 1, - Selector: map[string]string{"foo": "bar", "baz": "blah"}, - Template: &api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"foo": "bar", "baz": "blah"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceCPU: resource.MustParse("100m"), - api.ResourceMemory: resource.MustParse("100Mi"), - }, - Limits: api.ResourceList{ - api.ResourceCPU: resource.MustParse("400m"), - api.ResourceMemory: resource.MustParse("200Mi"), - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - generator := BasicReplicationController{} - for _, test := range tests { - obj, err := generator.Generate(test.params) - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v", err) - } - if test.expectErr && err != nil { - continue - } - if !reflect.DeepEqual(obj.(*api.ReplicationController).Spec.Template, test.expected.Spec.Template) { - t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected.Spec.Template, obj.(*api.ReplicationController).Spec.Template) - } - } -} - -func TestGeneratePod(t *testing.T) { - tests := []struct { - params map[string]interface{} - expected *api.Pod - expectErr bool - }{ - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "port": "-1", - }, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - ImagePullPolicy: api.PullIfNotPresent, - }, - }, - DNSPolicy: api.DNSClusterFirst, - RestartPolicy: api.RestartPolicyAlways, - }, - }, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "env": []string{"a", "c"}, - }, - - expected: nil, - expectErr: true, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "env": []string{"a=b", "c=d"}, - }, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - ImagePullPolicy: api.PullIfNotPresent, - Env: []api.EnvVar{ - { - Name: "a", - Value: "b", - }, - { - Name: "c", - Value: "d", - }, - }, - }, - }, - DNSPolicy: api.DNSClusterFirst, - RestartPolicy: api.RestartPolicyAlways, - }, - }, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "port": "80", - }, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - ImagePullPolicy: api.PullIfNotPresent, - Ports: []api.ContainerPort{ - { - ContainerPort: 80, - }, - }, - }, - }, - DNSPolicy: api.DNSClusterFirst, - RestartPolicy: api.RestartPolicyAlways, - }, - }, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "port": "80", - "hostport": "80", - }, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - ImagePullPolicy: api.PullIfNotPresent, - Ports: []api.ContainerPort{ - { - ContainerPort: 80, - HostPort: 80, - }, - }, - }, - }, - DNSPolicy: api.DNSClusterFirst, - RestartPolicy: api.RestartPolicyAlways, - }, - }, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "hostport": "80", - }, - expected: nil, - expectErr: true, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "labels": "foo=bar,baz=blah", - }, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "bar", "baz": "blah"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - ImagePullPolicy: api.PullIfNotPresent, - }, - }, - DNSPolicy: api.DNSClusterFirst, - RestartPolicy: api.RestartPolicyAlways, - }, - }, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "labels": "foo=bar,baz=blah", - "stdin": "true", - }, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "bar", "baz": "blah"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - ImagePullPolicy: api.PullIfNotPresent, - Stdin: true, - StdinOnce: true, - }, - }, - DNSPolicy: api.DNSClusterFirst, - RestartPolicy: api.RestartPolicyAlways, - }, - }, - }, - { - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "labels": "foo=bar,baz=blah", - "stdin": "true", - "leave-stdin-open": "true", - }, - expected: &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "bar", "baz": "blah"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - ImagePullPolicy: api.PullIfNotPresent, - Stdin: true, - StdinOnce: false, - }, - }, - DNSPolicy: api.DNSClusterFirst, - RestartPolicy: api.RestartPolicyAlways, - }, - }, - }, - } - generator := BasicPod{} - for _, test := range tests { - obj, err := generator.Generate(test.params) - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v", err) - } - if test.expectErr && err != nil { - continue - } - if !reflect.DeepEqual(obj.(*api.Pod), test.expected) { - t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*api.Pod)) - } - } -} - -func TestGenerateDeployment(t *testing.T) { - tests := []struct { - params map[string]interface{} - expected *extensions.Deployment - expectErr bool - }{ - { - params: map[string]interface{}{ - "labels": "foo=bar,baz=blah", - "name": "foo", - "replicas": "3", - "image": "someimage", - "port": "80", - "hostport": "80", - "stdin": "true", - "command": "true", - "args": []string{"bar", "baz", "blah"}, - "env": []string{"a=b", "c=d"}, - "requests": "cpu=100m,memory=100Mi", - "limits": "cpu=400m,memory=200Mi", - }, - expected: &extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "bar", "baz": "blah"}, - }, - Spec: extensions.DeploymentSpec{ - Replicas: 3, - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar", "baz": "blah"}}, - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"foo": "bar", "baz": "blah"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - Stdin: true, - Ports: []api.ContainerPort{ - { - ContainerPort: 80, - HostPort: 80, - }, - }, - Command: []string{"bar", "baz", "blah"}, - Env: []api.EnvVar{ - { - Name: "a", - Value: "b", - }, - { - Name: "c", - Value: "d", - }, - }, - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceCPU: resource.MustParse("100m"), - api.ResourceMemory: resource.MustParse("100Mi"), - }, - Limits: api.ResourceList{ - api.ResourceCPU: resource.MustParse("400m"), - api.ResourceMemory: resource.MustParse("200Mi"), - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - - generator := DeploymentV1Beta1{} - for _, test := range tests { - obj, err := generator.Generate(test.params) - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v", err) - } - if test.expectErr && err != nil { - continue - } - if !reflect.DeepEqual(obj.(*extensions.Deployment), test.expected) { - t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*extensions.Deployment)) - } - } -} - -func TestGenerateJob(t *testing.T) { - tests := []struct { - params map[string]interface{} - expected *extensions.Job - expectErr bool - }{ - { - params: map[string]interface{}{ - "labels": "foo=bar,baz=blah", - "name": "foo", - "image": "someimage", - "port": "80", - "hostport": "80", - "stdin": "true", - "leave-stdin-open": "true", - "command": "true", - "args": []string{"bar", "baz", "blah"}, - "env": []string{"a=b", "c=d"}, - "requests": "cpu=100m,memory=100Mi", - "limits": "cpu=400m,memory=200Mi", - "restart": "OnFailure", - }, - expected: &extensions.Job{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "bar", "baz": "blah"}, - }, - Spec: extensions.JobSpec{ - Selector: &unversioned.LabelSelector{ - MatchLabels: map[string]string{"foo": "bar", "baz": "blah"}, - }, - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: map[string]string{"foo": "bar", "baz": "blah"}, - }, - Spec: api.PodSpec{ - RestartPolicy: api.RestartPolicyOnFailure, - Containers: []api.Container{ - { - Name: "foo", - Image: "someimage", - Stdin: true, - StdinOnce: false, - Ports: []api.ContainerPort{ - { - ContainerPort: 80, - HostPort: 80, - }, - }, - Command: []string{"bar", "baz", "blah"}, - Env: []api.EnvVar{ - { - Name: "a", - Value: "b", - }, - { - Name: "c", - Value: "d", - }, - }, - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceCPU: resource.MustParse("100m"), - api.ResourceMemory: resource.MustParse("100Mi"), - }, - Limits: api.ResourceList{ - api.ResourceCPU: resource.MustParse("400m"), - api.ResourceMemory: resource.MustParse("200Mi"), - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - - generator := JobV1Beta1{} - for _, test := range tests { - obj, err := generator.Generate(test.params) - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v", err) - } - if test.expectErr && err != nil { - continue - } - if !reflect.DeepEqual(obj.(*extensions.Job), test.expected) { - t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*extensions.Job)) - } - } -} - -func TestParseEnv(t *testing.T) { - tests := []struct { - envArray []string - expected []api.EnvVar - expectErr bool - test string - }{ - { - envArray: []string{ - "THIS_ENV=isOK", - "HAS_COMMAS=foo,bar", - "HAS_EQUALS=jJnro54iUu75xNy==", - }, - expected: []api.EnvVar{ - { - Name: "THIS_ENV", - Value: "isOK", - }, - { - Name: "HAS_COMMAS", - Value: "foo,bar", - }, - { - Name: "HAS_EQUALS", - Value: "jJnro54iUu75xNy==", - }, - }, - expectErr: false, - test: "test case 1", - }, - { - envArray: []string{ - "WITH_OUT_EQUALS", - }, - expected: []api.EnvVar{}, - expectErr: true, - test: "test case 2", - }, - { - envArray: []string{ - "WITH_OUT_VALUES=", - }, - expected: []api.EnvVar{}, - expectErr: true, - test: "test case 3", - }, - { - envArray: []string{ - "=WITH_OUT_NAME", - }, - expected: []api.EnvVar{}, - expectErr: true, - test: "test case 4", - }, - } - - for _, test := range tests { - envs, err := parseEnvs(test.envArray) - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v (%s)", err, test.test) - } - if test.expectErr && err != nil { - continue - } - if !reflect.DeepEqual(envs, test.expected) { - t.Errorf("\nexpected:\n%#v\nsaw:\n%#v (%s)", test.expected, envs, test.test) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/scale.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/scale.go index 963f3050a..14951c028 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/scale.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/scale.go @@ -24,6 +24,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/extensions" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/util/wait" @@ -46,8 +47,8 @@ func ScalerFor(kind unversioned.GroupKind, c client.Interface) (Scaler, error) { return &ReplicationControllerScaler{c}, nil case extensions.Kind("ReplicaSet"): return &ReplicaSetScaler{c.Extensions()}, nil - case extensions.Kind("Job"): - return &JobScaler{c.Extensions()}, nil + case extensions.Kind("Job"), batch.Kind("Job"): + return &JobScaler{c.Extensions()}, nil // Either kind of job can be scaled with Extensions interface. case extensions.Kind("Deployment"): return &DeploymentScaler{c.Extensions()}, nil } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/scale_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/scale_test.go deleted file mode 100644 index 87b674355..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/scale_test.go +++ /dev/null @@ -1,728 +0,0 @@ -/* -Copyright 2014 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 kubectl - -import ( - "errors" - "testing" - - "k8s.io/kubernetes/pkg/api" - kerrors "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/apis/extensions" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient" -) - -type ErrorReplicationControllers struct { - testclient.FakeReplicationControllers - invalid bool -} - -func (c *ErrorReplicationControllers) Update(controller *api.ReplicationController) (*api.ReplicationController, error) { - if c.invalid { - return nil, kerrors.NewInvalid(api.Kind(controller.Kind), controller.Name, nil) - } - return nil, errors.New("Replication controller update failure") -} - -type ErrorReplicationControllerClient struct { - testclient.Fake - invalid bool -} - -func (c *ErrorReplicationControllerClient) ReplicationControllers(namespace string) client.ReplicationControllerInterface { - return &ErrorReplicationControllers{testclient.FakeReplicationControllers{Fake: &c.Fake, Namespace: namespace}, c.invalid} -} - -func TestReplicationControllerScaleRetry(t *testing.T) { - fake := &ErrorReplicationControllerClient{Fake: testclient.Fake{}, invalid: false} - scaler := ReplicationControllerScaler{fake} - preconditions := ScalePrecondition{-1, ""} - count := uint(3) - name := "foo" - namespace := "default" - - scaleFunc := ScaleCondition(&scaler, &preconditions, namespace, name, count) - pass, err := scaleFunc() - if pass { - t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) - } - if err != nil { - t.Errorf("Did not expect an error on update failure, got %v", err) - } - preconditions = ScalePrecondition{3, ""} - scaleFunc = ScaleCondition(&scaler, &preconditions, namespace, name, count) - pass, err = scaleFunc() - if err == nil { - t.Errorf("Expected error on precondition failure") - } -} - -func TestReplicationControllerScaleInvalid(t *testing.T) { - fake := &ErrorReplicationControllerClient{Fake: testclient.Fake{}, invalid: true} - scaler := ReplicationControllerScaler{fake} - preconditions := ScalePrecondition{-1, ""} - count := uint(3) - name := "foo" - namespace := "default" - - scaleFunc := ScaleCondition(&scaler, &preconditions, namespace, name, count) - pass, err := scaleFunc() - if pass { - t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) - } - e, ok := err.(ScaleError) - if err == nil || !ok || e.FailureType != ScaleUpdateInvalidFailure { - t.Errorf("Expected error on invalid update failure, got %v", err) - } -} - -func TestReplicationControllerScale(t *testing.T) { - fake := &testclient.Fake{} - scaler := ReplicationControllerScaler{fake} - preconditions := ScalePrecondition{-1, ""} - count := uint(3) - name := "foo" - scaler.Scale("default", name, count, &preconditions, nil, nil) - - actions := fake.Actions() - if len(actions) != 2 { - t.Errorf("unexpected actions: %v, expected 2 actions (get, update)", actions) - } - if action, ok := actions[0].(testclient.GetAction); !ok || action.GetResource() != "replicationcontrollers" || action.GetName() != name { - t.Errorf("unexpected action: %v, expected get-replicationController %s", actions[0], name) - } - if action, ok := actions[1].(testclient.UpdateAction); !ok || action.GetResource() != "replicationcontrollers" || action.GetObject().(*api.ReplicationController).Spec.Replicas != int(count) { - t.Errorf("unexpected action %v, expected update-replicationController with replicas = %d", actions[1], count) - } -} - -func TestReplicationControllerScaleFailsPreconditions(t *testing.T) { - fake := testclient.NewSimpleFake(&api.ReplicationController{ - Spec: api.ReplicationControllerSpec{ - Replicas: 10, - }, - }) - scaler := ReplicationControllerScaler{fake} - preconditions := ScalePrecondition{2, ""} - count := uint(3) - name := "foo" - scaler.Scale("default", name, count, &preconditions, nil, nil) - - actions := fake.Actions() - if len(actions) != 1 { - t.Errorf("unexpected actions: %v, expected 1 action (get)", actions) - } - if action, ok := actions[0].(testclient.GetAction); !ok || action.GetResource() != "replicationcontrollers" || action.GetName() != name { - t.Errorf("unexpected action: %v, expected get-replicationController %s", actions[0], name) - } -} - -func TestValidateReplicationController(t *testing.T) { - tests := []struct { - preconditions ScalePrecondition - controller api.ReplicationController - expectError bool - test string - }{ - { - preconditions: ScalePrecondition{-1, ""}, - expectError: false, - test: "defaults", - }, - { - preconditions: ScalePrecondition{-1, ""}, - controller: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 10, - }, - }, - expectError: false, - test: "defaults 2", - }, - { - preconditions: ScalePrecondition{0, ""}, - controller: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - }, - }, - expectError: false, - test: "size matches", - }, - { - preconditions: ScalePrecondition{-1, "foo"}, - controller: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 10, - }, - }, - expectError: false, - test: "resource version matches", - }, - { - preconditions: ScalePrecondition{10, "foo"}, - controller: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 10, - }, - }, - expectError: false, - test: "both match", - }, - { - preconditions: ScalePrecondition{10, "foo"}, - controller: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 20, - }, - }, - expectError: true, - test: "size different", - }, - { - preconditions: ScalePrecondition{10, "foo"}, - controller: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "bar", - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 10, - }, - }, - expectError: true, - test: "version different", - }, - { - preconditions: ScalePrecondition{10, "foo"}, - controller: api.ReplicationController{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "bar", - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 20, - }, - }, - expectError: true, - test: "both different", - }, - } - for _, test := range tests { - err := test.preconditions.ValidateReplicationController(&test.controller) - if err != nil && !test.expectError { - t.Errorf("unexpected error: %v (%s)", err, test.test) - } - if err == nil && test.expectError { - t.Errorf("unexpected non-error: %v (%s)", err, test.test) - } - } -} - -type ErrorJobs struct { - testclient.FakeJobs - invalid bool -} - -func (c *ErrorJobs) Update(job *extensions.Job) (*extensions.Job, error) { - if c.invalid { - return nil, kerrors.NewInvalid(extensions.Kind(job.Kind), job.Name, nil) - } - return nil, errors.New("Job update failure") -} - -func (c *ErrorJobs) Get(name string) (*extensions.Job, error) { - zero := 0 - return &extensions.Job{ - Spec: extensions.JobSpec{ - Parallelism: &zero, - }, - }, nil -} - -type ErrorJobClient struct { - testclient.FakeExperimental - invalid bool -} - -func (c *ErrorJobClient) Jobs(namespace string) client.JobInterface { - return &ErrorJobs{testclient.FakeJobs{Fake: &c.FakeExperimental, Namespace: namespace}, c.invalid} -} - -func TestJobScaleRetry(t *testing.T) { - fake := &ErrorJobClient{FakeExperimental: testclient.FakeExperimental{}, invalid: false} - scaler := JobScaler{fake} - preconditions := ScalePrecondition{-1, ""} - count := uint(3) - name := "foo" - namespace := "default" - - scaleFunc := ScaleCondition(&scaler, &preconditions, namespace, name, count) - pass, err := scaleFunc() - if pass != false { - t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) - } - if err != nil { - t.Errorf("Did not expect an error on update failure, got %v", err) - } - preconditions = ScalePrecondition{3, ""} - scaleFunc = ScaleCondition(&scaler, &preconditions, namespace, name, count) - pass, err = scaleFunc() - if err == nil { - t.Errorf("Expected error on precondition failure") - } -} - -func TestJobScale(t *testing.T) { - fake := &testclient.FakeExperimental{Fake: &testclient.Fake{}} - scaler := JobScaler{fake} - preconditions := ScalePrecondition{-1, ""} - count := uint(3) - name := "foo" - scaler.Scale("default", name, count, &preconditions, nil, nil) - - actions := fake.Actions() - if len(actions) != 2 { - t.Errorf("unexpected actions: %v, expected 2 actions (get, update)", actions) - } - if action, ok := actions[0].(testclient.GetAction); !ok || action.GetResource() != "jobs" || action.GetName() != name { - t.Errorf("unexpected action: %v, expected get-replicationController %s", actions[0], name) - } - if action, ok := actions[1].(testclient.UpdateAction); !ok || action.GetResource() != "jobs" || *action.GetObject().(*extensions.Job).Spec.Parallelism != int(count) { - t.Errorf("unexpected action %v, expected update-job with parallelism = %d", actions[1], count) - } -} - -func TestJobScaleInvalid(t *testing.T) { - fake := &ErrorJobClient{FakeExperimental: testclient.FakeExperimental{}, invalid: true} - scaler := JobScaler{fake} - preconditions := ScalePrecondition{-1, ""} - count := uint(3) - name := "foo" - namespace := "default" - - scaleFunc := ScaleCondition(&scaler, &preconditions, namespace, name, count) - pass, err := scaleFunc() - if pass { - t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) - } - e, ok := err.(ScaleError) - if err == nil || !ok || e.FailureType != ScaleUpdateInvalidFailure { - t.Errorf("Expected error on invalid update failure, got %v", err) - } -} - -func TestJobScaleFailsPreconditions(t *testing.T) { - ten := 10 - fake := testclient.NewSimpleFake(&extensions.Job{ - Spec: extensions.JobSpec{ - Parallelism: &ten, - }, - }) - scaler := JobScaler{&testclient.FakeExperimental{fake}} - preconditions := ScalePrecondition{2, ""} - count := uint(3) - name := "foo" - scaler.Scale("default", name, count, &preconditions, nil, nil) - - actions := fake.Actions() - if len(actions) != 1 { - t.Errorf("unexpected actions: %v, expected 1 actions (get)", actions) - } - if action, ok := actions[0].(testclient.GetAction); !ok || action.GetResource() != "jobs" || action.GetName() != name { - t.Errorf("unexpected action: %v, expected get-job %s", actions[0], name) - } -} - -func TestValidateJob(t *testing.T) { - zero, ten, twenty := 0, 10, 20 - tests := []struct { - preconditions ScalePrecondition - job extensions.Job - expectError bool - test string - }{ - { - preconditions: ScalePrecondition{-1, ""}, - expectError: false, - test: "defaults", - }, - { - preconditions: ScalePrecondition{-1, ""}, - job: extensions.Job{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - Spec: extensions.JobSpec{ - Parallelism: &ten, - }, - }, - expectError: false, - test: "defaults 2", - }, - { - preconditions: ScalePrecondition{0, ""}, - job: extensions.Job{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - Spec: extensions.JobSpec{ - Parallelism: &zero, - }, - }, - expectError: false, - test: "size matches", - }, - { - preconditions: ScalePrecondition{-1, "foo"}, - job: extensions.Job{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - Spec: extensions.JobSpec{ - Parallelism: &ten, - }, - }, - expectError: false, - test: "resource version matches", - }, - { - preconditions: ScalePrecondition{10, "foo"}, - job: extensions.Job{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - Spec: extensions.JobSpec{ - Parallelism: &ten, - }, - }, - expectError: false, - test: "both match", - }, - { - preconditions: ScalePrecondition{10, "foo"}, - job: extensions.Job{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - Spec: extensions.JobSpec{ - Parallelism: &twenty, - }, - }, - expectError: true, - test: "size different", - }, - { - preconditions: ScalePrecondition{10, "foo"}, - job: extensions.Job{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - }, - expectError: true, - test: "parallelism nil", - }, - { - preconditions: ScalePrecondition{10, "foo"}, - job: extensions.Job{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "bar", - }, - Spec: extensions.JobSpec{ - Parallelism: &ten, - }, - }, - expectError: true, - test: "version different", - }, - { - preconditions: ScalePrecondition{10, "foo"}, - job: extensions.Job{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "bar", - }, - Spec: extensions.JobSpec{ - Parallelism: &twenty, - }, - }, - expectError: true, - test: "both different", - }, - } - for _, test := range tests { - err := test.preconditions.ValidateJob(&test.job) - if err != nil && !test.expectError { - t.Errorf("unexpected error: %v (%s)", err, test.test) - } - if err == nil && test.expectError { - t.Errorf("unexpected non-error: %v (%s)", err, test.test) - } - } -} - -type ErrorDeployments struct { - testclient.FakeDeployments - invalid bool -} - -func (c *ErrorDeployments) Update(deployment *extensions.Deployment) (*extensions.Deployment, error) { - if c.invalid { - return nil, kerrors.NewInvalid(extensions.Kind(deployment.Kind), deployment.Name, nil) - } - return nil, errors.New("deployment update failure") -} - -func (c *ErrorDeployments) Get(name string) (*extensions.Deployment, error) { - return &extensions.Deployment{ - Spec: extensions.DeploymentSpec{ - Replicas: 0, - }, - }, nil -} - -type ErrorDeploymentClient struct { - testclient.FakeExperimental - invalid bool -} - -func (c *ErrorDeploymentClient) Deployments(namespace string) client.DeploymentInterface { - return &ErrorDeployments{testclient.FakeDeployments{Fake: &c.FakeExperimental, Namespace: namespace}, c.invalid} -} - -func TestDeploymentScaleRetry(t *testing.T) { - fake := &ErrorDeploymentClient{FakeExperimental: testclient.FakeExperimental{Fake: &testclient.Fake{}}, invalid: false} - scaler := &DeploymentScaler{fake} - preconditions := &ScalePrecondition{-1, ""} - count := uint(3) - name := "foo" - namespace := "default" - - scaleFunc := ScaleCondition(scaler, preconditions, namespace, name, count) - pass, err := scaleFunc() - if pass != false { - t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) - } - if err != nil { - t.Errorf("Did not expect an error on update failure, got %v", err) - } - preconditions = &ScalePrecondition{3, ""} - scaleFunc = ScaleCondition(scaler, preconditions, namespace, name, count) - pass, err = scaleFunc() - if err == nil { - t.Errorf("Expected error on precondition failure") - } -} - -func TestDeploymentScale(t *testing.T) { - fake := &testclient.FakeExperimental{Fake: &testclient.Fake{}} - scaler := DeploymentScaler{fake} - preconditions := ScalePrecondition{-1, ""} - count := uint(3) - name := "foo" - scaler.Scale("default", name, count, &preconditions, nil, nil) - - actions := fake.Actions() - if len(actions) != 2 { - t.Errorf("unexpected actions: %v, expected 2 actions (get, update)", actions) - } - if action, ok := actions[0].(testclient.GetAction); !ok || action.GetResource() != "deployments" || action.GetName() != name { - t.Errorf("unexpected action: %v, expected get-replicationController %s", actions[0], name) - } - if action, ok := actions[1].(testclient.UpdateAction); !ok || action.GetResource() != "deployments" || action.GetObject().(*extensions.Deployment).Spec.Replicas != int(count) { - t.Errorf("unexpected action %v, expected update-deployment with replicas = %d", actions[1], count) - } -} - -func TestDeploymentScaleInvalid(t *testing.T) { - fake := &ErrorDeploymentClient{FakeExperimental: testclient.FakeExperimental{Fake: &testclient.Fake{}}, invalid: true} - scaler := DeploymentScaler{fake} - preconditions := ScalePrecondition{-1, ""} - count := uint(3) - name := "foo" - namespace := "default" - - scaleFunc := ScaleCondition(&scaler, &preconditions, namespace, name, count) - pass, err := scaleFunc() - if pass { - t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) - } - e, ok := err.(ScaleError) - if err == nil || !ok || e.FailureType != ScaleUpdateInvalidFailure { - t.Errorf("Expected error on invalid update failure, got %v", err) - } -} - -func TestDeploymentScaleFailsPreconditions(t *testing.T) { - fake := testclient.NewSimpleFake(&extensions.Deployment{ - Spec: extensions.DeploymentSpec{ - Replicas: 10, - }, - }) - scaler := DeploymentScaler{&testclient.FakeExperimental{fake}} - preconditions := ScalePrecondition{2, ""} - count := uint(3) - name := "foo" - scaler.Scale("default", name, count, &preconditions, nil, nil) - - actions := fake.Actions() - if len(actions) != 1 { - t.Errorf("unexpected actions: %v, expected 1 actions (get)", actions) - } - if action, ok := actions[0].(testclient.GetAction); !ok || action.GetResource() != "deployments" || action.GetName() != name { - t.Errorf("unexpected action: %v, expected get-deployment %s", actions[0], name) - } -} - -func TestValidateDeployment(t *testing.T) { - zero, ten, twenty := 0, 10, 20 - tests := []struct { - preconditions ScalePrecondition - deployment extensions.Deployment - expectError bool - test string - }{ - { - preconditions: ScalePrecondition{-1, ""}, - expectError: false, - test: "defaults", - }, - { - preconditions: ScalePrecondition{-1, ""}, - deployment: extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - Spec: extensions.DeploymentSpec{ - Replicas: ten, - }, - }, - expectError: false, - test: "defaults 2", - }, - { - preconditions: ScalePrecondition{0, ""}, - deployment: extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - Spec: extensions.DeploymentSpec{ - Replicas: zero, - }, - }, - expectError: false, - test: "size matches", - }, - { - preconditions: ScalePrecondition{-1, "foo"}, - deployment: extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - Spec: extensions.DeploymentSpec{ - Replicas: ten, - }, - }, - expectError: false, - test: "resource version matches", - }, - { - preconditions: ScalePrecondition{10, "foo"}, - deployment: extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - Spec: extensions.DeploymentSpec{ - Replicas: ten, - }, - }, - expectError: false, - test: "both match", - }, - { - preconditions: ScalePrecondition{10, "foo"}, - deployment: extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - Spec: extensions.DeploymentSpec{ - Replicas: twenty, - }, - }, - expectError: true, - test: "size different", - }, - { - preconditions: ScalePrecondition{10, "foo"}, - deployment: extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "foo", - }, - }, - expectError: true, - test: "no replicas", - }, - { - preconditions: ScalePrecondition{10, "foo"}, - deployment: extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "bar", - }, - Spec: extensions.DeploymentSpec{ - Replicas: ten, - }, - }, - expectError: true, - test: "version different", - }, - { - preconditions: ScalePrecondition{10, "foo"}, - deployment: extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - ResourceVersion: "bar", - }, - Spec: extensions.DeploymentSpec{ - Replicas: twenty, - }, - }, - expectError: true, - test: "both different", - }, - } - for _, test := range tests { - err := test.preconditions.ValidateDeployment(&test.deployment) - if err != nil && !test.expectError { - t.Errorf("unexpected error: %v (%s)", err, test.test) - } - if err == nil && test.expectError { - t.Errorf("unexpected non-error: %v (%s)", err, test.test) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/secret_for_docker_registry_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/secret_for_docker_registry_test.go deleted file mode 100644 index 65d8d397d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/secret_for_docker_registry_test.go +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright 2015 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 kubectl - -import ( - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api" -) - -func TestSecretForDockerRegistryGenerate(t *testing.T) { - username, password, email, server := "test-user", "test-password", "test-user@example.org", "https://index.docker.io/v1/" - secretData, err := handleDockercfgContent(username, password, email, server) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - tests := map[string]struct { - params map[string]interface{} - expected *api.Secret - expectErr bool - }{ - "test-valid-use": { - params: map[string]interface{}{ - "name": "foo", - "docker-server": server, - "docker-username": username, - "docker-password": password, - "docker-email": email, - }, - expected: &api.Secret{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Data: map[string][]byte{ - api.DockerConfigKey: secretData, - }, - Type: api.SecretTypeDockercfg, - }, - expectErr: false, - }, - "test-missing-required-param": { - params: map[string]interface{}{ - "name": "foo", - "docker-server": server, - "docker-password": password, - "docker-email": email, - }, - expectErr: true, - }, - } - - generator := SecretForDockerRegistryGeneratorV1{} - for _, test := range tests { - obj, err := generator.Generate(test.params) - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v", err) - } - if test.expectErr && err != nil { - continue - } - if !reflect.DeepEqual(obj.(*api.Secret), test.expected) { - t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*api.Secret)) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/secret_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/secret_test.go deleted file mode 100644 index e713c9911..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/secret_test.go +++ /dev/null @@ -1,109 +0,0 @@ -/* -Copyright 2015 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 kubectl - -import ( - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api" -) - -func TestSecretGenerate(t *testing.T) { - tests := []struct { - params map[string]interface{} - expected *api.Secret - expectErr bool - }{ - { - params: map[string]interface{}{ - "name": "foo", - }, - expected: &api.Secret{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Data: map[string][]byte{}, - }, - expectErr: false, - }, - { - params: map[string]interface{}{ - "name": "foo", - "type": "my-type", - }, - expected: &api.Secret{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Data: map[string][]byte{}, - Type: "my-type", - }, - expectErr: false, - }, - { - params: map[string]interface{}{ - "name": "foo", - "from-literal": []string{"key1=value1", "key2=value2"}, - }, - expected: &api.Secret{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - }, - Data: map[string][]byte{ - "key1": []byte("value1"), - "key2": []byte("value2"), - }, - }, - expectErr: false, - }, - { - params: map[string]interface{}{ - "name": "foo", - "from-literal": []string{"key1value1"}, - }, - expectErr: true, - }, - { - params: map[string]interface{}{ - "name": "foo", - "from-file": []string{"key1=/file=2"}, - }, - expectErr: true, - }, - { - params: map[string]interface{}{ - "name": "foo", - "from-file": []string{"key1==value"}, - }, - expectErr: true, - }, - } - generator := SecretGeneratorV1{} - for _, test := range tests { - obj, err := generator.Generate(test.params) - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v", err) - } - if test.expectErr && err != nil { - continue - } - if !reflect.DeepEqual(obj.(*api.Secret), test.expected) { - t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*api.Secret)) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/service_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/service_test.go deleted file mode 100644 index 80144239e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/service_test.go +++ /dev/null @@ -1,417 +0,0 @@ -/* -Copyright 2014 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 kubectl - -import ( - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/util/intstr" -) - -func TestGenerateService(t *testing.T) { - tests := []struct { - generator Generator - params map[string]interface{} - expected api.Service - }{ - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "TCP", - "container-port": "1234", - }, - expected: api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - }, - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []api.ServicePort{ - { - Port: 80, - Protocol: "TCP", - TargetPort: intstr.FromInt(1234), - }, - }, - }, - }, - }, - { - - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "UDP", - "container-port": "foobar", - }, - expected: api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - }, - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []api.ServicePort{ - { - Port: 80, - Protocol: "UDP", - TargetPort: intstr.FromString("foobar"), - }, - }, - }, - }, - }, - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "labels": "key1=value1,key2=value2", - "name": "test", - "port": "80", - "protocol": "TCP", - "container-port": "1234", - }, - expected: api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - Labels: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - }, - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []api.ServicePort{ - { - Port: 80, - Protocol: "TCP", - TargetPort: intstr.FromInt(1234), - }, - }, - }, - }, - }, - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "UDP", - "container-port": "foobar", - "external-ip": "1.2.3.4", - }, - expected: api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - }, - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []api.ServicePort{ - { - Port: 80, - Protocol: "UDP", - TargetPort: intstr.FromString("foobar"), - }, - }, - ExternalIPs: []string{"1.2.3.4"}, - }, - }, - }, - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "UDP", - "container-port": "foobar", - "external-ip": "1.2.3.4", - "create-external-load-balancer": "true", - }, - expected: api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - }, - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []api.ServicePort{ - { - Port: 80, - Protocol: "UDP", - TargetPort: intstr.FromString("foobar"), - }, - }, - Type: api.ServiceTypeLoadBalancer, - ExternalIPs: []string{"1.2.3.4"}, - }, - }, - }, - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "UDP", - "container-port": "foobar", - "type": string(api.ServiceTypeNodePort), - }, - expected: api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - }, - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []api.ServicePort{ - { - Port: 80, - Protocol: "UDP", - TargetPort: intstr.FromString("foobar"), - }, - }, - Type: api.ServiceTypeNodePort, - }, - }, - }, - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "UDP", - "container-port": "foobar", - "create-external-load-balancer": "true", // ignored when type is present - "type": string(api.ServiceTypeNodePort), - }, - expected: api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - }, - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []api.ServicePort{ - { - Port: 80, - Protocol: "UDP", - TargetPort: intstr.FromString("foobar"), - }, - }, - Type: api.ServiceTypeNodePort, - }, - }, - }, - { - generator: ServiceGeneratorV1{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "TCP", - "container-port": "1234", - }, - expected: api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - }, - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []api.ServicePort{ - { - Name: "default", - Port: 80, - Protocol: "TCP", - TargetPort: intstr.FromInt(1234), - }, - }, - }, - }, - }, - { - generator: ServiceGeneratorV1{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "TCP", - "container-port": "1234", - "session-affinity": "ClientIP", - }, - expected: api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - }, - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []api.ServicePort{ - { - Name: "default", - Port: 80, - Protocol: "TCP", - TargetPort: intstr.FromInt(1234), - }, - }, - SessionAffinity: api.ServiceAffinityClientIP, - }, - }, - }, - { - generator: ServiceGeneratorV1{}, - params: map[string]interface{}{ - "selector": "foo=bar", - "name": "test", - "ports": "80,443", - "protocol": "TCP", - "container-port": "foobar", - }, - expected: api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - }, - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - Ports: []api.ServicePort{ - { - Name: "port-1", - Port: 80, - Protocol: api.ProtocolTCP, - TargetPort: intstr.FromString("foobar"), - }, - { - Name: "port-2", - Port: 443, - Protocol: api.ProtocolTCP, - TargetPort: intstr.FromString("foobar"), - }, - }, - }, - }, - }, - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar", - "name": "test", - "ports": "80,443", - "protocol": "UDP", - "target-port": "1234", - }, - expected: api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - }, - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - Ports: []api.ServicePort{ - { - Name: "port-1", - Port: 80, - Protocol: api.ProtocolUDP, - TargetPort: intstr.FromInt(1234), - }, - { - Name: "port-2", - Port: 443, - Protocol: api.ProtocolUDP, - TargetPort: intstr.FromInt(1234), - }, - }, - }, - }, - }, - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar", - "name": "test", - "ports": "80,443", - "protocol": "TCP", - }, - expected: api.Service{ - ObjectMeta: api.ObjectMeta{ - Name: "test", - }, - Spec: api.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - Ports: []api.ServicePort{ - { - Name: "port-1", - Port: 80, - Protocol: api.ProtocolTCP, - TargetPort: intstr.FromInt(80), - }, - { - Name: "port-2", - Port: 443, - Protocol: api.ProtocolTCP, - TargetPort: intstr.FromInt(443), - }, - }, - }, - }, - }, - } - for _, test := range tests { - obj, err := test.generator.Generate(test.params) - if !reflect.DeepEqual(obj, &test.expected) { - t.Errorf("expected:\n%#v\ngot\n%#v\n", &test.expected, obj) - } - if err != nil { - t.Errorf("unexpected error: %v", err) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/serviceaccount.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/serviceaccount.go new file mode 100644 index 000000000..2be08dd2d --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/serviceaccount.go @@ -0,0 +1,51 @@ +/* +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 kubectl + +import ( + "fmt" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/runtime" +) + +// ServiceAccountGeneratorV1 supports stable generation of a service account +type ServiceAccountGeneratorV1 struct { + // Name of service account + Name string +} + +// Ensure it supports the generator pattern that uses parameters specified during construction +var _ StructuredGenerator = &ServiceAccountGeneratorV1{} + +// StructuredGenerate outputs a service account object using the configured fields +func (g *ServiceAccountGeneratorV1) StructuredGenerate() (runtime.Object, error) { + if err := g.validate(); err != nil { + return nil, err + } + serviceAccount := &api.ServiceAccount{} + serviceAccount.Name = g.Name + return serviceAccount, nil +} + +// validate validates required fields are set to support structured generation +func (g *ServiceAccountGeneratorV1) validate() error { + if len(g.Name) == 0 { + return fmt.Errorf("name must be specified") + } + return nil +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/sorted_event_list_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/sorted_event_list_test.go deleted file mode 100644 index 471069ed2..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/sorted_event_list_test.go +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright 2014 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 kubectl - -import ( - "sort" - "strings" - "testing" - "time" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" -) - -// VerifyDatesInOrder checks the start of each line for a RFC1123Z date -// and posts error if all subsequent dates are not equal or increasing -func VerifyDatesInOrder( - resultToTest, rowDelimiter, columnDelimiter string, t *testing.T) { - lines := strings.Split(resultToTest, rowDelimiter) - var previousTime time.Time - for _, str := range lines { - columns := strings.Split(str, columnDelimiter) - if len(columns) > 0 { - currentTime, err := time.Parse(time.RFC1123Z, columns[0]) - if err == nil { - if previousTime.After(currentTime) { - t.Errorf( - "Output is not sorted by time. %s should be listed after %s. Complete output: %s", - previousTime.Format(time.RFC1123Z), - currentTime.Format(time.RFC1123Z), - resultToTest) - } - previousTime = currentTime - } - } - } - -} - -func TestSortableEvents(t *testing.T) { - // Arrange - list := SortableEvents([]api.Event{ - { - Source: api.EventSource{Component: "kubelet"}, - Message: "Item 1", - FirstTimestamp: unversioned.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), - LastTimestamp: unversioned.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), - Count: 1, - Type: api.EventTypeNormal, - }, - { - Source: api.EventSource{Component: "scheduler"}, - Message: "Item 2", - FirstTimestamp: unversioned.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)), - LastTimestamp: unversioned.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)), - Count: 1, - Type: api.EventTypeNormal, - }, - { - Source: api.EventSource{Component: "kubelet"}, - Message: "Item 3", - FirstTimestamp: unversioned.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)), - LastTimestamp: unversioned.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)), - Count: 1, - Type: api.EventTypeNormal, - }, - }) - - // Act - sort.Sort(list) - - // Assert - if list[0].Message != "Item 2" || - list[1].Message != "Item 3" || - list[2].Message != "Item 1" { - t.Fatal("List is not sorted by time. List: ", list) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/sorted_resource_name_list.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/sorted_resource_name_list.go index 6df122eac..ffaa08ee4 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/sorted_resource_name_list.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/sorted_resource_name_list.go @@ -33,3 +33,17 @@ func (list SortableResourceNames) Swap(i, j int) { func (list SortableResourceNames) Less(i, j int) bool { return list[i] < list[j] } + +type SortableResourceQuotas []api.ResourceQuota + +func (list SortableResourceQuotas) Len() int { + return len(list) +} + +func (list SortableResourceQuotas) Swap(i, j int) { + list[i], list[j] = list[j], list[i] +} + +func (list SortableResourceQuotas) Less(i, j int) bool { + return list[i].Name < list[j].Name +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/sorting_printer_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/sorting_printer_test.go deleted file mode 100644 index f1d1d0c65..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/sorting_printer_test.go +++ /dev/null @@ -1,236 +0,0 @@ -/* -Copyright 2014 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 kubectl - -import ( - "reflect" - "testing" - - internal "k8s.io/kubernetes/pkg/api" - api "k8s.io/kubernetes/pkg/api/v1" - "k8s.io/kubernetes/pkg/runtime" -) - -func encodeOrDie(obj runtime.Object) []byte { - data, err := runtime.Encode(internal.Codecs.LegacyCodec(api.SchemeGroupVersion), obj) - if err != nil { - panic(err.Error()) - } - return data -} - -func TestSortingPrinter(t *testing.T) { - intPtr := func(val int32) *int32 { return &val } - - a := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "a", - }, - } - - b := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "b", - }, - } - - c := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: "c", - }, - } - - tests := []struct { - obj runtime.Object - sort runtime.Object - field string - name string - }{ - { - name: "in-order-already", - obj: &api.PodList{ - Items: []api.Pod{ - { - ObjectMeta: api.ObjectMeta{ - Name: "a", - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "b", - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "c", - }, - }, - }, - }, - sort: &api.PodList{ - Items: []api.Pod{ - { - ObjectMeta: api.ObjectMeta{ - Name: "a", - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "b", - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "c", - }, - }, - }, - }, - field: "{.metadata.name}", - }, - { - name: "reverse-order", - obj: &api.PodList{ - Items: []api.Pod{ - { - ObjectMeta: api.ObjectMeta{ - Name: "b", - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "c", - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "a", - }, - }, - }, - }, - sort: &api.PodList{ - Items: []api.Pod{ - { - ObjectMeta: api.ObjectMeta{ - Name: "a", - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "b", - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "c", - }, - }, - }, - }, - field: "{.metadata.name}", - }, - { - name: "random-order-numbers", - obj: &api.ReplicationControllerList{ - Items: []api.ReplicationController{ - { - Spec: api.ReplicationControllerSpec{ - Replicas: intPtr(5), - }, - }, - { - Spec: api.ReplicationControllerSpec{ - Replicas: intPtr(1), - }, - }, - { - Spec: api.ReplicationControllerSpec{ - Replicas: intPtr(9), - }, - }, - }, - }, - sort: &api.ReplicationControllerList{ - Items: []api.ReplicationController{ - { - Spec: api.ReplicationControllerSpec{ - Replicas: intPtr(1), - }, - }, - { - Spec: api.ReplicationControllerSpec{ - Replicas: intPtr(5), - }, - }, - { - Spec: api.ReplicationControllerSpec{ - Replicas: intPtr(9), - }, - }, - }, - }, - field: "{.spec.replicas}", - }, - { - name: "v1.List in order", - obj: &api.List{ - Items: []runtime.RawExtension{ - {RawJSON: encodeOrDie(a)}, - {RawJSON: encodeOrDie(b)}, - {RawJSON: encodeOrDie(c)}, - }, - }, - sort: &api.List{ - Items: []runtime.RawExtension{ - {RawJSON: encodeOrDie(a)}, - {RawJSON: encodeOrDie(b)}, - {RawJSON: encodeOrDie(c)}, - }, - }, - field: "{.metadata.name}", - }, - { - name: "v1.List in reverse", - obj: &api.List{ - Items: []runtime.RawExtension{ - {RawJSON: encodeOrDie(c)}, - {RawJSON: encodeOrDie(b)}, - {RawJSON: encodeOrDie(a)}, - }, - }, - sort: &api.List{ - Items: []runtime.RawExtension{ - {RawJSON: encodeOrDie(a)}, - {RawJSON: encodeOrDie(b)}, - {RawJSON: encodeOrDie(c)}, - }, - }, - field: "{.metadata.name}", - }, - } - for _, test := range tests { - sort := &SortingPrinter{SortField: test.field, Decoder: internal.Codecs.UniversalDecoder()} - if err := sort.sortObj(test.obj); err != nil { - t.Errorf("unexpected error: %v (%s)", err, test.name) - continue - } - if !reflect.DeepEqual(test.obj, test.sort) { - t.Errorf("[%s]\nexpected:\n%v\nsaw:\n%v", test.name, test.sort, test.obj) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/stop.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/stop.go index 0f03d45ab..9112decf7 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/stop.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/stop.go @@ -25,10 +25,12 @@ import ( "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/extensions" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/util" + deploymentutil "k8s.io/kubernetes/pkg/util/deployment" utilerrors "k8s.io/kubernetes/pkg/util/errors" "k8s.io/kubernetes/pkg/util/wait" ) @@ -40,7 +42,8 @@ const ( // A Reaper handles terminating an object as gracefully as possible. // timeout is how long we'll wait for the termination to be successful -// gracePeriod is time given to an API object for it to delete itself cleanly (e.g. pod shutdown) +// gracePeriod is time given to an API object for it to delete itself cleanly, +// e.g., pod shutdown. It may or may not be supported by the API object. type Reaper interface { Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error } @@ -75,7 +78,7 @@ func ReaperFor(kind unversioned.GroupKind, c client.Interface) (Reaper, error) { case api.Kind("Service"): return &ServiceReaper{c}, nil - case extensions.Kind("Job"): + case extensions.Kind("Job"), batch.Kind("Job"): return &JobReaper{c, Interval, Timeout}, nil case extensions.Kind("Deployment"): @@ -270,7 +273,7 @@ func (reaper *ReplicaSetReaper) Stop(namespace, name string, timeout time.Durati } } - if err := rsc.Delete(name, gracePeriod); err != nil { + if err := rsc.Delete(name, nil); err != nil { return err } return nil @@ -353,7 +356,7 @@ func (reaper *JobReaper) Stop(namespace, name string, timeout time.Duration, gra return utilerrors.NewAggregate(errList) } // once we have all the pods removed we can safely remove the job itself - return jobs.Delete(name, gracePeriod) + return jobs.Delete(name, nil) } func (reaper *DeploymentReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error { @@ -364,55 +367,27 @@ func (reaper *DeploymentReaper) Stop(namespace, name string, timeout time.Durati deployment, err := reaper.updateDeploymentWithRetries(namespace, name, func(d *extensions.Deployment) { // set deployment's history and scale to 0 // TODO replace with patch when available: https://github.com/kubernetes/kubernetes/issues/20527 - zero := 0 - d.Spec.RevisionHistoryLimit = &zero + d.Spec.RevisionHistoryLimit = util.IntPtr(0) d.Spec.Replicas = 0 - // TODO: un-pausing should not be necessary, remove when this is fixed: - // https://github.com/kubernetes/kubernetes/issues/20966 - // Instead deployment should be Paused at this point and not at next TODO. - d.Spec.Paused = false - }) - if err != nil { - return err - } - - // wait for total no of pods drop to 0 - if err := wait.Poll(reaper.pollInterval, reaper.timeout, func() (bool, error) { - curr, err := deployments.Get(name) - // if deployment was not found it must have been deleted, error out - if err != nil && errors.IsNotFound(err) { - return false, err - } - // if other errors happen, retry - if err != nil { - return false, nil - } - // check if deployment wasn't recreated with the same name - // TODO use generations when deployment will have them - if curr.UID != deployment.UID { - return false, errors.NewNotFound(extensions.Resource("Deployment"), name) - } - return curr.Status.Replicas == 0, nil - }); err != nil { - return err - } - - // TODO: When deployments will allow running cleanup policy while being - // paused, move pausing to above update operation. Without it, we need to - // pause deployment before stopping RSs, to prevent creating new RSs. - // See https://github.com/kubernetes/kubernetes/issues/20966 - deployment, err = reaper.updateDeploymentWithRetries(namespace, name, func(d *extensions.Deployment) { d.Spec.Paused = true }) if err != nil { return err } - // remove remaining RSs + // Use observedGeneration to determine if the deployment controller noticed the pause. + if err := deploymentutil.WaitForObservedDeployment(func() (*extensions.Deployment, error) { + return deployments.Get(name) + }, deployment.Generation, 10*time.Millisecond, 1*time.Minute); err != nil { + return err + } + + // Stop all replica sets. selector, err := unversioned.LabelSelectorAsSelector(deployment.Spec.Selector) if err != nil { return err } + options := api.ListOptions{LabelSelector: selector} rsList, err := replicaSets.List(options) if err != nil { @@ -430,8 +405,9 @@ func (reaper *DeploymentReaper) Stop(namespace, name string, timeout time.Durati return utilerrors.NewAggregate(errList) } - // and finally deployment - return deployments.Delete(name, gracePeriod) + // Delete deployment at the end. + // Note: We delete deployment at the end so that if removing RSs fails, we atleast have the deployment to retry. + return deployments.Delete(name, nil) } type updateDeploymentFunc func(d *extensions.Deployment) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/stop_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/stop_test.go deleted file mode 100644 index 32a5f52d0..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/stop_test.go +++ /dev/null @@ -1,718 +0,0 @@ -/* -Copyright 2014 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 kubectl - -import ( - "fmt" - "reflect" - "strings" - "testing" - "time" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" - client "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/testclient" - "k8s.io/kubernetes/pkg/runtime" - deploymentutil "k8s.io/kubernetes/pkg/util/deployment" -) - -func TestReplicationControllerStop(t *testing.T) { - name := "foo" - ns := "default" - tests := []struct { - Name string - Objs []runtime.Object - StopError error - ExpectedActions []string - }{ - { - Name: "OnlyOneRC", - Objs: []runtime.Object{ - &api.ReplicationController{ // GET - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - Selector: map[string]string{"k1": "v1"}}, - }, - &api.ReplicationControllerList{ // LIST - Items: []api.ReplicationController{ - { - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - Selector: map[string]string{"k1": "v1"}}, - }, - }, - }, - }, - StopError: nil, - ExpectedActions: []string{"get", "list", "get", "update", "get", "get", "delete"}, - }, - { - Name: "NoOverlapping", - Objs: []runtime.Object{ - &api.ReplicationController{ // GET - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - Selector: map[string]string{"k1": "v1"}}, - }, - &api.ReplicationControllerList{ // LIST - Items: []api.ReplicationController{ - { - ObjectMeta: api.ObjectMeta{ - Name: "baz", - Namespace: ns, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - Selector: map[string]string{"k3": "v3"}}, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - Selector: map[string]string{"k1": "v1"}}, - }, - }, - }, - }, - StopError: nil, - ExpectedActions: []string{"get", "list", "get", "update", "get", "get", "delete"}, - }, - { - Name: "OverlappingError", - Objs: []runtime.Object{ - - &api.ReplicationController{ // GET - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - Selector: map[string]string{"k1": "v1"}}, - }, - &api.ReplicationControllerList{ // LIST - Items: []api.ReplicationController{ - { - ObjectMeta: api.ObjectMeta{ - Name: "baz", - Namespace: ns, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - Selector: map[string]string{"k1": "v1", "k2": "v2"}}, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - Selector: map[string]string{"k1": "v1"}}, - }, - }, - }, - }, - StopError: fmt.Errorf("Detected overlapping controllers for rc foo: baz, please manage deletion individually with --cascade=false."), - ExpectedActions: []string{"get", "list"}, - }, - - { - Name: "OverlappingButSafeDelete", - Objs: []runtime.Object{ - - &api.ReplicationController{ // GET - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - Selector: map[string]string{"k1": "v1", "k2": "v2"}}, - }, - &api.ReplicationControllerList{ // LIST - Items: []api.ReplicationController{ - { - ObjectMeta: api.ObjectMeta{ - Name: "baz", - Namespace: ns, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - Selector: map[string]string{"k1": "v1", "k2": "v2", "k3": "v3"}}, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: "zaz", - Namespace: ns, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - Selector: map[string]string{"k1": "v1"}}, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - Selector: map[string]string{"k1": "v1", "k2": "v2"}}, - }, - }, - }, - }, - - StopError: fmt.Errorf("Detected overlapping controllers for rc foo: baz,zaz, please manage deletion individually with --cascade=false."), - ExpectedActions: []string{"get", "list"}, - }, - - { - Name: "TwoExactMatchRCs", - Objs: []runtime.Object{ - - &api.ReplicationController{ // GET - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - Selector: map[string]string{"k1": "v1"}}, - }, - &api.ReplicationControllerList{ // LIST - Items: []api.ReplicationController{ - { - ObjectMeta: api.ObjectMeta{ - Name: "zaz", - Namespace: ns, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - Selector: map[string]string{"k1": "v1"}}, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: api.ReplicationControllerSpec{ - Replicas: 0, - Selector: map[string]string{"k1": "v1"}}, - }, - }, - }, - }, - - StopError: nil, - ExpectedActions: []string{"get", "list", "delete"}, - }, - } - - for _, test := range tests { - fake := testclient.NewSimpleFake(test.Objs...) - reaper := ReplicationControllerReaper{fake, time.Millisecond, time.Millisecond} - err := reaper.Stop(ns, name, 0, nil) - if !reflect.DeepEqual(err, test.StopError) { - t.Errorf("%s unexpected error: %v", test.Name, err) - continue - } - - actions := fake.Actions() - if len(actions) != len(test.ExpectedActions) { - t.Errorf("%s unexpected actions: %v, expected %d actions got %d", test.Name, actions, len(test.ExpectedActions), len(actions)) - continue - } - for i, verb := range test.ExpectedActions { - if actions[i].GetResource() != "replicationcontrollers" { - t.Errorf("%s unexpected action: %+v, expected %s-replicationController", test.Name, actions[i], verb) - } - if actions[i].GetVerb() != verb { - t.Errorf("%s unexpected action: %+v, expected %s-replicationController", test.Name, actions[i], verb) - } - } - } -} - -func TestReplicaSetStop(t *testing.T) { - name := "foo" - ns := "default" - tests := []struct { - Name string - Objs []runtime.Object - StopError error - ExpectedActions []string - }{ - { - Name: "OnlyOneRS", - Objs: []runtime.Object{ - &extensions.ReplicaSet{ // GET - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: extensions.ReplicaSetSpec{ - Replicas: 0, - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"k1": "v1"}}, - }, - }, - &extensions.ReplicaSetList{ // LIST - Items: []extensions.ReplicaSet{ - { - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: extensions.ReplicaSetSpec{ - Replicas: 0, - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"k1": "v1"}}, - }, - }, - }, - }, - }, - StopError: nil, - ExpectedActions: []string{"get", "get", "update", "get", "get", "delete"}, - }, - { - Name: "NoOverlapping", - Objs: []runtime.Object{ - &extensions.ReplicaSet{ // GET - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: extensions.ReplicaSetSpec{ - Replicas: 0, - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"k1": "v1"}}, - }, - }, - &extensions.ReplicaSetList{ // LIST - Items: []extensions.ReplicaSet{ - { - ObjectMeta: api.ObjectMeta{ - Name: "baz", - Namespace: ns, - }, - Spec: extensions.ReplicaSetSpec{ - Replicas: 0, - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"k3": "v3"}}, - }, - }, - { - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: extensions.ReplicaSetSpec{ - Replicas: 0, - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"k1": "v1"}}, - }, - }, - }, - }, - }, - StopError: nil, - ExpectedActions: []string{"get", "get", "update", "get", "get", "delete"}, - }, - // TODO: Implement tests for overlapping replica sets, similar to replication controllers, - // when the overlapping checks are implemented for replica sets. - } - - for _, test := range tests { - fake := testclient.NewSimpleFake(test.Objs...) - reaper := ReplicaSetReaper{fake, time.Millisecond, time.Millisecond} - err := reaper.Stop(ns, name, 0, nil) - if !reflect.DeepEqual(err, test.StopError) { - t.Errorf("%s unexpected error: %v", test.Name, err) - continue - } - - actions := fake.Actions() - if len(actions) != len(test.ExpectedActions) { - t.Errorf("%s unexpected actions: %v, expected %d actions got %d", test.Name, actions, len(test.ExpectedActions), len(actions)) - continue - } - for i, verb := range test.ExpectedActions { - if actions[i].GetResource() != "replicasets" { - t.Errorf("%s unexpected action: %+v, expected %s-replicaSet", test.Name, actions[i], verb) - } - if actions[i].GetVerb() != verb { - t.Errorf("%s unexpected action: %+v, expected %s-replicaSet", test.Name, actions[i], verb) - } - } - } -} - -func TestJobStop(t *testing.T) { - name := "foo" - ns := "default" - zero := 0 - tests := []struct { - Name string - Objs []runtime.Object - StopError error - ExpectedActions []string - }{ - { - Name: "OnlyOneJob", - Objs: []runtime.Object{ - &extensions.Job{ // GET - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: extensions.JobSpec{ - Parallelism: &zero, - Selector: &unversioned.LabelSelector{ - MatchLabels: map[string]string{"k1": "v1"}, - }, - }, - }, - &extensions.JobList{ // LIST - Items: []extensions.Job{ - { - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: extensions.JobSpec{ - Parallelism: &zero, - Selector: &unversioned.LabelSelector{ - MatchLabels: map[string]string{"k1": "v1"}, - }, - }, - }, - }, - }, - }, - StopError: nil, - ExpectedActions: []string{"get:jobs", "get:jobs", "update:jobs", - "get:jobs", "get:jobs", "list:pods", "delete:jobs"}, - }, - { - Name: "JobWithDeadPods", - Objs: []runtime.Object{ - &extensions.Job{ // GET - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: extensions.JobSpec{ - Parallelism: &zero, - Selector: &unversioned.LabelSelector{ - MatchLabels: map[string]string{"k1": "v1"}, - }, - }, - }, - &extensions.JobList{ // LIST - Items: []extensions.Job{ - { - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: extensions.JobSpec{ - Parallelism: &zero, - Selector: &unversioned.LabelSelector{ - MatchLabels: map[string]string{"k1": "v1"}, - }, - }, - }, - }, - }, - &api.PodList{ // LIST - Items: []api.Pod{ - { - ObjectMeta: api.ObjectMeta{ - Name: "pod1", - Namespace: ns, - Labels: map[string]string{"k1": "v1"}, - }, - }, - }, - }, - }, - StopError: nil, - ExpectedActions: []string{"get:jobs", "get:jobs", "update:jobs", - "get:jobs", "get:jobs", "list:pods", "delete:pods", "delete:jobs"}, - }, - } - - for _, test := range tests { - fake := testclient.NewSimpleFake(test.Objs...) - reaper := JobReaper{fake, time.Millisecond, time.Millisecond} - err := reaper.Stop(ns, name, 0, nil) - if !reflect.DeepEqual(err, test.StopError) { - t.Errorf("%s unexpected error: %v", test.Name, err) - continue - } - - actions := fake.Actions() - if len(actions) != len(test.ExpectedActions) { - t.Errorf("%s unexpected actions: %v, expected %d actions got %d", test.Name, actions, len(test.ExpectedActions), len(actions)) - continue - } - for i, expAction := range test.ExpectedActions { - action := strings.Split(expAction, ":") - if actions[i].GetVerb() != action[0] { - t.Errorf("%s unexpected verb: %+v, expected %s", test.Name, actions[i], expAction) - } - if actions[i].GetResource() != action[1] { - t.Errorf("%s unexpected resource: %+v, expected %s", test.Name, actions[i], expAction) - } - } - } -} - -func TestDeploymentStop(t *testing.T) { - name := "foo" - ns := "default" - deployment := extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: extensions.DeploymentSpec{ - Replicas: 0, - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"k1": "v1"}}, - }, - Status: extensions.DeploymentStatus{ - Replicas: 0, - }, - } - template := deploymentutil.GetNewReplicaSetTemplate(deployment) - tests := []struct { - Name string - Objs []runtime.Object - StopError error - ExpectedActions []string - }{ - { - Name: "SimpleDeployment", - Objs: []runtime.Object{ - &extensions.Deployment{ // GET - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: extensions.DeploymentSpec{ - Replicas: 0, - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"k1": "v1"}}, - }, - Status: extensions.DeploymentStatus{ - Replicas: 0, - }, - }, - }, - StopError: nil, - ExpectedActions: []string{"get:deployments", "update:deployments", - "get:deployments", "get:deployments", "update:deployments", - "list:replicasets", "delete:deployments"}, - }, - { - Name: "Deployment with single replicaset", - Objs: []runtime.Object{ - &deployment, // GET - &extensions.ReplicaSetList{ // LIST - Items: []extensions.ReplicaSet{ - { - ObjectMeta: api.ObjectMeta{ - Name: name, - Namespace: ns, - }, - Spec: extensions.ReplicaSetSpec{ - Template: &template, - }, - }, - }, - }, - }, - StopError: nil, - ExpectedActions: []string{"get:deployments", "update:deployments", - "get:deployments", "get:deployments", "update:deployments", - "list:replicasets", "get:replicasets", "get:replicasets", - "update:replicasets", "get:replicasets", "get:replicasets", - "delete:replicasets", "delete:deployments"}, - }, - } - - for _, test := range tests { - fake := testclient.NewSimpleFake(test.Objs...) - reaper := DeploymentReaper{fake, time.Millisecond, time.Millisecond} - err := reaper.Stop(ns, name, 0, nil) - if !reflect.DeepEqual(err, test.StopError) { - t.Errorf("%s unexpected error: %v", test.Name, err) - continue - } - - actions := fake.Actions() - if len(actions) != len(test.ExpectedActions) { - t.Errorf("%s unexpected actions: %v, expected %d actions got %d", test.Name, actions, len(test.ExpectedActions), len(actions)) - continue - } - for i, expAction := range test.ExpectedActions { - action := strings.Split(expAction, ":") - if actions[i].GetVerb() != action[0] { - t.Errorf("%s unexpected verb: %+v, expected %s", test.Name, actions[i], expAction) - } - if actions[i].GetResource() != action[1] { - t.Errorf("%s unexpected resource: %+v, expected %s", test.Name, actions[i], expAction) - } - if len(action) == 3 && actions[i].GetSubresource() != action[2] { - t.Errorf("%s unexpected subresource: %+v, expected %s", test.Name, actions[i], expAction) - } - } - } -} - -type noSuchPod struct { - *testclient.FakePods -} - -func (c *noSuchPod) Get(name string) (*api.Pod, error) { - return nil, fmt.Errorf("%s does not exist", name) -} - -type noDeleteService struct { - *testclient.FakeServices -} - -func (c *noDeleteService) Delete(service string) error { - return fmt.Errorf("I'm afraid I can't do that, Dave") -} - -type reaperFake struct { - *testclient.Fake - noSuchPod, noDeleteService bool -} - -func (c *reaperFake) Pods(namespace string) client.PodInterface { - pods := &testclient.FakePods{Fake: c.Fake, Namespace: namespace} - if c.noSuchPod { - return &noSuchPod{pods} - } - return pods -} - -func (c *reaperFake) Services(namespace string) client.ServiceInterface { - services := &testclient.FakeServices{Fake: c.Fake, Namespace: namespace} - if c.noDeleteService { - return &noDeleteService{services} - } - return services -} - -func TestSimpleStop(t *testing.T) { - tests := []struct { - fake *reaperFake - kind unversioned.GroupKind - actions []testclient.Action - expectError bool - test string - }{ - { - fake: &reaperFake{ - Fake: &testclient.Fake{}, - }, - kind: api.Kind("Pod"), - actions: []testclient.Action{ - testclient.NewGetAction("pods", api.NamespaceDefault, "foo"), - testclient.NewDeleteAction("pods", api.NamespaceDefault, "foo"), - }, - expectError: false, - test: "stop pod succeeds", - }, - { - fake: &reaperFake{ - Fake: &testclient.Fake{}, - }, - kind: api.Kind("Service"), - actions: []testclient.Action{ - testclient.NewGetAction("services", api.NamespaceDefault, "foo"), - testclient.NewDeleteAction("services", api.NamespaceDefault, "foo"), - }, - expectError: false, - test: "stop service succeeds", - }, - { - fake: &reaperFake{ - Fake: &testclient.Fake{}, - noSuchPod: true, - }, - kind: api.Kind("Pod"), - actions: []testclient.Action{}, - expectError: true, - test: "stop pod fails, no pod", - }, - { - fake: &reaperFake{ - Fake: &testclient.Fake{}, - noDeleteService: true, - }, - kind: api.Kind("Service"), - actions: []testclient.Action{ - testclient.NewGetAction("services", api.NamespaceDefault, "foo"), - }, - expectError: true, - test: "stop service fails, can't delete", - }, - } - for _, test := range tests { - fake := test.fake - reaper, err := ReaperFor(test.kind, fake) - if err != nil { - t.Errorf("unexpected error: %v (%s)", err, test.test) - } - err = reaper.Stop("default", "foo", 0, nil) - if err != nil && !test.expectError { - t.Errorf("unexpected error: %v (%s)", err, test.test) - } - if err == nil { - if test.expectError { - t.Errorf("unexpected non-error: %v (%s)", err, test.test) - } - } - actions := fake.Actions() - if len(test.actions) != len(actions) { - t.Errorf("unexpected actions: %v; expected %v (%s)", fake.Actions, test.actions, test.test) - } - for i, action := range actions { - testAction := test.actions[i] - if action != testAction { - t.Errorf("unexpected action: %#v; expected %v (%s)", action, testAction, test.test) - } - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/testing/types.generated.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/testing/types.generated.go deleted file mode 100644 index b3dd3628d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/testing/types.generated.go +++ /dev/null @@ -1,555 +0,0 @@ -/* -Copyright 2015 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. -*/ - -// ************************************************************ -// DO NOT EDIT. -// THIS FILE IS AUTO-GENERATED BY codecgen. -// ************************************************************ - -package testing - -import ( - "errors" - "fmt" - codec1978 "github.com/ugorji/go/codec" - pkg2_api "k8s.io/kubernetes/pkg/api" - pkg1_unversioned "k8s.io/kubernetes/pkg/api/unversioned" - pkg3_types "k8s.io/kubernetes/pkg/types" - "reflect" - "runtime" - time "time" -) - -const ( - // ----- content types ---- - codecSelferC_UTF81234 = 1 - codecSelferC_RAW1234 = 0 - // ----- value types used ---- - codecSelferValueTypeArray1234 = 10 - codecSelferValueTypeMap1234 = 9 - // ----- containerStateValues ---- - codecSelfer_containerMapKey1234 = 2 - codecSelfer_containerMapValue1234 = 3 - codecSelfer_containerMapEnd1234 = 4 - codecSelfer_containerArrayElem1234 = 6 - codecSelfer_containerArrayEnd1234 = 7 -) - -var ( - codecSelferBitsize1234 = uint8(reflect.TypeOf(uint(0)).Bits()) - codecSelferOnlyMapOrArrayEncodeToStructErr1234 = errors.New(`only encoded map or array can be decoded into a struct`) -) - -type codecSelfer1234 struct{} - -func init() { - if codec1978.GenVersion != 5 { - _, file, _, _ := runtime.Caller(0) - err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v", - 5, codec1978.GenVersion, file) - panic(err) - } - if false { // reference the types, but skip this branch at build/run time - var v0 pkg2_api.ObjectMeta - var v1 pkg1_unversioned.TypeMeta - var v2 pkg3_types.UID - var v3 time.Time - _, _, _, _ = v0, v1, v2, v3 - } -} - -func (x *TestStruct) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [7]bool - _, _, _ = yysep2, yyq2, yy2arr2 - const yyr2 bool = false - yyq2[0] = true - yyq2[5] = x.Kind != "" - yyq2[6] = x.APIVersion != "" - var yynn2 int - if yyr2 || yy2arr2 { - r.EncodeArrayStart(7) - } else { - yynn2 = 4 - for _, b := range yyq2 { - if b { - yynn2++ - } - } - r.EncodeMapStart(yynn2) - yynn2 = 0 - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[0] { - yy4 := &x.ObjectMeta - yy4.CodecEncodeSelf(e) - } else { - r.EncodeNil() - } - } else { - if yyq2[0] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("metadata")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yy6 := &x.ObjectMeta - yy6.CodecEncodeSelf(e) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - yym9 := z.EncBinary() - _ = yym9 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.Key)) - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("Key")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym10 := z.EncBinary() - _ = yym10 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.Key)) - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if x.Map == nil { - r.EncodeNil() - } else { - yym12 := z.EncBinary() - _ = yym12 - if false { - } else { - z.F.EncMapStringIntV(x.Map, false, e) - } - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("Map")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - if x.Map == nil { - r.EncodeNil() - } else { - yym13 := z.EncBinary() - _ = yym13 - if false { - } else { - z.F.EncMapStringIntV(x.Map, false, e) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if x.StringList == nil { - r.EncodeNil() - } else { - yym15 := z.EncBinary() - _ = yym15 - if false { - } else { - z.F.EncSliceStringV(x.StringList, false, e) - } - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("StringList")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - if x.StringList == nil { - r.EncodeNil() - } else { - yym16 := z.EncBinary() - _ = yym16 - if false { - } else { - z.F.EncSliceStringV(x.StringList, false, e) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if x.IntList == nil { - r.EncodeNil() - } else { - yym18 := z.EncBinary() - _ = yym18 - if false { - } else { - z.F.EncSliceIntV(x.IntList, false, e) - } - } - } else { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("IntList")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - if x.IntList == nil { - r.EncodeNil() - } else { - yym19 := z.EncBinary() - _ = yym19 - if false { - } else { - z.F.EncSliceIntV(x.IntList, false, e) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[5] { - yym21 := z.EncBinary() - _ = yym21 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) - } - } else { - r.EncodeString(codecSelferC_UTF81234, "") - } - } else { - if yyq2[5] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("kind")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym22 := z.EncBinary() - _ = yym22 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayElem1234) - if yyq2[6] { - yym24 := z.EncBinary() - _ = yym24 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) - } - } else { - r.EncodeString(codecSelferC_UTF81234, "") - } - } else { - if yyq2[6] { - z.EncSendContainerState(codecSelfer_containerMapKey1234) - r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) - z.EncSendContainerState(codecSelfer_containerMapValue1234) - yym25 := z.EncBinary() - _ = yym25 - if false { - } else { - r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) - } - } - } - if yyr2 || yy2arr2 { - z.EncSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - z.EncSendContainerState(codecSelfer_containerMapEnd1234) - } - } - } -} - -func (x *TestStruct) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap1234 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerMapEnd1234) - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray1234 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) - } - } -} - -func (x *TestStruct) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - z.DecSendContainerState(codecSelfer_containerMapKey1234) - yys3Slc = r.DecodeBytes(yys3Slc, true, true) - yys3 := string(yys3Slc) - z.DecSendContainerState(codecSelfer_containerMapValue1234) - switch yys3 { - case "metadata": - if r.TryDecodeAsNil() { - x.ObjectMeta = pkg2_api.ObjectMeta{} - } else { - yyv4 := &x.ObjectMeta - yyv4.CodecDecodeSelf(d) - } - case "Key": - if r.TryDecodeAsNil() { - x.Key = "" - } else { - x.Key = string(r.DecodeString()) - } - case "Map": - if r.TryDecodeAsNil() { - x.Map = nil - } else { - yyv6 := &x.Map - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - z.F.DecMapStringIntX(yyv6, false, d) - } - } - case "StringList": - if r.TryDecodeAsNil() { - x.StringList = nil - } else { - yyv8 := &x.StringList - yym9 := z.DecBinary() - _ = yym9 - if false { - } else { - z.F.DecSliceStringX(yyv8, false, d) - } - } - case "IntList": - if r.TryDecodeAsNil() { - x.IntList = nil - } else { - yyv10 := &x.IntList - yym11 := z.DecBinary() - _ = yym11 - if false { - } else { - z.F.DecSliceIntX(yyv10, false, d) - } - } - case "kind": - if r.TryDecodeAsNil() { - x.Kind = "" - } else { - x.Kind = string(r.DecodeString()) - } - case "apiVersion": - if r.TryDecodeAsNil() { - x.APIVersion = "" - } else { - x.APIVersion = string(r.DecodeString()) - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - z.DecSendContainerState(codecSelfer_containerMapEnd1234) -} - -func (x *TestStruct) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer1234 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj14 int - var yyb14 bool - var yyhl14 bool = l >= 0 - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l - } else { - yyb14 = r.CheckBreak() - } - if yyb14 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.ObjectMeta = pkg2_api.ObjectMeta{} - } else { - yyv15 := &x.ObjectMeta - yyv15.CodecDecodeSelf(d) - } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l - } else { - yyb14 = r.CheckBreak() - } - if yyb14 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.Key = "" - } else { - x.Key = string(r.DecodeString()) - } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l - } else { - yyb14 = r.CheckBreak() - } - if yyb14 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.Map = nil - } else { - yyv17 := &x.Map - yym18 := z.DecBinary() - _ = yym18 - if false { - } else { - z.F.DecMapStringIntX(yyv17, false, d) - } - } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l - } else { - yyb14 = r.CheckBreak() - } - if yyb14 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.StringList = nil - } else { - yyv19 := &x.StringList - yym20 := z.DecBinary() - _ = yym20 - if false { - } else { - z.F.DecSliceStringX(yyv19, false, d) - } - } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l - } else { - yyb14 = r.CheckBreak() - } - if yyb14 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.IntList = nil - } else { - yyv21 := &x.IntList - yym22 := z.DecBinary() - _ = yym22 - if false { - } else { - z.F.DecSliceIntX(yyv21, false, d) - } - } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l - } else { - yyb14 = r.CheckBreak() - } - if yyb14 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.Kind = "" - } else { - x.Kind = string(r.DecodeString()) - } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l - } else { - yyb14 = r.CheckBreak() - } - if yyb14 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.APIVersion = "" - } else { - x.APIVersion = string(r.DecodeString()) - } - for { - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l - } else { - yyb14 = r.CheckBreak() - } - if yyb14 { - break - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj14-1, "") - } - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/testing/types.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/testing/types.go deleted file mode 100644 index 2fbc2ed45..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubectl/testing/types.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -Copyright 2015 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 testing - -import ( - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" -) - -type TestStruct struct { - unversioned.TypeMeta `json:",inline"` - api.ObjectMeta `json:"metadata,omitempty"` - Key string `json:"Key"` - Map map[string]int `json:"Map"` - StringList []string `json:"StringList"` - IntList []int `json:"IntList"` -} - -func (obj *TestStruct) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubelet/qos/memory_policy_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubelet/qos/memory_policy_test.go deleted file mode 100644 index 27af3fd11..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/kubelet/qos/memory_policy_test.go +++ /dev/null @@ -1,187 +0,0 @@ -/* -Copyright 2015 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 qos - -import ( - "strconv" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/resource" -) - -const ( - standardMemoryAmount = 8000000000 -) - -var ( - zeroRequestMemoryBestEffort = api.Container{ - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("5m"), - api.ResourceName(api.ResourceMemory): resource.MustParse("0G"), - }, - Limits: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("5m"), - api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), - }, - }, - } - - edgeMemoryBestEffort = api.Container{ - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceMemory): resource.MustParse("0G"), - }, - Limits: api.ResourceList{ - api.ResourceName(api.ResourceMemory): resource.MustParse("0G"), - }, - }, - } - - noRequestMemoryBestEffort = api.Container{ - Resources: api.ResourceRequirements{ - Limits: api.ResourceList{ - api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), - }, - }, - } - - noLimitMemoryBestEffort = api.Container{} - - memoryGuaranteed = api.Container{ - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), - }, - Limits: api.ResourceList{ - api.ResourceName(api.ResourceCPU): resource.MustParse("5m"), - api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), - }, - }, - } - - memoryBurstable = api.Container{ - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceMemory): resource.MustParse(strconv.Itoa(standardMemoryAmount / 2)), - }, - Limits: api.ResourceList{ - api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), - }, - }, - } - - memoryBurstableNoLimit = api.Container{ - Resources: api.ResourceRequirements{ - Requests: api.ResourceList{ - api.ResourceName(api.ResourceMemory): resource.MustParse(strconv.Itoa(standardMemoryAmount - 1)), - }, - }, - } -) - -func TestIsMemoryBestEffort(t *testing.T) { - validCases := []api.Container{zeroRequestMemoryBestEffort, noRequestMemoryBestEffort, noLimitMemoryBestEffort, edgeMemoryBestEffort} - for _, container := range validCases { - if !isMemoryBestEffort(&container) { - t.Errorf("container %+v is memory best-effort", container) - } - } - invalidCases := []api.Container{memoryGuaranteed, memoryBurstable} - for _, container := range invalidCases { - if isMemoryBestEffort(&container) { - t.Errorf("container %+v is not memory best-effort", container) - } - } -} - -func TestIsMemoryGuaranteed(t *testing.T) { - validCases := []api.Container{memoryGuaranteed} - for _, container := range validCases { - if !isMemoryGuaranteed(&container) { - t.Errorf("container %+v is memory guaranteed", container) - } - } - invalidCases := []api.Container{zeroRequestMemoryBestEffort, noRequestMemoryBestEffort, noLimitMemoryBestEffort, edgeMemoryBestEffort, memoryBurstable} - for _, container := range invalidCases { - if isMemoryGuaranteed(&container) { - t.Errorf("container %+v is not memory guaranteed", container) - } - } -} - -type oomTest struct { - container *api.Container - memoryCapacity int64 - lowOOMScoreAdj int // The max oom_score_adj score the container should be assigned. - highOOMScoreAdj int // The min oom_score_adj score the container should be assigned. -} - -func TestGetContainerOOMScoreAdjust(t *testing.T) { - - oomTests := []oomTest{ - { - container: &zeroRequestMemoryBestEffort, - memoryCapacity: 4000000000, - lowOOMScoreAdj: 1000, - highOOMScoreAdj: 1000, - }, - { - container: &edgeMemoryBestEffort, - memoryCapacity: 8000000000, - lowOOMScoreAdj: 1000, - highOOMScoreAdj: 1000, - }, - { - container: &noRequestMemoryBestEffort, - memoryCapacity: 7230457451, - lowOOMScoreAdj: 1000, - highOOMScoreAdj: 1000, - }, - { - container: &noLimitMemoryBestEffort, - memoryCapacity: 4000000000, - lowOOMScoreAdj: 1000, - highOOMScoreAdj: 1000, - }, - { - container: &memoryGuaranteed, - memoryCapacity: 123456789, - lowOOMScoreAdj: -999, - highOOMScoreAdj: -999, - }, - { - container: &memoryBurstable, - memoryCapacity: standardMemoryAmount, - lowOOMScoreAdj: 495, - highOOMScoreAdj: 505, - }, - { - container: &memoryBurstableNoLimit, - memoryCapacity: standardMemoryAmount, - lowOOMScoreAdj: 2, - highOOMScoreAdj: 2, - }, - } - for _, test := range oomTests { - oomScoreAdj := GetContainerOOMScoreAdjust(test.container, test.memoryCapacity) - if oomScoreAdj < test.lowOOMScoreAdj || oomScoreAdj > test.highOOMScoreAdj { - t.Errorf("oom_score_adj should be between %d and %d, but was %d", test.lowOOMScoreAdj, test.highOOMScoreAdj, oomScoreAdj) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/labels/labels_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/labels/labels_test.go deleted file mode 100644 index 8d3834d51..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/labels/labels_test.go +++ /dev/null @@ -1,60 +0,0 @@ -/* -Copyright 2014 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 labels - -import ( - "testing" -) - -func matches(t *testing.T, ls Set, want string) { - if ls.String() != want { - t.Errorf("Expected '%s', but got '%s'", want, ls.String()) - } -} - -func TestSetString(t *testing.T) { - matches(t, Set{"x": "y"}, "x=y") - matches(t, Set{"foo": "bar"}, "foo=bar") - matches(t, Set{"foo": "bar", "baz": "qup"}, "baz=qup,foo=bar") - - // TODO: Make our label representation robust enough to handle labels - // with ",=!" characters in their names. -} - -func TestLabelHas(t *testing.T) { - labelHasTests := []struct { - Ls Labels - Key string - Has bool - }{ - {Set{"x": "y"}, "x", true}, - {Set{"x": ""}, "x", true}, - {Set{"x": "y"}, "foo", false}, - } - for _, lh := range labelHasTests { - if has := lh.Ls.Has(lh.Key); has != lh.Has { - t.Errorf("%#v.Has(%#v) => %v, expected %v", lh.Ls, lh.Key, has, lh.Has) - } - } -} - -func TestLabelGet(t *testing.T) { - ls := Set{"x": "y"} - if ls.Get("x") != "y" { - t.Errorf("Set.Get is broken") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/labels/selector.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/labels/selector.go index 520377486..fb48b10e9 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/labels/selector.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/labels/selector.go @@ -72,8 +72,8 @@ const ( NotEqualsOperator Operator = "!=" NotInOperator Operator = "notin" ExistsOperator Operator = "exists" - GreaterThanOperator Operator = "Gt" - LessThanOperator Operator = "Lt" + GreaterThanOperator Operator = "gt" + LessThanOperator Operator = "lt" ) func NewSelector() Selector { @@ -743,13 +743,25 @@ func (p *Parser) parseExactValue() (sets.String, error) { // (5) A requirement with just !KEY requires that the KEY not exist. // func Parse(selector string) (Selector, error) { - p := &Parser{l: &Lexer{s: selector, pos: 0}} - items, error := p.parse() - if error == nil { - sort.Sort(ByKey(items)) // sort to grant determistic parsing - return internalSelector(items), error + parsedSelector, err := parse(selector) + if err == nil { + return parsedSelector, nil } - return nil, error + return nil, err +} + +// parse parses the string representation of the selector and returns the internalSelector struct. +// The callers of this method can then decide how to return the internalSelector struct to their +// callers. This function has two callers now, one returns a Selector interface and the other +// returns a list of requirements. +func parse(selector string) (internalSelector, error) { + p := &Parser{l: &Lexer{s: selector, pos: 0}} + items, err := p.parse() + if err != nil { + return nil, err + } + sort.Sort(ByKey(items)) // sort to grant determistic parsing + return internalSelector(items), err } var qualifiedNameErrorMsg string = fmt.Sprintf(`must be a qualified name (at most %d characters, matching regex %s), with an optional DNS subdomain prefix (at most %d characters, matching regex %s) and slash (/): e.g. "MyName" or "example.com/MyName"`, validation.QualifiedNameMaxLength, validation.QualifiedNameFmt, validation.DNS1123SubdomainMaxLength, validation.DNS1123SubdomainFmt) @@ -788,3 +800,12 @@ func SelectorFromSet(ls Set) Selector { sort.Sort(ByKey(requirements)) return internalSelector(requirements) } + +// ParseToRequirements takes a string representing a selector and returns a list of +// requirements. This function is suitable for those callers that perform additional +// processing on selector requirements. +// See the documentation for Parse() function for more details. +// TODO: Consider exporting the internalSelector type instead. +func ParseToRequirements(selector string) ([]Requirement, error) { + return parse(selector) +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/labels/selector_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/labels/selector_test.go deleted file mode 100644 index 5fbb1fc76..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/labels/selector_test.go +++ /dev/null @@ -1,574 +0,0 @@ -/* -Copyright 2014 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 labels - -import ( - "reflect" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/util/sets" -) - -func TestSelectorParse(t *testing.T) { - testGoodStrings := []string{ - "x=a,y=b,z=c", - "", - "x!=a,y=b", - "x=", - "x= ", - "x=,z= ", - "x= ,z= ", - "!x", - "x>1.1", - "x>1.1,z<5.3", - } - testBadStrings := []string{ - "x=a||y=b", - "x==a==b", - "!x=a", - "x1.1", Set{"x": "1.2"}) - expectMatch(t, "x<1.1", Set{"x": "0.8"}) - expectNoMatch(t, "x=z", Set{}) - expectNoMatch(t, "x=y", Set{"x": "z"}) - expectNoMatch(t, "x=y,z=w", Set{"x": "w", "z": "w"}) - expectNoMatch(t, "x!=y,z!=w", Set{"x": "z", "z": "w"}) - expectNoMatch(t, "x", Set{"y": "z"}) - expectNoMatch(t, "!x", Set{"x": "z"}) - expectNoMatch(t, "x>1.1", Set{"x": "0.8"}) - expectNoMatch(t, "x<1.1", Set{"x": "1.1"}) - - labelset := Set{ - "foo": "bar", - "baz": "blah", - } - expectMatch(t, "foo=bar", labelset) - expectMatch(t, "baz=blah", labelset) - expectMatch(t, "foo=bar,baz=blah", labelset) - expectNoMatch(t, "foo=blah", labelset) - expectNoMatch(t, "baz=bar", labelset) - expectNoMatch(t, "foo=bar,foobar=bar,baz=blah", labelset) -} - -func expectMatchDirect(t *testing.T, selector, ls Set) { - if !SelectorFromSet(selector).Matches(ls) { - t.Errorf("Wanted %s to match '%s', but it did not.\n", selector, ls) - } -} - -func expectNoMatchDirect(t *testing.T, selector, ls Set) { - if SelectorFromSet(selector).Matches(ls) { - t.Errorf("Wanted '%s' to not match '%s', but it did.", selector, ls) - } -} - -func TestSetMatches(t *testing.T) { - labelset := Set{ - "foo": "bar", - "baz": "blah", - } - expectMatchDirect(t, Set{}, labelset) - expectMatchDirect(t, Set{"foo": "bar"}, labelset) - expectMatchDirect(t, Set{"baz": "blah"}, labelset) - expectMatchDirect(t, Set{"foo": "bar", "baz": "blah"}, labelset) - - //TODO: bad values not handled for the moment in SelectorFromSet - //expectNoMatchDirect(t, Set{"foo": "=blah"}, labelset) - //expectNoMatchDirect(t, Set{"baz": "=bar"}, labelset) - //expectNoMatchDirect(t, Set{"foo": "=bar", "foobar": "bar", "baz": "blah"}, labelset) -} - -func TestNilMapIsValid(t *testing.T) { - selector := Set(nil).AsSelector() - if selector == nil { - t.Errorf("Selector for nil set should be Everything") - } - if !selector.Empty() { - t.Errorf("Selector for nil set should be Empty") - } -} - -func TestSetIsEmpty(t *testing.T) { - if !(Set{}).AsSelector().Empty() { - t.Errorf("Empty set should be empty") - } - if !(NewSelector()).Empty() { - t.Errorf("Nil Selector should be empty") - } -} - -func TestLexer(t *testing.T) { - testcases := []struct { - s string - t Token - }{ - {"", EndOfStringToken}, - {",", CommaToken}, - {"notin", NotInToken}, - {"in", InToken}, - {"=", EqualsToken}, - {"==", DoubleEqualsToken}, - {">", GreaterThanToken}, - {"<", LessThanToken}, - //Note that Lex returns the longest valid token found - {"!", DoesNotExistToken}, - {"!=", NotEqualsToken}, - {"(", OpenParToken}, - {")", ClosedParToken}, - //Non-"special" characters are considered part of an identifier - {"~", IdentifierToken}, - {"||", IdentifierToken}, - } - for _, v := range testcases { - l := &Lexer{s: v.s, pos: 0} - token, lit := l.Lex() - if token != v.t { - t.Errorf("Got %d it should be %d for '%s'", token, v.t, v.s) - } - if v.t != ErrorToken && lit != v.s { - t.Errorf("Got '%s' it should be '%s'", lit, v.s) - } - } -} - -func min(l, r int) (m int) { - m = r - if l < r { - m = l - } - return m -} - -func TestLexerSequence(t *testing.T) { - testcases := []struct { - s string - t []Token - }{ - {"key in ( value )", []Token{IdentifierToken, InToken, OpenParToken, IdentifierToken, ClosedParToken}}, - {"key notin ( value )", []Token{IdentifierToken, NotInToken, OpenParToken, IdentifierToken, ClosedParToken}}, - {"key in ( value1, value2 )", []Token{IdentifierToken, InToken, OpenParToken, IdentifierToken, CommaToken, IdentifierToken, ClosedParToken}}, - {"key", []Token{IdentifierToken}}, - {"!key", []Token{DoesNotExistToken, IdentifierToken}}, - {"()", []Token{OpenParToken, ClosedParToken}}, - {"x in (),y", []Token{IdentifierToken, InToken, OpenParToken, ClosedParToken, CommaToken, IdentifierToken}}, - {"== != (), = notin", []Token{DoubleEqualsToken, NotEqualsToken, OpenParToken, ClosedParToken, CommaToken, EqualsToken, NotInToken}}, - {"key>1.1", []Token{IdentifierToken, GreaterThanToken, IdentifierToken}}, - {"key<0.8", []Token{IdentifierToken, LessThanToken, IdentifierToken}}, - } - for _, v := range testcases { - var literals []string - var tokens []Token - l := &Lexer{s: v.s, pos: 0} - for { - token, lit := l.Lex() - if token == EndOfStringToken { - break - } - tokens = append(tokens, token) - literals = append(literals, lit) - } - if len(tokens) != len(v.t) { - t.Errorf("Bad number of tokens for '%s %d, %d", v.s, len(tokens), len(v.t)) - } - for i := 0; i < min(len(tokens), len(v.t)); i++ { - if tokens[i] != v.t[i] { - t.Errorf("Test '%s': Mismatching in token type found '%v' it should be '%v'", v.s, tokens[i], v.t[i]) - } - } - } -} -func TestParserLookahead(t *testing.T) { - testcases := []struct { - s string - t []Token - }{ - {"key in ( value )", []Token{IdentifierToken, InToken, OpenParToken, IdentifierToken, ClosedParToken, EndOfStringToken}}, - {"key notin ( value )", []Token{IdentifierToken, NotInToken, OpenParToken, IdentifierToken, ClosedParToken, EndOfStringToken}}, - {"key in ( value1, value2 )", []Token{IdentifierToken, InToken, OpenParToken, IdentifierToken, CommaToken, IdentifierToken, ClosedParToken, EndOfStringToken}}, - {"key", []Token{IdentifierToken, EndOfStringToken}}, - {"!key", []Token{DoesNotExistToken, IdentifierToken, EndOfStringToken}}, - {"()", []Token{OpenParToken, ClosedParToken, EndOfStringToken}}, - {"", []Token{EndOfStringToken}}, - {"x in (),y", []Token{IdentifierToken, InToken, OpenParToken, ClosedParToken, CommaToken, IdentifierToken, EndOfStringToken}}, - {"== != (), = notin", []Token{DoubleEqualsToken, NotEqualsToken, OpenParToken, ClosedParToken, CommaToken, EqualsToken, NotInToken, EndOfStringToken}}, - {"key>1.1", []Token{IdentifierToken, GreaterThanToken, IdentifierToken, EndOfStringToken}}, - {"key<0.8", []Token{IdentifierToken, LessThanToken, IdentifierToken, EndOfStringToken}}, - } - for _, v := range testcases { - p := &Parser{l: &Lexer{s: v.s, pos: 0}, position: 0} - p.scan() - if len(p.scannedItems) != len(v.t) { - t.Errorf("Expected %d items found %d", len(v.t), len(p.scannedItems)) - } - for { - token, lit := p.lookahead(KeyAndOperator) - - token2, lit2 := p.consume(KeyAndOperator) - if token == EndOfStringToken { - break - } - if token != token2 || lit != lit2 { - t.Errorf("Bad values") - } - } - } -} - -func TestRequirementConstructor(t *testing.T) { - requirementConstructorTests := []struct { - Key string - Op Operator - Vals sets.String - Success bool - }{ - {"x", InOperator, nil, false}, - {"x", NotInOperator, sets.NewString(), false}, - {"x", InOperator, sets.NewString("foo"), true}, - {"x", NotInOperator, sets.NewString("foo"), true}, - {"x", ExistsOperator, nil, true}, - {"x", DoesNotExistOperator, nil, true}, - {"1foo", InOperator, sets.NewString("bar"), true}, - {"1234", InOperator, sets.NewString("bar"), true}, - {"y", GreaterThanOperator, sets.NewString("1.1"), true}, - {"z", LessThanOperator, sets.NewString("5.3"), true}, - {"foo", GreaterThanOperator, sets.NewString("bar"), false}, - {"barz", LessThanOperator, sets.NewString("blah"), false}, - {strings.Repeat("a", 254), ExistsOperator, nil, false}, //breaks DNS rule that len(key) <= 253 - } - for _, rc := range requirementConstructorTests { - if _, err := NewRequirement(rc.Key, rc.Op, rc.Vals); err == nil && !rc.Success { - t.Errorf("expected error with key:%#v op:%v vals:%v, got no error", rc.Key, rc.Op, rc.Vals) - } else if err != nil && rc.Success { - t.Errorf("expected no error with key:%#v op:%v vals:%v, got:%v", rc.Key, rc.Op, rc.Vals, err) - } - } -} - -func TestToString(t *testing.T) { - var req Requirement - toStringTests := []struct { - In *internalSelector - Out string - Valid bool - }{ - - {&internalSelector{ - getRequirement("x", InOperator, sets.NewString("abc", "def"), t), - getRequirement("y", NotInOperator, sets.NewString("jkl"), t), - getRequirement("z", ExistsOperator, nil, t)}, - "x in (abc,def),y notin (jkl),z", true}, - {&internalSelector{ - getRequirement("x", NotInOperator, sets.NewString("abc", "def"), t), - getRequirement("y", NotEqualsOperator, sets.NewString("jkl"), t), - getRequirement("z", DoesNotExistOperator, nil, t)}, - "x notin (abc,def),y!=jkl,!z", true}, - {&internalSelector{ - getRequirement("x", InOperator, sets.NewString("abc", "def"), t), - req}, // adding empty req for the trailing ',' - "x in (abc,def),", false}, - {&internalSelector{ - getRequirement("x", NotInOperator, sets.NewString("abc"), t), - getRequirement("y", InOperator, sets.NewString("jkl", "mno"), t), - getRequirement("z", NotInOperator, sets.NewString(""), t)}, - "x notin (abc),y in (jkl,mno),z notin ()", true}, - {&internalSelector{ - getRequirement("x", EqualsOperator, sets.NewString("abc"), t), - getRequirement("y", DoubleEqualsOperator, sets.NewString("jkl"), t), - getRequirement("z", NotEqualsOperator, sets.NewString("a"), t), - getRequirement("z", ExistsOperator, nil, t)}, - "x=abc,y==jkl,z!=a,z", true}, - {&internalSelector{ - getRequirement("x", GreaterThanOperator, sets.NewString("2.4"), t), - getRequirement("y", LessThanOperator, sets.NewString("7.1"), t), - getRequirement("z", ExistsOperator, nil, t)}, - "x>2.4,y<7.1,z", true}, - } - for _, ts := range toStringTests { - if out := ts.In.String(); out == "" && ts.Valid { - t.Errorf("%+v.String() => '%v' expected no error", ts.In, out) - } else if out != ts.Out { - t.Errorf("%+v.String() => '%v' want '%v'", ts.In, out, ts.Out) - } - } -} - -func TestRequirementSelectorMatching(t *testing.T) { - var req Requirement - labelSelectorMatchingTests := []struct { - Set Set - Sel Selector - Match bool - }{ - {Set{"x": "foo", "y": "baz"}, &internalSelector{ - req, - }, false}, - {Set{"x": "foo", "y": "baz"}, &internalSelector{ - getRequirement("x", InOperator, sets.NewString("foo"), t), - getRequirement("y", NotInOperator, sets.NewString("alpha"), t), - }, true}, - {Set{"x": "foo", "y": "baz"}, &internalSelector{ - getRequirement("x", InOperator, sets.NewString("foo"), t), - getRequirement("y", InOperator, sets.NewString("alpha"), t), - }, false}, - {Set{"y": ""}, &internalSelector{ - getRequirement("x", NotInOperator, sets.NewString(""), t), - getRequirement("y", ExistsOperator, nil, t), - }, true}, - {Set{"y": ""}, &internalSelector{ - getRequirement("x", DoesNotExistOperator, nil, t), - getRequirement("y", ExistsOperator, nil, t), - }, true}, - {Set{"y": ""}, &internalSelector{ - getRequirement("x", NotInOperator, sets.NewString(""), t), - getRequirement("y", DoesNotExistOperator, nil, t), - }, false}, - {Set{"y": "baz"}, &internalSelector{ - getRequirement("x", InOperator, sets.NewString(""), t), - }, false}, - {Set{"z": "1.2"}, &internalSelector{ - getRequirement("z", GreaterThanOperator, sets.NewString("1.0"), t), - }, true}, - {Set{"z": "v1.2"}, &internalSelector{ - getRequirement("z", GreaterThanOperator, sets.NewString("1.0"), t), - }, false}, - } - for _, lsm := range labelSelectorMatchingTests { - if match := lsm.Sel.Matches(lsm.Set); match != lsm.Match { - t.Errorf("%+v.Matches(%#v) => %v, want %v", lsm.Sel, lsm.Set, match, lsm.Match) - } - } -} - -func TestSetSelectorParser(t *testing.T) { - setSelectorParserTests := []struct { - In string - Out Selector - Match bool - Valid bool - }{ - {"", NewSelector(), true, true}, - {"\rx", internalSelector{ - getRequirement("x", ExistsOperator, nil, t), - }, true, true}, - {"this-is-a-dns.domain.com/key-with-dash", internalSelector{ - getRequirement("this-is-a-dns.domain.com/key-with-dash", ExistsOperator, nil, t), - }, true, true}, - {"this-is-another-dns.domain.com/key-with-dash in (so,what)", internalSelector{ - getRequirement("this-is-another-dns.domain.com/key-with-dash", InOperator, sets.NewString("so", "what"), t), - }, true, true}, - {"0.1.2.domain/99 notin (10.10.100.1, tick.tack.clock)", internalSelector{ - getRequirement("0.1.2.domain/99", NotInOperator, sets.NewString("10.10.100.1", "tick.tack.clock"), t), - }, true, true}, - {"foo in (abc)", internalSelector{ - getRequirement("foo", InOperator, sets.NewString("abc"), t), - }, true, true}, - {"x notin\n (abc)", internalSelector{ - getRequirement("x", NotInOperator, sets.NewString("abc"), t), - }, true, true}, - {"x notin \t (abc,def)", internalSelector{ - getRequirement("x", NotInOperator, sets.NewString("abc", "def"), t), - }, true, true}, - {"x in (abc,def)", internalSelector{ - getRequirement("x", InOperator, sets.NewString("abc", "def"), t), - }, true, true}, - {"x in (abc,)", internalSelector{ - getRequirement("x", InOperator, sets.NewString("abc", ""), t), - }, true, true}, - {"x in ()", internalSelector{ - getRequirement("x", InOperator, sets.NewString(""), t), - }, true, true}, - {"x notin (abc,,def),bar,z in (),w", internalSelector{ - getRequirement("bar", ExistsOperator, nil, t), - getRequirement("w", ExistsOperator, nil, t), - getRequirement("x", NotInOperator, sets.NewString("abc", "", "def"), t), - getRequirement("z", InOperator, sets.NewString(""), t), - }, true, true}, - {"x,y in (a)", internalSelector{ - getRequirement("y", InOperator, sets.NewString("a"), t), - getRequirement("x", ExistsOperator, nil, t), - }, false, true}, - {"x=a", internalSelector{ - getRequirement("x", EqualsOperator, sets.NewString("a"), t), - }, true, true}, - {"x>1.1", internalSelector{ - getRequirement("x", GreaterThanOperator, sets.NewString("1.1"), t), - }, true, true}, - {"x<7.1", internalSelector{ - getRequirement("x", LessThanOperator, sets.NewString("7.1"), t), - }, true, true}, - {"x=a,y!=b", internalSelector{ - getRequirement("x", EqualsOperator, sets.NewString("a"), t), - getRequirement("y", NotEqualsOperator, sets.NewString("b"), t), - }, true, true}, - {"x=a,y!=b,z in (h,i,j)", internalSelector{ - getRequirement("x", EqualsOperator, sets.NewString("a"), t), - getRequirement("y", NotEqualsOperator, sets.NewString("b"), t), - getRequirement("z", InOperator, sets.NewString("h", "i", "j"), t), - }, true, true}, - {"x=a||y=b", internalSelector{}, false, false}, - {"x,,y", nil, true, false}, - {",x,y", nil, true, false}, - {"x nott in (y)", nil, true, false}, - {"x notin ( )", internalSelector{ - getRequirement("x", NotInOperator, sets.NewString(""), t), - }, true, true}, - {"x notin (, a)", internalSelector{ - getRequirement("x", NotInOperator, sets.NewString("", "a"), t), - }, true, true}, - {"a in (xyz),", nil, true, false}, - {"a in (xyz)b notin ()", nil, true, false}, - {"a ", internalSelector{ - getRequirement("a", ExistsOperator, nil, t), - }, true, true}, - {"a in (x,y,notin, z,in)", internalSelector{ - getRequirement("a", InOperator, sets.NewString("in", "notin", "x", "y", "z"), t), - }, true, true}, // operator 'in' inside list of identifiers - {"a in (xyz abc)", nil, false, false}, // no comma - {"a notin(", nil, true, false}, // bad formed - {"a (", nil, false, false}, // cpar - {"(", nil, false, false}, // opar - } - - for _, ssp := range setSelectorParserTests { - if sel, err := Parse(ssp.In); err != nil && ssp.Valid { - t.Errorf("Parse(%s) => %v expected no error", ssp.In, err) - } else if err == nil && !ssp.Valid { - t.Errorf("Parse(%s) => %+v expected error", ssp.In, sel) - } else if ssp.Match && !reflect.DeepEqual(sel, ssp.Out) { - t.Errorf("Parse(%s) => parse output '%#v' doesn't match '%#v' expected match", ssp.In, sel, ssp.Out) - } - } -} - -func getRequirement(key string, op Operator, vals sets.String, t *testing.T) Requirement { - req, err := NewRequirement(key, op, vals) - if err != nil { - t.Errorf("NewRequirement(%v, %v, %v) resulted in error:%v", key, op, vals, err) - return Requirement{} - } - return *req -} - -func TestAdd(t *testing.T) { - testCases := []struct { - name string - sel Selector - key string - operator Operator - values []string - refSelector Selector - }{ - { - "keyInOperator", - internalSelector{}, - "key", - InOperator, - []string{"value"}, - internalSelector{Requirement{"key", InOperator, sets.NewString("value")}}, - }, - { - "keyEqualsOperator", - internalSelector{Requirement{"key", InOperator, sets.NewString("value")}}, - "key2", - EqualsOperator, - []string{"value2"}, - internalSelector{ - Requirement{"key", InOperator, sets.NewString("value")}, - Requirement{"key2", EqualsOperator, sets.NewString("value2")}, - }, - }, - } - for _, ts := range testCases { - req, err := NewRequirement(ts.key, ts.operator, sets.NewString(ts.values...)) - if err != nil { - t.Errorf("%s - Unable to create labels.Requirement", ts.name) - } - ts.sel = ts.sel.Add(*req) - if !reflect.DeepEqual(ts.sel, ts.refSelector) { - t.Errorf("%s - Expected %v found %v", ts.name, ts.refSelector, ts.sel) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/OWNERS b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/OWNERS new file mode 100644 index 000000000..d038b5e9b --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/OWNERS @@ -0,0 +1,5 @@ +assignees: + - caesarxuchao + - deads2k + - lavalamp + - smarterclayton diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/conversion_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/conversion_test.go deleted file mode 100644 index 6105e5aad..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/conversion_test.go +++ /dev/null @@ -1,135 +0,0 @@ -/* -Copyright 2014 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 runtime_test - -import ( - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" -) - -type InternalComplex struct { - runtime.TypeMeta - String string - Integer int - Integer64 int64 - Int64 int64 - Bool bool -} - -type ExternalComplex struct { - runtime.TypeMeta `json:",inline"` - String string `json:"string" description:"testing"` - Integer int `json:"int"` - Integer64 int64 `json:",omitempty"` - Int64 int64 - Bool bool `json:"bool"` -} - -func (obj *InternalComplex) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *ExternalComplex) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } - -func TestStringMapConversion(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} - externalGV := unversioned.GroupVersion{Group: "test.group", Version: "external"} - - scheme := runtime.NewScheme() - scheme.Log(t) - scheme.AddKnownTypeWithName(internalGV.WithKind("Complex"), &InternalComplex{}) - scheme.AddKnownTypeWithName(externalGV.WithKind("Complex"), &ExternalComplex{}) - - testCases := map[string]struct { - input map[string][]string - errFn func(error) bool - expected runtime.Object - }{ - "ignores omitempty": { - input: map[string][]string{ - "String": {"not_used"}, - "string": {"value"}, - "int": {"1"}, - "Integer64": {"2"}, - }, - expected: &ExternalComplex{String: "value", Integer: 1}, - }, - "returns error on bad int": { - input: map[string][]string{ - "int": {"a"}, - }, - errFn: func(err error) bool { return err != nil }, - expected: &ExternalComplex{}, - }, - "parses int64": { - input: map[string][]string{ - "Int64": {"-1"}, - }, - expected: &ExternalComplex{Int64: -1}, - }, - "returns error on bad int64": { - input: map[string][]string{ - "Int64": {"a"}, - }, - errFn: func(err error) bool { return err != nil }, - expected: &ExternalComplex{}, - }, - "parses boolean true": { - input: map[string][]string{ - "bool": {"true"}, - }, - expected: &ExternalComplex{Bool: true}, - }, - "parses boolean any value": { - input: map[string][]string{ - "bool": {"foo"}, - }, - expected: &ExternalComplex{Bool: true}, - }, - "parses boolean false": { - input: map[string][]string{ - "bool": {"false"}, - }, - expected: &ExternalComplex{Bool: false}, - }, - "parses boolean empty value": { - input: map[string][]string{ - "bool": {""}, - }, - expected: &ExternalComplex{Bool: true}, - }, - "parses boolean no value": { - input: map[string][]string{ - "bool": {}, - }, - expected: &ExternalComplex{Bool: false}, - }, - } - - for k, tc := range testCases { - out := &ExternalComplex{} - if err := scheme.Convert(&tc.input, out); (tc.errFn == nil && err != nil) || (tc.errFn != nil && !tc.errFn(err)) { - t.Errorf("%s: unexpected error: %v", k, err) - continue - } else if err != nil { - continue - } - if !reflect.DeepEqual(out, tc.expected) { - t.Errorf("%s: unexpected output: %#v", k, out) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/embedded_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/embedded_test.go deleted file mode 100644 index 64d6d74fb..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/embedded_test.go +++ /dev/null @@ -1,287 +0,0 @@ -/* -Copyright 2014 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 runtime_test - -import ( - "encoding/json" - "reflect" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/runtime/serializer" - "k8s.io/kubernetes/pkg/util" -) - -type EmbeddedTest struct { - runtime.TypeMeta - ID string - Object runtime.Object - EmptyObject runtime.Object -} - -type EmbeddedTestExternal struct { - runtime.TypeMeta `json:",inline"` - ID string `json:"id,omitempty"` - Object runtime.RawExtension `json:"object,omitempty"` - EmptyObject runtime.RawExtension `json:"emptyObject,omitempty"` -} - -type ObjectTest struct { - runtime.TypeMeta - - ID string - Items []runtime.Object -} - -type ObjectTestExternal struct { - runtime.TypeMeta `yaml:",inline" json:",inline"` - - ID string `json:"id,omitempty"` - Items []runtime.RawExtension `json:"items,omitempty"` -} - -func (obj *ObjectTest) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *ObjectTestExternal) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *EmbeddedTest) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *EmbeddedTestExternal) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } - -func TestDecodeEmptyRawExtensionAsObject(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} - externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} - externalGVK := externalGV.WithKind("ObjectTest") - - s := runtime.NewScheme() - s.AddKnownTypes(internalGV, &ObjectTest{}) - s.AddKnownTypeWithName(externalGVK, &ObjectTestExternal{}) - - codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) - - obj, gvk, err := codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{}]}`), nil, nil) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - test := obj.(*ObjectTest) - if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.RawJSON) != "{}" { - t.Fatalf("unexpected object: %#v", test.Items[0]) - } - if *gvk != externalGVK { - t.Fatalf("unexpected kind: %#v", gvk) - } - - obj, gvk, err = codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{"kind":"Other","apiVersion":"v1"}]}`), nil, nil) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - test = obj.(*ObjectTest) - if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.RawJSON) != `{"kind":"Other","apiVersion":"v1"}` { - t.Fatalf("unexpected object: %#v", test.Items[0]) - } - if *gvk != externalGVK { - t.Fatalf("unexpected kind: %#v", gvk) - } -} - -func TestArrayOfRuntimeObject(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} - externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} - - s := runtime.NewScheme() - s.AddKnownTypes(internalGV, &EmbeddedTest{}) - s.AddKnownTypeWithName(externalGV.WithKind("EmbeddedTest"), &EmbeddedTestExternal{}) - s.AddKnownTypes(internalGV, &ObjectTest{}) - s.AddKnownTypeWithName(externalGV.WithKind("ObjectTest"), &ObjectTestExternal{}) - - codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) - - innerItems := []runtime.Object{ - &EmbeddedTest{ID: "baz"}, - } - items := []runtime.Object{ - &EmbeddedTest{ID: "foo"}, - &EmbeddedTest{ID: "bar"}, - // TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization - &runtime.Unknown{RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","foo":"bar","kind":"OtherTest"}`)}, - &ObjectTest{ - Items: runtime.NewEncodableList(codec, innerItems), - }, - } - internal := &ObjectTest{ - Items: runtime.NewEncodableList(codec, items), - } - wire, err := runtime.Encode(codec, internal) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - t.Logf("Wire format is:\n%s\n", string(wire)) - - obj := &ObjectTestExternal{} - if err := json.Unmarshal(wire, obj); err != nil { - t.Fatalf("unexpected error: %v", err) - } - t.Logf("exact wire is: %s", string(obj.Items[0].RawJSON)) - - items[3] = &ObjectTest{Items: innerItems} - internal.Items = items - - decoded, err := runtime.Decode(codec, wire) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - list, err := meta.ExtractList(decoded) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if errs := runtime.DecodeList(list, codec); len(errs) > 0 { - t.Fatalf("unexpected error: %v", errs) - } - - list2, err := meta.ExtractList(list[3]) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if errs := runtime.DecodeList(list2, codec); len(errs) > 0 { - t.Fatalf("unexpected error: %v", errs) - } - if err := meta.SetList(list[3], list2); err != nil { - t.Fatalf("unexpected error: %v", err) - } - - // we want DecodeList to set type meta if possible, even on runtime.Unknown objects - internal.Items[2].(*runtime.Unknown).TypeMeta = runtime.TypeMeta{Kind: "OtherTest", APIVersion: "unknown.group/unknown"} - if e, a := internal.Items, list; !reflect.DeepEqual(e, a) { - t.Errorf("mismatched decoded: %s", util.ObjectGoPrintSideBySide(e, a)) - } -} - -func TestNestedObject(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} - externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} - embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest") - - s := runtime.NewScheme() - s.AddKnownTypes(internalGV, &EmbeddedTest{}) - s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{}) - - codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) - - inner := &EmbeddedTest{ - ID: "inner", - } - outer := &EmbeddedTest{ - ID: "outer", - Object: runtime.NewEncodable(codec, inner), - } - - wire, err := runtime.Encode(codec, outer) - if err != nil { - t.Fatalf("Unexpected encode error '%v'", err) - } - - t.Logf("Wire format is:\n%v\n", string(wire)) - - decoded, err := runtime.Decode(codec, wire) - if err != nil { - t.Fatalf("Unexpected decode error %v", err) - } - - // for later tests - outer.Object = inner - - if e, a := outer, decoded; reflect.DeepEqual(e, a) { - t.Errorf("Expected unequal %#v %#v", e, a) - } - - obj, err := runtime.Decode(codec, decoded.(*EmbeddedTest).Object.(*runtime.Unknown).RawJSON) - if err != nil { - t.Fatal(err) - } - decoded.(*EmbeddedTest).Object = obj - if e, a := outer, decoded; !reflect.DeepEqual(e, a) { - t.Errorf("Expected equal %#v %#v", e, a) - } - - // test JSON decoding of the external object, which should preserve - // raw bytes - var externalViaJSON EmbeddedTestExternal - err = json.Unmarshal(wire, &externalViaJSON) - if err != nil { - t.Fatalf("Unexpected decode error %v", err) - } - if externalViaJSON.Kind == "" || externalViaJSON.APIVersion == "" || externalViaJSON.ID != "outer" { - t.Errorf("Expected objects to have type info set, got %#v", externalViaJSON) - } - if !reflect.DeepEqual(externalViaJSON.EmptyObject.RawJSON, []byte("null")) || len(externalViaJSON.Object.RawJSON) == 0 { - t.Errorf("Expected deserialization of nested objects into bytes, got %#v", externalViaJSON) - } - - // test JSON decoding, too, since Decode uses yaml unmarshalling. - // Generic Unmarshalling of JSON cannot load the nested objects because there is - // no default schema set. Consumers wishing to get direct JSON decoding must use - // the external representation - var decodedViaJSON EmbeddedTest - err = json.Unmarshal(wire, &decodedViaJSON) - if err == nil || !strings.Contains(err.Error(), "unmarshal object into Go value of type runtime.Object") { - t.Fatalf("Unexpected decode error %v", err) - } - if a := decodedViaJSON; a.Object != nil || a.EmptyObject != nil { - t.Errorf("Expected embedded objects to be nil: %#v", a) - } -} - -// TestDeepCopyOfRuntimeObject checks to make sure that runtime.Objects's can be passed through DeepCopy with fidelity -func TestDeepCopyOfRuntimeObject(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} - externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} - embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest") - - s := runtime.NewScheme() - s.AddKnownTypes(internalGV, &EmbeddedTest{}) - s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{}) - - original := &EmbeddedTest{ - ID: "outer", - Object: &EmbeddedTest{ - ID: "inner", - }, - } - - codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) - - originalData, err := runtime.Encode(codec, original) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - t.Logf("originalRole = %v\n", string(originalData)) - - copyOfOriginal, err := s.DeepCopy(original) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - copiedData, err := runtime.Encode(codec, copyOfOriginal.(runtime.Object)) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - t.Logf("copyOfRole = %v\n", string(copiedData)) - - if !reflect.DeepEqual(original, copyOfOriginal) { - t.Errorf("expected \n%v\n, got \n%v", string(originalData), string(copiedData)) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/extension_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/extension_test.go deleted file mode 100644 index 3d8a087a9..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/extension_test.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2014 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 runtime_test - -import ( - "encoding/json" - "testing" - - "k8s.io/kubernetes/pkg/runtime" -) - -func TestEmbeddedRawExtensionMarshal(t *testing.T) { - type test struct { - Ext runtime.RawExtension - } - - extension := test{Ext: runtime.RawExtension{RawJSON: []byte(`{"foo":"bar"}`)}} - data, err := json.Marshal(extension) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if string(data) != `{"Ext":{"foo":"bar"}}` { - t.Errorf("unexpected data: %s", string(data)) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/helper_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/helper_test.go deleted file mode 100644 index be7f0dedd..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/helper_test.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2014 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 runtime_test - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/runtime" -) - -func TestDecodeList(t *testing.T) { - pl := &api.List{ - Items: []runtime.Object{ - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}}, - &runtime.Unknown{TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: testapi.Default.GroupVersion().String()}, RawJSON: []byte(`{"kind":"Pod","apiVersion":"` + testapi.Default.GroupVersion().String() + `","metadata":{"name":"test"}}`)}, - &runtime.Unstructured{TypeMeta: runtime.TypeMeta{Kind: "Foo", APIVersion: "Bar"}, Object: map[string]interface{}{"test": "value"}}, - }, - } - if errs := runtime.DecodeList(pl.Items, testapi.Default.Codec()); len(errs) != 0 { - t.Fatalf("unexpected error %v", errs) - } - if pod, ok := pl.Items[1].(*api.Pod); !ok || pod.Name != "test" { - t.Errorf("object not converted: %#v", pl.Items[1]) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/protobuf/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/protobuf/doc.go deleted file mode 100644 index 33316d0c4..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/protobuf/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2015 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 protobuf implements ProtoBuf serialization and deserialization. -package protobuf diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/protobuf/protobuf.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/protobuf/protobuf.go deleted file mode 100644 index cc050a50b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/protobuf/protobuf.go +++ /dev/null @@ -1,158 +0,0 @@ -// +build proto - -/* -Copyright 2015 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 protobuf - -import ( - "fmt" - "io" - "net/url" - "reflect" - - "github.com/gogo/protobuf/proto" - - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" -) - -// NewCodec -func NewCodec(version string, creater runtime.ObjectCreater, typer runtime.ObjectTyper, convertor runtime.ObjectConvertor) runtime.Codec { - return &codec{ - version: version, - creater: creater, - typer: typer, - convertor: convertor, - } -} - -// codec decodes protobuf objects -type codec struct { - version string - outputVersion string - creater runtime.ObjectCreater - typer runtime.ObjectTyper - convertor runtime.ObjectConvertor -} - -var _ runtime.Codec = codec{} - -func (c codec) Decode(data []byte) (runtime.Object, error) { - unknown := &runtime.Unknown{} - if err := proto.Unmarshal(data, unknown); err != nil { - return nil, err - } - obj, err := c.creater.New(unknown.APIVersion, unknown.Kind) - if err != nil { - return nil, err - } - pobj, ok := obj.(proto.Message) - if !ok { - return nil, fmt.Errorf("runtime object is not a proto.Message: %v", reflect.TypeOf(obj)) - } - if err := proto.Unmarshal(unknown.RawJSON, pobj); err != nil { - return nil, err - } - if unknown.APIVersion != c.outputVersion { - out, err := c.convertor.ConvertToVersion(obj, c.outputVersion) - if err != nil { - return nil, err - } - obj = out - } - return obj, nil -} - -func (c codec) DecodeToVersion(data []byte, version unversioned.GroupVersion) (runtime.Object, error) { - return nil, fmt.Errorf("unimplemented") -} - -func (c codec) DecodeInto(data []byte, obj runtime.Object) error { - version, kind, err := c.typer.ObjectVersionAndKind(obj) - if err != nil { - return err - } - unknown := &runtime.Unknown{} - if err := proto.Unmarshal(data, unknown); err != nil { - return err - } - if unknown.APIVersion == version && unknown.Kind == kind { - pobj, ok := obj.(proto.Message) - if !ok { - return fmt.Errorf("runtime object is not a proto.Message: %v", reflect.TypeOf(obj)) - } - - return proto.Unmarshal(unknown.RawJSON, pobj) - } - - versioned, err := c.creater.New(unknown.APIVersion, unknown.Kind) - if err != nil { - return err - } - - pobj, ok := versioned.(proto.Message) - if !ok { - return fmt.Errorf("runtime object is not a proto.Message: %v", reflect.TypeOf(obj)) - } - - if err := proto.Unmarshal(unknown.RawJSON, pobj); err != nil { - return err - } - return c.convertor.Convert(versioned, obj) -} - -func (c codec) DecodeIntoWithSpecifiedVersionKind(data []byte, obj runtime.Object, kind unversioned.GroupVersionKind) error { - return fmt.Errorf("unimplemented") -} - -func (c codec) DecodeParametersInto(parameters url.Values, obj runtime.Object) error { - return fmt.Errorf("unimplemented") -} - -func (c codec) Encode(obj runtime.Object) (data []byte, err error) { - version, kind, err := c.typer.ObjectVersionAndKind(obj) - if err != nil { - return nil, err - } - if len(version) == 0 { - version = c.version - converted, err := c.convertor.ConvertToVersion(obj, version) - if err != nil { - return nil, err - } - obj = converted - } - m, ok := obj.(proto.Marshaler) - if !ok { - return nil, fmt.Errorf("object %v (kind: %s in version: %s) does not implement ProtoBuf marshalling", reflect.TypeOf(obj), kind, c.version) - } - b, err := m.Marshal() - if err != nil { - return nil, err - } - return (&runtime.Unknown{ - TypeMeta: runtime.TypeMeta{ - Kind: kind, - APIVersion: version, - }, - RawJSON: b, - }).Marshal() -} - -func (c codec) EncodeToStream(obj runtime.Object, stream io.Writer) error { - return fmt.Errorf("unimplemented") -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/scheme_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/scheme_test.go deleted file mode 100644 index 5b933d84b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/scheme_test.go +++ /dev/null @@ -1,673 +0,0 @@ -/* -Copyright 2014 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 runtime_test - -import ( - "reflect" - "testing" - - "github.com/google/gofuzz" - flag "github.com/spf13/pflag" - - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/conversion" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/runtime/serializer" - "k8s.io/kubernetes/pkg/util" -) - -var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.") - -type InternalSimple struct { - runtime.TypeMeta `json:",inline"` - TestString string `json:"testString"` -} - -type ExternalSimple struct { - runtime.TypeMeta `json:",inline"` - TestString string `json:"testString"` -} - -func (obj *InternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *ExternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } - -func TestScheme(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} - externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} - - scheme := runtime.NewScheme() - scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) - scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) - - // If set, would clear TypeMeta during conversion. - //scheme.AddIgnoredConversionType(&TypeMeta{}, &TypeMeta{}) - - // test that scheme is an ObjectTyper - var _ runtime.ObjectTyper = scheme - - internalToExternalCalls := 0 - externalToInternalCalls := 0 - - // Register functions to verify that scope.Meta() gets set correctly. - err := scheme.AddConversionFuncs( - func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error { - if e, a := internalGV.String(), scope.Meta().SrcVersion; e != a { - t.Errorf("Expected '%v', got '%v'", e, a) - } - if e, a := externalGV.String(), scope.Meta().DestVersion; e != a { - t.Errorf("Expected '%v', got '%v'", e, a) - } - scope.Convert(&in.TypeMeta, &out.TypeMeta, 0) - scope.Convert(&in.TestString, &out.TestString, 0) - internalToExternalCalls++ - return nil - }, - func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error { - if e, a := externalGV.String(), scope.Meta().SrcVersion; e != a { - t.Errorf("Expected '%v', got '%v'", e, a) - } - if e, a := internalGV.String(), scope.Meta().DestVersion; e != a { - t.Errorf("Expected '%v', got '%v'", e, a) - } - scope.Convert(&in.TypeMeta, &out.TypeMeta, 0) - scope.Convert(&in.TestString, &out.TestString, 0) - externalToInternalCalls++ - return nil - }, - ) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - codecs := serializer.NewCodecFactory(scheme) - codec := codecs.LegacyCodec(externalGV) - jsonserializer, _ := codecs.SerializerForFileExtension("json") - - simple := &InternalSimple{ - TestString: "foo", - } - - // Test Encode, Decode, DecodeInto, and DecodeToVersion - obj := runtime.Object(simple) - data, err := runtime.Encode(codec, obj) - if err != nil { - t.Fatal(err) - } - - obj2, err := runtime.Decode(codec, data) - if err != nil { - t.Fatal(err) - } - if _, ok := obj2.(*InternalSimple); !ok { - t.Fatalf("Got wrong type") - } - if e, a := simple, obj2; !reflect.DeepEqual(e, a) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) - } - - obj3 := &InternalSimple{} - if err := runtime.DecodeInto(codec, data, obj3); err != nil { - t.Fatal(err) - } - // clearing TypeMeta is a function of the scheme, which we do not test here (ConvertToVersion - // does not automatically clear TypeMeta anymore). - simple.TypeMeta = runtime.TypeMeta{Kind: "Simple", APIVersion: externalGV.String()} - if e, a := simple, obj3; !reflect.DeepEqual(e, a) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) - } - - obj4, err := runtime.Decode(jsonserializer, data) - if err != nil { - t.Fatal(err) - } - if _, ok := obj4.(*ExternalSimple); !ok { - t.Fatalf("Got wrong type") - } - - // Test Convert - external := &ExternalSimple{} - err = scheme.Convert(simple, external) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if e, a := simple.TestString, external.TestString; e != a { - t.Errorf("Expected %v, got %v", e, a) - } - - // Encode and Convert should each have caused an increment. - if e, a := 2, internalToExternalCalls; e != a { - t.Errorf("Expected %v, got %v", e, a) - } - // DecodeInto and Decode should each have caused an increment because of a conversion - if e, a := 2, externalToInternalCalls; e != a { - t.Errorf("Expected %v, got %v", e, a) - } -} - -func TestBadJSONRejection(t *testing.T) { - scheme := runtime.NewScheme() - codecs := serializer.NewCodecFactory(scheme) - jsonserializer, _ := codecs.SerializerForFileExtension("json") - - badJSONMissingKind := []byte(`{ }`) - if _, err := runtime.Decode(jsonserializer, badJSONMissingKind); err == nil { - t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind) - } - badJSONUnknownType := []byte(`{"kind": "bar"}`) - if _, err1 := runtime.Decode(jsonserializer, badJSONUnknownType); err1 == nil { - t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType) - } - /*badJSONKindMismatch := []byte(`{"kind": "Pod"}`) - if err2 := DecodeInto(badJSONKindMismatch, &Minion{}); err2 == nil { - t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch) - }*/ -} - -type ExtensionA struct { - runtime.TypeMeta `json:",inline"` - TestString string `json:"testString"` -} - -type ExtensionB struct { - runtime.TypeMeta `json:",inline"` - TestString string `json:"testString"` -} - -type ExternalExtensionType struct { - runtime.TypeMeta `json:",inline"` - Extension runtime.RawExtension `json:"extension"` -} - -type InternalExtensionType struct { - runtime.TypeMeta `json:",inline"` - Extension runtime.Object `json:"extension"` -} - -type ExternalOptionalExtensionType struct { - runtime.TypeMeta `json:",inline"` - Extension runtime.RawExtension `json:"extension,omitempty"` -} - -type InternalOptionalExtensionType struct { - runtime.TypeMeta `json:",inline"` - Extension runtime.Object `json:"extension,omitempty"` -} - -func (obj *ExtensionA) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *ExtensionB) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *ExternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *InternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *ExternalOptionalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *InternalOptionalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } - -func TestExternalToInternalMapping(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} - externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} - - scheme := runtime.NewScheme() - scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{}) - scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{}) - - codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) - - table := []struct { - obj runtime.Object - encoded string - }{ - { - &InternalOptionalExtensionType{Extension: nil}, - `{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`, - }, - } - - for i, item := range table { - gotDecoded, err := runtime.Decode(codec, []byte(item.encoded)) - if err != nil { - t.Errorf("unexpected error '%v' (%v)", err, item.encoded) - } else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) { - t.Errorf("%d: unexpected objects:\n%s", i, util.ObjectGoPrintSideBySide(e, a)) - } - } -} - -func TestExtensionMapping(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} - externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} - - scheme := runtime.NewScheme() - scheme.AddKnownTypeWithName(internalGV.WithKind("ExtensionType"), &InternalExtensionType{}) - scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{}) - scheme.AddKnownTypeWithName(externalGV.WithKind("ExtensionType"), &ExternalExtensionType{}) - scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{}) - - // register external first when the object is the same in both schemes, so ObjectVersionAndKind reports the - // external version. - scheme.AddKnownTypeWithName(externalGV.WithKind("A"), &ExtensionA{}) - scheme.AddKnownTypeWithName(externalGV.WithKind("B"), &ExtensionB{}) - scheme.AddKnownTypeWithName(internalGV.WithKind("A"), &ExtensionA{}) - scheme.AddKnownTypeWithName(internalGV.WithKind("B"), &ExtensionB{}) - - codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) - - table := []struct { - obj runtime.Object - expected runtime.Object - encoded string - }{ - { - &InternalExtensionType{ - Extension: runtime.NewEncodable(codec, &ExtensionA{TestString: "foo"}), - }, - &InternalExtensionType{ - Extension: &runtime.Unknown{ - RawJSON: []byte(`{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}`), - }, - }, - // apiVersion is set in the serialized object for easier consumption by clients - `{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}} -`, - }, { - &InternalExtensionType{Extension: runtime.NewEncodable(codec, &ExtensionB{TestString: "bar"})}, - &InternalExtensionType{ - Extension: &runtime.Unknown{ - RawJSON: []byte(`{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}`), - }, - }, - // apiVersion is set in the serialized object for easier consumption by clients - `{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}} -`, - }, { - &InternalExtensionType{Extension: nil}, - &InternalExtensionType{ - Extension: nil, - }, - `{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":null} -`, - }, - } - - for i, item := range table { - gotEncoded, err := runtime.Encode(codec, item.obj) - if err != nil { - t.Errorf("unexpected error '%v' (%#v)", err, item.obj) - } else if e, a := item.encoded, string(gotEncoded); e != a { - t.Errorf("expected\n%#v\ngot\n%#v\n", e, a) - } - - gotDecoded, err := runtime.Decode(codec, []byte(item.encoded)) - if err != nil { - t.Errorf("unexpected error '%v' (%v)", err, item.encoded) - } else if e, a := item.expected, gotDecoded; !reflect.DeepEqual(e, a) { - t.Errorf("%d: unexpected objects:\n%s", i, util.ObjectGoPrintSideBySide(e, a)) - } - } -} - -func TestEncode(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} - externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} - - scheme := runtime.NewScheme() - scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) - scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) - - codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) - - test := &InternalSimple{ - TestString: "I'm the same", - } - obj := runtime.Object(test) - data, err := runtime.Encode(codec, obj) - obj2, gvk, err2 := codec.Decode(data, nil, nil) - if err != nil || err2 != nil { - t.Fatalf("Failure: '%v' '%v'", err, err2) - } - if _, ok := obj2.(*InternalSimple); !ok { - t.Fatalf("Got wrong type") - } - if !reflect.DeepEqual(obj2, test) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2) - } - if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "Simple"}) { - t.Errorf("unexpected gvk returned by decode: %#v", gvk) - } -} - -func TestUnversionedTypes(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} - externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} - otherGV := unversioned.GroupVersion{Group: "group", Version: "other"} - - scheme := runtime.NewScheme() - scheme.AddUnversionedTypes(externalGV, &InternalSimple{}) - scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) - scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) - scheme.AddKnownTypeWithName(otherGV.WithKind("Simple"), &ExternalSimple{}) - - codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) - - if unv, ok := scheme.IsUnversioned(&InternalSimple{}); !unv || !ok { - t.Fatal("type not unversioned and in scheme: %t %t", unv, ok) - } - - kind, err := scheme.ObjectKind(&InternalSimple{}) - if err != nil { - t.Fatal(err) - } - if kind != externalGV.WithKind("InternalSimple") { - t.Fatalf("unexpected: %#v", kind) - } - - test := &InternalSimple{ - TestString: "I'm the same", - } - obj := runtime.Object(test) - data, err := runtime.Encode(codec, obj) - if err != nil { - t.Fatal(err) - } - obj2, gvk, err := codec.Decode(data, nil, nil) - if err != nil { - t.Fatal(err) - } - if _, ok := obj2.(*InternalSimple); !ok { - t.Fatalf("Got wrong type") - } - if !reflect.DeepEqual(obj2, test) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2) - } - // object is serialized as an unversioned object (in the group and version it was defined in) - if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "InternalSimple"}) { - t.Errorf("unexpected gvk returned by decode: %#v", gvk) - } - - // when serialized to a different group, the object is kept in its preferred name - codec = serializer.NewCodecFactory(scheme).LegacyCodec(otherGV) - data, err = runtime.Encode(codec, obj) - if err != nil { - t.Fatal(err) - } - if string(data) != `{"apiVersion":"test.group/testExternal","kind":"InternalSimple","testString":"I'm the same"}`+"\n" { - t.Errorf("unexpected data: %s", data) - } -} - -// Test a weird version/kind embedding format. -type MyWeirdCustomEmbeddedVersionKindField struct { - ID string `json:"ID,omitempty"` - APIVersion string `json:"myVersionKey,omitempty"` - ObjectKind string `json:"myKindKey,omitempty"` - Z string `json:"Z,omitempty"` - Y uint64 `json:"Y,omitempty"` -} - -type TestType1 struct { - MyWeirdCustomEmbeddedVersionKindField `json:",inline"` - A string `json:"A,omitempty"` - B int `json:"B,omitempty"` - C int8 `json:"C,omitempty"` - D int16 `json:"D,omitempty"` - E int32 `json:"E,omitempty"` - F int64 `json:"F,omitempty"` - G uint `json:"G,omitempty"` - H uint8 `json:"H,omitempty"` - I uint16 `json:"I,omitempty"` - J uint32 `json:"J,omitempty"` - K uint64 `json:"K,omitempty"` - L bool `json:"L,omitempty"` - M map[string]int `json:"M,omitempty"` - N map[string]TestType2 `json:"N,omitempty"` - O *TestType2 `json:"O,omitempty"` - P []TestType2 `json:"Q,omitempty"` -} - -type TestType2 struct { - A string `json:"A,omitempty"` - B int `json:"B,omitempty"` -} - -type ExternalTestType2 struct { - A string `json:"A,omitempty"` - B int `json:"B,omitempty"` -} -type ExternalTestType1 struct { - MyWeirdCustomEmbeddedVersionKindField `json:",inline"` - A string `json:"A,omitempty"` - B int `json:"B,omitempty"` - C int8 `json:"C,omitempty"` - D int16 `json:"D,omitempty"` - E int32 `json:"E,omitempty"` - F int64 `json:"F,omitempty"` - G uint `json:"G,omitempty"` - H uint8 `json:"H,omitempty"` - I uint16 `json:"I,omitempty"` - J uint32 `json:"J,omitempty"` - K uint64 `json:"K,omitempty"` - L bool `json:"L,omitempty"` - M map[string]int `json:"M,omitempty"` - N map[string]ExternalTestType2 `json:"N,omitempty"` - O *ExternalTestType2 `json:"O,omitempty"` - P []ExternalTestType2 `json:"Q,omitempty"` -} - -type ExternalInternalSame struct { - MyWeirdCustomEmbeddedVersionKindField `json:",inline"` - A TestType2 `json:"A,omitempty"` -} - -func (obj *MyWeirdCustomEmbeddedVersionKindField) GetObjectKind() unversioned.ObjectKind { return obj } -func (obj *MyWeirdCustomEmbeddedVersionKindField) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.APIVersion, obj.ObjectKind = gvk.ToAPIVersionAndKind() -} -func (obj *MyWeirdCustomEmbeddedVersionKindField) GroupVersionKind() *unversioned.GroupVersionKind { - return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.ObjectKind) -} - -func (obj *ExternalInternalSame) GetObjectKind() unversioned.ObjectKind { - return &obj.MyWeirdCustomEmbeddedVersionKindField -} - -func (obj *TestType1) GetObjectKind() unversioned.ObjectKind { - return &obj.MyWeirdCustomEmbeddedVersionKindField -} - -func (obj *ExternalTestType1) GetObjectKind() unversioned.ObjectKind { - return &obj.MyWeirdCustomEmbeddedVersionKindField -} - -func (obj *TestType2) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind } -func (obj *ExternalTestType2) GetObjectKind() unversioned.ObjectKind { - return unversioned.EmptyObjectKind -} - -// TestObjectFuzzer can randomly populate all the above objects. -var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs( - func(j *MyWeirdCustomEmbeddedVersionKindField, c fuzz.Continue) { - // We have to customize the randomization of MyWeirdCustomEmbeddedVersionKindFields because their - // APIVersion and Kind must remain blank in memory. - j.APIVersion = "" - j.ObjectKind = "" - j.ID = c.RandString() - }, -) - -// Returns a new Scheme set up with the test objects. -func GetTestScheme() *runtime.Scheme { - internalGV := unversioned.GroupVersion{Version: "__internal"} - externalGV := unversioned.GroupVersion{Version: "v1"} - - s := runtime.NewScheme() - // Ordinarily, we wouldn't add TestType2, but because this is a test and - // both types are from the same package, we need to get it into the system - // so that converter will match it with ExternalType2. - s.AddKnownTypes(internalGV, &TestType1{}, &TestType2{}, &ExternalInternalSame{}) - s.AddKnownTypes(externalGV, &ExternalInternalSame{}) - s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{}) - s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{}) - s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{}) - s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{}) - return s -} - -func TestKnownTypes(t *testing.T) { - s := GetTestScheme() - if len(s.KnownTypes(unversioned.GroupVersion{Group: "group", Version: "v2"})) != 0 { - t.Errorf("should have no known types for v2") - } - - types := s.KnownTypes(unversioned.GroupVersion{Version: "v1"}) - for _, s := range []string{"TestType1", "TestType2", "TestType3", "ExternalInternalSame"} { - if _, ok := types[s]; !ok { - t.Errorf("missing type %q", s) - } - } -} - -func TestConvertToVersion(t *testing.T) { - s := GetTestScheme() - tt := &TestType1{A: "I'm not a pointer object"} - other, err := s.ConvertToVersion(tt, "v1") - if err != nil { - t.Fatalf("Failure: %v", err) - } - converted, ok := other.(*ExternalTestType1) - if !ok { - t.Fatalf("Got wrong type") - } - if tt.A != converted.A { - t.Fatalf("Failed to convert object correctly: %#v", converted) - } -} - -func TestMetaValues(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: "__internal"} - externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"} - - s := runtime.NewScheme() - s.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) - s.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) - - internalToExternalCalls := 0 - externalToInternalCalls := 0 - - // Register functions to verify that scope.Meta() gets set correctly. - err := s.AddConversionFuncs( - func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error { - t.Logf("internal -> external") - if e, a := internalGV.String(), scope.Meta().SrcVersion; e != a { - t.Fatalf("Expected '%v', got '%v'", e, a) - } - if e, a := externalGV.String(), scope.Meta().DestVersion; e != a { - t.Fatalf("Expected '%v', got '%v'", e, a) - } - scope.Convert(&in.TestString, &out.TestString, 0) - internalToExternalCalls++ - return nil - }, - func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error { - t.Logf("external -> internal") - if e, a := externalGV.String(), scope.Meta().SrcVersion; e != a { - t.Errorf("Expected '%v', got '%v'", e, a) - } - if e, a := internalGV.String(), scope.Meta().DestVersion; e != a { - t.Fatalf("Expected '%v', got '%v'", e, a) - } - scope.Convert(&in.TestString, &out.TestString, 0) - externalToInternalCalls++ - return nil - }, - ) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - simple := &InternalSimple{ - TestString: "foo", - } - - s.Log(t) - - out, err := s.ConvertToVersion(simple, externalGV.String()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - internal, err := s.ConvertToVersion(out, internalGV.String()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - if e, a := simple, internal; !reflect.DeepEqual(e, a) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) - } - - if e, a := 1, internalToExternalCalls; e != a { - t.Errorf("Expected %v, got %v", e, a) - } - if e, a := 1, externalToInternalCalls; e != a { - t.Errorf("Expected %v, got %v", e, a) - } -} - -func TestMetaValuesUnregisteredConvert(t *testing.T) { - type InternalSimple struct { - Version string `json:"apiVersion,omitempty"` - Kind string `json:"kind,omitempty"` - TestString string `json:"testString"` - } - type ExternalSimple struct { - Version string `json:"apiVersion,omitempty"` - Kind string `json:"kind,omitempty"` - TestString string `json:"testString"` - } - s := runtime.NewScheme() - // We deliberately don't register the types. - - internalToExternalCalls := 0 - - // Register functions to verify that scope.Meta() gets set correctly. - err := s.AddConversionFuncs( - func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error { - if e, a := "unknown/unknown", scope.Meta().SrcVersion; e != a { - t.Fatalf("Expected '%v', got '%v'", e, a) - } - if e, a := "unknown/unknown", scope.Meta().DestVersion; e != a { - t.Fatalf("Expected '%v', got '%v'", e, a) - } - scope.Convert(&in.TestString, &out.TestString, 0) - internalToExternalCalls++ - return nil - }, - ) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - simple := &InternalSimple{TestString: "foo"} - external := &ExternalSimple{} - err = s.Convert(simple, external) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if e, a := simple.TestString, external.TestString; e != a { - t.Errorf("Expected %v, got %v", e, a) - } - - // Verify that our conversion handler got called. - if e, a := 1, internalToExternalCalls; e != a { - t.Errorf("Expected %v, got %v", e, a) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/codec_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/codec_test.go deleted file mode 100644 index 6c1b2ff95..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/codec_test.go +++ /dev/null @@ -1,400 +0,0 @@ -/* -Copyright 2014 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 serializer - -import ( - "encoding/json" - "fmt" - "log" - "os" - "reflect" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/conversion" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util" - - "github.com/ghodss/yaml" - "github.com/google/gofuzz" - flag "github.com/spf13/pflag" -) - -var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.") - -type testMetaFactory struct{} - -func (testMetaFactory) Interpret(data []byte) (*unversioned.GroupVersionKind, error) { - findKind := struct { - APIVersion string `json:"myVersionKey,omitempty"` - ObjectKind string `json:"myKindKey,omitempty"` - }{} - // yaml is a superset of json, so we use it to decode here. That way, - // we understand both. - if err := yaml.Unmarshal(data, &findKind); err != nil { - return nil, fmt.Errorf("couldn't get version/kind: %v", err) - } - gv, err := unversioned.ParseGroupVersion(findKind.APIVersion) - if err != nil { - return nil, err - } - return &unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: findKind.ObjectKind}, nil -} - -// Test a weird version/kind embedding format. -type MyWeirdCustomEmbeddedVersionKindField struct { - ID string `json:"ID,omitempty"` - APIVersion string `json:"myVersionKey,omitempty"` - ObjectKind string `json:"myKindKey,omitempty"` - Z string `json:"Z,omitempty"` - Y uint64 `json:"Y,omitempty"` -} - -type TestType1 struct { - MyWeirdCustomEmbeddedVersionKindField `json:",inline"` - A string `json:"A,omitempty"` - B int `json:"B,omitempty"` - C int8 `json:"C,omitempty"` - D int16 `json:"D,omitempty"` - E int32 `json:"E,omitempty"` - F int64 `json:"F,omitempty"` - G uint `json:"G,omitempty"` - H uint8 `json:"H,omitempty"` - I uint16 `json:"I,omitempty"` - J uint32 `json:"J,omitempty"` - K uint64 `json:"K,omitempty"` - L bool `json:"L,omitempty"` - M map[string]int `json:"M,omitempty"` - N map[string]TestType2 `json:"N,omitempty"` - O *TestType2 `json:"O,omitempty"` - P []TestType2 `json:"Q,omitempty"` -} - -type TestType2 struct { - A string `json:"A,omitempty"` - B int `json:"B,omitempty"` -} - -type ExternalTestType2 struct { - A string `json:"A,omitempty"` - B int `json:"B,omitempty"` -} -type ExternalTestType1 struct { - MyWeirdCustomEmbeddedVersionKindField `json:",inline"` - A string `json:"A,omitempty"` - B int `json:"B,omitempty"` - C int8 `json:"C,omitempty"` - D int16 `json:"D,omitempty"` - E int32 `json:"E,omitempty"` - F int64 `json:"F,omitempty"` - G uint `json:"G,omitempty"` - H uint8 `json:"H,omitempty"` - I uint16 `json:"I,omitempty"` - J uint32 `json:"J,omitempty"` - K uint64 `json:"K,omitempty"` - L bool `json:"L,omitempty"` - M map[string]int `json:"M,omitempty"` - N map[string]ExternalTestType2 `json:"N,omitempty"` - O *ExternalTestType2 `json:"O,omitempty"` - P []ExternalTestType2 `json:"Q,omitempty"` -} - -type ExternalInternalSame struct { - MyWeirdCustomEmbeddedVersionKindField `json:",inline"` - A TestType2 `json:"A,omitempty"` -} - -// TestObjectFuzzer can randomly populate all the above objects. -var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs( - func(j *MyWeirdCustomEmbeddedVersionKindField, c fuzz.Continue) { - c.FuzzNoCustom(j) - j.APIVersion = "" - j.ObjectKind = "" - }, -) - -func (obj *MyWeirdCustomEmbeddedVersionKindField) GetObjectKind() unversioned.ObjectKind { return obj } -func (obj *MyWeirdCustomEmbeddedVersionKindField) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.APIVersion, obj.ObjectKind = gvk.ToAPIVersionAndKind() -} -func (obj *MyWeirdCustomEmbeddedVersionKindField) GroupVersionKind() *unversioned.GroupVersionKind { - return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.ObjectKind) -} - -func (obj *ExternalInternalSame) GetObjectKind() unversioned.ObjectKind { - return &obj.MyWeirdCustomEmbeddedVersionKindField -} - -func (obj *TestType1) GetObjectKind() unversioned.ObjectKind { - return &obj.MyWeirdCustomEmbeddedVersionKindField -} - -func (obj *ExternalTestType1) GetObjectKind() unversioned.ObjectKind { - return &obj.MyWeirdCustomEmbeddedVersionKindField -} - -func (obj *TestType2) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind } -func (obj *ExternalTestType2) GetObjectKind() unversioned.ObjectKind { - return unversioned.EmptyObjectKind -} - -// Returns a new Scheme set up with the test objects. -func GetTestScheme() (*runtime.Scheme, runtime.Codec) { - internalGV := unversioned.GroupVersion{Version: runtime.APIVersionInternal} - externalGV := unversioned.GroupVersion{Version: "v1"} - externalGV2 := unversioned.GroupVersion{Version: "v2"} - - s := runtime.NewScheme() - // Ordinarily, we wouldn't add TestType2, but because this is a test and - // both types are from the same package, we need to get it into the system - // so that converter will match it with ExternalType2. - s.AddKnownTypes(internalGV, &TestType1{}, &TestType2{}, &ExternalInternalSame{}) - s.AddKnownTypes(externalGV, &ExternalInternalSame{}) - s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{}) - s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{}) - s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{}) - s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{}) - s.AddKnownTypeWithName(externalGV2.WithKind("TestType1"), &ExternalTestType1{}) - - s.AddUnversionedTypes(externalGV, &unversioned.Status{}) - - cf := newCodecFactory(s, testMetaFactory{}) - codec := cf.LegacyCodec(unversioned.GroupVersion{Version: "v1"}) - return s, codec -} - -func objDiff(a, b interface{}) string { - ab, err := json.Marshal(a) - if err != nil { - panic("a") - } - bb, err := json.Marshal(b) - if err != nil { - panic("b") - } - return util.StringDiff(string(ab), string(bb)) - - // An alternate diff attempt, in case json isn't showing you - // the difference. (reflect.DeepEqual makes a distinction between - // nil and empty slices, for example.) - //return util.StringDiff( - // fmt.Sprintf("%#v", a), - // fmt.Sprintf("%#v", b), - //) -} - -var semantic = conversion.EqualitiesOrDie( - func(a, b MyWeirdCustomEmbeddedVersionKindField) bool { - a.APIVersion, a.ObjectKind = "", "" - b.APIVersion, b.ObjectKind = "", "" - return a == b - }, -) - -func runTest(t *testing.T, source interface{}) { - name := reflect.TypeOf(source).Elem().Name() - TestObjectFuzzer.Fuzz(source) - - _, codec := GetTestScheme() - data, err := runtime.Encode(codec, source.(runtime.Object)) - if err != nil { - t.Errorf("%v: %v (%#v)", name, err, source) - return - } - obj2, err := runtime.Decode(codec, data) - if err != nil { - t.Errorf("%v: %v (%v)", name, err, string(data)) - return - } - if !semantic.DeepEqual(source, obj2) { - t.Errorf("1: %v: diff: %v", name, util.ObjectGoPrintSideBySide(source, obj2)) - return - } - obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface() - if err := runtime.DecodeInto(codec, data, obj3.(runtime.Object)); err != nil { - t.Errorf("2: %v: %v", name, err) - return - } - if !semantic.DeepEqual(source, obj3) { - t.Errorf("3: %v: diff: %v", name, objDiff(source, obj3)) - return - } -} - -func TestTypes(t *testing.T) { - table := []interface{}{ - &TestType1{}, - &ExternalInternalSame{}, - } - for _, item := range table { - // Try a few times, since runTest uses random values. - for i := 0; i < *fuzzIters; i++ { - runTest(t, item) - } - } -} - -func TestVersionedEncoding(t *testing.T) { - s, codec := GetTestScheme() - out, err := runtime.Encode(codec, &TestType1{}, unversioned.GroupVersion{Version: "v2"}) - if err != nil { - t.Fatal(err) - } - if string(out) != `{"myVersionKey":"v2","myKindKey":"TestType1"}`+"\n" { - t.Fatal(string(out)) - } - _, err = runtime.Encode(codec, &TestType1{}, unversioned.GroupVersion{Version: "v3"}) - if err == nil { - t.Fatal(err) - } - - cf := newCodecFactory(s, testMetaFactory{}) - encoder, _ := cf.SerializerForFileExtension("json") - - // codec that is unversioned uses the target version - unversionedCodec := cf.CodecForVersions(encoder, nil, nil) - _, err = runtime.Encode(unversionedCodec, &TestType1{}, unversioned.GroupVersion{Version: "v3"}) - if err == nil || !runtime.IsNotRegisteredError(err) { - t.Fatal(err) - } - - // unversioned encode with no versions is written directly to wire - out, err = runtime.Encode(unversionedCodec, &TestType1{}) - if err != nil { - t.Fatal(err) - } - if string(out) != `{"myVersionKey":"__internal","myKindKey":"TestType1"}`+"\n" { - t.Fatal(string(out)) - } -} - -func TestMultipleNames(t *testing.T) { - _, codec := GetTestScheme() - - obj, _, err := codec.Decode([]byte(`{"myKindKey":"TestType3","myVersionKey":"v1","A":"value"}`), nil, nil) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - internal := obj.(*TestType1) - if internal.A != "value" { - t.Fatalf("unexpected decoded object: %#v", internal) - } - - out, err := runtime.Encode(codec, internal) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !strings.Contains(string(out), `"myKindKey":"TestType1"`) { - t.Errorf("unexpected encoded output: %s", string(out)) - } -} - -func TestConvertTypesWhenDefaultNamesMatch(t *testing.T) { - internalGV := unversioned.GroupVersion{Version: runtime.APIVersionInternal} - externalGV := unversioned.GroupVersion{Version: "v1"} - - s := runtime.NewScheme() - // create two names internally, with TestType1 being preferred - s.AddKnownTypeWithName(internalGV.WithKind("TestType1"), &TestType1{}) - s.AddKnownTypeWithName(internalGV.WithKind("OtherType1"), &TestType1{}) - // create two names externally, with TestType1 being preferred - s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{}) - s.AddKnownTypeWithName(externalGV.WithKind("OtherType1"), &ExternalTestType1{}) - - ext := &ExternalTestType1{} - ext.APIVersion = "v1" - ext.ObjectKind = "OtherType1" - ext.A = "test" - data, err := json.Marshal(ext) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - expect := &TestType1{A: "test"} - - codec := newCodecFactory(s, testMetaFactory{}).LegacyCodec(unversioned.GroupVersion{Version: "v1"}) - - obj, err := runtime.Decode(codec, data) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !semantic.DeepEqual(expect, obj) { - t.Errorf("unexpected object: %#v", obj) - } - - into := &TestType1{} - if err := runtime.DecodeInto(codec, data, into); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !semantic.DeepEqual(expect, into) { - t.Errorf("unexpected object: %#v", obj) - } -} - -func TestEncode_Ptr(t *testing.T) { - _, codec := GetTestScheme() - tt := &TestType1{A: "I am a pointer object"} - data, err := runtime.Encode(codec, tt) - obj2, err2 := runtime.Decode(codec, data) - if err != nil || err2 != nil { - t.Fatalf("Failure: '%v' '%v'\n%s", err, err2, data) - } - if _, ok := obj2.(*TestType1); !ok { - t.Fatalf("Got wrong type") - } - if !semantic.DeepEqual(obj2, tt) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", tt, obj2) - } -} - -func TestBadJSONRejection(t *testing.T) { - log.SetOutput(os.Stderr) - _, codec := GetTestScheme() - badJSONs := [][]byte{ - []byte(`{"myVersionKey":"v1"}`), // Missing kind - []byte(`{"myVersionKey":"v1","myKindKey":"bar"}`), // Unknown kind - []byte(`{"myVersionKey":"bar","myKindKey":"TestType1"}`), // Unknown version - []byte(`{"myKindKey":"TestType1"}`), // Missing version - } - for _, b := range badJSONs { - if _, err := runtime.Decode(codec, b); err == nil { - t.Errorf("Did not reject bad json: %s", string(b)) - } - } - badJSONKindMismatch := []byte(`{"myVersionKey":"v1","myKindKey":"ExternalInternalSame"}`) - if err := runtime.DecodeInto(codec, badJSONKindMismatch, &TestType1{}); err == nil { - t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch) - } - if err := runtime.DecodeInto(codec, []byte(``), &TestType1{}); err != nil { - t.Errorf("Should allow empty decode: %v", err) - } - if _, _, err := codec.Decode([]byte(``), &unversioned.GroupVersionKind{Kind: "ExternalInternalSame"}, nil); err == nil { - t.Errorf("Did not give error for empty data with only kind default") - } - if _, _, err := codec.Decode([]byte(`{"myVersionKey":"v1"}`), &unversioned.GroupVersionKind{Kind: "ExternalInternalSame"}, nil); err != nil { - t.Errorf("Gave error for version and kind default") - } - if _, _, err := codec.Decode([]byte(`{"myKindKey":"ExternalInternalSame"}`), &unversioned.GroupVersionKind{Version: "v1"}, nil); err != nil { - t.Errorf("Gave error for version and kind default") - } - if _, _, err := codec.Decode([]byte(``), &unversioned.GroupVersionKind{Kind: "ExternalInternalSame", Version: "v1"}, nil); err != nil { - t.Errorf("Gave error for version and kind defaulted: %v", err) - } - if _, err := runtime.Decode(codec, []byte(``)); err == nil { - t.Errorf("Did not give error for empty data") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/json_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/json_test.go deleted file mode 100644 index b9e1319fa..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/json_test.go +++ /dev/null @@ -1,268 +0,0 @@ -/* -Copyright 2015 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 json_test - -import ( - "fmt" - "reflect" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/runtime/serializer/json" - "k8s.io/kubernetes/pkg/util" -) - -type testDecodable struct { - Other string - Value int `json:"value"` - gvk *unversioned.GroupVersionKind -} - -func (d *testDecodable) GetObjectKind() unversioned.ObjectKind { return d } -func (d *testDecodable) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { d.gvk = gvk } -func (d *testDecodable) GroupVersionKind() *unversioned.GroupVersionKind { return d.gvk } - -func TestDecode(t *testing.T) { - testCases := []struct { - creater runtime.ObjectCreater - typer runtime.Typer - yaml bool - pretty bool - - data []byte - defaultGVK *unversioned.GroupVersionKind - into runtime.Object - - errFn func(error) bool - expectedObject runtime.Object - expectedGVK *unversioned.GroupVersionKind - }{ - { - data: []byte("{}"), - - expectedGVK: &unversioned.GroupVersionKind{}, - errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, - }, - { - data: []byte("{}"), - defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - creater: &mockCreater{err: fmt.Errorf("fake error")}, - - expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - errFn: func(err error) bool { return err.Error() == "fake error" }, - }, - { - data: []byte("{}"), - defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - creater: &mockCreater{err: fmt.Errorf("fake error")}, - - expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - errFn: func(err error) bool { return err.Error() == "fake error" }, - }, - { - data: []byte("{}"), - defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - creater: &mockCreater{obj: &testDecodable{}}, - expectedObject: &testDecodable{ - gvk: nil, // json serializer does NOT set GVK - }, - expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - }, - - // version without group is not defaulted - { - data: []byte(`{"apiVersion":"blah"}`), - defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - creater: &mockCreater{obj: &testDecodable{}}, - expectedObject: &testDecodable{ - gvk: nil, // json serializer does NOT set GVK - }, - expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "", Version: "blah"}, - }, - // group without version is defaulted - { - data: []byte(`{"apiVersion":"other/"}`), - defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - creater: &mockCreater{obj: &testDecodable{}}, - expectedObject: &testDecodable{ - gvk: nil, // json serializer does NOT set GVK - }, - expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - }, - - // accept runtime.Unknown as into and bypass creator - { - data: []byte(`{}`), - into: &runtime.Unknown{}, - - expectedGVK: &unversioned.GroupVersionKind{}, - expectedObject: &runtime.Unknown{ - RawJSON: []byte(`{}`), - }, - }, - { - data: []byte(`{"test":"object"}`), - into: &runtime.Unknown{}, - - expectedGVK: &unversioned.GroupVersionKind{}, - expectedObject: &runtime.Unknown{ - RawJSON: []byte(`{"test":"object"}`), - }, - }, - { - data: []byte(`{"test":"object"}`), - into: &runtime.Unknown{}, - defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - expectedObject: &runtime.Unknown{ - TypeMeta: runtime.TypeMeta{APIVersion: "other/blah", Kind: "Test"}, - RawJSON: []byte(`{"test":"object"}`), - }, - }, - - // unregistered objects can be decoded into directly - { - data: []byte(`{"kind":"Test","apiVersion":"other/blah","value":1,"Other":"test"}`), - into: &testDecodable{}, - typer: &mockTyper{err: runtime.NewNotRegisteredErr(unversioned.GroupVersionKind{}, nil)}, - expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - expectedObject: &testDecodable{ - Other: "test", - Value: 1, - }, - }, - // registered types get defaulted by the into object kind - { - data: []byte(`{"value":1,"Other":"test"}`), - into: &testDecodable{}, - typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, - expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - expectedObject: &testDecodable{ - Other: "test", - Value: 1, - }, - }, - // registered types get defaulted by the into object kind even without version, but return an error - { - data: []byte(`{"value":1,"Other":"test"}`), - into: &testDecodable{}, - typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: ""}}, - expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: ""}, - errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'apiVersion' is missing in") }, - expectedObject: &testDecodable{ - Other: "test", - Value: 1, - }, - }, - - // runtime.VersionedObjects are decoded - { - data: []byte(`{"value":1,"Other":"test"}`), - into: &runtime.VersionedObjects{Objects: []runtime.Object{}}, - creater: &mockCreater{obj: &testDecodable{}}, - typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, - defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - expectedObject: &runtime.VersionedObjects{ - Objects: []runtime.Object{ - &testDecodable{ - Other: "test", - Value: 1, - }, - }, - }, - }, - // runtime.VersionedObjects with an object are decoded into - { - data: []byte(`{"Other":"test"}`), - into: &runtime.VersionedObjects{Objects: []runtime.Object{&testDecodable{Value: 2}}}, - typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, - expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, - expectedObject: &runtime.VersionedObjects{ - Objects: []runtime.Object{ - &testDecodable{ - Other: "test", - Value: 2, - }, - }, - }, - }, - } - - for i, test := range testCases { - var s runtime.Serializer - if test.yaml { - s = json.NewYAMLSerializer(json.DefaultMetaFactory, test.creater, test.typer) - } else { - s = json.NewSerializer(json.DefaultMetaFactory, test.creater, test.typer, test.pretty) - } - obj, gvk, err := s.Decode([]byte(test.data), test.defaultGVK, test.into) - - if !reflect.DeepEqual(test.expectedGVK, gvk) { - t.Errorf("%d: unexpected GVK: %v", i, gvk) - } - - switch { - case err == nil && test.errFn != nil: - t.Errorf("%d: failed: %v", i, err) - continue - case err != nil && test.errFn == nil: - t.Errorf("%d: failed: %v", i, err) - continue - case err != nil: - if !test.errFn(err) { - t.Errorf("%d: failed: %v", i, err) - } - if obj != nil { - t.Errorf("%d: should have returned nil object", i) - } - continue - } - - if test.into != nil && test.into != obj { - t.Errorf("%d: expected into to be returned: %v", i, obj) - continue - } - - if !reflect.DeepEqual(test.expectedObject, obj) { - t.Errorf("%d: unexpected object:\n%s", i, util.ObjectGoPrintSideBySide(test.expectedObject, obj)) - } - } -} - -type mockCreater struct { - apiVersion string - kind string - err error - obj runtime.Object -} - -func (c *mockCreater) New(kind unversioned.GroupVersionKind) (runtime.Object, error) { - c.apiVersion, c.kind = kind.GroupVersion().String(), kind.Kind - return c.obj, c.err -} - -type mockTyper struct { - gvk *unversioned.GroupVersionKind - err error -} - -func (t *mockTyper) ObjectKind(obj runtime.Object) (*unversioned.GroupVersionKind, bool, error) { - return t.gvk, false, t.err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/meta_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/meta_test.go deleted file mode 100644 index 4b6351286..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/json/meta_test.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright 2014 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 json - -import "testing" - -func TestSimpleMetaFactoryInterpret(t *testing.T) { - factory := SimpleMetaFactory{} - gvk, err := factory.Interpret([]byte(`{"apiVersion":"1","kind":"object"}`)) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if gvk.Version != "1" || gvk.Kind != "object" { - t.Errorf("unexpected interpret: %#v", gvk) - } - - // no kind or version - gvk, err = factory.Interpret([]byte(`{}`)) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if gvk.Version != "" || gvk.Kind != "" { - t.Errorf("unexpected interpret: %#v", gvk) - } - - // unparsable - gvk, err = factory.Interpret([]byte(`{`)) - if err == nil { - t.Errorf("unexpected non-error") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/recognizer/recognizer_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/recognizer/recognizer_test.go deleted file mode 100644 index c83b87aa4..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/recognizer/recognizer_test.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright 2014 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 recognizer - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/runtime/serializer/json" -) - -type A struct{} - -func (A) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind } - -func TestRecognizer(t *testing.T) { - s := runtime.NewScheme() - s.AddKnownTypes(unversioned.GroupVersion{Version: "v1"}, &A{}) - d := NewDecoder( - json.NewSerializer(json.DefaultMetaFactory, s, runtime.ObjectTyperToTyper(s), false), - json.NewYAMLSerializer(json.DefaultMetaFactory, s, runtime.ObjectTyperToTyper(s)), - ) - out, _, err := d.Decode([]byte(` -kind: A -apiVersion: v1 -`), nil, nil) - if err != nil { - t.Fatal(err) - } - t.Logf("%#v", out) - - out, _, err = d.Decode([]byte(` -{ - "kind":"A", - "apiVersion":"v1" -} -`), nil, nil) - if err != nil { - t.Fatal(err) - } - t.Logf("%#v", out) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/versioning/versioning_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/versioning/versioning_test.go deleted file mode 100644 index 3d7eba27e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/versioning/versioning_test.go +++ /dev/null @@ -1,300 +0,0 @@ -/* -Copyright 2015 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 versioning - -import ( - "fmt" - "io" - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util" -) - -type testDecodable struct { - Other string - Value int `json:"value"` - gvk *unversioned.GroupVersionKind -} - -func (d *testDecodable) GetObjectKind() unversioned.ObjectKind { return d } -func (d *testDecodable) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { d.gvk = gvk } -func (d *testDecodable) GroupVersionKind() *unversioned.GroupVersionKind { return d.gvk } - -func TestDecode(t *testing.T) { - gvk1 := &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"} - decodable1 := &testDecodable{} - decodable2 := &testDecodable{} - decodable3 := &testDecodable{} - versionedDecodable1 := &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}} - - testCases := []struct { - serializer runtime.Serializer - convertor runtime.ObjectConvertor - creater runtime.ObjectCreater - copier runtime.ObjectCopier - typer runtime.Typer - yaml bool - pretty bool - - encodes, decodes []unversioned.GroupVersion - - defaultGVK *unversioned.GroupVersionKind - into runtime.Object - - errFn func(error) bool - expectedObject runtime.Object - sameObject runtime.Object - expectedGVK *unversioned.GroupVersionKind - }{ - { - serializer: &mockSerializer{actual: gvk1}, - convertor: &checkConvertor{groupVersion: "other/__internal"}, - expectedGVK: gvk1, - }, - { - serializer: &mockSerializer{actual: gvk1, obj: decodable1}, - convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "other/__internal"}, - expectedGVK: gvk1, - sameObject: decodable2, - }, - // defaultGVK.Group is allowed to force a conversion to the destination group - { - serializer: &mockSerializer{actual: gvk1, obj: decodable1}, - defaultGVK: &unversioned.GroupVersionKind{Group: "force"}, - convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "force/__internal"}, - expectedGVK: gvk1, - sameObject: decodable2, - }, - // uses direct conversion for into when objects differ - { - into: decodable3, - serializer: &mockSerializer{actual: gvk1, obj: decodable1}, - convertor: &checkConvertor{in: decodable1, obj: decodable3, directConvert: true}, - expectedGVK: gvk1, - sameObject: decodable3, - }, - { - into: versionedDecodable1, - serializer: &mockSerializer{actual: gvk1, obj: decodable3}, - convertor: &checkConvertor{in: decodable3, obj: decodable1, directConvert: true}, - expectedGVK: gvk1, - sameObject: versionedDecodable1, - }, - // returns directly when serializer returns into - { - into: decodable3, - serializer: &mockSerializer{actual: gvk1, obj: decodable3}, - expectedGVK: gvk1, - sameObject: decodable3, - }, - // returns directly when serializer returns into - { - into: versionedDecodable1, - serializer: &mockSerializer{actual: gvk1, obj: decodable1}, - expectedGVK: gvk1, - sameObject: versionedDecodable1, - }, - - // runtime.VersionedObjects are decoded - { - into: &runtime.VersionedObjects{Objects: []runtime.Object{}}, - - serializer: &mockSerializer{actual: gvk1, obj: decodable1}, - copier: &checkCopy{in: decodable1, obj: decodable1}, - convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "other/__internal"}, - expectedGVK: gvk1, - expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}}, - }, - { - into: &runtime.VersionedObjects{Objects: []runtime.Object{}}, - - serializer: &mockSerializer{actual: gvk1, obj: decodable1}, - copier: &checkCopy{in: decodable1, obj: nil, err: fmt.Errorf("error on copy")}, - convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "other/__internal"}, - expectedGVK: gvk1, - expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}}, - }, - - // decode into the same version as the serialized object - { - decodes: []unversioned.GroupVersion{gvk1.GroupVersion()}, - - serializer: &mockSerializer{actual: gvk1, obj: decodable1}, - expectedGVK: gvk1, - expectedObject: decodable1, - }, - { - into: &runtime.VersionedObjects{Objects: []runtime.Object{}}, - decodes: []unversioned.GroupVersion{gvk1.GroupVersion()}, - - serializer: &mockSerializer{actual: gvk1, obj: decodable1}, - expectedGVK: gvk1, - expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}}, - }, - - // codec with non matching version skips conversion altogether - { - decodes: []unversioned.GroupVersion{{Group: "something", Version: "else"}}, - - serializer: &mockSerializer{actual: gvk1, obj: decodable1}, - expectedGVK: gvk1, - expectedObject: decodable1, - }, - { - into: &runtime.VersionedObjects{Objects: []runtime.Object{}}, - decodes: []unversioned.GroupVersion{{Group: "something", Version: "else"}}, - - serializer: &mockSerializer{actual: gvk1, obj: decodable1}, - expectedGVK: gvk1, - expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}}, - }, - } - - for i, test := range testCases { - t.Logf("%d", i) - s := NewCodec(test.serializer, test.convertor, test.creater, test.copier, test.typer, test.encodes, test.decodes) - obj, gvk, err := s.Decode([]byte(`{}`), test.defaultGVK, test.into) - - if !reflect.DeepEqual(test.expectedGVK, gvk) { - t.Errorf("%d: unexpected GVK: %v", i, gvk) - } - - switch { - case err == nil && test.errFn != nil: - t.Errorf("%d: failed: %v", i, err) - continue - case err != nil && test.errFn == nil: - t.Errorf("%d: failed: %v", i, err) - continue - case err != nil: - if !test.errFn(err) { - t.Errorf("%d: failed: %v", i, err) - } - if obj != nil { - t.Errorf("%d: should have returned nil object", i) - } - continue - } - - if test.into != nil && test.into != obj { - t.Errorf("%d: expected into to be returned: %v", i, obj) - continue - } - - switch { - case test.expectedObject != nil: - if !reflect.DeepEqual(test.expectedObject, obj) { - t.Errorf("%d: unexpected object:\n%s", i, util.ObjectGoPrintSideBySide(test.expectedObject, obj)) - } - case test.sameObject != nil: - if test.sameObject != obj { - t.Errorf("%d: unexpected object:\n%s", i, util.ObjectGoPrintSideBySide(test.sameObject, obj)) - } - case obj != nil: - t.Errorf("%d: unexpected object: %#v", i, obj) - } - } -} - -type checkCopy struct { - in, obj runtime.Object - err error -} - -func (c *checkCopy) Copy(obj runtime.Object) (runtime.Object, error) { - if c.in != nil && c.in != obj { - return nil, fmt.Errorf("unexpected input to copy: %#v", obj) - } - return c.obj, c.err -} - -type checkConvertor struct { - err error - in, obj runtime.Object - groupVersion string - directConvert bool -} - -func (c *checkConvertor) Convert(in, out interface{}) error { - if !c.directConvert { - return fmt.Errorf("unexpected call to Convert") - } - if c.in != nil && c.in != in { - return fmt.Errorf("unexpected in: %s", in) - } - if c.obj != nil && c.obj != out { - return fmt.Errorf("unexpected out: %s", out) - } - return c.err -} -func (c *checkConvertor) ConvertToVersion(in runtime.Object, outVersion string) (out runtime.Object, err error) { - if c.directConvert { - return nil, fmt.Errorf("unexpected call to ConvertToVersion") - } - if c.in != nil && c.in != in { - return nil, fmt.Errorf("unexpected in: %s", in) - } - if c.groupVersion != outVersion { - return nil, fmt.Errorf("unexpected outversion: %s", outVersion) - } - return c.obj, c.err -} -func (c *checkConvertor) ConvertFieldLabel(version, kind, label, value string) (string, string, error) { - return "", "", fmt.Errorf("unexpected call to ConvertFieldLabel") -} - -type mockSerializer struct { - err error - obj runtime.Object - versions []unversioned.GroupVersion - - defaults, actual *unversioned.GroupVersionKind - into runtime.Object -} - -func (s *mockSerializer) Decode(data []byte, defaults *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) { - s.defaults = defaults - s.into = into - return s.obj, s.actual, s.err -} - -func (s *mockSerializer) EncodeToStream(obj runtime.Object, w io.Writer, versions ...unversioned.GroupVersion) error { - s.obj = obj - s.versions = versions - return s.err -} - -type mockCreater struct { - err error - obj runtime.Object -} - -func (c *mockCreater) New(kind unversioned.GroupVersionKind) (runtime.Object, error) { - return c.obj, c.err -} - -type mockTyper struct { - gvk *unversioned.GroupVersionKind - err error -} - -func (t *mockTyper) ObjectKind(obj runtime.Object) (*unversioned.GroupVersionKind, bool, error) { - return t.gvk, false, t.err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/yaml/yaml.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/yaml/yaml.go deleted file mode 100644 index 637c777be..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/serializer/yaml/yaml.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2014 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 yaml - -import ( - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/yaml" -) - -// yamlSerializer converts YAML passed to the Decoder methods to JSON. -type yamlSerializer struct { - // the nested serializer - runtime.Serializer -} - -// yamlSerializer implements Serializer -var _ runtime.Serializer = yamlSerializer{} - -// NewDecodingSerializer adds YAML decoding support to a serializer that supports JSON. -func NewDecodingSerializer(jsonSerializer runtime.Serializer) runtime.Serializer { - return &yamlSerializer{jsonSerializer} -} - -func (c yamlSerializer) Decode(data []byte, gvk *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) { - out, err := yaml.ToJSON(data) - if err != nil { - return nil, nil, err - } - data = out - return c.Serializer.Decode(data, gvk, into) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/swagger_doc_generator_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/swagger_doc_generator_test.go deleted file mode 100644 index 095dddff5..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/swagger_doc_generator_test.go +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright 2015 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 runtime - -import ( - "testing" -) - -func TestFmtRawDoc(t *testing.T) { - tests := []struct { - t, expected string - }{ - {"aaa\n --- asd\n TODO: tooooodo\n toooodoooooo\n", "aaa"}, - {"aaa\nasd\n TODO: tooooodo\nbbbb\n --- toooodoooooo\n", "aaa asd bbbb"}, - {" TODO: tooooodo\n", ""}, - {"Par1\n\nPar2\n\n", "Par1\\n\\nPar2"}, - {"", ""}, - {" ", ""}, - {" \n", ""}, - {" \n\n ", ""}, - {"Example:\n\tl1\n\t\tl2\n", "Example:\\n\\tl1\\n\\t\\tl2"}, - } - - for _, test := range tests { - if o := fmtRawDoc(test.t); o != test.expected { - t.Fatalf("Expected: %q, got %q", test.expected, o) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/unstructured_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/unstructured_test.go deleted file mode 100644 index ea1d87440..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/unstructured_test.go +++ /dev/null @@ -1,123 +0,0 @@ -/* -Copyright 2015 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 runtime_test - -import ( - "fmt" - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/runtime" -) - -func TestDecodeUnstructured(t *testing.T) { - groupVersionString := testapi.Default.GroupVersion().String() - rawJson := fmt.Sprintf(`{"kind":"Pod","apiVersion":"%s","metadata":{"name":"test"}}`, groupVersionString) - pl := &api.List{ - Items: []runtime.Object{ - &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}}, - &runtime.Unknown{TypeMeta: runtime.TypeMeta{Kind: "Pod", APIVersion: groupVersionString}, RawJSON: []byte(rawJson)}, - &runtime.Unknown{TypeMeta: runtime.TypeMeta{Kind: "", APIVersion: groupVersionString}, RawJSON: []byte(rawJson)}, - &runtime.Unstructured{TypeMeta: runtime.TypeMeta{Kind: "Foo", APIVersion: "Bar"}, Object: map[string]interface{}{"test": "value"}}, - }, - } - if errs := runtime.DecodeList(pl.Items, runtime.UnstructuredJSONScheme); len(errs) == 1 { - t.Fatalf("unexpected error %v", errs) - } - if pod, ok := pl.Items[1].(*runtime.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" { - t.Errorf("object not converted: %#v", pl.Items[1]) - } - if pod, ok := pl.Items[2].(*runtime.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" { - t.Errorf("object not converted: %#v", pl.Items[2]) - } -} - -func TestDecode(t *testing.T) { - tcs := []struct { - json []byte - want runtime.Object - }{ - { - json: []byte(`{"apiVersion": "test", "kind": "test_kind"}`), - want: &runtime.Unstructured{ - TypeMeta: runtime.TypeMeta{ - APIVersion: "test", - Kind: "test_kind", - }, - Object: map[string]interface{}{"apiVersion": "test", "kind": "test_kind"}, - }, - }, - { - json: []byte(`{"apiVersion": "test", "kind": "test_list", "items": []}`), - want: &runtime.UnstructuredList{ - TypeMeta: runtime.TypeMeta{ - APIVersion: "test", - Kind: "test_list", - }, - }, - }, - { - json: []byte(`{"items": [{"metadata": {"name": "object1"}, "apiVersion": "test", "kind": "test_kind"}, {"metadata": {"name": "object2"}, "apiVersion": "test", "kind": "test_kind"}], "apiVersion": "test", "kind": "test_list"}`), - want: &runtime.UnstructuredList{ - TypeMeta: runtime.TypeMeta{ - APIVersion: "test", - Kind: "test_list", - }, - Items: []*runtime.Unstructured{ - { - TypeMeta: runtime.TypeMeta{ - APIVersion: "test", - Kind: "test_kind", - }, - Name: "object1", - Object: map[string]interface{}{ - "metadata": map[string]interface{}{"name": "object1"}, - "apiVersion": "test", - "kind": "test_kind", - }, - }, - { - TypeMeta: runtime.TypeMeta{ - APIVersion: "test", - Kind: "test_kind", - }, - Name: "object2", - Object: map[string]interface{}{ - "metadata": map[string]interface{}{"name": "object2"}, - "apiVersion": "test", - "kind": "test_kind", - }, - }, - }, - }, - }, - } - - for _, tc := range tcs { - got, _, err := runtime.UnstructuredJSONScheme.Decode(tc.json, nil, nil) - if err != nil { - t.Errorf("Unexpected error for %q: %v", string(tc.json), err) - continue - } - - if !reflect.DeepEqual(got, tc.want) { - t.Errorf("Decode(%q) want: %v\ngot: %v", string(tc.json), tc.want, got) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/unversioned_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/unversioned_test.go deleted file mode 100644 index 7943581ea..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime/unversioned_test.go +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright 2015 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 runtime_test - -import ( - "encoding/json" - "reflect" - "testing" - - // TODO: Ideally we should create the necessary package structure in e.g., - // pkg/conversion/test/... instead of importing pkg/api here. - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/runtime" -) - -var status = &unversioned.Status{ - Status: unversioned.StatusFailure, - Code: 200, - Reason: unversioned.StatusReasonUnknown, - Message: "", -} - -func TestV1EncodeDecodeStatus(t *testing.T) { - - v1Codec := testapi.Default.Codec() - - encoded, err := runtime.Encode(v1Codec, status) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - typeMeta := unversioned.TypeMeta{} - if err := json.Unmarshal(encoded, &typeMeta); err != nil { - t.Errorf("unexpected error: %v", err) - } - if typeMeta.Kind != "Status" { - t.Errorf("Kind is not set to \"Status\". Got %v", string(encoded)) - } - if typeMeta.APIVersion != "v1" { - t.Errorf("APIVersion is not set to \"v1\". Got %v", string(encoded)) - } - decoded, err := runtime.Decode(v1Codec, encoded) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !reflect.DeepEqual(status, decoded) { - t.Errorf("expected: %v, got: %v", status, decoded) - } -} - -func TestExperimentalEncodeDecodeStatus(t *testing.T) { - // TODO: caesarxuchao: use the testapi.Extensions.Codec() once the PR that - // moves experimental from v1 to v1beta1 got merged. - expCodec := api.Codecs.LegacyCodec(extensions.SchemeGroupVersion) - encoded, err := runtime.Encode(expCodec, status) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - typeMeta := unversioned.TypeMeta{} - if err := json.Unmarshal(encoded, &typeMeta); err != nil { - t.Errorf("unexpected error: %v", err) - } - if typeMeta.Kind != "Status" { - t.Errorf("Kind is not set to \"Status\". Got %s", encoded) - } - if typeMeta.APIVersion != "v1" { - t.Errorf("APIVersion is not set to \"\". Got %s", encoded) - } - decoded, err := runtime.Decode(expCodec, encoded) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !reflect.DeepEqual(status, decoded) { - t.Errorf("expected: %v, got: %v", status, decoded) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_scale.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/types/unix_user_id.go similarity index 77% rename from Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_scale.go rename to Godeps/_workspace/src/k8s.io/kubernetes/pkg/types/unix_user_id.go index d2cfc5f7b..b59792abf 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/typed/generated/extensions/unversioned/fake/fake_scale.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/types/unix_user_id.go @@ -14,10 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -package fake +package types -// FakeScales implements ScaleInterface -type FakeScales struct { - Fake *FakeExtensions - ns string -} +// int64 is used as a safe bet against wrap-around (uid's are general +// int32) and to support uid_t -1, and -2. + +type UnixUserID int64 +type UnixGroupID int64 diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/atomic/value.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/atomic/value.go deleted file mode 100644 index a9bc8cd81..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/atomic/value.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2015 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 atomic - -import ( - "sync" -) - -// TODO(ArtfulCoder) -// sync/atomic/Value was added in golang 1.4 -// Once support is dropped for go 1.3, this type must be deprecated in favor of sync/atomic/Value. -// The functions are named Load/Store to match sync/atomic/Value function names. -type Value struct { - value interface{} - valueMutex sync.RWMutex -} - -func (at *Value) Store(val interface{}) { - at.valueMutex.Lock() - defer at.valueMutex.Unlock() - at.value = val -} - -func (at *Value) Load() interface{} { - at.valueMutex.RLock() - defer at.valueMutex.RUnlock() - return at.value -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/atomic/value_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/atomic/value_test.go deleted file mode 100644 index 052b35082..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/atomic/value_test.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2014 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 atomic - -import ( - "testing" - "time" - - "k8s.io/kubernetes/pkg/util/wait" -) - -func ExpectValue(t *testing.T, atomicValue *Value, expectedValue interface{}) { - actualValue := atomicValue.Load() - if actualValue != expectedValue { - t.Errorf("Expected to find %v, found %v", expectedValue, actualValue) - } - ch := make(chan interface{}) - go func() { - ch <- atomicValue.Load() - }() - select { - case actualValue = <-ch: - if actualValue != expectedValue { - t.Errorf("Expected to find %v, found %v", expectedValue, actualValue) - return - } - case <-time.After(wait.ForeverTestTimeout): - t.Error("Value could not be read") - return - } -} - -func TestAtomicValue(t *testing.T) { - atomicValue := &Value{} - ExpectValue(t, atomicValue, nil) - atomicValue.Store(10) - ExpectValue(t, atomicValue, 10) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/backoff.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/backoff.go index 1426590ac..275a58a22 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/backoff.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/backoff.go @@ -36,6 +36,15 @@ type Backoff struct { perItemBackoff map[string]*backoffEntry } +func NewFakeBackOff(initial, max time.Duration, tc *FakeClock) *Backoff { + return &Backoff{ + perItemBackoff: map[string]*backoffEntry{}, + Clock: tc, + defaultDuration: initial, + maxDuration: max, + } +} + func NewBackOff(initial, max time.Duration) *Backoff { return &Backoff{ perItemBackoff: map[string]*backoffEntry{}, @@ -120,6 +129,12 @@ func (p *Backoff) GC() { } } +func (p *Backoff) DeleteEntry(id string) { + p.Lock() + defer p.Unlock() + delete(p.perItemBackoff, id) +} + // Take a lock on *Backoff, before calling initEntryUnsafe func (p *Backoff) initEntryUnsafe(id string) *backoffEntry { entry := &backoffEntry{backoff: p.defaultDuration} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/backoff_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/backoff_test.go deleted file mode 100644 index d5b744cb3..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/backoff_test.go +++ /dev/null @@ -1,202 +0,0 @@ -/* -Copyright 2015 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 util - -import ( - "testing" - "time" -) - -func NewFakeBackOff(initial, max time.Duration, tc *FakeClock) *Backoff { - return &Backoff{ - perItemBackoff: map[string]*backoffEntry{}, - Clock: tc, - defaultDuration: initial, - maxDuration: max, - } -} - -func TestSlowBackoff(t *testing.T) { - id := "_idSlow" - tc := NewFakeClock(time.Now()) - step := time.Second - maxDuration := 50 * step - - b := NewFakeBackOff(step, maxDuration, tc) - cases := []time.Duration{0, 1, 2, 4, 8, 16, 32, 50, 50, 50} - for ix, c := range cases { - tc.Step(step) - w := b.Get(id) - if w != c*step { - t.Errorf("input: '%d': expected %s, got %s", ix, c*step, w) - } - b.Next(id, tc.Now()) - } - - //Now confirm that the Reset cancels backoff. - b.Next(id, tc.Now()) - b.Reset(id) - if b.Get(id) != 0 { - t.Errorf("Reset didn't clear the backoff.") - } - -} - -func TestBackoffReset(t *testing.T) { - id := "_idReset" - tc := NewFakeClock(time.Now()) - step := time.Second - maxDuration := step * 5 - b := NewFakeBackOff(step, maxDuration, tc) - startTime := tc.Now() - - // get to backoff = maxDuration - for i := 0; i <= int(maxDuration/step); i++ { - tc.Step(step) - b.Next(id, tc.Now()) - } - - // backoff should be capped at maxDuration - if !b.IsInBackOffSince(id, tc.Now()) { - t.Errorf("expected to be in Backoff got %s", b.Get(id)) - } - - lastUpdate := tc.Now() - tc.Step(2*maxDuration + step) // time += 11s, 11 > 2*maxDuration - if b.IsInBackOffSince(id, lastUpdate) { - t.Errorf("now=%s lastUpdate=%s (%s) expected Backoff reset got %s b.lastUpdate=%s", tc.Now(), startTime, tc.Now().Sub(lastUpdate), b.Get(id)) - } -} - -func TestBackoffHightWaterMark(t *testing.T) { - id := "_idHiWaterMark" - tc := NewFakeClock(time.Now()) - step := time.Second - maxDuration := 5 * step - b := NewFakeBackOff(step, maxDuration, tc) - - // get to backoff = maxDuration - for i := 0; i <= int(maxDuration/step); i++ { - tc.Step(step) - b.Next(id, tc.Now()) - } - - // backoff high watermark expires after 2*maxDuration - tc.Step(maxDuration + step) - b.Next(id, tc.Now()) - - if b.Get(id) != maxDuration { - t.Errorf("expected Backoff to stay at high watermark %s got %s", maxDuration, b.Get(id)) - } -} - -func TestBackoffGC(t *testing.T) { - id := "_idGC" - tc := NewFakeClock(time.Now()) - step := time.Second - maxDuration := 5 * step - - b := NewFakeBackOff(step, maxDuration, tc) - - for i := 0; i <= int(maxDuration/step); i++ { - tc.Step(step) - b.Next(id, tc.Now()) - } - lastUpdate := tc.Now() - tc.Step(maxDuration + step) - b.GC() - _, found := b.perItemBackoff[id] - if !found { - t.Errorf("expected GC to skip entry, elapsed time=%s maxDuration=%s", tc.Now().Sub(lastUpdate), maxDuration) - } - - tc.Step(maxDuration + step) - b.GC() - r, found := b.perItemBackoff[id] - if found { - t.Errorf("expected GC of entry after %s got entry %v", tc.Now().Sub(lastUpdate), r) - } -} - -func TestIsInBackOffSinceUpdate(t *testing.T) { - id := "_idIsInBackOffSinceUpdate" - tc := NewFakeClock(time.Now()) - step := time.Second - maxDuration := 10 * step - b := NewFakeBackOff(step, maxDuration, tc) - startTime := tc.Now() - - cases := []struct { - tick time.Duration - inBackOff bool - value int - }{ - {tick: 0, inBackOff: false, value: 0}, - {tick: 1, inBackOff: false, value: 1}, - {tick: 2, inBackOff: true, value: 2}, - {tick: 3, inBackOff: false, value: 2}, - {tick: 4, inBackOff: true, value: 4}, - {tick: 5, inBackOff: true, value: 4}, - {tick: 6, inBackOff: true, value: 4}, - {tick: 7, inBackOff: false, value: 4}, - {tick: 8, inBackOff: true, value: 8}, - {tick: 9, inBackOff: true, value: 8}, - {tick: 10, inBackOff: true, value: 8}, - {tick: 11, inBackOff: true, value: 8}, - {tick: 12, inBackOff: true, value: 8}, - {tick: 13, inBackOff: true, value: 8}, - {tick: 14, inBackOff: true, value: 8}, - {tick: 15, inBackOff: false, value: 8}, - {tick: 16, inBackOff: true, value: 10}, - {tick: 17, inBackOff: true, value: 10}, - {tick: 18, inBackOff: true, value: 10}, - {tick: 19, inBackOff: true, value: 10}, - {tick: 20, inBackOff: true, value: 10}, - {tick: 21, inBackOff: true, value: 10}, - {tick: 22, inBackOff: true, value: 10}, - {tick: 23, inBackOff: true, value: 10}, - {tick: 24, inBackOff: true, value: 10}, - {tick: 25, inBackOff: false, value: 10}, - {tick: 26, inBackOff: true, value: 10}, - {tick: 27, inBackOff: true, value: 10}, - {tick: 28, inBackOff: true, value: 10}, - {tick: 29, inBackOff: true, value: 10}, - {tick: 30, inBackOff: true, value: 10}, - {tick: 31, inBackOff: true, value: 10}, - {tick: 32, inBackOff: true, value: 10}, - {tick: 33, inBackOff: true, value: 10}, - {tick: 34, inBackOff: true, value: 10}, - {tick: 35, inBackOff: false, value: 10}, - {tick: 56, inBackOff: false, value: 0}, - {tick: 57, inBackOff: false, value: 1}, - } - - for _, c := range cases { - tc.SetTime(startTime.Add(c.tick * step)) - if c.inBackOff != b.IsInBackOffSinceUpdate(id, tc.Now()) { - t.Errorf("expected IsInBackOffSinceUpdate %v got %v at tick %s", c.inBackOff, b.IsInBackOffSinceUpdate(id, tc.Now()), c.tick*step) - } - - if c.inBackOff && (time.Duration(c.value)*step != b.Get(id)) { - t.Errorf("expected backoff value=%s got %s at tick %s", time.Duration(c.value)*step, b.Get(id), c.tick*step) - } - - if !c.inBackOff { - b.Next(id, tc.Now()) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/doc.go deleted file mode 100644 index 5ae73e8be..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2015 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 bandwidth provides utilities for bandwidth shaping -package bandwidth diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/fake_shaper.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/fake_shaper.go deleted file mode 100644 index 12798bde1..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/fake_shaper.go +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright 2015 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 bandwidth - -import ( - "errors" - - "k8s.io/kubernetes/pkg/api/resource" -) - -type FakeShaper struct { - CIDRs []string - ResetCIDRs []string -} - -func (f *FakeShaper) Limit(cidr string, egress, ingress *resource.Quantity) error { - return errors.New("unimplemented") -} - -func (f *FakeShaper) Reset(cidr string) error { - f.ResetCIDRs = append(f.ResetCIDRs, cidr) - return nil -} - -func (f *FakeShaper) ReconcileInterface() error { - return errors.New("unimplemented") -} - -func (f *FakeShaper) ReconcileCIDR(cidr string, egress, ingress *resource.Quantity) error { - return errors.New("unimplemented") -} - -func (f *FakeShaper) GetCIDRs() ([]string, error) { - return f.CIDRs, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/interfaces.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/interfaces.go deleted file mode 100644 index 62b20d87d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/interfaces.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2015 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 bandwidth - -import "k8s.io/kubernetes/pkg/api/resource" - -type BandwidthShaper interface { - // Limit the bandwidth for a particular CIDR on a particular interface - // * ingress and egress are in bits/second - // * cidr is expected to be a valid network CIDR (e.g. '1.2.3.4/32' or '10.20.0.1/16') - // 'egress' bandwidth limit applies to all packets on the interface whose source matches 'cidr' - // 'ingress' bandwidth limit applies to all packets on the interface whose destination matches 'cidr' - // Limits are aggregate limits for the CIDR, not per IP address. CIDRs must be unique, but can be overlapping, traffic - // that matches multiple CIDRs counts against all limits. - Limit(cidr string, egress, ingress *resource.Quantity) error - // Remove a bandwidth limit for a particular CIDR on a particular network interface - Reset(cidr string) error - // Reconcile the interface managed by this shaper with the state on the ground. - ReconcileInterface() error - // Reconcile a CIDR managed by this shaper with the state on the ground - ReconcileCIDR(cidr string, egress, ingress *resource.Quantity) error - // GetCIDRs returns the set of CIDRs that are being managed by this shaper - GetCIDRs() ([]string, error) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/linux.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/linux.go deleted file mode 100644 index edb480c69..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/linux.go +++ /dev/null @@ -1,322 +0,0 @@ -// +build linux - -/* -Copyright 2015 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 bandwidth - -import ( - "bufio" - "bytes" - "encoding/hex" - "fmt" - "net" - "strings" - - "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/util/exec" - "k8s.io/kubernetes/pkg/util/sets" - - "github.com/golang/glog" -) - -// tcShaper provides an implementation of the BandwidthShaper interface on Linux using the 'tc' tool. -// In general, using this requires that the caller posses the NET_CAP_ADMIN capability, though if you -// do this within an container, it only requires the NS_CAPABLE capability for manipulations to that -// container's network namespace. -// Uses the hierarchical token bucket queuing discipline (htb), this requires Linux 2.4.20 or newer -// or a custom kernel with that queuing discipline backported. -type tcShaper struct { - e exec.Interface - iface string -} - -func NewTCShaper(iface string) BandwidthShaper { - shaper := &tcShaper{ - e: exec.New(), - iface: iface, - } - return shaper -} - -func (t *tcShaper) execAndLog(cmdStr string, args ...string) error { - glog.V(6).Infof("Running: %s %s", cmdStr, strings.Join(args, " ")) - cmd := t.e.Command(cmdStr, args...) - out, err := cmd.CombinedOutput() - glog.V(6).Infof("Output from tc: %s", string(out)) - return err -} - -func (t *tcShaper) nextClassID() (int, error) { - data, err := t.e.Command("tc", "class", "show", "dev", t.iface).CombinedOutput() - if err != nil { - return -1, err - } - - scanner := bufio.NewScanner(bytes.NewBuffer(data)) - classes := sets.String{} - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - // skip empty lines - if len(line) == 0 { - continue - } - parts := strings.Split(line, " ") - // expected tc line: - // class htb 1:1 root prio 0 rate 1000Kbit ceil 1000Kbit burst 1600b cburst 1600b - if len(parts) != 14 { - return -1, fmt.Errorf("unexpected output from tc: %s (%v)", scanner.Text(), parts) - } - classes.Insert(parts[2]) - } - - // Make sure it doesn't go forever - for nextClass := 1; nextClass < 10000; nextClass++ { - if !classes.Has(fmt.Sprintf("1:%d", nextClass)) { - return nextClass, nil - } - } - // This should really never happen - return -1, fmt.Errorf("exhausted class space, please try again") -} - -// Convert a CIDR from text to a hex representation -// Strips any masked parts of the IP, so 1.2.3.4/16 becomes hex(1.2.0.0)/ffffffff -func hexCIDR(cidr string) (string, error) { - ip, ipnet, err := net.ParseCIDR(cidr) - if err != nil { - return "", err - } - ip = ip.Mask(ipnet.Mask) - hexIP := hex.EncodeToString([]byte(ip.To4())) - hexMask := ipnet.Mask.String() - return hexIP + "/" + hexMask, nil -} - -// Convert a CIDR from hex representation to text, opposite of the above. -func asciiCIDR(cidr string) (string, error) { - parts := strings.Split(cidr, "/") - if len(parts) != 2 { - return "", fmt.Errorf("unexpected CIDR format: %s", cidr) - } - ipData, err := hex.DecodeString(parts[0]) - if err != nil { - return "", err - } - ip := net.IP(ipData) - - maskData, err := hex.DecodeString(parts[1]) - mask := net.IPMask(maskData) - size, _ := mask.Size() - - return fmt.Sprintf("%s/%d", ip.String(), size), nil -} - -func (t *tcShaper) findCIDRClass(cidr string) (class, handle string, found bool, err error) { - data, err := t.e.Command("tc", "filter", "show", "dev", t.iface).CombinedOutput() - if err != nil { - return "", "", false, err - } - - hex, err := hexCIDR(cidr) - if err != nil { - return "", "", false, err - } - spec := fmt.Sprintf("match %s", hex) - - scanner := bufio.NewScanner(bytes.NewBuffer(data)) - filter := "" - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if len(line) == 0 { - continue - } - if strings.HasPrefix(line, "filter") { - filter = line - continue - } - if strings.Contains(line, spec) { - parts := strings.Split(filter, " ") - // expected tc line: - // filter parent 1: protocol ip pref 1 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 - if len(parts) != 19 { - return "", "", false, fmt.Errorf("unexpected output from tc: %s %d (%v)", filter, len(parts), parts) - } - return parts[18], parts[9], true, nil - } - } - return "", "", false, nil -} - -func makeKBitString(rsrc *resource.Quantity) string { - return fmt.Sprintf("%dkbit", (rsrc.Value() / 1000)) -} - -func (t *tcShaper) makeNewClass(rate string) (int, error) { - class, err := t.nextClassID() - if err != nil { - return -1, err - } - if err := t.execAndLog("tc", "class", "add", - "dev", t.iface, - "parent", "1:", - "classid", fmt.Sprintf("1:%d", class), - "htb", "rate", rate); err != nil { - return -1, err - } - return class, nil -} - -func (t *tcShaper) Limit(cidr string, upload, download *resource.Quantity) (err error) { - var downloadClass, uploadClass int - if download != nil { - if downloadClass, err = t.makeNewClass(makeKBitString(download)); err != nil { - return err - } - if err := t.execAndLog("tc", "filter", "add", - "dev", t.iface, - "protocol", "ip", - "parent", "1:0", - "prio", "1", "u32", - "match", "ip", "dst", cidr, - "flowid", fmt.Sprintf("1:%d", downloadClass)); err != nil { - return err - } - } - if upload != nil { - if uploadClass, err = t.makeNewClass(makeKBitString(upload)); err != nil { - return err - } - if err := t.execAndLog("tc", "filter", "add", - "dev", t.iface, - "protocol", "ip", - "parent", "1:0", - "prio", "1", "u32", - "match", "ip", "src", cidr, - "flowid", fmt.Sprintf("1:%d", uploadClass)); err != nil { - return err - } - } - return nil -} - -// tests to see if an interface exists, if it does, return true and the status line for the interface -// returns false, "", if an error occurs. -func (t *tcShaper) interfaceExists() (bool, string, error) { - data, err := t.e.Command("tc", "qdisc", "show", "dev", t.iface).CombinedOutput() - if err != nil { - return false, "", err - } - value := strings.TrimSpace(string(data)) - if len(value) == 0 { - return false, "", nil - } - // Newer versions of tc and/or the kernel return the following instead of nothing: - // qdisc noqueue 0: root refcnt 2 - fields := strings.Fields(value) - if len(fields) > 1 && fields[1] == "noqueue" { - return false, "", nil - } - return true, value, nil -} - -func (t *tcShaper) ReconcileCIDR(cidr string, upload, download *resource.Quantity) error { - _, _, found, err := t.findCIDRClass(cidr) - if err != nil { - return err - } - if !found { - return t.Limit(cidr, upload, download) - } - // TODO: actually check bandwidth limits here - return nil -} - -func (t *tcShaper) ReconcileInterface() error { - exists, output, err := t.interfaceExists() - if err != nil { - return err - } - if !exists { - glog.V(4).Info("Didn't find bandwidth interface, creating") - return t.initializeInterface() - } - fields := strings.Split(output, " ") - if len(fields) != 12 || fields[1] != "htb" || fields[2] != "1:" { - if err := t.deleteInterface(fields[2]); err != nil { - return err - } - return t.initializeInterface() - } - return nil -} - -func (t *tcShaper) initializeInterface() error { - return t.execAndLog("tc", "qdisc", "add", "dev", t.iface, "root", "handle", "1:", "htb", "default", "30") -} - -func (t *tcShaper) Reset(cidr string) error { - class, handle, found, err := t.findCIDRClass(cidr) - if err != nil { - return err - } - if !found { - return fmt.Errorf("Failed to find cidr: %s on interface: %s", cidr, t.iface) - } - if err := t.execAndLog("tc", "filter", "del", - "dev", t.iface, - "parent", "1:", - "proto", "ip", - "prio", "1", - "handle", handle, "u32"); err != nil { - return err - } - return t.execAndLog("tc", "class", "del", "dev", t.iface, "parent", "1:", "classid", class) -} - -func (t *tcShaper) deleteInterface(class string) error { - return t.execAndLog("tc", "qdisc", "delete", "dev", t.iface, "root", "handle", class) -} - -func (t *tcShaper) GetCIDRs() ([]string, error) { - data, err := t.e.Command("tc", "filter", "show", "dev", t.iface).CombinedOutput() - if err != nil { - return nil, err - } - - result := []string{} - scanner := bufio.NewScanner(bytes.NewBuffer(data)) - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if len(line) == 0 { - continue - } - if strings.Contains(line, "match") { - parts := strings.Split(line, " ") - // expected tc line: - // match at - if len(parts) != 4 { - return nil, fmt.Errorf("unexpected output: %v", parts) - } - cidr, err := asciiCIDR(parts[1]) - if err != nil { - return nil, err - } - result = append(result, cidr) - } - } - return result, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/linux_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/linux_test.go deleted file mode 100644 index b6d2b559c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/linux_test.go +++ /dev/null @@ -1,634 +0,0 @@ -// +build linux - -/* -Copyright 2015 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 bandwidth - -import ( - "errors" - "reflect" - "strings" - "testing" - - "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/util/exec" -) - -var tcClassOutput = `class htb 1:1 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b -class htb 1:2 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b -class htb 1:3 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b -class htb 1:4 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b -` - -var tcClassOutput2 = `class htb 1:1 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b -class htb 1:2 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b -class htb 1:3 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b -class htb 1:4 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b -class htb 1:5 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b -` - -func TestNextClassID(t *testing.T) { - tests := []struct { - output string - expectErr bool - expected int - err error - }{ - { - output: tcClassOutput, - expected: 5, - }, - { - output: "\n", - expected: 1, - }, - { - expected: -1, - expectErr: true, - err: errors.New("test error"), - }, - } - for _, test := range tests { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - func() ([]byte, error) { return []byte(test.output), test.err }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { - return exec.InitFakeCmd(&fcmd, cmd, args...) - }, - }, - } - shaper := &tcShaper{e: &fexec} - class, err := shaper.nextClassID() - if test.expectErr { - if err == nil { - t.Errorf("unexpected non-error") - } - } else { - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if class != test.expected { - t.Errorf("expected: %d, found %d", test.expected, class) - } - } - } -} - -func TestHexCIDR(t *testing.T) { - tests := []struct { - input string - output string - expectErr bool - }{ - { - input: "1.2.0.0/16", - output: "01020000/ffff0000", - }, - { - input: "172.17.0.2/32", - output: "ac110002/ffffffff", - }, - { - input: "foo", - expectErr: true, - }, - } - for _, test := range tests { - output, err := hexCIDR(test.input) - if test.expectErr { - if err == nil { - t.Error("unexpected non-error") - } - } else { - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if output != test.output { - t.Errorf("expected: %s, saw: %s", test.output, output) - } - input, err := asciiCIDR(output) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if input != test.input { - t.Errorf("expected: %s, saw: %s", test.input, input) - } - } - } -} - -var tcFilterOutput = `filter parent 1: protocol ip pref 1 u32 -filter parent 1: protocol ip pref 1 u32 fh 800: ht divisor 1 -filter parent 1: protocol ip pref 1 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 - match ac110002/ffffffff at 16 -filter parent 1: protocol ip pref 1 u32 fh 800::801 order 2049 key ht 800 bkt 0 flowid 1:2 - match 01020000/ffff0000 at 16 -` - -func TestFindCIDRClass(t *testing.T) { - tests := []struct { - cidr string - output string - expectErr bool - expectNotFound bool - expectedClass string - expectedHandle string - err error - }{ - { - cidr: "172.17.0.2/32", - output: tcFilterOutput, - expectedClass: "1:1", - expectedHandle: "800::800", - }, - { - cidr: "1.2.3.4/16", - output: tcFilterOutput, - expectedClass: "1:2", - expectedHandle: "800::801", - }, - { - cidr: "2.2.3.4/16", - output: tcFilterOutput, - expectNotFound: true, - }, - { - err: errors.New("test error"), - expectErr: true, - }, - } - for _, test := range tests { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - func() ([]byte, error) { return []byte(test.output), test.err }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { - return exec.InitFakeCmd(&fcmd, cmd, args...) - }, - }, - } - shaper := &tcShaper{e: &fexec} - class, handle, found, err := shaper.findCIDRClass(test.cidr) - if test.expectErr { - if err == nil { - t.Errorf("unexpected non-error") - } - } else { - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if test.expectNotFound { - if found { - t.Errorf("unexpectedly found an interface: %s %s", class, handle) - } - } else { - if class != test.expectedClass { - t.Errorf("expected: %s, found %s", test.expectedClass, class) - } - if handle != test.expectedHandle { - t.Errorf("expected: %s, found %s", test.expectedHandle, handle) - } - } - } - } -} - -func TestGetCIDRs(t *testing.T) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - func() ([]byte, error) { return []byte(tcFilterOutput), nil }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { - return exec.InitFakeCmd(&fcmd, cmd, args...) - }, - }, - } - shaper := &tcShaper{e: &fexec} - cidrs, err := shaper.GetCIDRs() - if err != nil { - t.Errorf("unexpected error: %v", err) - } - expectedCidrs := []string{"172.17.0.2/32", "1.2.0.0/16"} - if !reflect.DeepEqual(cidrs, expectedCidrs) { - t.Errorf("expected: %v, saw: %v", expectedCidrs, cidrs) - } -} - -func TestLimit(t *testing.T) { - tests := []struct { - cidr string - ingress *resource.Quantity - egress *resource.Quantity - expectErr bool - expectedCalls int - err error - }{ - { - cidr: "1.2.3.4/32", - ingress: resource.NewQuantity(10, resource.DecimalSI), - egress: resource.NewQuantity(20, resource.DecimalSI), - expectedCalls: 6, - }, - { - cidr: "1.2.3.4/32", - ingress: resource.NewQuantity(10, resource.DecimalSI), - egress: nil, - expectedCalls: 3, - }, - { - cidr: "1.2.3.4/32", - ingress: nil, - egress: resource.NewQuantity(20, resource.DecimalSI), - expectedCalls: 3, - }, - { - cidr: "1.2.3.4/32", - ingress: nil, - egress: nil, - expectedCalls: 0, - }, - { - err: errors.New("test error"), - ingress: resource.NewQuantity(10, resource.DecimalSI), - egress: resource.NewQuantity(20, resource.DecimalSI), - expectErr: true, - }, - } - - for _, test := range tests { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - func() ([]byte, error) { return []byte(tcClassOutput), test.err }, - func() ([]byte, error) { return []byte{}, test.err }, - func() ([]byte, error) { return []byte{}, test.err }, - func() ([]byte, error) { return []byte(tcClassOutput2), test.err }, - func() ([]byte, error) { return []byte{}, test.err }, - func() ([]byte, error) { return []byte{}, test.err }, - }, - } - - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - iface := "cbr0" - shaper := &tcShaper{e: &fexec, iface: iface} - if err := shaper.Limit(test.cidr, test.ingress, test.egress); err != nil && !test.expectErr { - t.Errorf("unexpected error: %v", err) - return - } else if err == nil && test.expectErr { - t.Error("unexpected non-error") - return - } - // No more testing in the error case - if test.expectErr { - if fcmd.CombinedOutputCalls != 1 { - t.Errorf("unexpected number of calls: %d, expected: 1", fcmd.CombinedOutputCalls) - } - return - } - - if fcmd.CombinedOutputCalls != test.expectedCalls { - t.Errorf("unexpected number of calls: %d, expected: %d", fcmd.CombinedOutputCalls, test.expectedCalls) - } - - for ix := range fcmd.CombinedOutputLog { - output := fcmd.CombinedOutputLog[ix] - if output[0] != "tc" { - t.Errorf("unexpected command: %s, expected tc", output[0]) - } - if output[4] != iface { - t.Errorf("unexpected interface: %s, expected %s (%v)", output[4], iface, output) - } - if ix == 1 { - var expectedRate string - if test.ingress != nil { - expectedRate = makeKBitString(test.ingress) - } else { - expectedRate = makeKBitString(test.egress) - } - if output[11] != expectedRate { - t.Errorf("unexpected ingress: %s, expected: %s", output[11], expectedRate) - } - if output[8] != "1:5" { - t.Errorf("unexpected class: %s, expected: %s", output[8], "1:5") - } - } - if ix == 2 { - if output[15] != test.cidr { - t.Errorf("unexpected cidr: %s, expected: %s", output[15], test.cidr) - } - if output[17] != "1:5" { - t.Errorf("unexpected class: %s, expected: %s", output[17], "1:5") - } - } - if ix == 4 { - if output[11] != makeKBitString(test.egress) { - t.Errorf("unexpected egress: %s, expected: %s", output[11], makeKBitString(test.egress)) - } - if output[8] != "1:6" { - t.Errorf("unexpected class: %s, expected: %s", output[8], "1:6") - } - } - if ix == 5 { - if output[15] != test.cidr { - t.Errorf("unexpected cidr: %s, expected: %s", output[15], test.cidr) - } - if output[17] != "1:6" { - t.Errorf("unexpected class: %s, expected: %s", output[17], "1:5") - } - } - } - } -} - -func TestReset(t *testing.T) { - tests := []struct { - cidr string - err error - expectErr bool - expectedHandle string - expectedClass string - }{ - { - cidr: "1.2.3.4/16", - expectedHandle: "800::801", - expectedClass: "1:2", - }, - { - cidr: "172.17.0.2/32", - expectedHandle: "800::800", - expectedClass: "1:1", - }, - { - err: errors.New("test error"), - expectErr: true, - }, - } - for _, test := range tests { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - func() ([]byte, error) { return []byte(tcFilterOutput), test.err }, - func() ([]byte, error) { return []byte{}, test.err }, - func() ([]byte, error) { return []byte{}, test.err }, - }, - } - - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - iface := "cbr0" - shaper := &tcShaper{e: &fexec, iface: iface} - - if err := shaper.Reset(test.cidr); err != nil && !test.expectErr { - t.Errorf("unexpected error: %v", err) - return - } else if test.expectErr && err == nil { - t.Error("unexpected non-error") - return - } - - // No more testing in the error case - if test.expectErr { - if fcmd.CombinedOutputCalls != 1 { - t.Errorf("unexpected number of calls: %d, expected: 1", fcmd.CombinedOutputCalls) - } - return - } - - if fcmd.CombinedOutputCalls != 3 { - t.Errorf("unexpected number of calls: %d, expected: 3", fcmd.CombinedOutputCalls) - } - - for ix := range fcmd.CombinedOutputLog { - output := fcmd.CombinedOutputLog[ix] - if output[0] != "tc" { - t.Errorf("unexpected command: %s, expected tc", output[0]) - } - if output[4] != iface { - t.Errorf("unexpected interface: %s, expected %s (%v)", output[4], iface, output) - } - if ix == 1 && output[12] != test.expectedHandle { - t.Errorf("unexpected handle: %s, expected: %s", output[12], test.expectedHandle) - } - if ix == 2 && output[8] != test.expectedClass { - t.Errorf("unexpected class: %s, expected: %s", output[8], test.expectedClass) - } - } - } -} - -var tcQdisc = "qdisc htb 1: root refcnt 2 r2q 10 default 30 direct_packets_stat 0\n" - -func TestReconcileInterfaceExists(t *testing.T) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - func() ([]byte, error) { return []byte(tcQdisc), nil }, - }, - } - - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - iface := "cbr0" - shaper := &tcShaper{e: &fexec, iface: iface} - err := shaper.ReconcileInterface() - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if fcmd.CombinedOutputCalls != 1 { - t.Errorf("unexpected number of calls: %d", fcmd.CombinedOutputCalls) - } - - output := fcmd.CombinedOutputLog[0] - if len(output) != 5 { - t.Errorf("unexpected command: %v", output) - } - if output[0] != "tc" { - t.Errorf("unexpected command: %s", output[0]) - } - if output[4] != iface { - t.Errorf("unexpected interface: %s, expected %s", output[4], iface) - } - if output[2] != "show" { - t.Errorf("unexpected action: %s", output[2]) - } -} - -func testReconcileInterfaceHasNoData(t *testing.T, output string) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - func() ([]byte, error) { return []byte(output), nil }, - func() ([]byte, error) { return []byte(output), nil }, - }, - } - - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - iface := "cbr0" - shaper := &tcShaper{e: &fexec, iface: iface} - err := shaper.ReconcileInterface() - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("unexpected number of calls: %d", fcmd.CombinedOutputCalls) - } - - for ix, output := range fcmd.CombinedOutputLog { - if output[0] != "tc" { - t.Errorf("unexpected command: %s", output[0]) - } - if output[4] != iface { - t.Errorf("unexpected interface: %s, expected %s", output[4], iface) - } - if ix == 0 { - if len(output) != 5 { - t.Errorf("unexpected command: %v", output) - } - if output[2] != "show" { - t.Errorf("unexpected action: %s", output[2]) - } - } - if ix == 1 { - if len(output) != 11 { - t.Errorf("unexpected command: %v", output) - } - if output[2] != "add" { - t.Errorf("unexpected action: %s", output[2]) - } - if output[7] != "1:" { - t.Errorf("unexpected root class: %s", output[7]) - } - if output[8] != "htb" { - t.Errorf("unexpected qdisc algo: %s", output[8]) - } - } - } -} - -func TestReconcileInterfaceDoesntExist(t *testing.T) { - testReconcileInterfaceHasNoData(t, "\n") -} - -var tcQdiscNoqueue = "qdisc noqueue 0: root refcnt 2 \n" - -func TestReconcileInterfaceExistsWithNoqueue(t *testing.T) { - testReconcileInterfaceHasNoData(t, tcQdiscNoqueue) -} - -var tcQdiscWrong = []string{ - "qdisc htb 2: root refcnt 2 r2q 10 default 30 direct_packets_stat 0\n", - "qdisc foo 1: root refcnt 2 r2q 10 default 30 direct_packets_stat 0\n", -} - -func TestReconcileInterfaceIsWrong(t *testing.T) { - for _, test := range tcQdiscWrong { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - func() ([]byte, error) { return []byte(test), nil }, - func() ([]byte, error) { return []byte("\n"), nil }, - func() ([]byte, error) { return []byte("\n"), nil }, - }, - } - - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - iface := "cbr0" - shaper := &tcShaper{e: &fexec, iface: iface} - err := shaper.ReconcileInterface() - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if fcmd.CombinedOutputCalls != 3 { - t.Errorf("unexpected number of calls: %d", fcmd.CombinedOutputCalls) - } - - for ix, output := range fcmd.CombinedOutputLog { - if output[0] != "tc" { - t.Errorf("unexpected command: %s", output[0]) - } - if output[4] != iface { - t.Errorf("unexpected interface: %s, expected %s", output[4], iface) - } - if ix == 0 { - if len(output) != 5 { - t.Errorf("unexpected command: %v", output) - } - if output[2] != "show" { - t.Errorf("unexpected action: %s", output[2]) - } - } - if ix == 1 { - if len(output) != 8 { - t.Errorf("unexpected command: %v", output) - } - if output[2] != "delete" { - t.Errorf("unexpected action: %s", output[2]) - } - if output[7] != strings.Split(test, " ")[2] { - t.Errorf("unexpected class: %s, expected: %s", output[7], strings.Split(test, " ")[2]) - } - } - if ix == 2 { - if len(output) != 11 { - t.Errorf("unexpected command: %v", output) - } - if output[7] != "1:" { - t.Errorf("unexpected root class: %s", output[7]) - } - if output[8] != "htb" { - t.Errorf("unexpected qdisc algo: %s", output[8]) - } - } - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/unsupported.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/unsupported.go deleted file mode 100644 index 7f980a265..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/bandwidth/unsupported.go +++ /dev/null @@ -1,52 +0,0 @@ -// +build !linux - -/* -Copyright 2015 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 bandwidth - -import ( - "errors" - - "k8s.io/kubernetes/pkg/api/resource" -) - -type unsupportedShaper struct { -} - -func NewTCShaper(iface string) BandwidthShaper { - return &unsupportedShaper{} -} - -func (f *unsupportedShaper) Limit(cidr string, egress, ingress *resource.Quantity) error { - return errors.New("unimplemented") -} - -func (f *unsupportedShaper) Reset(cidr string) error { - return nil -} - -func (f *unsupportedShaper) ReconcileInterface() error { - return errors.New("unimplemented") -} - -func (f *unsupportedShaper) ReconcileCIDR(cidr string, egress, ingress *resource.Quantity) error { - return errors.New("unimplemented") -} - -func (f *unsupportedShaper) GetCIDRs() ([]string, error) { - return []string{}, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/cache_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/cache_test.go deleted file mode 100644 index a54f9e609..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/cache_test.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2014 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 util - -import ( - "testing" -) - -const ( - maxTestCacheSize int = shardsCount * 2 -) - -func ExpectEntry(t *testing.T, cache Cache, index uint64, expectedValue interface{}) { - elem, found := cache.Get(index) - if !found { - t.Error("Expected to find entry with key 1") - } else if elem != expectedValue { - t.Errorf("Expected to find %v, got %v", expectedValue, elem) - } -} - -func TestBasic(t *testing.T) { - cache := NewCache(maxTestCacheSize) - cache.Add(1, "xxx") - ExpectEntry(t, cache, 1, "xxx") -} - -func TestOverflow(t *testing.T) { - cache := NewCache(maxTestCacheSize) - for i := 0; i < maxTestCacheSize+1; i++ { - cache.Add(uint64(i), "xxx") - } - foundIndexes := make([]uint64, 0) - for i := 0; i < maxTestCacheSize+1; i++ { - _, found := cache.Get(uint64(i)) - if found { - foundIndexes = append(foundIndexes, uint64(i)) - } - } - if len(foundIndexes) != maxTestCacheSize { - t.Errorf("Expect to find %d elements, got %d %v", maxTestCacheSize, len(foundIndexes), foundIndexes) - } -} - -func TestOverwrite(t *testing.T) { - cache := NewCache(maxTestCacheSize) - cache.Add(1, "xxx") - ExpectEntry(t, cache, 1, "xxx") - cache.Add(1, "yyy") - ExpectEntry(t, cache, 1, "yyy") -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/chmod/chmod.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/chmod/chmod.go deleted file mode 100644 index 5b646c95b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/chmod/chmod.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2015 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 chmod - -import ( - "os" -) - -// Interface is something that knows how to run the chmod system call. -// It is non-recursive. -type Interface interface { - // Chmod changes the mode of the given file, implementing the same - // semantics as os.Chmod. - Chmod(path string, filemode os.FileMode) error -} - -func New() Interface { - return &chmodRunner{} -} - -type chmodRunner struct{} - -func (_ *chmodRunner) Chmod(path string, mode os.FileMode) error { - return os.Chmod(path, mode) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/chmod/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/chmod/doc.go deleted file mode 100644 index a230e98fd..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/chmod/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2014 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 chown provides an interface and implementations -// for things that run run the chmod system call. -package chmod diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/chown/chown.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/chown/chown.go deleted file mode 100644 index 0d90629de..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/chown/chown.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2015 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 chown - -import ( - "os" -) - -// Interface is something that knows how to run the chown system call. -// It is non-recursive. -type Interface interface { - // Chown changes the owning UID and GID of a file, implementing - // the exact same semantics as os.Chown. - Chown(path string, uid, gid int) error -} - -func New() Interface { - return &chownRunner{} -} - -type chownRunner struct{} - -func (_ *chownRunner) Chown(path string, uid, gid int) error { - return os.Chown(path, uid, gid) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/clock.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/clock.go index 56ea16c69..ac2d738d6 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/clock.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/clock.go @@ -27,6 +27,7 @@ type Clock interface { Now() time.Time Since(time.Time) time.Duration After(d time.Duration) <-chan time.Time + Sleep(d time.Duration) } var ( @@ -53,6 +54,10 @@ func (RealClock) After(d time.Duration) <-chan time.Time { return time.After(d) } +func (RealClock) Sleep(d time.Duration) { + time.Sleep(d) +} + // FakeClock implements Clock, but returns an arbitrary time. type FakeClock struct { lock sync.RWMutex @@ -137,6 +142,10 @@ func (f *FakeClock) HasWaiters() bool { return len(f.waiters) > 0 } +func (f *FakeClock) Sleep(d time.Duration) { + f.Step(d) +} + // IntervalClock implements Clock, but each invocation of Now steps the clock forward the specified duration type IntervalClock struct { Time time.Time @@ -159,3 +168,7 @@ func (i *IntervalClock) Since(ts time.Time) time.Duration { func (*IntervalClock) After(d time.Duration) <-chan time.Time { panic("IntervalClock doesn't implement After") } + +func (*IntervalClock) Sleep(d time.Duration) { + panic("IntervalClock doesn't implement Sleep") +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/clock_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/clock_test.go deleted file mode 100644 index db0cce40a..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/clock_test.go +++ /dev/null @@ -1,96 +0,0 @@ -/* -Copyright 2015 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 util - -import ( - "testing" - "time" -) - -func TestFakeClock(t *testing.T) { - startTime := time.Now() - tc := NewFakeClock(startTime) - tc.Step(time.Second) - now := tc.Now() - if now.Sub(startTime) != time.Second { - t.Errorf("input: %s now=%s gap=%s expected=%s", startTime, now, now.Sub(startTime), time.Second) - } - - tt := tc.Now() - tc.SetTime(tt.Add(time.Hour)) - if tc.Now().Sub(tt) != time.Hour { - t.Errorf("input: %s now=%s gap=%s expected=%s", tt, tc.Now(), tc.Now().Sub(tt), time.Hour) - } -} - -func TestFakeAfter(t *testing.T) { - tc := NewFakeClock(time.Now()) - if tc.HasWaiters() { - t.Errorf("unexpected waiter?") - } - oneSec := tc.After(time.Second) - if !tc.HasWaiters() { - t.Errorf("unexpected lack of waiter?") - } - - oneOhOneSec := tc.After(time.Second + time.Millisecond) - twoSec := tc.After(2 * time.Second) - select { - case <-oneSec: - t.Errorf("unexpected channel read") - case <-oneOhOneSec: - t.Errorf("unexpected channel read") - case <-twoSec: - t.Errorf("unexpected channel read") - default: - } - - tc.Step(999 * time.Millisecond) - select { - case <-oneSec: - t.Errorf("unexpected channel read") - case <-oneOhOneSec: - t.Errorf("unexpected channel read") - case <-twoSec: - t.Errorf("unexpected channel read") - default: - } - - tc.Step(time.Millisecond) - select { - case <-oneSec: - // Expected! - case <-oneOhOneSec: - t.Errorf("unexpected channel read") - case <-twoSec: - t.Errorf("unexpected channel read") - default: - t.Errorf("unexpected non-channel read") - } - tc.Step(time.Millisecond) - select { - case <-oneSec: - // should not double-trigger! - t.Errorf("unexpected channel read") - case <-oneOhOneSec: - // Expected! - case <-twoSec: - t.Errorf("unexpected channel read") - default: - t.Errorf("unexpected non-channel read") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/config/config.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/config/config.go deleted file mode 100644 index 37f6f6ab1..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/config/config.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright 2014 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 config - -import ( - "sync" - - "k8s.io/kubernetes/pkg/util/wait" -) - -type Merger interface { - // Invoked when a change from a source is received. May also function as an incremental - // merger if you wish to consume changes incrementally. Must be reentrant when more than - // one source is defined. - Merge(source string, update interface{}) error -} - -// MergeFunc implements the Merger interface -type MergeFunc func(source string, update interface{}) error - -func (f MergeFunc) Merge(source string, update interface{}) error { - return f(source, update) -} - -// Mux is a class for merging configuration from multiple sources. Changes are -// pushed via channels and sent to the merge function. -type Mux struct { - // Invoked when an update is sent to a source. - merger Merger - - // Sources and their lock. - sourceLock sync.RWMutex - // Maps source names to channels - sources map[string]chan interface{} -} - -// NewMux creates a new mux that can merge changes from multiple sources. -func NewMux(merger Merger) *Mux { - mux := &Mux{ - sources: make(map[string]chan interface{}), - merger: merger, - } - return mux -} - -// Channel returns a channel where a configuration source -// can send updates of new configurations. Multiple calls with the same -// source will return the same channel. This allows change and state based sources -// to use the same channel. Different source names however will be treated as a -// union. -func (m *Mux) Channel(source string) chan interface{} { - if len(source) == 0 { - panic("Channel given an empty name") - } - m.sourceLock.Lock() - defer m.sourceLock.Unlock() - channel, exists := m.sources[source] - if exists { - return channel - } - newChannel := make(chan interface{}) - m.sources[source] = newChannel - go wait.Until(func() { m.listen(source, newChannel) }, 0, wait.NeverStop) - return newChannel -} - -func (m *Mux) listen(source string, listenChannel <-chan interface{}) { - for update := range listenChannel { - m.merger.Merge(source, update) - } -} - -// Accessor is an interface for retrieving the current merge state. -type Accessor interface { - // MergedState returns a representation of the current merge state. - // Must be reentrant when more than one source is defined. - MergedState() interface{} -} - -// AccessorFunc implements the Accessor interface. -type AccessorFunc func() interface{} - -func (f AccessorFunc) MergedState() interface{} { - return f() -} - -type Listener interface { - // OnUpdate is invoked when a change is made to an object. - OnUpdate(instance interface{}) -} - -// ListenerFunc receives a representation of the change or object. -type ListenerFunc func(instance interface{}) - -func (f ListenerFunc) OnUpdate(instance interface{}) { - f(instance) -} - -type Broadcaster struct { - // Listeners for changes and their lock. - listenerLock sync.RWMutex - listeners []Listener -} - -// NewBroadcaster registers a set of listeners that support the Listener interface -// and notifies them all on changes. -func NewBroadcaster() *Broadcaster { - return &Broadcaster{} -} - -// Add registers listener to receive updates of changes. -func (b *Broadcaster) Add(listener Listener) { - b.listenerLock.Lock() - defer b.listenerLock.Unlock() - b.listeners = append(b.listeners, listener) -} - -// Notify notifies all listeners. -func (b *Broadcaster) Notify(instance interface{}) { - b.listenerLock.RLock() - listeners := b.listeners - b.listenerLock.RUnlock() - for _, listener := range listeners { - listener.OnUpdate(instance) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/config/config_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/config/config_test.go deleted file mode 100644 index 4ebab7bf9..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/config/config_test.go +++ /dev/null @@ -1,120 +0,0 @@ -/* -Copyright 2014 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 config - -import ( - "reflect" - "testing" -) - -func TestConfigurationChannels(t *testing.T) { - mux := NewMux(nil) - channelOne := mux.Channel("one") - if channelOne != mux.Channel("one") { - t.Error("Didn't get the same muxuration channel back with the same name") - } - channelTwo := mux.Channel("two") - if channelOne == channelTwo { - t.Error("Got back the same muxuration channel for different names") - } -} - -type MergeMock struct { - source string - update interface{} - t *testing.T -} - -func (m MergeMock) Merge(source string, update interface{}) error { - if m.source != source { - m.t.Errorf("Expected %s, Got %s", m.source, source) - } - if !reflect.DeepEqual(m.update, update) { - m.t.Errorf("Expected %s, Got %s", m.update, update) - } - return nil -} - -func TestMergeInvoked(t *testing.T) { - merger := MergeMock{"one", "test", t} - mux := NewMux(&merger) - mux.Channel("one") <- "test" -} - -func TestMergeFuncInvoked(t *testing.T) { - ch := make(chan bool) - mux := NewMux(MergeFunc(func(source string, update interface{}) error { - if source != "one" { - t.Errorf("Expected %s, Got %s", "one", source) - } - if update.(string) != "test" { - t.Errorf("Expected %s, Got %s", "test", update) - } - ch <- true - return nil - })) - mux.Channel("one") <- "test" - <-ch -} - -func TestSimultaneousMerge(t *testing.T) { - ch := make(chan bool, 2) - mux := NewMux(MergeFunc(func(source string, update interface{}) error { - switch source { - case "one": - if update.(string) != "test" { - t.Errorf("Expected %s, Got %s", "test", update) - } - case "two": - if update.(string) != "test2" { - t.Errorf("Expected %s, Got %s", "test2", update) - } - default: - t.Errorf("Unexpected source, Got %s", update) - } - ch <- true - return nil - })) - source := mux.Channel("one") - source2 := mux.Channel("two") - source <- "test" - source2 <- "test2" - <-ch - <-ch -} - -func TestBroadcaster(t *testing.T) { - b := NewBroadcaster() - b.Notify(struct{}{}) - - ch := make(chan bool, 2) - b.Add(ListenerFunc(func(object interface{}) { - if object != "test" { - t.Errorf("Expected %s, Got %s", "test", object) - } - ch <- true - })) - b.Add(ListenerFunc(func(object interface{}) { - if object != "test" { - t.Errorf("Expected %s, Got %s", "test", object) - } - ch <- true - })) - b.Notify("test") - <-ch - <-ch -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/config/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/config/doc.go deleted file mode 100644 index 697f3ab56..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/config/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright 2014 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 config provides utility objects for decoupling sources of configuration and the -// actual configuration state. Consumers must implement the Merger interface to unify -// the sources of change into an object. -package config diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/dbus.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/dbus.go deleted file mode 100644 index 13da1469e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/dbus.go +++ /dev/null @@ -1,133 +0,0 @@ -/* -Copyright 2015 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 dbus - -import ( - godbus "github.com/godbus/dbus" -) - -// Interface is an interface that presents a subset of the godbus/dbus API. Use this -// when you want to inject fakeable/mockable D-Bus behavior. -type Interface interface { - // SystemBus returns a connection to the system bus, connecting to it - // first if necessary - SystemBus() (Connection, error) - // SessionBus returns a connection to the session bus, connecting to it - // first if necessary - SessionBus() (Connection, error) -} - -// Connection represents a D-Bus connection -type Connection interface { - // Returns an Object representing the bus itself - BusObject() Object - - // Object creates a representation of a remote D-Bus object - Object(name, path string) Object - - // Signal registers or unregisters a channel to receive D-Bus signals - Signal(ch chan<- *godbus.Signal) -} - -// Object represents a remote D-Bus object -type Object interface { - // Call synchronously calls a D-Bus method - Call(method string, flags godbus.Flags, args ...interface{}) Call -} - -// Call represents a pending or completed D-Bus method call -type Call interface { - // Store returns a completed call's return values, or an error - Store(retvalues ...interface{}) error -} - -// Implements Interface in terms of actually talking to D-Bus -type dbusImpl struct { - systemBus *connImpl - sessionBus *connImpl -} - -// Implements Connection as a godbus.Conn -type connImpl struct { - conn *godbus.Conn -} - -// Implements Object as a godbus.Object -type objectImpl struct { - object godbus.BusObject -} - -// Implements Call as a godbus.Call -type callImpl struct { - call *godbus.Call -} - -// New returns a new Interface which will use godbus to talk to D-Bus -func New() Interface { - return &dbusImpl{} -} - -// SystemBus is part of Interface -func (db *dbusImpl) SystemBus() (Connection, error) { - if db.systemBus == nil { - bus, err := godbus.SystemBus() - if err != nil { - return nil, err - } - db.systemBus = &connImpl{bus} - } - - return db.systemBus, nil -} - -// SessionBus is part of Interface -func (db *dbusImpl) SessionBus() (Connection, error) { - if db.sessionBus == nil { - bus, err := godbus.SessionBus() - if err != nil { - return nil, err - } - db.sessionBus = &connImpl{bus} - } - - return db.sessionBus, nil -} - -// BusObject is part of the Connection interface -func (conn *connImpl) BusObject() Object { - return &objectImpl{conn.conn.BusObject()} -} - -// Object is part of the Connection interface -func (conn *connImpl) Object(name, path string) Object { - return &objectImpl{conn.conn.Object(name, godbus.ObjectPath(path))} -} - -// Signal is part of the Connection interface -func (conn *connImpl) Signal(ch chan<- *godbus.Signal) { - conn.conn.Signal(ch) -} - -// Call is part of the Object interface -func (obj *objectImpl) Call(method string, flags godbus.Flags, args ...interface{}) Call { - return &callImpl{obj.object.Call(method, flags, args...)} -} - -// Store is part of the Call interface -func (call *callImpl) Store(retvalues ...interface{}) error { - return call.call.Store(retvalues...) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/dbus_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/dbus_test.go deleted file mode 100644 index 3cf52263a..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/dbus_test.go +++ /dev/null @@ -1,249 +0,0 @@ -/* -Copyright 2015 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 dbus - -import ( - "fmt" - "os" - "testing" - - godbus "github.com/godbus/dbus" -) - -const ( - DBusNameFlagAllowReplacement uint32 = 1 << (iota + 1) - DBusNameFlagReplaceExisting - DBusNameFlagDoNotQueue -) - -const ( - DBusRequestNameReplyPrimaryOwner uint32 = iota + 1 - DBusRequestNameReplyInQueue - DBusRequestNameReplyExists - DBusRequestNameReplyAlreadyOwner -) - -const ( - DBusReleaseNameReplyReleased uint32 = iota + 1 - DBusReleaseNameReplyNonExistent - DBusReleaseNameReplyNotOwner -) - -func doDBusTest(t *testing.T, dbus Interface, real bool) { - bus, err := dbus.SystemBus() - if err != nil { - if !real { - t.Errorf("dbus.SystemBus() failed with fake Interface") - } - t.Skipf("D-Bus is not running: %v", err) - } - busObj := bus.BusObject() - - id := "" - err = busObj.Call("org.freedesktop.DBus.GetId", 0).Store(&id) - if err != nil { - t.Errorf("expected success, got %v", err) - } - if len(id) == 0 { - t.Errorf("expected non-empty Id, got \"\"") - } - - // Switch to the session bus for the rest, since the system bus is more - // locked down (and thus harder to trick into emitting signals). - - bus, err = dbus.SessionBus() - if err != nil { - if !real { - t.Errorf("dbus.SystemBus() failed with fake Interface") - } - t.Skipf("D-Bus session bus is not available: %v", err) - } - busObj = bus.BusObject() - - name := fmt.Sprintf("io.kubernetes.dbus_test_%d", os.Getpid()) - owner := "" - err = busObj.Call("org.freedesktop.DBus.GetNameOwner", 0, name).Store(&owner) - if err == nil { - t.Errorf("expected '%s' to be un-owned, but found owner %s", name, owner) - } - dbuserr, ok := err.(godbus.Error) - if !ok { - t.Errorf("expected godbus.Error, but got %#v", err) - } - if dbuserr.Name != "org.freedesktop.DBus.Error.NameHasNoOwner" { - t.Errorf("expected NameHasNoOwner error but got %v", err) - } - - sigchan := make(chan *godbus.Signal, 10) - bus.Signal(sigchan) - - rule := fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'", name) - err = busObj.Call("org.freedesktop.DBus.AddMatch", 0, rule).Store() - if err != nil { - t.Errorf("expected success, got %v", err) - } - - var ret uint32 - err = busObj.Call("org.freedesktop.DBus.RequestName", 0, name, DBusNameFlagDoNotQueue).Store(&ret) - if err != nil { - t.Errorf("expected success, got %v", err) - } - if ret != DBusRequestNameReplyPrimaryOwner { - t.Errorf("expected %v, got %v", DBusRequestNameReplyPrimaryOwner, ret) - } - - err = busObj.Call("org.freedesktop.DBus.GetNameOwner", 0, name).Store(&owner) - if err != nil { - t.Errorf("expected success, got %v", err) - } - - var changedSignal, acquiredSignal, lostSignal *godbus.Signal - - sig1 := <-sigchan - sig2 := <-sigchan - // We get two signals, but the order isn't guaranteed - if sig1.Name == "org.freedesktop.DBus.NameOwnerChanged" { - changedSignal = sig1 - acquiredSignal = sig2 - } else { - acquiredSignal = sig1 - changedSignal = sig2 - } - - if acquiredSignal.Sender != "org.freedesktop.DBus" || acquiredSignal.Name != "org.freedesktop.DBus.NameAcquired" { - t.Errorf("expected NameAcquired signal, got %v", acquiredSignal) - } - acquiredName := acquiredSignal.Body[0].(string) - if acquiredName != name { - t.Errorf("unexpected NameAcquired arguments: %v", acquiredSignal) - } - - if changedSignal.Sender != "org.freedesktop.DBus" || changedSignal.Name != "org.freedesktop.DBus.NameOwnerChanged" { - t.Errorf("expected NameOwnerChanged signal, got %v", changedSignal) - } - - changedName := changedSignal.Body[0].(string) - oldOwner := changedSignal.Body[1].(string) - newOwner := changedSignal.Body[2].(string) - if changedName != name || oldOwner != "" || newOwner != owner { - t.Errorf("unexpected NameOwnerChanged arguments: %v", changedSignal) - } - - err = busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&ret) - if err != nil { - t.Errorf("expected success, got %v", err) - } - if ret != DBusReleaseNameReplyReleased { - t.Errorf("expected %v, got %v", DBusReleaseNameReplyReleased, ret) - } - - sig1 = <-sigchan - sig2 = <-sigchan - if sig1.Name == "org.freedesktop.DBus.NameOwnerChanged" { - changedSignal = sig1 - lostSignal = sig2 - } else { - lostSignal = sig1 - changedSignal = sig2 - } - - if lostSignal.Sender != "org.freedesktop.DBus" || lostSignal.Name != "org.freedesktop.DBus.NameLost" { - t.Errorf("expected NameLost signal, got %v", lostSignal) - } - lostName := lostSignal.Body[0].(string) - if lostName != name { - t.Errorf("unexpected NameLost arguments: %v", lostSignal) - } - - if changedSignal.Sender != "org.freedesktop.DBus" || changedSignal.Name != "org.freedesktop.DBus.NameOwnerChanged" { - t.Errorf("expected NameOwnerChanged signal, got %v", changedSignal) - } - - changedName = changedSignal.Body[0].(string) - oldOwner = changedSignal.Body[1].(string) - newOwner = changedSignal.Body[2].(string) - if changedName != name || oldOwner != owner || newOwner != "" { - t.Errorf("unexpected NameOwnerChanged arguments: %v", changedSignal) - } - - if len(sigchan) != 0 { - t.Errorf("unexpected extra signals (%d)", len(sigchan)) - } - - // Unregister sigchan - bus.Signal(sigchan) -} - -func TestRealDBus(t *testing.T) { - dbus := New() - doDBusTest(t, dbus, true) -} - -func TestFakeDBus(t *testing.T) { - uniqueName := ":1.1" - ownedName := "" - - fakeSystem := NewFakeConnection() - fakeSystem.SetBusObject( - func(method string, args ...interface{}) ([]interface{}, error) { - if method == "org.freedesktop.DBus.GetId" { - return []interface{}{"foo"}, nil - } - return nil, fmt.Errorf("unexpected method call '%s'", method) - }, - ) - - fakeSession := NewFakeConnection() - fakeSession.SetBusObject( - func(method string, args ...interface{}) ([]interface{}, error) { - if method == "org.freedesktop.DBus.GetNameOwner" { - checkName := args[0].(string) - if checkName != ownedName { - return nil, godbus.Error{"org.freedesktop.DBus.Error.NameHasNoOwner", nil} - } else { - return []interface{}{uniqueName}, nil - } - } else if method == "org.freedesktop.DBus.RequestName" { - reqName := args[0].(string) - _ = args[1].(uint32) - if ownedName != "" { - return []interface{}{DBusRequestNameReplyAlreadyOwner}, nil - } - ownedName = reqName - fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameAcquired", reqName) - fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", reqName, "", uniqueName) - return []interface{}{DBusRequestNameReplyPrimaryOwner}, nil - } else if method == "org.freedesktop.DBus.ReleaseName" { - reqName := args[0].(string) - if reqName != ownedName { - return []interface{}{DBusReleaseNameReplyNotOwner}, nil - } - ownedName = "" - fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", reqName, uniqueName, "") - fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameLost", reqName) - return []interface{}{DBusReleaseNameReplyReleased}, nil - } else if method == "org.freedesktop.DBus.AddMatch" { - return nil, nil - } else { - return nil, fmt.Errorf("unexpected method call '%s'", method) - } - }, - ) - - dbus := NewFake(fakeSystem, fakeSession) - doDBusTest(t, dbus, false) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/doc.go deleted file mode 100644 index 59bec0e56..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2015 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 dbus provides an injectable interface and implementations for D-Bus communication -package dbus diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/fake_dbus.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/fake_dbus.go deleted file mode 100644 index eb97febae..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/dbus/fake_dbus.go +++ /dev/null @@ -1,135 +0,0 @@ -/* -Copyright 2015 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 dbus - -import ( - "fmt" - - godbus "github.com/godbus/dbus" -) - -// DBusFake is a simple fake Interface type. -type DBusFake struct { - systemBus *DBusFakeConnection - sessionBus *DBusFakeConnection -} - -// DBusFakeConnection represents a fake D-Bus connection -type DBusFakeConnection struct { - busObject *fakeObject - objects map[string]*fakeObject - signalHandlers []chan<- *godbus.Signal -} - -// DBusFakeHandler is used to handle fake D-Bus method calls -type DBusFakeHandler func(method string, args ...interface{}) ([]interface{}, error) - -type fakeObject struct { - handler DBusFakeHandler -} - -type fakeCall struct { - ret []interface{} - err error -} - -// NewFake returns a new Interface which will fake talking to D-Bus -func NewFake(systemBus *DBusFakeConnection, sessionBus *DBusFakeConnection) *DBusFake { - return &DBusFake{systemBus, sessionBus} -} - -func NewFakeConnection() *DBusFakeConnection { - return &DBusFakeConnection{ - objects: make(map[string]*fakeObject), - } -} - -// SystemBus is part of Interface -func (db *DBusFake) SystemBus() (Connection, error) { - if db.systemBus != nil { - return db.systemBus, nil - } else { - return nil, fmt.Errorf("DBus is not running") - } -} - -// SessionBus is part of Interface -func (db *DBusFake) SessionBus() (Connection, error) { - if db.sessionBus != nil { - return db.sessionBus, nil - } else { - return nil, fmt.Errorf("DBus is not running") - } -} - -// BusObject is part of the Connection interface -func (conn *DBusFakeConnection) BusObject() Object { - return conn.busObject -} - -// Object is part of the Connection interface -func (conn *DBusFakeConnection) Object(name, path string) Object { - return conn.objects[name+path] -} - -// Signal is part of the Connection interface -func (conn *DBusFakeConnection) Signal(ch chan<- *godbus.Signal) { - for i := range conn.signalHandlers { - if conn.signalHandlers[i] == ch { - conn.signalHandlers = append(conn.signalHandlers[:i], conn.signalHandlers[i+1:]...) - return - } - } - conn.signalHandlers = append(conn.signalHandlers, ch) -} - -// SetBusObject sets the handler for the BusObject of conn -func (conn *DBusFakeConnection) SetBusObject(handler DBusFakeHandler) { - conn.busObject = &fakeObject{handler} -} - -// AddObject adds a handler for the Object at name and path -func (conn *DBusFakeConnection) AddObject(name, path string, handler DBusFakeHandler) { - conn.objects[name+path] = &fakeObject{handler} -} - -// EmitSignal emits a signal on conn -func (conn *DBusFakeConnection) EmitSignal(name, path, iface, signal string, args ...interface{}) { - sig := &godbus.Signal{ - Sender: name, - Path: godbus.ObjectPath(path), - Name: iface + "." + signal, - Body: args, - } - for _, ch := range conn.signalHandlers { - ch <- sig - } -} - -// Call is part of the Object interface -func (obj *fakeObject) Call(method string, flags godbus.Flags, args ...interface{}) Call { - ret, err := obj.handler(method, args...) - return &fakeCall{ret, err} -} - -// Store is part of the Call interface -func (call *fakeCall) Store(retvalues ...interface{}) error { - if call.err != nil { - return call.err - } - return godbus.Store(call.ret, retvalues...) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/deadlock-detector_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/deadlock-detector_test.go deleted file mode 100644 index 51d111245..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/deadlock-detector_test.go +++ /dev/null @@ -1,127 +0,0 @@ -/* -Copyright 2015 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 util - -import ( - "sync" - "testing" - "time" -) - -type fakeExiter struct { - format string - args []interface{} - exited bool -} - -func (f *fakeExiter) Exitf(format string, args ...interface{}) { - f.format = format - f.args = args - f.exited = true -} - -func TestMaxLockPeriod(t *testing.T) { - lock := &sync.RWMutex{} - panicked := false - func() { - defer func() { - if r := recover(); r != nil { - panicked = true - } - }() - DeadlockWatchdogReadLock(lock, "test lock", 0) - }() - if !panicked { - t.Errorf("expected a panic for a zero max lock period") - } -} - -func TestDeadlockWatchdogLocked(t *testing.T) { - lock := &sync.RWMutex{} - lock.Lock() - - exitCh := make(chan time.Time, 1) - fake := fakeExiter{} - - detector := &deadlockDetector{ - lock: &rwMutexToLockableAdapter{lock}, - name: "test deadlock", - exitChannelFn: func() <-chan time.Time { return exitCh }, - exiter: &fake, - } - - exitCh <- time.Time{} - - detector.run() - - if !fake.exited { - t.Errorf("expected to have exited") - } - - if len(fake.args) != 1 || fake.args[0].(string) != detector.name { - t.Errorf("unexpected args: %v", fake.args) - } -} - -func TestDeadlockWatchdogUnlocked(t *testing.T) { - lock := &sync.RWMutex{} - - fake := fakeExiter{} - - detector := &deadlockDetector{ - lock: &rwMutexToLockableAdapter{lock}, - name: "test deadlock", - exitChannelFn: func() <-chan time.Time { return time.After(time.Second * 5) }, - exiter: &fake, - } - - for i := 0; i < 100; i++ { - detector.runOnce() - } - - if fake.exited { - t.Errorf("expected to have not exited") - } -} - -func TestDeadlockWatchdogLocking(t *testing.T) { - lock := &sync.RWMutex{} - - fake := fakeExiter{} - - go func() { - for { - lock.Lock() - lock.Unlock() - } - }() - - detector := &deadlockDetector{ - lock: &rwMutexToLockableAdapter{lock}, - name: "test deadlock", - exitChannelFn: func() <-chan time.Time { return time.After(time.Second * 5) }, - exiter: &fake, - } - - for i := 0; i < 100; i++ { - detector.runOnce() - } - - if fake.exited { - t.Errorf("expected to have not exited") - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/deployment/deployment.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/deployment/deployment.go index 5f5baa782..6160f5d13 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/deployment/deployment.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/deployment/deployment.go @@ -21,15 +21,20 @@ import ( "strconv" "time" + "github.com/golang/glog" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apis/extensions" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/labels" + "k8s.io/kubernetes/pkg/util/errors" "k8s.io/kubernetes/pkg/util/integer" intstrutil "k8s.io/kubernetes/pkg/util/intstr" labelsutil "k8s.io/kubernetes/pkg/util/labels" podutil "k8s.io/kubernetes/pkg/util/pod" + "k8s.io/kubernetes/pkg/util/wait" ) const ( @@ -44,40 +49,88 @@ const ( // GetOldReplicaSets returns the old replica sets targeted by the given Deployment; get PodList and ReplicaSetList from client interface. // Note that the first set of old replica sets doesn't include the ones with no pods, and the second set of old replica sets include all old replica sets. -func GetOldReplicaSets(deployment extensions.Deployment, c clientset.Interface) ([]*extensions.ReplicaSet, []*extensions.ReplicaSet, error) { - return GetOldReplicaSetsFromLists(deployment, c, - func(namespace string, options api.ListOptions) (*api.PodList, error) { - return c.Core().Pods(namespace).List(options) - }, +func GetOldReplicaSets(deployment *extensions.Deployment, c clientset.Interface) ([]*extensions.ReplicaSet, []*extensions.ReplicaSet, error) { + rsList, err := ListReplicaSets(deployment, func(namespace string, options api.ListOptions) ([]extensions.ReplicaSet, error) { rsList, err := c.Extensions().ReplicaSets(namespace).List(options) return rsList.Items, err }) + if err != nil { + return nil, nil, fmt.Errorf("error listing ReplicaSets: %v", err) + } + podList, err := ListPods(deployment, + func(namespace string, options api.ListOptions) (*api.PodList, error) { + return c.Core().Pods(namespace).List(options) + }) + if err != nil { + return nil, nil, fmt.Errorf("error listing Pods: %v", err) + } + return FindOldReplicaSets(deployment, rsList, podList) } -// GetOldReplicaSetsFromLists returns two sets of old replica sets targeted by the given Deployment; get PodList and ReplicaSetList with input functions. -// Note that the first set of old replica sets doesn't include the ones with no pods, and the second set of old replica sets include all old replica sets. -func GetOldReplicaSetsFromLists(deployment extensions.Deployment, c clientset.Interface, getPodList func(string, api.ListOptions) (*api.PodList, error), getRSList func(string, api.ListOptions) ([]extensions.ReplicaSet, error)) ([]*extensions.ReplicaSet, []*extensions.ReplicaSet, error) { - namespace := deployment.ObjectMeta.Namespace +// GetNewReplicaSet returns a replica set that matches the intent of the given deployment; get ReplicaSetList from client interface. +// Returns nil if the new replica set doesn't exist yet. +func GetNewReplicaSet(deployment *extensions.Deployment, c clientset.Interface) (*extensions.ReplicaSet, error) { + rsList, err := ListReplicaSets(deployment, + func(namespace string, options api.ListOptions) ([]extensions.ReplicaSet, error) { + rsList, err := c.Extensions().ReplicaSets(namespace).List(options) + return rsList.Items, err + }) + if err != nil { + return nil, fmt.Errorf("error listing ReplicaSets: %v", err) + } + return FindNewReplicaSet(deployment, rsList) +} + +// TODO: switch this to full namespacers +type rsListFunc func(string, api.ListOptions) ([]extensions.ReplicaSet, error) +type podListFunc func(string, api.ListOptions) (*api.PodList, error) + +// ListReplicaSets returns a slice of RSes the given deployment targets. +func ListReplicaSets(deployment *extensions.Deployment, getRSList rsListFunc) ([]extensions.ReplicaSet, error) { + // TODO: Right now we list replica sets by their labels. We should list them by selector, i.e. the replica set's selector + // should be a superset of the deployment's selector, see https://github.com/kubernetes/kubernetes/issues/19830; + // or use controllerRef, see https://github.com/kubernetes/kubernetes/issues/2210 + namespace := deployment.Namespace selector, err := unversioned.LabelSelectorAsSelector(deployment.Spec.Selector) if err != nil { - return nil, nil, fmt.Errorf("invalid label selector: %v", err) + return nil, err } - - // 1. Find all pods whose labels match deployment.Spec.Selector options := api.ListOptions{LabelSelector: selector} - podList, err := getPodList(namespace, options) + return getRSList(namespace, options) +} + +// ListPods returns a list of pods the given deployment targets. +func ListPods(deployment *extensions.Deployment, getPodList podListFunc) (*api.PodList, error) { + namespace := deployment.Namespace + selector, err := unversioned.LabelSelectorAsSelector(deployment.Spec.Selector) if err != nil { - return nil, nil, fmt.Errorf("error listing pods: %v", err) + return nil, err } - // 2. Find the corresponding replica sets for pods in podList. - // TODO: Right now we list all replica sets and then filter. We should add an API for this. + options := api.ListOptions{LabelSelector: selector} + return getPodList(namespace, options) +} + +// FindNewReplicaSet returns the new RS this given deployment targets (the one with the same pod template). +func FindNewReplicaSet(deployment *extensions.Deployment, rsList []extensions.ReplicaSet) (*extensions.ReplicaSet, error) { + newRSTemplate := GetNewReplicaSetTemplate(deployment) + for i := range rsList { + if api.Semantic.DeepEqual(rsList[i].Spec.Template, newRSTemplate) { + // This is the new ReplicaSet. + return &rsList[i], nil + } + } + // new ReplicaSet does not exist. + return nil, nil +} + +// FindOldReplicaSets returns the old replica sets targeted by the given Deployment, with the given PodList and slice of RSes. +// Note that the first set of old replica sets doesn't include the ones with no pods, and the second set of old replica sets include all old replica sets. +func FindOldReplicaSets(deployment *extensions.Deployment, rsList []extensions.ReplicaSet, podList *api.PodList) ([]*extensions.ReplicaSet, []*extensions.ReplicaSet, error) { + // Find all pods whose labels match deployment.Spec.Selector, and corresponding replica sets for pods in podList. + // All pods and replica sets are labeled with pod-template-hash to prevent overlapping oldRSs := map[string]extensions.ReplicaSet{} allOldRSs := map[string]extensions.ReplicaSet{} - rsList, err := getRSList(namespace, options) - if err != nil { - return nil, nil, fmt.Errorf("error listing replica sets: %v", err) - } newRSTemplate := GetNewReplicaSetTemplate(deployment) for _, pod := range podList.Items { podLabelsSelector := labels.Set(pod.ObjectMeta.Labels) @@ -87,7 +140,7 @@ func GetOldReplicaSetsFromLists(deployment extensions.Deployment, c clientset.In return nil, nil, fmt.Errorf("invalid label selector: %v", err) } // Filter out replica set that has the same pod template spec as the deployment - that is the new replica set. - if api.Semantic.DeepEqual(rs.Spec.Template, &newRSTemplate) { + if api.Semantic.DeepEqual(rs.Spec.Template, newRSTemplate) { continue } allOldRSs[rs.ObjectMeta.Name] = rs @@ -109,43 +162,58 @@ func GetOldReplicaSetsFromLists(deployment extensions.Deployment, c clientset.In return requiredRSs, allRSs, nil } -// GetNewReplicaSet returns a replica set that matches the intent of the given deployment; get ReplicaSetList from client interface. -// Returns nil if the new replica set doesn't exist yet. -func GetNewReplicaSet(deployment extensions.Deployment, c clientset.Interface) (*extensions.ReplicaSet, error) { - return GetNewReplicaSetFromList(deployment, c, - func(namespace string, options api.ListOptions) ([]extensions.ReplicaSet, error) { - rsList, err := c.Extensions().ReplicaSets(namespace).List(options) - return rsList.Items, err - }) +func WaitForReplicaSetUpdated(c clientset.Interface, desiredGeneration int64, namespace, name string) error { + return wait.Poll(10*time.Millisecond, 1*time.Minute, func() (bool, error) { + rs, err := c.Extensions().ReplicaSets(namespace).Get(name) + if err != nil { + return false, err + } + return rs.Status.ObservedGeneration >= desiredGeneration, nil + }) } -// GetNewReplicaSetFromList returns a replica set that matches the intent of the given deployment; get ReplicaSetList with the input function. -// Returns nil if the new replica set doesn't exist yet. -func GetNewReplicaSetFromList(deployment extensions.Deployment, c clientset.Interface, getRSList func(string, api.ListOptions) ([]extensions.ReplicaSet, error)) (*extensions.ReplicaSet, error) { - namespace := deployment.ObjectMeta.Namespace - selector, err := unversioned.LabelSelectorAsSelector(deployment.Spec.Selector) - if err != nil { - return nil, fmt.Errorf("invalid label selector: %v", err) - } +func WaitForPodsHashPopulated(c clientset.Interface, desiredGeneration int64, namespace, name string) error { + return wait.Poll(1*time.Second, 1*time.Minute, func() (bool, error) { + rs, err := c.Extensions().ReplicaSets(namespace).Get(name) + if err != nil { + return false, err + } + return rs.Status.ObservedGeneration >= desiredGeneration && + rs.Status.FullyLabeledReplicas == rs.Spec.Replicas, nil + }) +} - rsList, err := getRSList(namespace, api.ListOptions{LabelSelector: selector}) - if err != nil { - return nil, fmt.Errorf("error listing ReplicaSets: %v", err) - } - newRSTemplate := GetNewReplicaSetTemplate(deployment) - - for i := range rsList { - if api.Semantic.DeepEqual(rsList[i].Spec.Template, &newRSTemplate) { - // This is the new ReplicaSet. - return &rsList[i], nil +// LabelPodsWithHash labels all pods in the given podList with the new hash label. +// The returned bool value can be used to tell if all pods are actually labeled. +func LabelPodsWithHash(podList *api.PodList, rs *extensions.ReplicaSet, c clientset.Interface, namespace, hash string) (bool, error) { + allPodsLabeled := true + for _, pod := range podList.Items { + // Only label the pod that doesn't already have the new hash + if pod.Labels[extensions.DefaultDeploymentUniqueLabelKey] != hash { + if _, podUpdated, err := podutil.UpdatePodWithRetries(c.Core().Pods(namespace), &pod, + func(podToUpdate *api.Pod) error { + // Precondition: the pod doesn't contain the new hash in its label. + if podToUpdate.Labels[extensions.DefaultDeploymentUniqueLabelKey] == hash { + return errors.ErrPreconditionViolated + } + podToUpdate.Labels = labelsutil.AddLabel(podToUpdate.Labels, extensions.DefaultDeploymentUniqueLabelKey, hash) + return nil + }); err != nil { + return false, fmt.Errorf("error in adding template hash label %s to pod %+v: %s", hash, pod, err) + } else if podUpdated { + glog.V(4).Infof("Labeled %s %s/%s of %s %s/%s with hash %s.", pod.Kind, pod.Namespace, pod.Name, rs.Kind, rs.Namespace, rs.Name, hash) + } else { + // If the pod wasn't updated but didn't return error when we try to update it, we've hit "pod not found" or "precondition violated" error. + // Then we can't say all pods are labeled + allPodsLabeled = false + } } } - // new ReplicaSet does not exist. - return nil, nil + return allPodsLabeled, nil } // Returns the desired PodTemplateSpec for the new ReplicaSet corresponding to the given ReplicaSet. -func GetNewReplicaSetTemplate(deployment extensions.Deployment) api.PodTemplateSpec { +func GetNewReplicaSetTemplate(deployment *extensions.Deployment) api.PodTemplateSpec { // newRS will have the same template as in deployment spec, plus a unique label in some cases. newRSTemplate := api.PodTemplateSpec{ ObjectMeta: deployment.Spec.Template.ObjectMeta, @@ -172,7 +240,20 @@ func SetFromReplicaSetTemplate(deployment *extensions.Deployment, template api.P func GetReplicaCountForReplicaSets(replicaSets []*extensions.ReplicaSet) int { totalReplicaCount := 0 for _, rs := range replicaSets { - totalReplicaCount += rs.Spec.Replicas + if rs != nil { + totalReplicaCount += rs.Spec.Replicas + } + } + return totalReplicaCount +} + +// GetActualReplicaCountForReplicaSets returns the sum of actual replicas of the given replica sets. +func GetActualReplicaCountForReplicaSets(replicaSets []*extensions.ReplicaSet) int { + totalReplicaCount := 0 + for _, rs := range replicaSets { + if rs != nil { + totalReplicaCount += rs.Status.Replicas + } } return totalReplicaCount } @@ -197,6 +278,9 @@ func getReadyPodsCount(pods []api.Pod, minReadySeconds int) int { } func IsPodAvailable(pod *api.Pod, minReadySeconds int) bool { + if !controller.IsPodActive(*pod) { + return false + } // Check if we've passed minReadySeconds since LastTransitionTime // If so, this pod is ready for _, c := range pod.Status.Conditions { @@ -215,20 +299,28 @@ func IsPodAvailable(pod *api.Pod, minReadySeconds int) bool { } func GetPodsForReplicaSets(c clientset.Interface, replicaSets []*extensions.ReplicaSet) ([]api.Pod, error) { - allPods := []api.Pod{} + allPods := map[string]api.Pod{} for _, rs := range replicaSets { - selector, err := unversioned.LabelSelectorAsSelector(rs.Spec.Selector) - if err != nil { - return nil, fmt.Errorf("invalid label selector: %v", err) + if rs != nil { + selector, err := unversioned.LabelSelectorAsSelector(rs.Spec.Selector) + if err != nil { + return nil, fmt.Errorf("invalid label selector: %v", err) + } + options := api.ListOptions{LabelSelector: selector} + podList, err := c.Core().Pods(rs.ObjectMeta.Namespace).List(options) + if err != nil { + return nil, fmt.Errorf("error listing pods: %v", err) + } + for _, pod := range podList.Items { + allPods[pod.Name] = pod + } } - options := api.ListOptions{LabelSelector: selector} - podList, err := c.Core().Pods(rs.ObjectMeta.Namespace).List(options) - if err != nil { - return allPods, fmt.Errorf("error listing pods: %v", err) - } - allPods = append(allPods, podList.Items...) } - return allPods, nil + requiredPods := []api.Pod{} + for _, pod := range allPods { + requiredPods = append(requiredPods, pod) + } + return requiredPods, nil } // Revision returns the revision number of the input replica set @@ -252,7 +344,7 @@ func NewRSNewReplicas(deployment *extensions.Deployment, allRSs []*extensions.Re switch deployment.Spec.Strategy.Type { case extensions.RollingUpdateDeploymentStrategyType: // Check if we can scale up. - maxSurge, err := intstrutil.GetValueFromIntOrPercent(&deployment.Spec.Strategy.RollingUpdate.MaxSurge, deployment.Spec.Replicas) + maxSurge, err := intstrutil.GetValueFromIntOrPercent(&deployment.Spec.Strategy.RollingUpdate.MaxSurge, deployment.Spec.Replicas, true) if err != nil { return 0, err } @@ -274,3 +366,46 @@ func NewRSNewReplicas(deployment *extensions.Deployment, allRSs []*extensions.Re return 0, fmt.Errorf("deployment type %v isn't supported", deployment.Spec.Strategy.Type) } } + +// Polls for deployment to be updated so that deployment.Status.ObservedGeneration >= desiredGeneration. +// Returns error if polling timesout. +func WaitForObservedDeployment(getDeploymentFunc func() (*extensions.Deployment, error), desiredGeneration int64, interval, timeout time.Duration) error { + // TODO: This should take clientset.Interface when all code is updated to use clientset. Keeping it this way allows the function to be used by callers who have client.Interface. + return wait.Poll(interval, timeout, func() (bool, error) { + deployment, err := getDeploymentFunc() + if err != nil { + return false, err + } + return deployment.Status.ObservedGeneration >= desiredGeneration, nil + }) +} + +// ResolveFenceposts resolves both maxSurge and maxUnavailable. This needs to happen in one +// step. For example: +// +// 2 desired, max unavailable 1%, surge 0% - should scale old(-1), then new(+1), then old(-1), then new(+1) +// 1 desired, max unavailable 1%, surge 0% - should scale old(-1), then new(+1) +// 2 desired, max unavailable 25%, surge 1% - should scale new(+1), then old(-1), then new(+1), then old(-1) +// 1 desired, max unavailable 25%, surge 1% - should scale new(+1), then old(-1) +// 2 desired, max unavailable 0%, surge 1% - should scale new(+1), then old(-1), then new(+1), then old(-1) +// 1 desired, max unavailable 0%, surge 1% - should scale new(+1), then old(-1) +func ResolveFenceposts(maxSurge, maxUnavailable *intstrutil.IntOrString, desired int) (int, int, error) { + surge, err := intstrutil.GetValueFromIntOrPercent(maxSurge, desired, true) + if err != nil { + return 0, 0, err + } + unavailable, err := intstrutil.GetValueFromIntOrPercent(maxUnavailable, desired, false) + if err != nil { + return 0, 0, err + } + + if surge == 0 && unavailable == 0 { + // Validation should never allow the user to explicitly use zero values for both maxSurge + // maxUnavailable. Due to rounding down maxUnavailable though, it may resolve to zero. + // If both fenceposts resolve to zero, then we should set maxUnavailable to 1 on the + // theory that surge might not work due to quota. + unavailable = 1 + } + + return surge, unavailable, nil +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/deployment/deployment_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/deployment/deployment_test.go deleted file mode 100644 index 4383bda02..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/deployment/deployment_test.go +++ /dev/null @@ -1,344 +0,0 @@ -/* -Copyright 2015 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 deployment - -import ( - "reflect" - "testing" - "time" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" - "k8s.io/kubernetes/pkg/client/unversioned/testclient/simple" - "k8s.io/kubernetes/pkg/runtime" -) - -func newPod(now time.Time, ready bool, beforeSec int) api.Pod { - conditionStatus := api.ConditionFalse - if ready { - conditionStatus = api.ConditionTrue - } - return api.Pod{ - Status: api.PodStatus{ - Conditions: []api.PodCondition{ - { - Type: api.PodReady, - LastTransitionTime: unversioned.NewTime(now.Add(-1 * time.Duration(beforeSec) * time.Second)), - Status: conditionStatus, - }, - }, - }, - } -} - -func TestGetReadyPodsCount(t *testing.T) { - now := time.Now() - tests := []struct { - pods []api.Pod - minReadySeconds int - expected int - }{ - { - []api.Pod{ - newPod(now, true, 0), - newPod(now, true, 2), - newPod(now, false, 1), - }, - 1, - 1, - }, - { - []api.Pod{ - newPod(now, true, 2), - newPod(now, true, 11), - newPod(now, true, 5), - }, - 10, - 1, - }, - } - - for _, test := range tests { - if count := getReadyPodsCount(test.pods, test.minReadySeconds); count != test.expected { - t.Errorf("Pods = %#v, minReadySeconds = %d, expected %d, got %d", test.pods, test.minReadySeconds, test.expected, count) - } - } -} - -// generatePodFromRS creates a pod, with the input ReplicaSet's selector and its template -func generatePodFromRS(rs extensions.ReplicaSet) api.Pod { - return api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: rs.Labels, - }, - Spec: rs.Spec.Template.Spec, - } -} - -func generatePod(labels map[string]string, image string) api.Pod { - return api.Pod{ - ObjectMeta: api.ObjectMeta{ - Labels: labels, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: image, - Image: image, - ImagePullPolicy: api.PullAlways, - TerminationMessagePath: api.TerminationMessagePathDefault, - }, - }, - }, - } -} - -func generateRSWithLabel(labels map[string]string, image string) extensions.ReplicaSet { - return extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{ - Name: api.SimpleNameGenerator.GenerateName("replicaset"), - Labels: labels, - }, - Spec: extensions.ReplicaSetSpec{ - Replicas: 1, - Selector: &unversioned.LabelSelector{MatchLabels: labels}, - Template: &api.PodTemplateSpec{ - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: image, - Image: image, - ImagePullPolicy: api.PullAlways, - TerminationMessagePath: api.TerminationMessagePathDefault, - }, - }, - }, - }, - }, - } -} - -// generateRS creates a replica set, with the input deployment's template as its template -func generateRS(deployment extensions.Deployment) extensions.ReplicaSet { - template := GetNewReplicaSetTemplate(deployment) - return extensions.ReplicaSet{ - ObjectMeta: api.ObjectMeta{ - Name: api.SimpleNameGenerator.GenerateName("replicaset"), - Labels: template.Labels, - }, - Spec: extensions.ReplicaSetSpec{ - Template: &template, - Selector: &unversioned.LabelSelector{MatchLabels: template.Labels}, - }, - } -} - -// generateDeployment creates a deployment, with the input image as its template -func generateDeployment(image string) extensions.Deployment { - podLabels := map[string]string{"name": image} - terminationSec := int64(30) - return extensions.Deployment{ - ObjectMeta: api.ObjectMeta{ - Name: image, - }, - Spec: extensions.DeploymentSpec{ - Replicas: 1, - Selector: &unversioned.LabelSelector{MatchLabels: podLabels}, - Template: api.PodTemplateSpec{ - ObjectMeta: api.ObjectMeta{ - Labels: podLabels, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: image, - Image: image, - ImagePullPolicy: api.PullAlways, - TerminationMessagePath: api.TerminationMessagePathDefault, - }, - }, - DNSPolicy: api.DNSClusterFirst, - TerminationGracePeriodSeconds: &terminationSec, - RestartPolicy: api.RestartPolicyAlways, - SecurityContext: &api.PodSecurityContext{}, - }, - }, - }, - } -} - -func TestGetNewRC(t *testing.T) { - newDeployment := generateDeployment("nginx") - newRC := generateRS(newDeployment) - - tests := []struct { - test string - rsList extensions.ReplicaSetList - expected *extensions.ReplicaSet - }{ - { - "No new ReplicaSet", - extensions.ReplicaSetList{ - Items: []extensions.ReplicaSet{ - generateRS(generateDeployment("foo")), - generateRS(generateDeployment("bar")), - }, - }, - nil, - }, - { - "Has new ReplicaSet", - extensions.ReplicaSetList{ - Items: []extensions.ReplicaSet{ - generateRS(generateDeployment("foo")), - generateRS(generateDeployment("bar")), - generateRS(generateDeployment("abc")), - newRC, - generateRS(generateDeployment("xyz")), - }, - }, - &newRC, - }, - } - - ns := api.NamespaceDefault - for _, test := range tests { - c := &simple.Client{ - Request: simple.Request{ - Method: "GET", - Path: testapi.Default.ResourcePath("replicaSets", ns, ""), - }, - Response: simple.Response{ - StatusCode: 200, - Body: &test.rsList, - }, - } - rs, err := GetNewReplicaSet(newDeployment, c.Setup(t).Clientset) - if err != nil { - t.Errorf("In test case %s, got unexpected error %v", test.test, err) - } - if !api.Semantic.DeepEqual(rs, test.expected) { - t.Errorf("In test case %s, expected %+v, got %+v", test.test, test.expected, rs) - } - } -} - -func TestGetOldRCs(t *testing.T) { - newDeployment := generateDeployment("nginx") - newRS := generateRS(newDeployment) - newPod := generatePodFromRS(newRS) - - // create 2 old deployments and related replica sets/pods, with the same labels but different template - oldDeployment := generateDeployment("nginx") - oldDeployment.Spec.Template.Spec.Containers[0].Name = "nginx-old-1" - oldRS := generateRS(oldDeployment) - oldPod := generatePodFromRS(oldRS) - oldDeployment2 := generateDeployment("nginx") - oldDeployment2.Spec.Template.Spec.Containers[0].Name = "nginx-old-2" - oldRS2 := generateRS(oldDeployment2) - oldPod2 := generatePodFromRS(oldRS2) - - // create 1 ReplicaSet that existed before the deployment, with the same labels as the deployment - existedPod := generatePod(newDeployment.Spec.Template.Labels, "foo") - existedRS := generateRSWithLabel(newDeployment.Spec.Template.Labels, "foo") - - tests := []struct { - test string - objs []runtime.Object - expected []*extensions.ReplicaSet - }{ - { - "No old ReplicaSets", - []runtime.Object{ - &api.PodList{ - Items: []api.Pod{ - generatePod(newDeployment.Spec.Template.Labels, "foo"), - generatePod(newDeployment.Spec.Template.Labels, "bar"), - newPod, - }, - }, - &extensions.ReplicaSetList{ - Items: []extensions.ReplicaSet{ - generateRS(generateDeployment("foo")), - newRS, - generateRS(generateDeployment("bar")), - }, - }, - }, - []*extensions.ReplicaSet{}, - }, - { - "Has old ReplicaSet", - []runtime.Object{ - &api.PodList{ - Items: []api.Pod{ - oldPod, - oldPod2, - generatePod(map[string]string{"name": "bar"}, "bar"), - generatePod(map[string]string{"name": "xyz"}, "xyz"), - existedPod, - generatePod(newDeployment.Spec.Template.Labels, "abc"), - }, - }, - &extensions.ReplicaSetList{ - Items: []extensions.ReplicaSet{ - oldRS2, - oldRS, - existedRS, - newRS, - generateRSWithLabel(map[string]string{"name": "xyz"}, "xyz"), - generateRSWithLabel(map[string]string{"name": "bar"}, "bar"), - }, - }, - }, - []*extensions.ReplicaSet{&oldRS, &oldRS2, &existedRS}, - }, - } - - for _, test := range tests { - rss, _, err := GetOldReplicaSets(newDeployment, fake.NewSimpleClientset(test.objs...)) - if err != nil { - t.Errorf("In test case %s, got unexpected error %v", test.test, err) - } - if !equal(rss, test.expected) { - t.Errorf("In test case %q, expected %v, got %v", test.test, test.expected, rss) - } - } -} - -// equal compares the equality of two ReplicaSet slices regardless of their ordering -func equal(rss1, rss2 []*extensions.ReplicaSet) bool { - if reflect.DeepEqual(rss1, rss2) { - return true - } - if rss1 == nil || rss2 == nil || len(rss1) != len(rss2) { - return false - } - count := 0 - for _, rs1 := range rss1 { - for _, rs2 := range rss2 { - if reflect.DeepEqual(rs1, rs2) { - count++ - break - } - } - } - return count == len(rss1) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/env_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/env_test.go deleted file mode 100644 index a640fbe2a..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/env_test.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright 2015 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 util - -import ( - "os" - "strconv" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestGetEnvAsStringOrFallback(t *testing.T) { - const expected = "foo" - - assert := assert.New(t) - - key := "FLOCKER_SET_VAR" - os.Setenv(key, expected) - assert.Equal(expected, GetEnvAsStringOrFallback(key, "~"+expected)) - - key = "FLOCKER_UNSET_VAR" - assert.Equal(expected, GetEnvAsStringOrFallback(key, expected)) -} - -func TestGetEnvAsIntOrFallback(t *testing.T) { - const expected = 1 - - assert := assert.New(t) - - key := "FLOCKER_SET_VAR" - os.Setenv(key, strconv.Itoa(expected)) - returnVal, _ := GetEnvAsIntOrFallback(key, 1) - assert.Equal(expected, returnVal) - - key = "FLOCKER_UNSET_VAR" - returnVal, _ = GetEnvAsIntOrFallback(key, expected) - assert.Equal(expected, returnVal) -} - -func TestGetEnvAsFloat64OrFallback(t *testing.T) { - const expected = 1.0 - - assert := assert.New(t) - - key := "FLOCKER_SET_VAR" - os.Setenv(key, "1.0") - returnVal, _ := GetEnvAsFloat64OrFallback(key, 2.0) - assert.Equal(expected, returnVal) - - key = "FLOCKER_UNSET_VAR" - returnVal, _ = GetEnvAsFloat64OrFallback(key, 1.0) - assert.Equal(expected, returnVal) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/errors/errors.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/errors/errors.go index a1a8e7aa2..df3adaf3e 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/errors/errors.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/errors/errors.go @@ -16,7 +16,10 @@ limitations under the License. package errors -import "fmt" +import ( + "errors" + "fmt" +) // Aggregate represents an object that contains multiple errors, but does not // necessarily have singular semantic meaning. @@ -148,3 +151,6 @@ func AggregateGoroutines(funcs ...func() error) Aggregate { } return NewAggregate(errs) } + +// ErrPreconditionViolated is returned when the precondition is violated +var ErrPreconditionViolated = errors.New("precondition is violated") diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/errors/errors_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/errors/errors_test.go deleted file mode 100644 index 7ecf919ff..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/errors/errors_test.go +++ /dev/null @@ -1,286 +0,0 @@ -/* -Copyright 2015 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 errors - -import ( - "fmt" - "reflect" - "testing" -) - -func TestEmptyAggregate(t *testing.T) { - var slice []error - var agg Aggregate - var err error - - agg = NewAggregate(slice) - if agg != nil { - t.Errorf("expected nil, got %#v", agg) - } - err = NewAggregate(slice) - if err != nil { - t.Errorf("expected nil, got %#v", err) - } - - // This is not normally possible, but pedantry demands I test it. - agg = aggregate(slice) // empty aggregate - if s := agg.Error(); s != "" { - t.Errorf("expected empty string, got %q", s) - } - if s := agg.Errors(); len(s) != 0 { - t.Errorf("expected empty slice, got %#v", s) - } - err = agg.(error) - if s := err.Error(); s != "" { - t.Errorf("expected empty string, got %q", s) - } -} - -func TestSingularAggregate(t *testing.T) { - var slice []error = []error{fmt.Errorf("err")} - var agg Aggregate - var err error - - agg = NewAggregate(slice) - if agg == nil { - t.Errorf("expected non-nil") - } - if s := agg.Error(); s != "err" { - t.Errorf("expected 'err', got %q", s) - } - if s := agg.Errors(); len(s) != 1 { - t.Errorf("expected one-element slice, got %#v", s) - } - if s := agg.Errors()[0].Error(); s != "err" { - t.Errorf("expected 'err', got %q", s) - } - - err = agg.(error) - if err == nil { - t.Errorf("expected non-nil") - } - if s := err.Error(); s != "err" { - t.Errorf("expected 'err', got %q", s) - } -} - -func TestPluralAggregate(t *testing.T) { - var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("123")} - var agg Aggregate - var err error - - agg = NewAggregate(slice) - if agg == nil { - t.Errorf("expected non-nil") - } - if s := agg.Error(); s != "[abc, 123]" { - t.Errorf("expected '[abc, 123]', got %q", s) - } - if s := agg.Errors(); len(s) != 2 { - t.Errorf("expected two-elements slice, got %#v", s) - } - if s := agg.Errors()[0].Error(); s != "abc" { - t.Errorf("expected '[abc, 123]', got %q", s) - } - - err = agg.(error) - if err == nil { - t.Errorf("expected non-nil") - } - if s := err.Error(); s != "[abc, 123]" { - t.Errorf("expected '[abc, 123]', got %q", s) - } -} - -func TestFilterOut(t *testing.T) { - testCases := []struct { - err error - filter []Matcher - expected error - }{ - { - nil, - []Matcher{}, - nil, - }, - { - aggregate{}, - []Matcher{}, - nil, - }, - { - aggregate{fmt.Errorf("abc")}, - []Matcher{}, - aggregate{fmt.Errorf("abc")}, - }, - { - aggregate{fmt.Errorf("abc")}, - []Matcher{func(err error) bool { return false }}, - aggregate{fmt.Errorf("abc")}, - }, - { - aggregate{fmt.Errorf("abc")}, - []Matcher{func(err error) bool { return true }}, - nil, - }, - { - aggregate{fmt.Errorf("abc")}, - []Matcher{func(err error) bool { return false }, func(err error) bool { return false }}, - aggregate{fmt.Errorf("abc")}, - }, - { - aggregate{fmt.Errorf("abc")}, - []Matcher{func(err error) bool { return false }, func(err error) bool { return true }}, - nil, - }, - { - aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")}, - []Matcher{func(err error) bool { return err.Error() == "def" }}, - aggregate{fmt.Errorf("abc"), fmt.Errorf("ghi")}, - }, - { - aggregate{aggregate{fmt.Errorf("abc")}}, - []Matcher{}, - aggregate{aggregate{fmt.Errorf("abc")}}, - }, - { - aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}}, - []Matcher{}, - aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}}, - }, - { - aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}}, - []Matcher{func(err error) bool { return err.Error() == "def" }}, - aggregate{aggregate{fmt.Errorf("abc")}}, - }, - } - for i, testCase := range testCases { - err := FilterOut(testCase.err, testCase.filter...) - if !reflect.DeepEqual(testCase.expected, err) { - t.Errorf("%d: expected %v, got %v", i, testCase.expected, err) - } - } -} - -func TestFlatten(t *testing.T) { - testCases := []struct { - agg Aggregate - expected Aggregate - }{ - { - nil, - nil, - }, - { - aggregate{}, - nil, - }, - { - aggregate{fmt.Errorf("abc")}, - aggregate{fmt.Errorf("abc")}, - }, - { - aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")}, - aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")}, - }, - { - aggregate{aggregate{fmt.Errorf("abc")}}, - aggregate{fmt.Errorf("abc")}, - }, - { - aggregate{aggregate{aggregate{fmt.Errorf("abc")}}}, - aggregate{fmt.Errorf("abc")}, - }, - { - aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}}, - aggregate{fmt.Errorf("abc"), fmt.Errorf("def")}, - }, - { - aggregate{aggregate{aggregate{fmt.Errorf("abc")}, fmt.Errorf("def"), aggregate{fmt.Errorf("ghi")}}}, - aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")}, - }, - } - for i, testCase := range testCases { - agg := Flatten(testCase.agg) - if !reflect.DeepEqual(testCase.expected, agg) { - t.Errorf("%d: expected %v, got %v", i, testCase.expected, agg) - } - } -} - -func TestAggregateGoroutines(t *testing.T) { - testCases := []struct { - errs []error - expected map[string]bool // can't compare directly to Aggregate due to non-deterministic ordering - }{ - { - []error{}, - nil, - }, - { - []error{nil}, - nil, - }, - { - []error{nil, nil}, - nil, - }, - { - []error{fmt.Errorf("1")}, - map[string]bool{"1": true}, - }, - { - []error{fmt.Errorf("1"), nil}, - map[string]bool{"1": true}, - }, - { - []error{fmt.Errorf("1"), fmt.Errorf("267")}, - map[string]bool{"1": true, "267": true}, - }, - { - []error{fmt.Errorf("1"), nil, fmt.Errorf("1234")}, - map[string]bool{"1": true, "1234": true}, - }, - { - []error{nil, fmt.Errorf("1"), nil, fmt.Errorf("1234"), fmt.Errorf("22")}, - map[string]bool{"1": true, "1234": true, "22": true}, - }, - } - for i, testCase := range testCases { - funcs := make([]func() error, len(testCase.errs)) - for i := range testCase.errs { - err := testCase.errs[i] - funcs[i] = func() error { return err } - } - agg := AggregateGoroutines(funcs...) - if agg == nil { - if len(testCase.expected) > 0 { - t.Errorf("%d: expected %v, got nil", i, testCase.expected) - } - continue - } - if len(agg.Errors()) != len(testCase.expected) { - t.Errorf("%d: expected %d errors in aggregate, got %v", i, len(testCase.expected), agg) - continue - } - for _, err := range agg.Errors() { - if !testCase.expected[err.Error()] { - t.Errorf("%d: expected %v, got aggregate containing %v", i, testCase.expected, err) - } - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/doc.go deleted file mode 100644 index 64c1541bf..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2014 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 exec provides an injectable interface and implementations for running commands. -package exec diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/exec.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/exec.go deleted file mode 100644 index 80444a9d8..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/exec.go +++ /dev/null @@ -1,129 +0,0 @@ -/* -Copyright 2014 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 exec - -import ( - osexec "os/exec" - "syscall" -) - -// ErrExecutableNotFound is returned if the executable is not found. -var ErrExecutableNotFound = osexec.ErrNotFound - -// Interface is an interface that presents a subset of the os/exec API. Use this -// when you want to inject fakeable/mockable exec behavior. -type Interface interface { - // Command returns a Cmd instance which can be used to run a single command. - // This follows the pattern of package os/exec. - Command(cmd string, args ...string) Cmd - - // LookPath wraps os/exec.LookPath - LookPath(file string) (string, error) -} - -// Cmd is an interface that presents an API that is very similar to Cmd from os/exec. -// As more functionality is needed, this can grow. Since Cmd is a struct, we will have -// to replace fields with get/set method pairs. -type Cmd interface { - // CombinedOutput runs the command and returns its combined standard output - // and standard error. This follows the pattern of package os/exec. - CombinedOutput() ([]byte, error) - // Output runs the command and returns standard output, but not standard err - Output() ([]byte, error) - SetDir(dir string) -} - -// ExitError is an interface that presents an API similar to os.ProcessState, which is -// what ExitError from os/exec is. This is designed to make testing a bit easier and -// probably loses some of the cross-platform properties of the underlying library. -type ExitError interface { - String() string - Error() string - Exited() bool - ExitStatus() int -} - -// Implements Interface in terms of really exec()ing. -type executor struct{} - -// New returns a new Interface which will os/exec to run commands. -func New() Interface { - return &executor{} -} - -// Command is part of the Interface interface. -func (executor *executor) Command(cmd string, args ...string) Cmd { - return (*cmdWrapper)(osexec.Command(cmd, args...)) -} - -// LookPath is part of the Interface interface -func (executor *executor) LookPath(file string) (string, error) { - return osexec.LookPath(file) -} - -// Wraps exec.Cmd so we can capture errors. -type cmdWrapper osexec.Cmd - -func (cmd *cmdWrapper) SetDir(dir string) { - cmd.Dir = dir -} - -// CombinedOutput is part of the Cmd interface. -func (cmd *cmdWrapper) CombinedOutput() ([]byte, error) { - out, err := (*osexec.Cmd)(cmd).CombinedOutput() - if err != nil { - return out, handleError(err) - } - return out, nil -} - -func (cmd *cmdWrapper) Output() ([]byte, error) { - out, err := (*osexec.Cmd)(cmd).Output() - if err != nil { - return out, handleError(err) - } - return out, nil -} - -func handleError(err error) error { - if ee, ok := err.(*osexec.ExitError); ok { - // Force a compile fail if exitErrorWrapper can't convert to ExitError. - var x ExitError = &exitErrorWrapper{ee} - return x - } - if ee, ok := err.(*osexec.Error); ok { - if ee.Err == osexec.ErrNotFound { - return ErrExecutableNotFound - } - } - return err -} - -// exitErrorWrapper is an implementation of ExitError in terms of os/exec ExitError. -// Note: standard exec.ExitError is type *os.ProcessState, which already implements Exited(). -type exitErrorWrapper struct { - *osexec.ExitError -} - -// ExitStatus is part of the ExitError interface. -func (eew exitErrorWrapper) ExitStatus() int { - ws, ok := eew.Sys().(syscall.WaitStatus) - if !ok { - panic("can't call ExitStatus() on a non-WaitStatus exitErrorWrapper") - } - return ws.ExitStatus() -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/exec_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/exec_test.go deleted file mode 100644 index d24f9cc19..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/exec_test.go +++ /dev/null @@ -1,103 +0,0 @@ -/* -Copyright 2014 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 exec - -import ( - osexec "os/exec" - "testing" -) - -func TestExecutorNoArgs(t *testing.T) { - ex := New() - - cmd := ex.Command("true") - out, err := cmd.CombinedOutput() - if err != nil { - t.Errorf("expected success, got %v", err) - } - if len(out) != 0 { - t.Errorf("expected no output, got %q", string(out)) - } - - cmd = ex.Command("false") - out, err = cmd.CombinedOutput() - if err == nil { - t.Errorf("expected failure, got nil error") - } - if len(out) != 0 { - t.Errorf("expected no output, got %q", string(out)) - } - ee, ok := err.(ExitError) - if !ok { - t.Errorf("expected an ExitError, got %+v", err) - } - if ee.Exited() { - if code := ee.ExitStatus(); code != 1 { - t.Errorf("expected exit status 1, got %d", code) - } - } - - cmd = ex.Command("/does/not/exist") - out, err = cmd.CombinedOutput() - if err == nil { - t.Errorf("expected failure, got nil error") - } - if ee, ok := err.(ExitError); ok { - t.Errorf("expected non-ExitError, got %+v", ee) - } -} - -func TestExecutorWithArgs(t *testing.T) { - ex := New() - - cmd := ex.Command("/bin/echo", "stdout") - out, err := cmd.CombinedOutput() - if err != nil { - t.Errorf("expected success, got %+v", err) - } - if string(out) != "stdout\n" { - t.Errorf("unexpected output: %q", string(out)) - } - - cmd = ex.Command("/bin/sh", "-c", "echo stderr > /dev/stderr") - out, err = cmd.CombinedOutput() - if err != nil { - t.Errorf("expected success, got %+v", err) - } - if string(out) != "stderr\n" { - t.Errorf("unexpected output: %q", string(out)) - } -} - -func TestLookPath(t *testing.T) { - ex := New() - - shExpected, _ := osexec.LookPath("sh") - sh, _ := ex.LookPath("sh") - if sh != shExpected { - t.Errorf("unexpected result for LookPath: got %s, expected %s", sh, shExpected) - } -} - -func TestExecutableNotFound(t *testing.T) { - exec := New() - cmd := exec.Command("fake_executable_name") - _, err := cmd.CombinedOutput() - if err != ErrExecutableNotFound { - t.Errorf("Expected error ErrExecutableNotFound but got %v", err) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/fake_exec.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/fake_exec.go deleted file mode 100644 index 40df52921..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/exec/fake_exec.go +++ /dev/null @@ -1,101 +0,0 @@ -/* -Copyright 2014 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 exec - -import ( - "fmt" -) - -// A simple scripted Interface type. -type FakeExec struct { - CommandScript []FakeCommandAction - CommandCalls int - LookPathFunc func(string) (string, error) -} - -type FakeCommandAction func(cmd string, args ...string) Cmd - -func (fake *FakeExec) Command(cmd string, args ...string) Cmd { - if fake.CommandCalls > len(fake.CommandScript)-1 { - panic(fmt.Sprintf("ran out of Command() actions. Could not handle command [%d]: %s args: %v", fake.CommandCalls, cmd, args)) - } - i := fake.CommandCalls - fake.CommandCalls++ - return fake.CommandScript[i](cmd, args...) -} - -func (fake *FakeExec) LookPath(file string) (string, error) { - return fake.LookPathFunc(file) -} - -// A simple scripted Cmd type. -type FakeCmd struct { - Argv []string - CombinedOutputScript []FakeCombinedOutputAction - CombinedOutputCalls int - CombinedOutputLog [][]string - Dirs []string -} - -func InitFakeCmd(fake *FakeCmd, cmd string, args ...string) Cmd { - fake.Argv = append([]string{cmd}, args...) - return fake -} - -type FakeCombinedOutputAction func() ([]byte, error) - -func (fake *FakeCmd) SetDir(dir string) { - fake.Dirs = append(fake.Dirs, dir) -} - -func (fake *FakeCmd) CombinedOutput() ([]byte, error) { - if fake.CombinedOutputCalls > len(fake.CombinedOutputScript)-1 { - panic("ran out of CombinedOutput() actions") - } - if fake.CombinedOutputLog == nil { - fake.CombinedOutputLog = [][]string{} - } - i := fake.CombinedOutputCalls - fake.CombinedOutputLog = append(fake.CombinedOutputLog, append([]string{}, fake.Argv...)) - fake.CombinedOutputCalls++ - return fake.CombinedOutputScript[i]() -} - -func (fake *FakeCmd) Output() ([]byte, error) { - return nil, fmt.Errorf("unimplemented") -} - -// A simple fake ExitError type. -type FakeExitError struct { - Status int -} - -func (fake *FakeExitError) String() string { - return fmt.Sprintf("exit %d", fake.Status) -} - -func (fake *FakeExitError) Error() string { - return fake.String() -} - -func (fake *FakeExitError) Exited() bool { - return true -} - -func (fake *FakeExitError) ExitStatus() int { - return fake.Status -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flock/flock_unix.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flock/flock_unix.go deleted file mode 100644 index 88ca8d700..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flock/flock_unix.go +++ /dev/null @@ -1,51 +0,0 @@ -// +build linux darwin freebsd openbsd netbsd dragonfly - -/* -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 flock - -import ( - "os" - "sync" - - "golang.org/x/sys/unix" -) - -var ( - // lock guards lockfile. Assignment is not atomic. - lock sync.Mutex - // os.File has a runtime.Finalizer so the fd will be closed if the struct - // is garbage collected. Let's hold onto a reference so that doesn't happen. - lockfile *os.File -) - -// Acquire acquires a lock on a file for the duration of the process. This method -// is reentrant. -func Acquire(path string) error { - lock.Lock() - defer lock.Unlock() - var err error - if lockfile, err = os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600); err != nil { - return err - } - - opts := unix.Flock_t{Type: unix.F_WRLCK} - if err := unix.FcntlFlock(lockfile.Fd(), unix.F_SETLKW, &opts); err != nil { - return err - } - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flushwriter/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flushwriter/doc.go deleted file mode 100644 index 485b99926..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flushwriter/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2014 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 flushwriter implements a wrapper for a writer that flushes on every -// write if that writer implements the io.Flusher interface -package flushwriter diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flushwriter/writer.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flushwriter/writer.go deleted file mode 100644 index 0a9d88a78..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flushwriter/writer.go +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2014 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 flushwriter - -import ( - "io" - "net/http" -) - -// Wrap wraps an io.Writer into a writer that flushes after every write if -// the writer implements the Flusher interface. -func Wrap(w io.Writer) io.Writer { - fw := &flushWriter{ - writer: w, - } - if flusher, ok := w.(http.Flusher); ok { - fw.flusher = flusher - } - return fw -} - -// flushWriter provides wrapper for responseWriter with HTTP streaming capabilities -type flushWriter struct { - flusher http.Flusher - writer io.Writer -} - -// Write is a FlushWriter implementation of the io.Writer that sends any buffered -// data to the client. -func (fw *flushWriter) Write(p []byte) (n int, err error) { - n, err = fw.writer.Write(p) - if err != nil { - return - } - if fw.flusher != nil { - fw.flusher.Flush() - } - return -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flushwriter/writer_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flushwriter/writer_test.go deleted file mode 100644 index d40b0bb00..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/flushwriter/writer_test.go +++ /dev/null @@ -1,86 +0,0 @@ -/* -Copyright 2014 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 flushwriter - -import ( - "fmt" - "testing" -) - -type writerWithFlush struct { - writeCount, flushCount int - err error -} - -func (w *writerWithFlush) Flush() { - w.flushCount++ -} - -func (w *writerWithFlush) Write(p []byte) (n int, err error) { - w.writeCount++ - return len(p), w.err -} - -type writerWithNoFlush struct { - writeCount int -} - -func (w *writerWithNoFlush) Write(p []byte) (n int, err error) { - w.writeCount++ - return len(p), nil -} - -func TestWriteWithFlush(t *testing.T) { - w := &writerWithFlush{} - fw := Wrap(w) - for i := 0; i < 10; i++ { - _, err := fw.Write([]byte("Test write")) - if err != nil { - t.Errorf("Unexpected error while writing with flush writer: %v", err) - } - } - if w.flushCount != 10 { - t.Errorf("Flush not called the expected number of times. Actual: %d", w.flushCount) - } - if w.writeCount != 10 { - t.Errorf("Write not called the expected number of times. Actual: %d", w.writeCount) - } -} - -func TestWriteWithoutFlush(t *testing.T) { - w := &writerWithNoFlush{} - fw := Wrap(w) - for i := 0; i < 10; i++ { - _, err := fw.Write([]byte("Test write")) - if err != nil { - t.Errorf("Unexpected error while writing with flush writer: %v", err) - } - } - if w.writeCount != 10 { - t.Errorf("Write not called the expected number of times. Actual: %d", w.writeCount) - } -} - -func TestWriteError(t *testing.T) { - e := fmt.Errorf("Error") - w := &writerWithFlush{err: e} - fw := Wrap(w) - _, err := fw.Write([]byte("Test write")) - if err != e { - t.Errorf("Did not get expected error. Got: %#v", err) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/hash/hash_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/hash/hash_test.go deleted file mode 100644 index 3bba3b074..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/hash/hash_test.go +++ /dev/null @@ -1,147 +0,0 @@ -/* -Copyright 2015 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 hash - -import ( - "fmt" - "hash/adler32" - "testing" - - "github.com/davecgh/go-spew/spew" -) - -type A struct { - x int - y string -} - -type B struct { - x []int - y map[string]bool -} - -type C struct { - x int - y string -} - -func (c C) String() string { - return fmt.Sprintf("%d:%s", c.x, c.y) -} - -func TestDeepHashObject(t *testing.T) { - successCases := []func() interface{}{ - func() interface{} { return 8675309 }, - func() interface{} { return "Jenny, I got your number" }, - func() interface{} { return []string{"eight", "six", "seven"} }, - func() interface{} { return [...]int{5, 3, 0, 9} }, - func() interface{} { return map[int]string{8: "8", 6: "6", 7: "7"} }, - func() interface{} { return map[string]int{"5": 5, "3": 3, "0": 0, "9": 9} }, - func() interface{} { return A{867, "5309"} }, - func() interface{} { return &A{867, "5309"} }, - func() interface{} { - return B{[]int{8, 6, 7}, map[string]bool{"5": true, "3": true, "0": true, "9": true}} - }, - func() interface{} { return map[A]bool{A{8675309, "Jenny"}: true, A{9765683, "!Jenny"}: false} }, - func() interface{} { return map[C]bool{C{8675309, "Jenny"}: true, C{9765683, "!Jenny"}: false} }, - func() interface{} { return map[*A]bool{&A{8675309, "Jenny"}: true, &A{9765683, "!Jenny"}: false} }, - func() interface{} { return map[*C]bool{&C{8675309, "Jenny"}: true, &C{9765683, "!Jenny"}: false} }, - } - - for _, tc := range successCases { - hasher1 := adler32.New() - DeepHashObject(hasher1, tc()) - hash1 := hasher1.Sum32() - DeepHashObject(hasher1, tc()) - hash2 := hasher1.Sum32() - if hash1 != hash2 { - t.Fatalf("hash of the same object (%q) produced different results: %d vs %d", toString(tc()), hash1, hash2) - } - for i := 0; i < 100; i++ { - hasher2 := adler32.New() - - DeepHashObject(hasher1, tc()) - hash1a := hasher1.Sum32() - DeepHashObject(hasher2, tc()) - hash2a := hasher2.Sum32() - - if hash1a != hash1 { - t.Errorf("repeated hash of the same object (%q) produced different results: %d vs %d", toString(tc()), hash1, hash1a) - } - if hash2a != hash2 { - t.Errorf("repeated hash of the same object (%q) produced different results: %d vs %d", toString(tc()), hash2, hash2a) - } - if hash1a != hash2a { - t.Errorf("hash of the same object produced (%q) different results: %d vs %d", toString(tc()), hash1a, hash2a) - } - } - } -} - -func toString(obj interface{}) string { - return spew.Sprintf("%#v", obj) -} - -type wheel struct { - radius uint32 -} - -type unicycle struct { - primaryWheel *wheel - licencePlateID string - tags map[string]string -} - -func TestDeepObjectPointer(t *testing.T) { - // Arrange - wheel1 := wheel{radius: 17} - wheel2 := wheel{radius: 22} - wheel3 := wheel{radius: 17} - - myUni1 := unicycle{licencePlateID: "blah", primaryWheel: &wheel1, tags: map[string]string{"color": "blue", "name": "john"}} - myUni2 := unicycle{licencePlateID: "blah", primaryWheel: &wheel2, tags: map[string]string{"color": "blue", "name": "john"}} - myUni3 := unicycle{licencePlateID: "blah", primaryWheel: &wheel3, tags: map[string]string{"color": "blue", "name": "john"}} - - // Run it more than once to verify determinism of hasher. - for i := 0; i < 100; i++ { - hasher1 := adler32.New() - hasher2 := adler32.New() - hasher3 := adler32.New() - // Act - DeepHashObject(hasher1, myUni1) - hash1 := hasher1.Sum32() - DeepHashObject(hasher1, myUni1) - hash1a := hasher1.Sum32() - DeepHashObject(hasher2, myUni2) - hash2 := hasher2.Sum32() - DeepHashObject(hasher3, myUni3) - hash3 := hasher3.Sum32() - - // Assert - if hash1 != hash1a { - t.Errorf("repeated hash of the same object produced different results: %d vs %d", hash1, hash1a) - } - - if hash1 == hash2 { - t.Errorf("hash1 (%d) and hash2(%d) must be different because they have different values for wheel size", hash1, hash2) - } - - if hash1 != hash3 { - t.Errorf("hash1 (%d) and hash3(%d) must be the same because although they point to different objects, they have the same values for wheel size", hash1, hash3) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/doc.go deleted file mode 100644 index b474fc572..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2015 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 httpstream adds multiplexed streaming support to HTTP requests and -// responses via connection upgrades. -package httpstream diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/httpstream.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/httpstream.go deleted file mode 100644 index 4f6b608ce..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/httpstream.go +++ /dev/null @@ -1,145 +0,0 @@ -/* -Copyright 2015 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 httpstream - -import ( - "fmt" - "io" - "net/http" - "strings" - "time" -) - -const ( - HeaderConnection = "Connection" - HeaderUpgrade = "Upgrade" - HeaderProtocolVersion = "X-Stream-Protocol-Version" - HeaderAcceptedProtocolVersions = "X-Accepted-Stream-Protocol-Versions" -) - -// NewStreamHandler defines a function that is called when a new Stream is -// received. If no error is returned, the Stream is accepted; otherwise, -// the stream is rejected. After the reply frame has been sent, replySent is closed. -type NewStreamHandler func(stream Stream, replySent <-chan struct{}) error - -// NoOpNewStreamHandler is a stream handler that accepts a new stream and -// performs no other logic. -func NoOpNewStreamHandler(stream Stream, replySent <-chan struct{}) error { return nil } - -// Dialer knows how to open a streaming connection to a server. -type Dialer interface { - - // Dial opens a streaming connection to a server using one of the protocols - // specified (in order of most preferred to least preferred). - Dial(protocols ...string) (Connection, string, error) -} - -// UpgradeRoundTripper is a type of http.RoundTripper that is able to upgrade -// HTTP requests to support multiplexed bidirectional streams. After RoundTrip() -// is invoked, if the upgrade is successful, clients may retrieve the upgraded -// connection by calling UpgradeRoundTripper.Connection(). -type UpgradeRoundTripper interface { - http.RoundTripper - // NewConnection validates the response and creates a new Connection. - NewConnection(resp *http.Response) (Connection, error) -} - -// ResponseUpgrader knows how to upgrade HTTP requests and responses to -// add streaming support to them. -type ResponseUpgrader interface { - // UpgradeResponse upgrades an HTTP response to one that supports multiplexed - // streams. newStreamHandler will be called asynchronously whenever the - // other end of the upgraded connection creates a new stream. - UpgradeResponse(w http.ResponseWriter, req *http.Request, newStreamHandler NewStreamHandler) Connection -} - -// Connection represents an upgraded HTTP connection. -type Connection interface { - // CreateStream creates a new Stream with the supplied headers. - CreateStream(headers http.Header) (Stream, error) - // Close resets all streams and closes the connection. - Close() error - // CloseChan returns a channel that is closed when the underlying connection is closed. - CloseChan() <-chan bool - // SetIdleTimeout sets the amount of time the connection may remain idle before - // it is automatically closed. - SetIdleTimeout(timeout time.Duration) -} - -// Stream represents a bidirectional communications channel that is part of an -// upgraded connection. -type Stream interface { - io.ReadWriteCloser - // Reset closes both directions of the stream, indicating that neither client - // or server can use it any more. - Reset() error - // Headers returns the headers used to create the stream. - Headers() http.Header - // Identifier returns the stream's ID. - Identifier() uint32 -} - -// IsUpgradeRequest returns true if the given request is a connection upgrade request -func IsUpgradeRequest(req *http.Request) bool { - for _, h := range req.Header[http.CanonicalHeaderKey(HeaderConnection)] { - if strings.Contains(strings.ToLower(h), strings.ToLower(HeaderUpgrade)) { - return true - } - } - return false -} - -func negotiateProtocol(clientProtocols, serverProtocols []string) string { - for i := range clientProtocols { - for j := range serverProtocols { - if clientProtocols[i] == serverProtocols[j] { - return clientProtocols[i] - } - } - } - return "" -} - -// Handshake performs a subprotocol negotiation. If the client did not request -// a specific subprotocol, defaultProtocol is used. If the client did request a -// subprotocol, Handshake will select the first common value found in -// serverProtocols. If a match is found, Handshake adds a response header -// indicating the chosen subprotocol. If no match is found, HTTP forbidden is -// returned, along with a response header containing the list of protocols the -// server can accept. -func Handshake(req *http.Request, w http.ResponseWriter, serverProtocols []string, defaultProtocol string) (string, error) { - clientProtocols := req.Header[http.CanonicalHeaderKey(HeaderProtocolVersion)] - if len(clientProtocols) == 0 { - // Kube 1.0 client that didn't support subprotocol negotiation - // TODO remove this defaulting logic once Kube 1.0 is no longer supported - w.Header().Add(HeaderProtocolVersion, defaultProtocol) - return defaultProtocol, nil - } - - negotiatedProtocol := negotiateProtocol(clientProtocols, serverProtocols) - if len(negotiatedProtocol) == 0 { - w.WriteHeader(http.StatusForbidden) - for i := range serverProtocols { - w.Header().Add(HeaderAcceptedProtocolVersions, serverProtocols[i]) - } - fmt.Fprintf(w, "unable to upgrade: unable to negotiate protocol: client supports %v, server accepts %v", clientProtocols, serverProtocols) - return "", fmt.Errorf("unable to upgrade: unable to negotiate protocol: client supports %v, server supports %v", clientProtocols, serverProtocols) - } - - w.Header().Add(HeaderProtocolVersion, negotiatedProtocol) - return negotiatedProtocol, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/httpstream_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/httpstream_test.go deleted file mode 100644 index 7a1bbaefb..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/httpstream_test.go +++ /dev/null @@ -1,120 +0,0 @@ -/* -Copyright 2015 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 httpstream - -import ( - "net/http" - "reflect" - "testing" -) - -type responseWriter struct { - header http.Header - statusCode *int -} - -func newResponseWriter() *responseWriter { - return &responseWriter{ - header: make(http.Header), - } -} - -func (r *responseWriter) Header() http.Header { - return r.header -} - -func (r *responseWriter) WriteHeader(code int) { - r.statusCode = &code -} - -func (r *responseWriter) Write([]byte) (int, error) { - return 0, nil -} - -func TestHandshake(t *testing.T) { - defaultProtocol := "default" - - tests := map[string]struct { - clientProtocols []string - serverProtocols []string - expectedProtocol string - expectError bool - }{ - "no client protocols": { - clientProtocols: []string{}, - serverProtocols: []string{"a", "b"}, - expectedProtocol: defaultProtocol, - }, - "no common protocol": { - clientProtocols: []string{"c"}, - serverProtocols: []string{"a", "b"}, - expectedProtocol: "", - expectError: true, - }, - "common protocol": { - clientProtocols: []string{"b"}, - serverProtocols: []string{"a", "b"}, - expectedProtocol: "b", - }, - } - - for name, test := range tests { - req, err := http.NewRequest("GET", "http://www.example.com/", nil) - if err != nil { - t.Fatalf("%s: error creating request: %v", name, err) - } - - for _, p := range test.clientProtocols { - req.Header.Add(HeaderProtocolVersion, p) - } - - w := newResponseWriter() - negotiated, err := Handshake(req, w, test.serverProtocols, defaultProtocol) - - // verify negotiated protocol - if e, a := test.expectedProtocol, negotiated; e != a { - t.Errorf("%s: protocol: expected %q, got %q", name, e, a) - } - - if test.expectError { - if err == nil { - t.Errorf("%s: expected error but did not get one", name) - } - if w.statusCode == nil { - t.Errorf("%s: expected w.statusCode to be set", name) - } else if e, a := http.StatusForbidden, *w.statusCode; e != a { - t.Errorf("%s: w.statusCode: expected %d, got %d", name, e, a) - } - if e, a := test.serverProtocols, w.Header()[HeaderAcceptedProtocolVersions]; !reflect.DeepEqual(e, a) { - t.Errorf("%s: accepted server protocols: expected %v, got %v", name, e, a) - } - continue - } - if !test.expectError && err != nil { - t.Errorf("%s: unexpected error: %v", name, err) - continue - } - if w.statusCode != nil { - t.Errorf("%s: unexpected non-nil w.statusCode: %d", w.statusCode) - } - - // verify response headers - if e, a := []string{test.expectedProtocol}, w.Header()[HeaderProtocolVersion]; !reflect.DeepEqual(e, a) { - t.Errorf("%s: protocol response header: expected %v, got %v", name, e, a) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/connection.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/connection.go deleted file mode 100644 index 884c6e203..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/connection.go +++ /dev/null @@ -1,141 +0,0 @@ -/* -Copyright 2015 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 spdy - -import ( - "net" - "net/http" - "sync" - "time" - - "github.com/docker/spdystream" - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/util/httpstream" -) - -// connection maintains state about a spdystream.Connection and its associated -// streams. -type connection struct { - conn *spdystream.Connection - streams []httpstream.Stream - streamLock sync.Mutex - newStreamHandler httpstream.NewStreamHandler -} - -// NewClientConnection creates a new SPDY client connection. -func NewClientConnection(conn net.Conn) (httpstream.Connection, error) { - spdyConn, err := spdystream.NewConnection(conn, false) - if err != nil { - defer conn.Close() - return nil, err - } - - return newConnection(spdyConn, httpstream.NoOpNewStreamHandler), nil -} - -// NewServerConnection creates a new SPDY server connection. newStreamHandler -// will be invoked when the server receives a newly created stream from the -// client. -func NewServerConnection(conn net.Conn, newStreamHandler httpstream.NewStreamHandler) (httpstream.Connection, error) { - spdyConn, err := spdystream.NewConnection(conn, true) - if err != nil { - defer conn.Close() - return nil, err - } - - return newConnection(spdyConn, newStreamHandler), nil -} - -// newConnection returns a new connection wrapping conn. newStreamHandler -// will be invoked when the server receives a newly created stream from the -// client. -func newConnection(conn *spdystream.Connection, newStreamHandler httpstream.NewStreamHandler) httpstream.Connection { - c := &connection{conn: conn, newStreamHandler: newStreamHandler} - go conn.Serve(c.newSpdyStream) - return c -} - -// createStreamResponseTimeout indicates how long to wait for the other side to -// acknowledge the new stream before timing out. -const createStreamResponseTimeout = 30 * time.Second - -// Close first sends a reset for all of the connection's streams, and then -// closes the underlying spdystream.Connection. -func (c *connection) Close() error { - c.streamLock.Lock() - for _, s := range c.streams { - s.Close() - } - c.streams = make([]httpstream.Stream, 0) - c.streamLock.Unlock() - - return c.conn.Close() -} - -// CreateStream creates a new stream with the specified headers and registers -// it with the connection. -func (c *connection) CreateStream(headers http.Header) (httpstream.Stream, error) { - stream, err := c.conn.CreateStream(headers, nil, false) - if err != nil { - return nil, err - } - if err = stream.WaitTimeout(createStreamResponseTimeout); err != nil { - return nil, err - } - - c.registerStream(stream) - return stream, nil -} - -// registerStream adds the stream s to the connection's list of streams that -// it owns. -func (c *connection) registerStream(s httpstream.Stream) { - c.streamLock.Lock() - c.streams = append(c.streams, s) - c.streamLock.Unlock() -} - -// CloseChan returns a channel that, when closed, indicates that the underlying -// spdystream.Connection has been closed. -func (c *connection) CloseChan() <-chan bool { - return c.conn.CloseChan() -} - -// newSpdyStream is the internal new stream handler used by spdystream.Connection.Serve. -// It calls connection's newStreamHandler, giving it the opportunity to accept or reject -// the stream. If newStreamHandler returns an error, the stream is rejected. If not, the -// stream is accepted and registered with the connection. -func (c *connection) newSpdyStream(stream *spdystream.Stream) { - replySent := make(chan struct{}) - err := c.newStreamHandler(stream, replySent) - rejectStream := (err != nil) - if rejectStream { - glog.Warningf("Stream rejected: %v", err) - stream.Reset() - return - } - - c.registerStream(stream) - stream.SendReply(http.Header{}, rejectStream) - close(replySent) -} - -// SetIdleTimeout sets the amount of time the connection may remain idle before -// it is automatically closed. -func (c *connection) SetIdleTimeout(timeout time.Duration) { - c.conn.SetIdleTimeout(timeout) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/roundtripper.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/roundtripper.go deleted file mode 100644 index a054d30c2..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/roundtripper.go +++ /dev/null @@ -1,229 +0,0 @@ -/* -Copyright 2015 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 spdy - -import ( - "bufio" - "crypto/tls" - "fmt" - "io/ioutil" - "net" - "net/http" - "net/http/httputil" - "net/url" - "strings" - - "k8s.io/kubernetes/pkg/api" - apierrors "k8s.io/kubernetes/pkg/api/errors" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/util/httpstream" - "k8s.io/kubernetes/third_party/golang/netutil" -) - -// SpdyRoundTripper knows how to upgrade an HTTP request to one that supports -// multiplexed streams. After RoundTrip() is invoked, Conn will be set -// and usable. SpdyRoundTripper implements the UpgradeRoundTripper interface. -type SpdyRoundTripper struct { - //tlsConfig holds the TLS configuration settings to use when connecting - //to the remote server. - tlsConfig *tls.Config - - /* TODO according to http://golang.org/pkg/net/http/#RoundTripper, a RoundTripper - must be safe for use by multiple concurrent goroutines. If this is absolutely - necessary, we could keep a map from http.Request to net.Conn. In practice, - a client will create an http.Client, set the transport to a new insteace of - SpdyRoundTripper, and use it a single time, so this hopefully won't be an issue. - */ - // conn is the underlying network connection to the remote server. - conn net.Conn - - // Dialer is the dialer used to connect. Used if non-nil. - Dialer *net.Dialer -} - -// NewRoundTripper creates a new SpdyRoundTripper that will use -// the specified tlsConfig. -func NewRoundTripper(tlsConfig *tls.Config) httpstream.UpgradeRoundTripper { - return NewSpdyRoundTripper(tlsConfig) -} - -// NewSpdyRoundTripper creates a new SpdyRoundTripper that will use -// the specified tlsConfig. This function is mostly meant for unit tests. -func NewSpdyRoundTripper(tlsConfig *tls.Config) *SpdyRoundTripper { - return &SpdyRoundTripper{tlsConfig: tlsConfig} -} - -// dial dials the host specified by req, using TLS if appropriate, optionally -// using a proxy server if one is configured via environment variables. -func (s *SpdyRoundTripper) dial(req *http.Request) (net.Conn, error) { - proxyURL, err := http.ProxyFromEnvironment(req) - if err != nil { - return nil, err - } - - if proxyURL == nil { - return s.dialWithoutProxy(req.URL) - } - - // ensure we use a canonical host with proxyReq - targetHost := netutil.CanonicalAddr(req.URL) - - // proxying logic adapted from http://blog.h6t.eu/post/74098062923/golang-websocket-with-http-proxy-support - proxyReq := http.Request{ - Method: "CONNECT", - URL: &url.URL{}, - Host: targetHost, - } - - proxyDialConn, err := s.dialWithoutProxy(proxyURL) - if err != nil { - return nil, err - } - - proxyClientConn := httputil.NewProxyClientConn(proxyDialConn, nil) - _, err = proxyClientConn.Do(&proxyReq) - if err != nil && err != httputil.ErrPersistEOF { - return nil, err - } - - rwc, _ := proxyClientConn.Hijack() - - if req.URL.Scheme != "https" { - return rwc, nil - } - - host, _, err := net.SplitHostPort(req.URL.Host) - if err != nil { - return nil, err - } - - if len(s.tlsConfig.ServerName) == 0 { - s.tlsConfig.ServerName = host - } - - tlsConn := tls.Client(rwc, s.tlsConfig) - - // need to manually call Handshake() so we can call VerifyHostname() below - if err := tlsConn.Handshake(); err != nil { - return nil, err - } - - if err := tlsConn.VerifyHostname(host); err != nil { - return nil, err - } - - return tlsConn, nil -} - -// dialWithoutProxy dials the host specified by url, using TLS if appropriate. -func (s *SpdyRoundTripper) dialWithoutProxy(url *url.URL) (net.Conn, error) { - dialAddr := netutil.CanonicalAddr(url) - - if url.Scheme == "http" { - if s.Dialer == nil { - return net.Dial("tcp", dialAddr) - } else { - return s.Dialer.Dial("tcp", dialAddr) - } - } - - // TODO validate the TLSClientConfig is set up? - var conn *tls.Conn - var err error - if s.Dialer == nil { - conn, err = tls.Dial("tcp", dialAddr, s.tlsConfig) - } else { - conn, err = tls.DialWithDialer(s.Dialer, "tcp", dialAddr, s.tlsConfig) - } - if err != nil { - return nil, err - } - - // Return if we were configured to skip validation - if s.tlsConfig != nil && s.tlsConfig.InsecureSkipVerify { - return conn, nil - } - - host, _, err := net.SplitHostPort(dialAddr) - if err != nil { - return nil, err - } - err = conn.VerifyHostname(host) - if err != nil { - return nil, err - } - - return conn, nil -} - -// RoundTrip executes the Request and upgrades it. After a successful upgrade, -// clients may call SpdyRoundTripper.Connection() to retrieve the upgraded -// connection. -func (s *SpdyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - // TODO what's the best way to clone the request? - r := *req - req = &r - req.Header.Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade) - req.Header.Add(httpstream.HeaderUpgrade, HeaderSpdy31) - - conn, err := s.dial(req) - if err != nil { - return nil, err - } - - err = req.Write(conn) - if err != nil { - return nil, err - } - - resp, err := http.ReadResponse(bufio.NewReader(conn), req) - if err != nil { - return nil, err - } - - s.conn = conn - - return resp, nil -} - -// NewConnection validates the upgrade response, creating and returning a new -// httpstream.Connection if there were no errors. -func (s *SpdyRoundTripper) NewConnection(resp *http.Response) (httpstream.Connection, error) { - connectionHeader := strings.ToLower(resp.Header.Get(httpstream.HeaderConnection)) - upgradeHeader := strings.ToLower(resp.Header.Get(httpstream.HeaderUpgrade)) - if (resp.StatusCode != http.StatusSwitchingProtocols) || !strings.Contains(connectionHeader, strings.ToLower(httpstream.HeaderUpgrade)) || !strings.Contains(upgradeHeader, strings.ToLower(HeaderSpdy31)) { - defer resp.Body.Close() - responseError := "" - responseErrorBytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - responseError = "unable to read error from server response" - } else { - // TODO: I don't belong here, I should be abstracted from this class - if obj, _, err := api.Codecs.UniversalDecoder().Decode(responseErrorBytes, nil, &unversioned.Status{}); err == nil { - if status, ok := obj.(*unversioned.Status); ok { - return nil, &apierrors.StatusError{ErrStatus: *status} - } - } - responseError = string(responseErrorBytes) - responseError = strings.TrimSpace(responseError) - } - - return nil, fmt.Errorf("unable to upgrade connection: %s", responseError) - } - - return NewClientConnection(s.conn) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/roundtripper_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/roundtripper_test.go deleted file mode 100644 index 7ca1d42ad..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/roundtripper_test.go +++ /dev/null @@ -1,251 +0,0 @@ -/* -Copyright 2015 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 spdy - -import ( - "crypto/tls" - "crypto/x509" - "io" - "net/http" - "net/http/httptest" - "testing" - - "k8s.io/kubernetes/pkg/util/httpstream" -) - -func TestRoundTripAndNewConnection(t *testing.T) { - - localhostPool := x509.NewCertPool() - if !localhostPool.AppendCertsFromPEM(localhostCert) { - t.Errorf("error setting up localhostCert pool") - } - - testCases := map[string]struct { - serverFunc func(http.Handler) *httptest.Server - clientTLS *tls.Config - serverConnectionHeader string - serverUpgradeHeader string - serverStatusCode int - shouldError bool - }{ - "no headers": { - serverFunc: httptest.NewServer, - serverConnectionHeader: "", - serverUpgradeHeader: "", - serverStatusCode: http.StatusSwitchingProtocols, - shouldError: true, - }, - "no upgrade header": { - serverFunc: httptest.NewServer, - serverConnectionHeader: "Upgrade", - serverUpgradeHeader: "", - serverStatusCode: http.StatusSwitchingProtocols, - shouldError: true, - }, - "no connection header": { - serverFunc: httptest.NewServer, - serverConnectionHeader: "", - serverUpgradeHeader: "SPDY/3.1", - serverStatusCode: http.StatusSwitchingProtocols, - shouldError: true, - }, - "no switching protocol status code": { - serverFunc: httptest.NewServer, - serverConnectionHeader: "Upgrade", - serverUpgradeHeader: "SPDY/3.1", - serverStatusCode: http.StatusForbidden, - shouldError: true, - }, - "http": { - serverFunc: httptest.NewServer, - serverConnectionHeader: "Upgrade", - serverUpgradeHeader: "SPDY/3.1", - serverStatusCode: http.StatusSwitchingProtocols, - shouldError: false, - }, - "https (invalid hostname + InsecureSkipVerify)": { - serverFunc: func(h http.Handler) *httptest.Server { - cert, err := tls.X509KeyPair(exampleCert, exampleKey) - if err != nil { - t.Errorf("https (invalid hostname): proxy_test: %v", err) - } - ts := httptest.NewUnstartedServer(h) - ts.TLS = &tls.Config{ - Certificates: []tls.Certificate{cert}, - } - ts.StartTLS() - return ts - }, - clientTLS: &tls.Config{InsecureSkipVerify: true}, - serverConnectionHeader: "Upgrade", - serverUpgradeHeader: "SPDY/3.1", - serverStatusCode: http.StatusSwitchingProtocols, - shouldError: false, - }, - "https (valid hostname + RootCAs)": { - serverFunc: func(h http.Handler) *httptest.Server { - cert, err := tls.X509KeyPair(localhostCert, localhostKey) - if err != nil { - t.Errorf("https (valid hostname): proxy_test: %v", err) - } - ts := httptest.NewUnstartedServer(h) - ts.TLS = &tls.Config{ - Certificates: []tls.Certificate{cert}, - } - ts.StartTLS() - return ts - }, - clientTLS: &tls.Config{RootCAs: localhostPool}, - serverConnectionHeader: "Upgrade", - serverUpgradeHeader: "SPDY/3.1", - serverStatusCode: http.StatusSwitchingProtocols, - shouldError: false, - }, - } - - for k, testCase := range testCases { - server := testCase.serverFunc(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - if testCase.shouldError { - if e, a := httpstream.HeaderUpgrade, req.Header.Get(httpstream.HeaderConnection); e != a { - t.Fatalf("%s: Expected connection=upgrade header, got '%s", k, a) - } - - w.Header().Set(httpstream.HeaderConnection, testCase.serverConnectionHeader) - w.Header().Set(httpstream.HeaderUpgrade, testCase.serverUpgradeHeader) - w.WriteHeader(testCase.serverStatusCode) - - return - } - - streamCh := make(chan httpstream.Stream) - - responseUpgrader := NewResponseUpgrader() - spdyConn := responseUpgrader.UpgradeResponse(w, req, func(s httpstream.Stream, replySent <-chan struct{}) error { - streamCh <- s - return nil - }) - if spdyConn == nil { - t.Fatalf("%s: unexpected nil spdyConn", k) - } - defer spdyConn.Close() - - stream := <-streamCh - io.Copy(stream, stream) - })) - // TODO: Uncomment when fix #19254 - // defer server.Close() - - req, err := http.NewRequest("GET", server.URL, nil) - if err != nil { - t.Fatalf("%s: Error creating request: %s", k, err) - } - - spdyTransport := NewRoundTripper(testCase.clientTLS) - client := &http.Client{Transport: spdyTransport} - - resp, err := client.Do(req) - if err != nil { - t.Fatalf("%s: unexpected error from client.Do: %s", k, err) - } - - conn, err := spdyTransport.NewConnection(resp) - haveErr := err != nil - if e, a := testCase.shouldError, haveErr; e != a { - t.Fatalf("%s: shouldError=%t, got %t: %v", k, e, a, err) - } - if testCase.shouldError { - continue - } - defer conn.Close() - - if resp.StatusCode != http.StatusSwitchingProtocols { - t.Fatalf("%s: expected http 101 switching protocols, got %d", k, resp.StatusCode) - } - - stream, err := conn.CreateStream(http.Header{}) - if err != nil { - t.Fatalf("%s: error creating client stream: %s", k, err) - } - - n, err := stream.Write([]byte("hello")) - if err != nil { - t.Fatalf("%s: error writing to stream: %s", k, err) - } - if n != 5 { - t.Fatalf("%s: Expected to write 5 bytes, but actually wrote %d", k, n) - } - - b := make([]byte, 5) - n, err = stream.Read(b) - if err != nil { - t.Fatalf("%s: error reading from stream: %s", k, err) - } - if n != 5 { - t.Fatalf("%s: Expected to read 5 bytes, but actually read %d", k, n) - } - if e, a := "hello", string(b[0:n]); e != a { - t.Fatalf("%s: expected '%s', got '%s'", k, e, a) - } - } -} - -// exampleCert was generated from crypto/tls/generate_cert.go with the following command: -// go run generate_cert.go --rsa-bits 512 --host example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h -var exampleCert = []byte(`-----BEGIN CERTIFICATE----- -MIIBcjCCAR6gAwIBAgIQBOUTYowZaENkZi0faI9DgTALBgkqhkiG9w0BAQswEjEQ -MA4GA1UEChMHQWNtZSBDbzAgFw03MDAxMDEwMDAwMDBaGA8yMDg0MDEyOTE2MDAw -MFowEjEQMA4GA1UEChMHQWNtZSBDbzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCZ -xfR3sgeHBraGFfF/24tTn4PRVAHOf2UOOxSQRs+aYjNqimFqf/SRIblQgeXdBJDR -gVK5F1Js2zwlehw0bHxRAgMBAAGjUDBOMA4GA1UdDwEB/wQEAwIApDATBgNVHSUE -DDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MBYGA1UdEQQPMA2CC2V4YW1w -bGUuY29tMAsGCSqGSIb3DQEBCwNBAI/mfBB8dm33IpUl+acSyWfL6gX5Wc0FFyVj -dKeesE1XBuPX1My/rzU6Oy/YwX7LOL4FaeNUS6bbL4axSLPKYSs= ------END CERTIFICATE-----`) - -var exampleKey = []byte(`-----BEGIN RSA PRIVATE KEY----- -MIIBOgIBAAJBAJnF9HeyB4cGtoYV8X/bi1Ofg9FUAc5/ZQ47FJBGz5piM2qKYWp/ -9JEhuVCB5d0EkNGBUrkXUmzbPCV6HDRsfFECAwEAAQJBAJLH9yPuButniACTn5L5 -IJQw1mWQt6zBw9eCo41YWkA0866EgjC53aPZaRjXMp0uNJGdIsys2V5rCOOLWN2C -ODECIQDICHsi8QQQ9wpuJy8X5l8MAfxHL+DIqI84wQTeVM91FQIhAMTME8A18/7h -1Ad6drdnxAkuC0tX6Sx0LDozrmen+HFNAiAlcEDrt0RVkIcpOrg7tuhPLQf0oudl -Zvb3Xlj069awSQIgcT15E/43w2+RASifzVNhQ2MCTr1sSA8lL+xzK+REmnUCIBhQ -j4139pf8Re1J50zBxS/JlQfgDQi9sO9pYeiHIxNs ------END RSA PRIVATE KEY-----`) - -// localhostCert was generated from crypto/tls/generate_cert.go with the following command: -// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h -var localhostCert = []byte(`-----BEGIN CERTIFICATE----- -MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD -bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj -bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBAN55NcYKZeInyTuhcCwFMhDHCmwa -IUSdtXdcbItRB/yfXGBhiex00IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEA -AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud -EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA -AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAAoQn/ytgqpiLcZu9XKbCJsJcvkgk -Se6AbGXgSlq+ZCEVo0qIwSgeBqmsJxUu7NCSOwVJLYNEBO2DtIxoYVk+MA== ------END CERTIFICATE-----`) - -// localhostKey is the private key for localhostCert. -var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY----- -MIIBPAIBAAJBAN55NcYKZeInyTuhcCwFMhDHCmwaIUSdtXdcbItRB/yfXGBhiex0 -0IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEAAQJBAQdUx66rfh8sYsgfdcvV -NoafYpnEcB5s4m/vSVe6SU7dCK6eYec9f9wpT353ljhDUHq3EbmE4foNzJngh35d -AekCIQDhRQG5Li0Wj8TM4obOnnXUXf1jRv0UkzE9AHWLG5q3AwIhAPzSjpYUDjVW -MCUXgckTpKCuGwbJk7424Nb8bLzf3kllAiA5mUBgjfr/WtFSJdWcPQ4Zt9KTMNKD -EUO0ukpTwEIl6wIhAMbGqZK3zAAFdq8DD2jPx+UJXnh0rnOkZBzDtJ6/iN69AiEA -1Aq8MJgTaYsDQWyU/hDq5YkDJc9e9DSCvUIzqxQWMQE= ------END RSA PRIVATE KEY-----`) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/upgrade.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/upgrade.go deleted file mode 100644 index b7d126a5d..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/upgrade.go +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright 2015 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 spdy - -import ( - "fmt" - "net/http" - "strings" - - "k8s.io/kubernetes/pkg/util/httpstream" - "k8s.io/kubernetes/pkg/util/runtime" -) - -const HeaderSpdy31 = "SPDY/3.1" - -// responseUpgrader knows how to upgrade HTTP responses. It -// implements the httpstream.ResponseUpgrader interface. -type responseUpgrader struct { -} - -// NewResponseUpgrader returns a new httpstream.ResponseUpgrader that is -// capable of upgrading HTTP responses using SPDY/3.1 via the -// spdystream package. -func NewResponseUpgrader() httpstream.ResponseUpgrader { - return responseUpgrader{} -} - -// UpgradeResponse upgrades an HTTP response to one that supports multiplexed -// streams. newStreamHandler will be called synchronously whenever the -// other end of the upgraded connection creates a new stream. -func (u responseUpgrader) UpgradeResponse(w http.ResponseWriter, req *http.Request, newStreamHandler httpstream.NewStreamHandler) httpstream.Connection { - connectionHeader := strings.ToLower(req.Header.Get(httpstream.HeaderConnection)) - upgradeHeader := strings.ToLower(req.Header.Get(httpstream.HeaderUpgrade)) - if !strings.Contains(connectionHeader, strings.ToLower(httpstream.HeaderUpgrade)) || !strings.Contains(upgradeHeader, strings.ToLower(HeaderSpdy31)) { - w.WriteHeader(http.StatusBadRequest) - fmt.Fprintf(w, "unable to upgrade: missing upgrade headers in request: %#v", req.Header) - return nil - } - - hijacker, ok := w.(http.Hijacker) - if !ok { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "unable to upgrade: unable to hijack response") - return nil - } - - w.Header().Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade) - w.Header().Add(httpstream.HeaderUpgrade, HeaderSpdy31) - w.WriteHeader(http.StatusSwitchingProtocols) - - conn, _, err := hijacker.Hijack() - if err != nil { - runtime.HandleError(fmt.Errorf("unable to upgrade: error hijacking response: %v", err)) - return nil - } - - spdyConn, err := NewServerConnection(conn, newStreamHandler) - if err != nil { - runtime.HandleError(fmt.Errorf("unable to upgrade: error creating SPDY server connection: %v", err)) - return nil - } - - return spdyConn -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/upgrade_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/upgrade_test.go deleted file mode 100644 index 2ddfd5a59..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/httpstream/spdy/upgrade_test.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright 2015 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 spdy - -import ( - "net/http" - "net/http/httptest" - "testing" -) - -func TestUpgradeResponse(t *testing.T) { - testCases := []struct { - connectionHeader string - upgradeHeader string - shouldError bool - }{ - { - connectionHeader: "", - upgradeHeader: "", - shouldError: true, - }, - { - connectionHeader: "Upgrade", - upgradeHeader: "", - shouldError: true, - }, - { - connectionHeader: "", - upgradeHeader: "SPDY/3.1", - shouldError: true, - }, - { - connectionHeader: "Upgrade", - upgradeHeader: "SPDY/3.1", - shouldError: false, - }, - } - - for i, testCase := range testCases { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - upgrader := NewResponseUpgrader() - conn := upgrader.UpgradeResponse(w, req, nil) - haveErr := conn == nil - if e, a := testCase.shouldError, haveErr; e != a { - t.Fatalf("%d: expected shouldErr=%t, got %t", i, testCase.shouldError, haveErr) - } - if haveErr { - return - } - if conn == nil { - t.Fatalf("%d: unexpected nil conn", i) - } - defer conn.Close() - })) - // TODO: Uncomment when fix #19254 - // defer server.Close() - - req, err := http.NewRequest("GET", server.URL, nil) - if err != nil { - t.Fatalf("%d: error creating request: %s", i, err) - } - - req.Header.Set("Connection", testCase.connectionHeader) - req.Header.Set("Upgrade", testCase.upgradeHeader) - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - t.Fatalf("%d: unexpected non-nil err from client.Do: %s", i, err) - } - - if testCase.shouldError { - continue - } - - if resp.StatusCode != http.StatusSwitchingProtocols { - t.Fatalf("%d: expected status 101 switching protocols, got %d", i, resp.StatusCode) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/integer/integer_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/integer/integer_test.go deleted file mode 100644 index 0f8856738..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/integer/integer_test.go +++ /dev/null @@ -1,143 +0,0 @@ -/* -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 integer - -import "testing" - -func TestIntMax(t *testing.T) { - tests := []struct { - nums []int - expectedMax int - }{ - { - nums: []int{-1, 0}, - expectedMax: 0, - }, - { - nums: []int{-1, -2}, - expectedMax: -1, - }, - { - nums: []int{0, 1}, - expectedMax: 1, - }, - { - nums: []int{1, 2}, - expectedMax: 2, - }, - } - - for i, test := range tests { - t.Logf("executing scenario %d", i) - if max := IntMax(test.nums[0], test.nums[1]); max != test.expectedMax { - t.Errorf("expected %v, got %v", test.expectedMax, max) - } - } -} - -func TestIntMin(t *testing.T) { - tests := []struct { - nums []int - expectedMin int - }{ - { - nums: []int{-1, 0}, - expectedMin: -1, - }, - { - nums: []int{-1, -2}, - expectedMin: -2, - }, - { - nums: []int{0, 1}, - expectedMin: 0, - }, - { - nums: []int{1, 2}, - expectedMin: 1, - }, - } - - for i, test := range tests { - t.Logf("executing scenario %d", i) - if min := IntMin(test.nums[0], test.nums[1]); min != test.expectedMin { - t.Errorf("expected %v, got %v", test.expectedMin, min) - } - } -} - -func TestInt64Max(t *testing.T) { - tests := []struct { - nums []int64 - expectedMax int64 - }{ - { - nums: []int64{-1, 0}, - expectedMax: 0, - }, - { - nums: []int64{-1, -2}, - expectedMax: -1, - }, - { - nums: []int64{0, 1}, - expectedMax: 1, - }, - { - nums: []int64{1, 2}, - expectedMax: 2, - }, - } - - for i, test := range tests { - t.Logf("executing scenario %d", i) - if max := Int64Max(test.nums[0], test.nums[1]); max != test.expectedMax { - t.Errorf("expected %v, got %v", test.expectedMax, max) - } - } -} - -func TestInt64Min(t *testing.T) { - tests := []struct { - nums []int64 - expectedMin int64 - }{ - { - nums: []int64{-1, 0}, - expectedMin: -1, - }, - { - nums: []int64{-1, -2}, - expectedMin: -2, - }, - { - nums: []int64{0, 1}, - expectedMin: 0, - }, - { - nums: []int64{1, 2}, - expectedMin: 1, - }, - } - - for i, test := range tests { - t.Logf("executing scenario %d", i) - if min := Int64Min(test.nums[0], test.nums[1]); min != test.expectedMin { - t.Errorf("expected %v, got %v", test.expectedMin, min) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/intstr/intstr.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/intstr/intstr.go index 05b572cf5..aa3cde1fa 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/intstr/intstr.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/intstr/intstr.go @@ -116,13 +116,17 @@ func (intstr *IntOrString) Fuzz(c fuzz.Continue) { } } -func GetValueFromIntOrPercent(intOrPercent *IntOrString, total int) (int, error) { +func GetValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool) (int, error) { value, isPercent, err := getIntOrPercentValue(intOrPercent) if err != nil { return 0, fmt.Errorf("invalid value for IntOrString: %v", err) } if isPercent { - value = int(math.Ceil(float64(value) * (float64(total)) / 100)) + if roundUp { + value = int(math.Ceil(float64(value) * (float64(total)) / 100)) + } else { + value = int(math.Floor(float64(value) * (float64(total)) / 100)) + } } return value, nil } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/intstr/intstr_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/intstr/intstr_test.go deleted file mode 100644 index fb36f9d66..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/intstr/intstr_test.go +++ /dev/null @@ -1,160 +0,0 @@ -/* -Copyright 2014 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 intstr - -import ( - "encoding/json" - "reflect" - "testing" - - "github.com/ghodss/yaml" -) - -func TestFromInt(t *testing.T) { - i := FromInt(93) - if i.Type != Int || i.IntVal != 93 { - t.Errorf("Expected IntVal=93, got %+v", i) - } -} - -func TestFromString(t *testing.T) { - i := FromString("76") - if i.Type != String || i.StrVal != "76" { - t.Errorf("Expected StrVal=\"76\", got %+v", i) - } -} - -type IntOrStringHolder struct { - IOrS IntOrString `json:"val"` -} - -func TestIntOrStringUnmarshalJSON(t *testing.T) { - cases := []struct { - input string - result IntOrString - }{ - {"{\"val\": 123}", FromInt(123)}, - {"{\"val\": \"123\"}", FromString("123")}, - } - - for _, c := range cases { - var result IntOrStringHolder - if err := json.Unmarshal([]byte(c.input), &result); err != nil { - t.Errorf("Failed to unmarshal input '%v': %v", c.input, err) - } - if result.IOrS != c.result { - t.Errorf("Failed to unmarshal input '%v': expected %+v, got %+v", c.input, c.result, result) - } - } -} - -func TestIntOrStringMarshalJSON(t *testing.T) { - cases := []struct { - input IntOrString - result string - }{ - {FromInt(123), "{\"val\":123}"}, - {FromString("123"), "{\"val\":\"123\"}"}, - } - - for _, c := range cases { - input := IntOrStringHolder{c.input} - result, err := json.Marshal(&input) - if err != nil { - t.Errorf("Failed to marshal input '%v': %v", input, err) - } - if string(result) != c.result { - t.Errorf("Failed to marshal input '%v': expected: %+v, got %q", input, c.result, string(result)) - } - } -} - -func TestIntOrStringMarshalJSONUnmarshalYAML(t *testing.T) { - cases := []struct { - input IntOrString - }{ - {FromInt(123)}, - {FromString("123")}, - } - - for _, c := range cases { - input := IntOrStringHolder{c.input} - jsonMarshalled, err := json.Marshal(&input) - if err != nil { - t.Errorf("1: Failed to marshal input: '%v': %v", input, err) - } - - var result IntOrStringHolder - err = yaml.Unmarshal(jsonMarshalled, &result) - if err != nil { - t.Errorf("2: Failed to unmarshal '%+v': %v", string(jsonMarshalled), err) - } - - if !reflect.DeepEqual(input, result) { - t.Errorf("3: Failed to marshal input '%+v': got %+v", input, result) - } - } -} - -func TestGetValueFromIntOrPercent(t *testing.T) { - tests := []struct { - input IntOrString - total int - expectErr bool - expectVal int - }{ - { - input: FromInt(123), - expectErr: false, - expectVal: 123, - }, - { - input: FromString("90%"), - total: 100, - expectErr: false, - expectVal: 90, - }, - { - input: FromString("%"), - expectErr: true, - }, - { - input: FromString("90#"), - expectErr: true, - }, - { - input: FromString("#%"), - expectErr: true, - }, - } - - for i, test := range tests { - t.Logf("test case %d", i) - value, err := GetValueFromIntOrPercent(&test.input, test.total) - if test.expectErr && err == nil { - t.Errorf("expected error, but got none") - continue - } - if !test.expectErr && err != nil { - t.Errorf("unexpected err: %v", err) - continue - } - if test.expectVal != value { - t.Errorf("expected %v, but got %v", test.expectErr, value) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/io/io.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/io/io.go deleted file mode 100644 index d600c08c1..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/io/io.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright 2015 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 io - -import ( - "fmt" - "io/ioutil" - "os" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apimachinery/registered" - "k8s.io/kubernetes/pkg/runtime" -) - -// LoadPodFromFile will read, decode, and return a Pod from a file. -func LoadPodFromFile(filePath string) (*api.Pod, error) { - if filePath == "" { - return nil, fmt.Errorf("file path not specified") - } - podDef, err := ioutil.ReadFile(filePath) - if err != nil { - return nil, fmt.Errorf("failed to read file path %s: %+v", filePath, err) - } - if len(podDef) == 0 { - return nil, fmt.Errorf("file was empty: %s", filePath) - } - pod := &api.Pod{} - - codec := api.Codecs.LegacyCodec(registered.GroupOrDie(api.GroupName).GroupVersion) - if err := runtime.DecodeInto(codec, podDef, pod); err != nil { - return nil, fmt.Errorf("failed decoding file: %v", err) - } - return pod, nil -} - -// SavePodToFile will encode and save a pod to a given path & permissions -func SavePodToFile(pod *api.Pod, filePath string, perm os.FileMode) error { - if filePath == "" { - return fmt.Errorf("file path not specified") - } - codec := api.Codecs.LegacyCodec(registered.GroupOrDie(api.GroupName).GroupVersion) - data, err := runtime.Encode(codec, pod) - if err != nil { - return fmt.Errorf("failed encoding pod: %v", err) - } - return ioutil.WriteFile(filePath, data, perm) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/io/io_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/io/io_test.go deleted file mode 100644 index 1b9d4c67a..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/io/io_test.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2015 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 io_test - -import ( - "fmt" - "os" - "testing" - - "github.com/pborman/uuid" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apimachinery/registered" - "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/io" - utiltesting "k8s.io/kubernetes/pkg/util/testing" - "k8s.io/kubernetes/pkg/volume" -) - -func TestSavePodToFile(t *testing.T) { - pod := volume.NewPersistentVolumeRecyclerPodTemplate() - - // sets all default values on a pod for equality comparison after decoding from file - codec := api.Codecs.LegacyCodec(registered.GroupOrDie(api.GroupName).GroupVersion) - encoded, err := runtime.Encode(codec, pod) - runtime.DecodeInto(codec, encoded, pod) - - tmpDir := utiltesting.MkTmpdirOrDie("kube-io-test") - defer os.RemoveAll(tmpDir) - path := fmt.Sprintf("/%s/kube-io-test-%s", tmpDir, uuid.New()) - - if err := io.SavePodToFile(pod, path, 777); err != nil { - t.Fatalf("failed to save pod to file: %v", err) - } - - podFromFile, err := io.LoadPodFromFile(path) - if err != nil { - t.Fatalf("failed to load pod from file: %v", err) - } - if !api.Semantic.DeepEqual(pod, podFromFile) { - t.Errorf("\nexpected %#v\ngot %#v\n", pod, podFromFile) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/io/writer.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/io/writer.go deleted file mode 100644 index 3046c4d9b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/io/writer.go +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright 2015 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 io - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "os/exec" - - "github.com/golang/glog" -) - -// Writer is an interface which allows to write data to a file. -type Writer interface { - WriteFile(filename string, data []byte, perm os.FileMode) error -} - -// StdWriter implements Writer interface and uses standard libraries -// for writing data to files. -type StdWriter struct { -} - -func (writer *StdWriter) WriteFile(filename string, data []byte, perm os.FileMode) error { - return ioutil.WriteFile(filename, data, perm) -} - -// Alternative implementation of Writer interface that allows writing data to file -// using nsenter command. -// If a program (e.g. kubelet) runs in a container it may want to write data to -// a mounted device. Since in Docker, mount propagation mode is set to private, -// it will not see the mounted device in its own namespace. To work around this -// limitaion one has to first enter hosts namespace (by using 'nsenter') and only -// then write data. -type NsenterWriter struct { -} - -// TODO: should take a writer, not []byte -func (writer *NsenterWriter) WriteFile(filename string, data []byte, perm os.FileMode) error { - cmd := "nsenter" - base_args := []string{ - "--mount=/rootfs/proc/1/ns/mnt", - "--", - } - - echo_args := append(base_args, "sh", "-c", fmt.Sprintf("cat > %s", filename)) - glog.V(5).Infof("Command to write data to file: %v %v", cmd, echo_args) - command := exec.Command(cmd, echo_args...) - command.Stdin = bytes.NewBuffer(data) - outputBytes, err := command.CombinedOutput() - if err != nil { - glog.Errorf("Output from writing to %q: %v", filename, string(outputBytes)) - return err - } - - chmod_args := append(base_args, "chmod", fmt.Sprintf("%o", perm), filename) - glog.V(5).Infof("Command to change permissions to file: %v %v", cmd, chmod_args) - outputBytes, err = exec.Command(cmd, chmod_args...).CombinedOutput() - if err != nil { - glog.Errorf("Output from chmod command: %v", string(outputBytes)) - return err - } - - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/doc.go deleted file mode 100644 index 5824f3b4e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2014 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 iptables provides an interface and implementations for running iptables commands. -package iptables diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/iptables.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/iptables.go deleted file mode 100644 index 801cc8bf7..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/iptables.go +++ /dev/null @@ -1,595 +0,0 @@ -/* -Copyright 2014 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 iptables - -import ( - "fmt" - "io/ioutil" - "os" - "regexp" - "strings" - "sync" - - "github.com/coreos/go-semver/semver" - godbus "github.com/godbus/dbus" - "github.com/golang/glog" - utildbus "k8s.io/kubernetes/pkg/util/dbus" - utilexec "k8s.io/kubernetes/pkg/util/exec" - "k8s.io/kubernetes/pkg/util/sets" -) - -type RulePosition string - -const ( - Prepend RulePosition = "-I" - Append RulePosition = "-A" -) - -// An injectable interface for running iptables commands. Implementations must be goroutine-safe. -type Interface interface { - // GetVersion returns the "X.Y.Z" semver string for iptables. - GetVersion() (string, error) - // EnsureChain checks if the specified chain exists and, if not, creates it. If the chain existed, return true. - EnsureChain(table Table, chain Chain) (bool, error) - // FlushChain clears the specified chain. If the chain did not exist, return error. - FlushChain(table Table, chain Chain) error - // DeleteChain deletes the specified chain. If the chain did not exist, return error. - DeleteChain(table Table, chain Chain) error - // EnsureRule checks if the specified rule is present and, if not, creates it. If the rule existed, return true. - EnsureRule(position RulePosition, table Table, chain Chain, args ...string) (bool, error) - // DeleteRule checks if the specified rule is present and, if so, deletes it. - DeleteRule(table Table, chain Chain, args ...string) error - // IsIpv6 returns true if this is managing ipv6 tables - IsIpv6() bool - // TODO: (BenTheElder) Unit-Test Save/SaveAll, Restore/RestoreAll - // Save calls `iptables-save` for table. - Save(table Table) ([]byte, error) - // SaveAll calls `iptables-save`. - SaveAll() ([]byte, error) - // Restore runs `iptables-restore` passing data through a temporary file. - // table is the Table to restore - // data should be formatted like the output of Save() - // flush sets the presence of the "--noflush" flag. see: FlushFlag - // counters sets the "--counters" flag. see: RestoreCountersFlag - Restore(table Table, data []byte, flush FlushFlag, counters RestoreCountersFlag) error - // RestoreAll is the same as Restore except that no table is specified. - RestoreAll(data []byte, flush FlushFlag, counters RestoreCountersFlag) error - // AddReloadFunc adds a function to call on iptables reload - AddReloadFunc(reloadFunc func()) - // Destroy cleans up resources used by the Interface - Destroy() -} - -type Protocol byte - -const ( - ProtocolIpv4 Protocol = iota + 1 - ProtocolIpv6 -) - -type Table string - -const ( - TableNAT Table = "nat" - TableFilter Table = "filter" -) - -type Chain string - -const ( - ChainPostrouting Chain = "POSTROUTING" - ChainPrerouting Chain = "PREROUTING" - ChainOutput Chain = "OUTPUT" - ChainInput Chain = "INPUT" -) - -const ( - cmdIptablesSave string = "iptables-save" - cmdIptablesRestore string = "iptables-restore" - cmdIptables string = "iptables" - cmdIp6tables string = "ip6tables" -) - -// Option flag for Restore -type RestoreCountersFlag bool - -const RestoreCounters RestoreCountersFlag = true -const NoRestoreCounters RestoreCountersFlag = false - -// Option flag for Flush -type FlushFlag bool - -const FlushTables FlushFlag = true -const NoFlushTables FlushFlag = false - -// Versions of iptables less than this do not support the -C / --check flag -// (test whether a rule exists). -const MinCheckVersion = "1.4.11" - -// Minimum iptables versions supporting the -w and -w2 flags -const MinWaitVersion = "1.4.20" -const MinWait2Version = "1.4.22" - -// runner implements Interface in terms of exec("iptables"). -type runner struct { - mu sync.Mutex - exec utilexec.Interface - dbus utildbus.Interface - protocol Protocol - hasCheck bool - waitFlag []string - - reloadFuncs []func() - signal chan *godbus.Signal -} - -// New returns a new Interface which will exec iptables. -func New(exec utilexec.Interface, dbus utildbus.Interface, protocol Protocol) Interface { - vstring, err := getIptablesVersionString(exec) - if err != nil { - glog.Warningf("Error checking iptables version, assuming version at least %s: %v", MinCheckVersion, err) - vstring = MinCheckVersion - } - runner := &runner{ - exec: exec, - dbus: dbus, - protocol: protocol, - hasCheck: getIptablesHasCheckCommand(vstring), - waitFlag: getIptablesWaitFlag(vstring), - } - runner.connectToFirewallD() - return runner -} - -// Destroy is part of Interface. -func (runner *runner) Destroy() { - if runner.signal != nil { - runner.signal <- nil - } -} - -const ( - firewalldName = "org.fedoraproject.FirewallD1" - firewalldPath = "/org/fedoraproject/FirewallD1" - firewalldInterface = "org.fedoraproject.FirewallD1" -) - -// Connects to D-Bus and listens for FirewallD start/restart. (On non-FirewallD-using -// systems, this is effectively a no-op; we listen for the signals, but they will never be -// emitted, so reload() will never be called.) -func (runner *runner) connectToFirewallD() { - bus, err := runner.dbus.SystemBus() - if err != nil { - glog.V(1).Infof("Could not connect to D-Bus system bus: %s", err) - return - } - - rule := fmt.Sprintf("type='signal',sender='%s',path='%s',interface='%s',member='Reloaded'", firewalldName, firewalldPath, firewalldInterface) - bus.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule) - - rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'", firewalldName) - bus.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule) - - runner.signal = make(chan *godbus.Signal, 10) - bus.Signal(runner.signal) - - go runner.dbusSignalHandler(bus) -} - -// GetVersion returns the version string. -func (runner *runner) GetVersion() (string, error) { - return getIptablesVersionString(runner.exec) -} - -// EnsureChain is part of Interface. -func (runner *runner) EnsureChain(table Table, chain Chain) (bool, error) { - fullArgs := makeFullArgs(table, chain) - - runner.mu.Lock() - defer runner.mu.Unlock() - - out, err := runner.run(opCreateChain, fullArgs) - if err != nil { - if ee, ok := err.(utilexec.ExitError); ok { - if ee.Exited() && ee.ExitStatus() == 1 { - return true, nil - } - } - return false, fmt.Errorf("error creating chain %q: %v: %s", chain, err, out) - } - return false, nil -} - -// FlushChain is part of Interface. -func (runner *runner) FlushChain(table Table, chain Chain) error { - fullArgs := makeFullArgs(table, chain) - - runner.mu.Lock() - defer runner.mu.Unlock() - - out, err := runner.run(opFlushChain, fullArgs) - if err != nil { - return fmt.Errorf("error flushing chain %q: %v: %s", chain, err, out) - } - return nil -} - -// DeleteChain is part of Interface. -func (runner *runner) DeleteChain(table Table, chain Chain) error { - fullArgs := makeFullArgs(table, chain) - - runner.mu.Lock() - defer runner.mu.Unlock() - - // TODO: we could call iptables -S first, ignore the output and check for non-zero return (more like DeleteRule) - out, err := runner.run(opDeleteChain, fullArgs) - if err != nil { - return fmt.Errorf("error deleting chain %q: %v: %s", chain, err, out) - } - return nil -} - -// EnsureRule is part of Interface. -func (runner *runner) EnsureRule(position RulePosition, table Table, chain Chain, args ...string) (bool, error) { - fullArgs := makeFullArgs(table, chain, args...) - - runner.mu.Lock() - defer runner.mu.Unlock() - - exists, err := runner.checkRule(table, chain, args...) - if err != nil { - return false, err - } - if exists { - return true, nil - } - out, err := runner.run(operation(position), fullArgs) - if err != nil { - return false, fmt.Errorf("error appending rule: %v: %s", err, out) - } - return false, nil -} - -// DeleteRule is part of Interface. -func (runner *runner) DeleteRule(table Table, chain Chain, args ...string) error { - fullArgs := makeFullArgs(table, chain, args...) - - runner.mu.Lock() - defer runner.mu.Unlock() - - exists, err := runner.checkRule(table, chain, args...) - if err != nil { - return err - } - if !exists { - return nil - } - out, err := runner.run(opDeleteRule, fullArgs) - if err != nil { - return fmt.Errorf("error deleting rule: %v: %s", err, out) - } - return nil -} - -func (runner *runner) IsIpv6() bool { - return runner.protocol == ProtocolIpv6 -} - -// Save is part of Interface. -func (runner *runner) Save(table Table) ([]byte, error) { - runner.mu.Lock() - defer runner.mu.Unlock() - - // run and return - args := []string{"-t", string(table)} - glog.V(4).Infof("running iptables-save %v", args) - return runner.exec.Command(cmdIptablesSave, args...).CombinedOutput() -} - -// SaveAll is part of Interface. -func (runner *runner) SaveAll() ([]byte, error) { - runner.mu.Lock() - defer runner.mu.Unlock() - - // run and return - glog.V(4).Infof("running iptables-save") - return runner.exec.Command(cmdIptablesSave, []string{}...).CombinedOutput() -} - -// Restore is part of Interface. -func (runner *runner) Restore(table Table, data []byte, flush FlushFlag, counters RestoreCountersFlag) error { - // setup args - args := []string{"-T", string(table)} - return runner.restoreInternal(args, data, flush, counters) -} - -// RestoreAll is part of Interface. -func (runner *runner) RestoreAll(data []byte, flush FlushFlag, counters RestoreCountersFlag) error { - // setup args - args := make([]string, 0) - return runner.restoreInternal(args, data, flush, counters) -} - -// restoreInternal is the shared part of Restore/RestoreAll -func (runner *runner) restoreInternal(args []string, data []byte, flush FlushFlag, counters RestoreCountersFlag) error { - runner.mu.Lock() - defer runner.mu.Unlock() - - if !flush { - args = append(args, "--noflush") - } - if counters { - args = append(args, "--counters") - } - // create temp file through which to pass data - temp, err := ioutil.TempFile("", "kube-temp-iptables-restore-") - if err != nil { - return err - } - // make sure we delete the temp file - defer os.Remove(temp.Name()) - // Put the filename at the end of args. - // NOTE: the filename must be at the end. - // See: https://git.netfilter.org/iptables/commit/iptables-restore.c?id=e6869a8f59d779ff4d5a0984c86d80db70784962 - args = append(args, temp.Name()) - if err != nil { - return err - } - // write data to the file - _, err = temp.Write(data) - temp.Close() - if err != nil { - return err - } - // run the command and return the output or an error including the output and error - glog.V(4).Infof("running iptables-restore %v", args) - b, err := runner.exec.Command(cmdIptablesRestore, args...).CombinedOutput() - if err != nil { - return fmt.Errorf("%v (%s)", err, b) - } - return nil -} - -func (runner *runner) iptablesCommand() string { - if runner.IsIpv6() { - return cmdIp6tables - } else { - return cmdIptables - } -} - -func (runner *runner) run(op operation, args []string) ([]byte, error) { - iptablesCmd := runner.iptablesCommand() - - fullArgs := append(runner.waitFlag, string(op)) - fullArgs = append(fullArgs, args...) - glog.V(4).Infof("running iptables %s %v", string(op), args) - return runner.exec.Command(iptablesCmd, fullArgs...).CombinedOutput() - // Don't log err here - callers might not think it is an error. -} - -// Returns (bool, nil) if it was able to check the existence of the rule, or -// (, error) if the process of checking failed. -func (runner *runner) checkRule(table Table, chain Chain, args ...string) (bool, error) { - if runner.hasCheck { - return runner.checkRuleUsingCheck(makeFullArgs(table, chain, args...)) - } else { - return runner.checkRuleWithoutCheck(table, chain, args...) - } -} - -// Executes the rule check without using the "-C" flag, instead parsing iptables-save. -// Present for compatibility with <1.4.11 versions of iptables. This is full -// of hack and half-measures. We should nix this ASAP. -func (runner *runner) checkRuleWithoutCheck(table Table, chain Chain, args ...string) (bool, error) { - glog.V(1).Infof("running iptables-save -t %s", string(table)) - out, err := runner.exec.Command(cmdIptablesSave, "-t", string(table)).CombinedOutput() - if err != nil { - return false, fmt.Errorf("error checking rule: %v", err) - } - - // Sadly, iptables has inconsistent quoting rules for comments. Just remove all quotes. - // Also, quoted multi-word comments (which are counted as a single arg) - // will be unpacked into multiple args, - // in order to compare against iptables-save output (which will be split at whitespace boundary) - // e.g. a single arg('"this must be before the NodePort rules"') will be unquoted and unpacked into 7 args. - var argsCopy []string - for i := range args { - tmpField := strings.Trim(args[i], "\"") - argsCopy = append(argsCopy, strings.Fields(tmpField)...) - } - argset := sets.NewString(argsCopy...) - - for _, line := range strings.Split(string(out), "\n") { - var fields = strings.Fields(line) - - // Check that this is a rule for the correct chain, and that it has - // the correct number of argument (+2 for "-A ") - if !strings.HasPrefix(line, fmt.Sprintf("-A %s", string(chain))) || len(fields) != len(argsCopy)+2 { - continue - } - - // Sadly, iptables has inconsistent quoting rules for comments. - // Just remove all quotes. - for i := range fields { - fields[i] = strings.Trim(fields[i], "\"") - } - - // TODO: This misses reorderings e.g. "-x foo ! -y bar" will match "! -x foo -y bar" - if sets.NewString(fields...).IsSuperset(argset) { - return true, nil - } - glog.V(5).Infof("DBG: fields is not a superset of args: fields=%v args=%v", fields, args) - } - - return false, nil -} - -// Executes the rule check using the "-C" flag -func (runner *runner) checkRuleUsingCheck(args []string) (bool, error) { - out, err := runner.run(opCheckRule, args) - if err == nil { - return true, nil - } - if ee, ok := err.(utilexec.ExitError); ok { - // iptables uses exit(1) to indicate a failure of the operation, - // as compared to a malformed commandline, for example. - if ee.Exited() && ee.ExitStatus() == 1 { - return false, nil - } - } - return false, fmt.Errorf("error checking rule: %v: %s", err, out) -} - -type operation string - -const ( - opCreateChain operation = "-N" - opFlushChain operation = "-F" - opDeleteChain operation = "-X" - opAppendRule operation = "-A" - opCheckRule operation = "-C" - opDeleteRule operation = "-D" -) - -func makeFullArgs(table Table, chain Chain, args ...string) []string { - return append([]string{string(chain), "-t", string(table)}, args...) -} - -// Checks if iptables has the "-C" flag -func getIptablesHasCheckCommand(vstring string) bool { - minVersion, err := semver.NewVersion(MinCheckVersion) - if err != nil { - glog.Errorf("MinCheckVersion (%s) is not a valid version string: %v", MinCheckVersion, err) - return true - } - version, err := semver.NewVersion(vstring) - if err != nil { - glog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err) - return true - } - if version.LessThan(*minVersion) { - return false - } - return true -} - -// Checks if iptables version has a "wait" flag -func getIptablesWaitFlag(vstring string) []string { - version, err := semver.NewVersion(vstring) - if err != nil { - glog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err) - return nil - } - - minVersion, err := semver.NewVersion(MinWaitVersion) - if err != nil { - glog.Errorf("MinWaitVersion (%s) is not a valid version string: %v", MinWaitVersion, err) - return nil - } - if version.LessThan(*minVersion) { - return nil - } - - minVersion, err = semver.NewVersion(MinWait2Version) - if err != nil { - glog.Errorf("MinWait2Version (%s) is not a valid version string: %v", MinWait2Version, err) - return nil - } - if version.LessThan(*minVersion) { - return []string{"-w"} - } else { - return []string{"-w2"} - } -} - -// getIptablesVersionString runs "iptables --version" to get the version string -// in the form "X.X.X" -func getIptablesVersionString(exec utilexec.Interface) (string, error) { - // this doesn't access mutable state so we don't need to use the interface / runner - bytes, err := exec.Command(cmdIptables, "--version").CombinedOutput() - if err != nil { - return "", err - } - versionMatcher := regexp.MustCompile("v([0-9]+\\.[0-9]+\\.[0-9]+)") - match := versionMatcher.FindStringSubmatch(string(bytes)) - if match == nil { - return "", fmt.Errorf("no iptables version found in string: %s", bytes) - } - return match[1], nil -} - -// goroutine to listen for D-Bus signals -func (runner *runner) dbusSignalHandler(bus utildbus.Connection) { - firewalld := bus.Object(firewalldName, firewalldPath) - - for s := range runner.signal { - if s == nil { - // Unregister - bus.Signal(runner.signal) - return - } - - switch s.Name { - case "org.freedesktop.DBus.NameOwnerChanged": - name := s.Body[0].(string) - new_owner := s.Body[2].(string) - - if name != firewalldName || len(new_owner) == 0 { - continue - } - - // FirewallD startup (specifically the part where it deletes - // all existing iptables rules) may not yet be complete when - // we get this signal, so make a dummy request to it to - // synchronize. - firewalld.Call(firewalldInterface+".getDefaultZone", 0) - - runner.reload() - case firewalldInterface + ".Reloaded": - runner.reload() - } - } -} - -// AddReloadFunc is part of Interface -func (runner *runner) AddReloadFunc(reloadFunc func()) { - runner.reloadFuncs = append(runner.reloadFuncs, reloadFunc) -} - -// runs all reload funcs to re-sync iptables rules -func (runner *runner) reload() { - glog.V(1).Infof("reloading iptables rules") - - for _, f := range runner.reloadFuncs { - f() - } -} - -// IsNotFoundError returns true if the error indicates "not found". It parses -// the error string looking for known values, which is imperfect but works in -// practice. -func IsNotFoundError(err error) bool { - es := err.Error() - if strings.Contains(es, "No such file or directory") { - return true - } - if strings.Contains(es, "No chain/target/match by that name") { - return true - } - return false -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/iptables_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/iptables_test.go deleted file mode 100644 index d5bb34691..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/iptables_test.go +++ /dev/null @@ -1,768 +0,0 @@ -/* -Copyright 2014 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 iptables - -import ( - "strings" - "testing" - "time" - - "k8s.io/kubernetes/pkg/util/dbus" - "k8s.io/kubernetes/pkg/util/exec" - "k8s.io/kubernetes/pkg/util/sets" -) - -func getIptablesCommand(protocol Protocol) string { - if protocol == ProtocolIpv4 { - return cmdIptables - } - if protocol == ProtocolIpv6 { - return cmdIp6tables - } - panic("Unknown protocol") -} - -func testEnsureChain(t *testing.T, protocol Protocol) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // iptables version check - func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, - // Success. - func() ([]byte, error) { return []byte{}, nil }, - // Exists. - func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, - // Failure. - func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 2} }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := New(&fexec, dbus.NewFake(nil, nil), protocol) - defer runner.Destroy() - // Success. - exists, err := runner.EnsureChain(TableNAT, Chain("FOOBAR")) - if err != nil { - t.Errorf("expected success, got %v", err) - } - if exists { - t.Errorf("expected exists = false") - } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) - } - cmd := getIptablesCommand(protocol) - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll(cmd, "-t", "nat", "-N", "FOOBAR") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) - } - // Exists. - exists, err = runner.EnsureChain(TableNAT, Chain("FOOBAR")) - if err != nil { - t.Errorf("expected success, got %v", err) - } - if !exists { - t.Errorf("expected exists = true") - } - // Failure. - _, err = runner.EnsureChain(TableNAT, Chain("FOOBAR")) - if err == nil { - t.Errorf("expected failure") - } -} - -func TestEnsureChainIpv4(t *testing.T) { - testEnsureChain(t, ProtocolIpv4) -} - -func TestEnsureChainIpv6(t *testing.T) { - testEnsureChain(t, ProtocolIpv6) -} - -func TestFlushChain(t *testing.T) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // iptables version check - func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, - // Success. - func() ([]byte, error) { return []byte{}, nil }, - // Failure. - func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) - defer runner.Destroy() - // Success. - err := runner.FlushChain(TableNAT, Chain("FOOBAR")) - if err != nil { - t.Errorf("expected success, got %v", err) - } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) - } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-F", "FOOBAR") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) - } - // Failure. - err = runner.FlushChain(TableNAT, Chain("FOOBAR")) - if err == nil { - t.Errorf("expected failure") - } -} - -func TestDeleteChain(t *testing.T) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // iptables version check - func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, - // Success. - func() ([]byte, error) { return []byte{}, nil }, - // Failure. - func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) - defer runner.Destroy() - // Success. - err := runner.DeleteChain(TableNAT, Chain("FOOBAR")) - if err != nil { - t.Errorf("expected success, got %v", err) - } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) - } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-X", "FOOBAR") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) - } - // Failure. - err = runner.DeleteChain(TableNAT, Chain("FOOBAR")) - if err == nil { - t.Errorf("expected failure") - } -} - -func TestEnsureRuleAlreadyExists(t *testing.T) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // iptables version check - func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, - // Success. - func() ([]byte, error) { return []byte{}, nil }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - // iptables version check - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - // The second Command() call is checking the rule. Success of that exec means "done". - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) - defer runner.Destroy() - exists, err := runner.EnsureRule(Append, TableNAT, ChainOutput, "abc", "123") - if err != nil { - t.Errorf("expected success, got %v", err) - } - if !exists { - t.Errorf("expected exists = true") - } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) - } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) - } -} - -func TestEnsureRuleNew(t *testing.T) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // iptables version check - func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, - // Status 1 on the first call. - func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, - // Success on the second call. - func() ([]byte, error) { return []byte{}, nil }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - // iptables version check - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - // The second Command() call is checking the rule. Failure of that means create it. - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) - defer runner.Destroy() - exists, err := runner.EnsureRule(Append, TableNAT, ChainOutput, "abc", "123") - if err != nil { - t.Errorf("expected success, got %v", err) - } - if exists { - t.Errorf("expected exists = false") - } - if fcmd.CombinedOutputCalls != 3 { - t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) - } - if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-t", "nat", "-A", "OUTPUT", "abc", "123") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) - } -} - -func TestEnsureRuleErrorChecking(t *testing.T) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // iptables version check - func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, - // Status 2 on the first call. - func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 2} }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - // iptables version check - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - // The second Command() call is checking the rule. Failure of that means create it. - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) - defer runner.Destroy() - _, err := runner.EnsureRule(Append, TableNAT, ChainOutput, "abc", "123") - if err == nil { - t.Errorf("expected failure") - } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) - } -} - -func TestEnsureRuleErrorCreating(t *testing.T) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // iptables version check - func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, - // Status 1 on the first call. - func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, - // Status 1 on the second call. - func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - // iptables version check - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - // The second Command() call is checking the rule. Failure of that means create it. - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) - defer runner.Destroy() - _, err := runner.EnsureRule(Append, TableNAT, ChainOutput, "abc", "123") - if err == nil { - t.Errorf("expected failure") - } - if fcmd.CombinedOutputCalls != 3 { - t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) - } -} - -func TestDeleteRuleAlreadyExists(t *testing.T) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // iptables version check - func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, - // Status 1 on the first call. - func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - // iptables version check - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - // The second Command() call is checking the rule. Failure of that exec means "does not exist". - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) - defer runner.Destroy() - err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123") - if err != nil { - t.Errorf("expected success, got %v", err) - } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) - } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) - } -} - -func TestDeleteRuleNew(t *testing.T) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // iptables version check - func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, - // Success on the first call. - func() ([]byte, error) { return []byte{}, nil }, - // Success on the second call. - func() ([]byte, error) { return []byte{}, nil }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - // iptables version check - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - // The second Command() call is checking the rule. Success of that means delete it. - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) - defer runner.Destroy() - err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123") - if err != nil { - t.Errorf("expected success, got %v", err) - } - if fcmd.CombinedOutputCalls != 3 { - t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) - } - if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-t", "nat", "-D", "OUTPUT", "abc", "123") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) - } -} - -func TestDeleteRuleErrorChecking(t *testing.T) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // iptables version check - func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, - // Status 2 on the first call. - func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 2} }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - // iptables version check - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - // The second Command() call is checking the rule. Failure of that means create it. - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) - defer runner.Destroy() - err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123") - if err == nil { - t.Errorf("expected failure") - } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) - } -} - -func TestDeleteRuleErrorCreating(t *testing.T) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // iptables version check - func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, - // Success on the first call. - func() ([]byte, error) { return []byte{}, nil }, - // Status 1 on the second call. - func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - // iptables version check - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - // The second Command() call is checking the rule. Success of that means delete it. - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) - defer runner.Destroy() - err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123") - if err == nil { - t.Errorf("expected failure") - } - if fcmd.CombinedOutputCalls != 3 { - t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) - } -} - -func TestGetIptablesHasCheckCommand(t *testing.T) { - testCases := []struct { - Version string - Err bool - Expected bool - }{ - {"iptables v1.4.7", false, false}, - {"iptables v1.4.11", false, true}, - {"iptables v1.4.19.1", false, true}, - {"iptables v2.0.0", false, true}, - {"total junk", true, false}, - } - - for _, testCase := range testCases { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - func() ([]byte, error) { return []byte(testCase.Version), nil }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - version, err := getIptablesVersionString(&fexec) - if (err != nil) != testCase.Err { - t.Errorf("Expected error: %v, Got error: %v", testCase.Err, err) - } - if err == nil { - check := getIptablesHasCheckCommand(version) - if testCase.Expected != check { - t.Errorf("Expected result: %v, Got result: %v", testCase.Expected, check) - } - } - } -} - -func TestCheckRuleWithoutCheckPresent(t *testing.T) { - iptables_save_output := `# Generated by iptables-save v1.4.7 on Wed Oct 29 14:56:01 2014 -*nat -:PREROUTING ACCEPT [2136997:197881818] -:POSTROUTING ACCEPT [4284525:258542680] -:OUTPUT ACCEPT [5901660:357267963] --A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER -COMMIT -# Completed on Wed Oct 29 14:56:01 2014` - - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // Success. - func() ([]byte, error) { return []byte(iptables_save_output), nil }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - // The first Command() call is checking the rule. Success of that exec means "done". - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := &runner{exec: &fexec} - exists, err := runner.checkRuleWithoutCheck(TableNAT, ChainPrerouting, "-m", "addrtype", "-j", "DOCKER", "--dst-type", "LOCAL") - if err != nil { - t.Errorf("expected success, got %v", err) - } - if !exists { - t.Errorf("expected exists = true") - } - if fcmd.CombinedOutputCalls != 1 { - t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.CombinedOutputCalls) - } - if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("iptables-save", "-t", "nat") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0]) - } -} - -func TestCheckRuleWithoutCheckAbsent(t *testing.T) { - iptables_save_output := `# Generated by iptables-save v1.4.7 on Wed Oct 29 14:56:01 2014 -*nat -:PREROUTING ACCEPT [2136997:197881818] -:POSTROUTING ACCEPT [4284525:258542680] -:OUTPUT ACCEPT [5901660:357267963] --A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER -COMMIT -# Completed on Wed Oct 29 14:56:01 2014` - - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // Success. - func() ([]byte, error) { return []byte(iptables_save_output), nil }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - // The first Command() call is checking the rule. Success of that exec means "done". - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := &runner{exec: &fexec} - exists, err := runner.checkRuleWithoutCheck(TableNAT, ChainPrerouting, "-m", "addrtype", "-j", "DOCKER") - if err != nil { - t.Errorf("expected success, got %v", err) - } - if exists { - t.Errorf("expected exists = false") - } - if fcmd.CombinedOutputCalls != 1 { - t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.CombinedOutputCalls) - } - if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("iptables-save", "-t", "nat") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0]) - } -} - -func TestIptablesWaitFlag(t *testing.T) { - testCases := []struct { - Version string - Result string - }{ - {"0.55.55", ""}, - {"1.0.55", ""}, - {"1.4.19", ""}, - {"1.4.20", "-w"}, - {"1.4.21", "-w"}, - {"1.4.22", "-w2"}, - {"1.5.0", "-w2"}, - {"2.0.0", "-w2"}, - } - - for _, testCase := range testCases { - result := getIptablesWaitFlag(testCase.Version) - if strings.Join(result, "") != testCase.Result { - t.Errorf("For %s expected %v got %v", testCase.Version, testCase.Result, result) - } - } -} - -func TestWaitFlagUnavailable(t *testing.T) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // iptables version check - func() ([]byte, error) { return []byte("iptables v1.4.19"), nil }, - // Success. - func() ([]byte, error) { return []byte{}, nil }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) - defer runner.Destroy() - err := runner.DeleteChain(TableNAT, Chain("FOOBAR")) - if err != nil { - t.Errorf("expected success, got %v", err) - } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) - } - if sets.NewString(fcmd.CombinedOutputLog[1]...).HasAny("-w", "-w2") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) - } -} - -func TestWaitFlagOld(t *testing.T) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // iptables version check - func() ([]byte, error) { return []byte("iptables v1.4.20"), nil }, - // Success. - func() ([]byte, error) { return []byte{}, nil }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) - defer runner.Destroy() - err := runner.DeleteChain(TableNAT, Chain("FOOBAR")) - if err != nil { - t.Errorf("expected success, got %v", err) - } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) - } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-w") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) - } - if sets.NewString(fcmd.CombinedOutputLog[1]...).HasAny("-w2") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) - } -} - -func TestWaitFlagNew(t *testing.T) { - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // iptables version check - func() ([]byte, error) { return []byte("iptables v1.4.22"), nil }, - // Success. - func() ([]byte, error) { return []byte{}, nil }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) - defer runner.Destroy() - err := runner.DeleteChain(TableNAT, Chain("FOOBAR")) - if err != nil { - t.Errorf("expected success, got %v", err) - } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) - } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-w2") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) - } - if sets.NewString(fcmd.CombinedOutputLog[1]...).HasAny("-w") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) - } -} - -func TestReload(t *testing.T) { - dbusConn := dbus.NewFakeConnection() - dbusConn.SetBusObject(func(method string, args ...interface{}) ([]interface{}, error) { return nil, nil }) - dbusConn.AddObject(firewalldName, firewalldPath, func(method string, args ...interface{}) ([]interface{}, error) { return nil, nil }) - fdbus := dbus.NewFake(dbusConn, nil) - - reloaded := make(chan bool, 2) - - fcmd := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - // iptables version check - func() ([]byte, error) { return []byte("iptables v1.4.22"), nil }, - - // first reload - // EnsureChain - func() ([]byte, error) { return []byte{}, nil }, - // EnsureRule abc check - func() ([]byte, error) { return []byte{}, &exec.FakeExitError{Status: 1} }, - // EnsureRule abc - func() ([]byte, error) { return []byte{}, nil }, - - // second reload - // EnsureChain - func() ([]byte, error) { return []byte{}, nil }, - // EnsureRule abc check - func() ([]byte, error) { return []byte{}, &exec.FakeExitError{Status: 1} }, - // EnsureRule abc - func() ([]byte, error) { return []byte{}, nil }, - }, - } - fexec := exec.FakeExec{ - CommandScript: []exec.FakeCommandAction{ - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, - }, - } - - runner := New(&fexec, fdbus, ProtocolIpv4) - defer runner.Destroy() - - runner.AddReloadFunc(func() { - exists, err := runner.EnsureChain(TableNAT, Chain("FOOBAR")) - if err != nil { - t.Errorf("expected success, got %v", err) - } - if exists { - t.Errorf("expected exists = false") - } - reloaded <- true - }) - - runner.AddReloadFunc(func() { - exists, err := runner.EnsureRule(Append, TableNAT, ChainOutput, "abc", "123") - if err != nil { - t.Errorf("expected success, got %v", err) - } - if exists { - t.Errorf("expected exists = false") - } - reloaded <- true - }) - - dbusConn.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", firewalldName, "", ":1.1") - <-reloaded - <-reloaded - - if fcmd.CombinedOutputCalls != 4 { - t.Errorf("expected 4 CombinedOutput() calls total, got %d", fcmd.CombinedOutputCalls) - } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-N", "FOOBAR") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) - } - if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) - } - if !sets.NewString(fcmd.CombinedOutputLog[3]...).HasAll("iptables", "-t", "nat", "-A", "OUTPUT", "abc", "123") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[3]) - } - - go func() { time.Sleep(time.Second / 100); reloaded <- true }() - dbusConn.EmitSignal(firewalldName, firewalldPath, firewalldInterface, "DefaultZoneChanged", "public") - dbusConn.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", "io.k8s.Something", "", ":1.1") - <-reloaded - - if fcmd.CombinedOutputCalls != 4 { - t.Errorf("Incorrect signal caused a reload") - } - - dbusConn.EmitSignal(firewalldName, firewalldPath, firewalldInterface, "Reloaded") - <-reloaded - <-reloaded - - if fcmd.CombinedOutputCalls != 7 { - t.Errorf("expected 7 CombinedOutput() calls total, got %d", fcmd.CombinedOutputCalls) - } - if !sets.NewString(fcmd.CombinedOutputLog[4]...).HasAll("iptables", "-t", "nat", "-N", "FOOBAR") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[4]) - } - if !sets.NewString(fcmd.CombinedOutputLog[5]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[5]) - } - if !sets.NewString(fcmd.CombinedOutputLog[6]...).HasAll("iptables", "-t", "nat", "-A", "OUTPUT", "abc", "123") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[6]) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/testing/fake.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/testing/fake.go deleted file mode 100644 index ac9967727..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/iptables/testing/fake.go +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2015 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 testing - -import "k8s.io/kubernetes/pkg/util/iptables" - -// no-op implemenatation of iptables Interface -type fake struct{} - -func NewFake() *fake { - return &fake{} -} - -func (*fake) GetVersion() (string, error) { - return "0.0.0", nil -} - -func (*fake) EnsureChain(table iptables.Table, chain iptables.Chain) (bool, error) { - return true, nil -} - -func (*fake) FlushChain(table iptables.Table, chain iptables.Chain) error { - return nil -} - -func (*fake) DeleteChain(table iptables.Table, chain iptables.Chain) error { - return nil -} - -func (*fake) EnsureRule(position iptables.RulePosition, table iptables.Table, chain iptables.Chain, args ...string) (bool, error) { - return true, nil -} - -func (*fake) DeleteRule(table iptables.Table, chain iptables.Chain, args ...string) error { - return nil -} - -func (*fake) IsIpv6() bool { - return false -} - -func (*fake) Save(table iptables.Table) ([]byte, error) { - return make([]byte, 0), nil -} - -func (*fake) SaveAll() ([]byte, error) { - return make([]byte, 0), nil -} - -func (*fake) Restore(table iptables.Table, data []byte, flush iptables.FlushFlag, counters iptables.RestoreCountersFlag) error { - return nil -} - -func (*fake) RestoreAll(data []byte, flush iptables.FlushFlag, counters iptables.RestoreCountersFlag) error { - return nil -} -func (*fake) AddReloadFunc(reloadFunc func()) {} - -func (*fake) Destroy() {} - -var _ = iptables.Interface(&fake{}) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/jsonpath/jsonpath_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/jsonpath/jsonpath_test.go deleted file mode 100644 index 01f07c908..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/jsonpath/jsonpath_test.go +++ /dev/null @@ -1,255 +0,0 @@ -/* -Copyright 2015 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 jsonpath - -import ( - "bytes" - "encoding/json" - "fmt" - "reflect" - "sort" - "strings" - "testing" -) - -type jsonpathTest struct { - name string - template string - input interface{} - expect string -} - -func testJSONPath(tests []jsonpathTest, t *testing.T) { - for _, test := range tests { - j := New(test.name) - err := j.Parse(test.template) - if err != nil { - t.Errorf("in %s, parse %s error %v", test.name, test.template, err) - } - buf := new(bytes.Buffer) - err = j.Execute(buf, test.input) - if err != nil { - t.Errorf("in %s, execute error %v", test.name, err) - } - out := buf.String() - if out != test.expect { - t.Errorf(`in %s, expect to get "%s", got "%s"`, test.name, test.expect, out) - } - } -} - -// testJSONPathSortOutput test cases related to map, the results may print in random order -func testJSONPathSortOutput(tests []jsonpathTest, t *testing.T) { - for _, test := range tests { - j := New(test.name) - err := j.Parse(test.template) - if err != nil { - t.Errorf("in %s, parse %s error %v", test.name, test.template, err) - } - buf := new(bytes.Buffer) - err = j.Execute(buf, test.input) - if err != nil { - t.Errorf("in %s, execute error %v", test.name, err) - } - out := buf.String() - //since map is visited in random order, we need to sort the results. - sortedOut := strings.Fields(out) - sort.Strings(sortedOut) - sortedExpect := strings.Fields(test.expect) - sort.Strings(sortedExpect) - if !reflect.DeepEqual(sortedOut, sortedExpect) { - t.Errorf(`in %s, expect to get "%s", got "%s"`, test.name, test.expect, out) - } - } -} - -func testFailJSONPath(tests []jsonpathTest, t *testing.T) { - for _, test := range tests { - j := New(test.name) - err := j.Parse(test.template) - if err != nil { - t.Errorf("in %s, parse %s error %v", test.name, test.template, err) - } - buf := new(bytes.Buffer) - err = j.Execute(buf, test.input) - var out string - if err == nil { - out = "nil" - } else { - out = err.Error() - } - if out != test.expect { - t.Errorf("in %s, expect to get error %q, got %q", test.name, test.expect, out) - } - } -} - -type book struct { - Category string - Author string - Title string - Price float32 -} - -func (b book) String() string { - return fmt.Sprintf("{Category: %s, Author: %s, Title: %s, Price: %v}", b.Category, b.Author, b.Title, b.Price) -} - -type bicycle struct { - Color string - Price float32 -} - -type store struct { - Book []book - Bicycle bicycle - Name string - Labels map[string]int -} - -func TestStructInput(t *testing.T) { - - storeData := store{ - Name: "jsonpath", - Book: []book{ - {"reference", "Nigel Rees", "Sayings of the Centurey", 8.95}, - {"fiction", "Evelyn Waugh", "Sword of Honour", 12.99}, - {"fiction", "Herman Melville", "Moby Dick", 8.99}, - }, - Bicycle: bicycle{"red", 19.95}, - Labels: map[string]int{ - "engieer": 10, - "web/html": 15, - "k8s-app": 20, - }, - } - - storeTests := []jsonpathTest{ - {"plain", "hello jsonpath", nil, "hello jsonpath"}, - {"recursive", "{..}", []int{1, 2, 3}, "[1 2 3]"}, - {"filter", "{[?(@<5)]}", []int{2, 6, 3, 7}, "2 3"}, - {"quote", `{"{"}`, nil, "{"}, - {"union", "{[1,3,4]}", []int{0, 1, 2, 3, 4}, "1 3 4"}, - {"array", "{[0:2]}", []string{"Monday", "Tudesday"}, "Monday Tudesday"}, - {"variable", "hello {.Name}", storeData, "hello jsonpath"}, - {"dict/", "{$.Labels.web/html}", storeData, "15"}, - {"dict-", "{.Labels.k8s-app}", storeData, "20"}, - {"nest", "{.Bicycle.Color}", storeData, "red"}, - {"allarray", "{.Book[*].Author}", storeData, "Nigel Rees Evelyn Waugh Herman Melville"}, - {"allfileds", "{.Bicycle.*}", storeData, "red 19.95"}, - {"recurfileds", "{..Price}", storeData, "8.95 12.99 8.99 19.95"}, - {"lastarray", "{.Book[-1:]}", storeData, - "{Category: fiction, Author: Herman Melville, Title: Moby Dick, Price: 8.99}"}, - {"recurarray", "{..Book[2]}", storeData, - "{Category: fiction, Author: Herman Melville, Title: Moby Dick, Price: 8.99}"}, - } - testJSONPath(storeTests, t) - - failStoreTests := []jsonpathTest{ - {"invalid identfier", "{hello}", storeData, "unrecognized identifier hello"}, - {"nonexistent field", "{.hello}", storeData, "hello is not found"}, - {"invalid array", "{.Labels[0]}", storeData, "map[string]int is not array or slice"}, - {"invalid filter operator", "{.Book[?(@.Price<>10)]}", storeData, "unrecognized filter operator <>"}, - {"redundent end", "{range .Labels.*}{@}{end}{end}", storeData, "not in range, nothing to end"}, - } - testFailJSONPath(failStoreTests, t) -} - -func TestJSONInput(t *testing.T) { - var pointsJSON = []byte(`[ - {"id": "i1", "x":4, "y":-5}, - {"id": "i2", "x":-2, "y":-5, "z":1}, - {"id": "i3", "x": 8, "y": 3 }, - {"id": "i4", "x": -6, "y": -1 }, - {"id": "i5", "x": 0, "y": 2, "z": 1 }, - {"id": "i6", "x": 1, "y": 4 } - ]`) - var pointsData interface{} - err := json.Unmarshal(pointsJSON, &pointsData) - if err != nil { - t.Error(err) - } - pointsTests := []jsonpathTest{ - {"exists filter", "{[?(@.z)].id}", pointsData, "i2 i5"}, - {"bracket key", "{[0]['id']}", pointsData, "i1"}, - } - testJSONPath(pointsTests, t) -} - -// TestKubernetes tests some use cases from kubernetes -func TestKubernetes(t *testing.T) { - var input = []byte(`{ - "kind": "List", - "items":[ - { - "kind":"None", - "metadata":{"name":"127.0.0.1"}, - "status":{ - "capacity":{"cpu":"4"}, - "addresses":[{"type": "LegacyHostIP", "address":"127.0.0.1"}] - } - }, - { - "kind":"None", - "metadata":{"name":"127.0.0.2"}, - "status":{ - "capacity":{"cpu":"8"}, - "addresses":[ - {"type": "LegacyHostIP", "address":"127.0.0.2"}, - {"type": "another", "address":"127.0.0.3"} - ] - } - } - ], - "users":[ - { - "name": "myself", - "user": {} - }, - { - "name": "e2e", - "user": {"username": "admin", "password": "secret"} - } - ] - }`) - var nodesData interface{} - err := json.Unmarshal(input, &nodesData) - if err != nil { - t.Error(err) - } - - nodesTests := []jsonpathTest{ - {"range item", `{range .items[*]}{.metadata.name}, {end}{.kind}`, nodesData, "127.0.0.1, 127.0.0.2, List"}, - {"range item with quote", `{range .items[*]}{.metadata.name}{"\t"}{end}`, nodesData, "127.0.0.1\t127.0.0.2\t"}, - {"range addresss", `{.items[*].status.addresses[*].address}`, nodesData, - "127.0.0.1 127.0.0.2 127.0.0.3"}, - {"double range", `{range .items[*]}{range .status.addresses[*]}{.address}, {end}{end}`, nodesData, - "127.0.0.1, 127.0.0.2, 127.0.0.3, "}, - {"item name", `{.items[*].metadata.name}`, nodesData, "127.0.0.1 127.0.0.2"}, - {"union nodes capacity", `{.items[*]['metadata.name', 'status.capacity']}`, nodesData, - "127.0.0.1 127.0.0.2 map[cpu:4] map[cpu:8]"}, - {"range nodes capacity", `{range .items[*]}[{.metadata.name}, {.status.capacity}] {end}`, nodesData, - "[127.0.0.1, map[cpu:4]] [127.0.0.2, map[cpu:8]] "}, - {"user password", `{.users[?(@.name=="e2e")].user.password}`, &nodesData, "secret"}, - } - testJSONPath(nodesTests, t) - - randomPrintOrderTests := []jsonpathTest{ - {"recursive name", "{..name}", nodesData, `127.0.0.1 127.0.0.2 myself e2e`}, - } - testJSONPathSortOutput(randomPrintOrderTests, t) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/jsonpath/parser_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/jsonpath/parser_test.go deleted file mode 100644 index a061043b8..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/jsonpath/parser_test.go +++ /dev/null @@ -1,136 +0,0 @@ -/* -Copyright 2015 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 jsonpath - -import ( - "testing" -) - -type parserTest struct { - name string - text string - nodes []Node - shouldError bool -} - -var parserTests = []parserTest{ - {"plain", `hello jsonpath`, []Node{newText("hello jsonpath")}, false}, - {"variable", `hello {.jsonpath}`, - []Node{newText("hello "), newList(), newField("jsonpath")}, false}, - {"arrayfiled", `hello {['jsonpath']}`, - []Node{newText("hello "), newList(), newField("jsonpath")}, false}, - {"quote", `{"{"}`, []Node{newList(), newText("{")}, false}, - {"array", `{[1:3]}`, []Node{newList(), - newArray([3]ParamsEntry{{1, true}, {3, true}, {0, false}})}, false}, - {"allarray", `{.book[*].author}`, - []Node{newList(), newField("book"), - newArray([3]ParamsEntry{{0, false}, {0, false}, {0, false}}), newField("author")}, false}, - {"wildcard", `{.bicycle.*}`, - []Node{newList(), newField("bicycle"), newWildcard()}, false}, - {"filter", `{[?(@.price<3)]}`, - []Node{newList(), newFilter(newList(), newList(), "<"), - newList(), newField("price"), newList(), newInt(3)}, false}, - {"recursive", `{..}`, []Node{newList(), newRecursive()}, false}, - {"recurField", `{..price}`, - []Node{newList(), newRecursive(), newField("price")}, false}, - {"arraydict", `{['book.price']}`, []Node{newList(), - newField("book"), newField("price"), - }, false}, - {"union", `{['bicycle.price', 3, 'book.price']}`, []Node{newList(), newUnion([]*ListNode{}), - newList(), newField("bicycle"), newField("price"), - newList(), newArray([3]ParamsEntry{{3, true}, {4, true}, {0, false}}), - newList(), newField("book"), newField("price"), - }, false}, - {"range", `{range .items}{.name},{end}`, []Node{ - newList(), newIdentifier("range"), newField("items"), - newList(), newField("name"), newText(","), - newList(), newIdentifier("end"), - }, false}, - {"malformat input", `{\\\}`, []Node{}, true}, -} - -func collectNode(nodes []Node, cur Node) []Node { - nodes = append(nodes, cur) - switch cur.Type() { - case NodeList: - for _, node := range cur.(*ListNode).Nodes { - nodes = collectNode(nodes, node) - } - case NodeFilter: - nodes = collectNode(nodes, cur.(*FilterNode).Left) - nodes = collectNode(nodes, cur.(*FilterNode).Right) - case NodeUnion: - for _, node := range cur.(*UnionNode).Nodes { - nodes = collectNode(nodes, node) - } - } - return nodes -} - -func TestParser(t *testing.T) { - for _, test := range parserTests { - parser, err := Parse(test.name, test.text) - if test.shouldError { - if err == nil { - t.Errorf("unexpected non-error when parsing %s", test.name) - } - continue - } - if err != nil { - t.Errorf("parse %s error %v", test.name, err) - } - result := collectNode([]Node{}, parser.Root)[1:] - if len(result) != len(test.nodes) { - t.Errorf("in %s, expect to get %d nodes, got %d nodes", test.name, len(test.nodes), len(result)) - t.Error(result) - } - for i, expect := range test.nodes { - if result[i].String() != expect.String() { - t.Errorf("in %s, %dth node, expect %v, got %v", test.name, i, expect, result[i]) - } - } - } -} - -type failParserTest struct { - name string - text string - err string -} - -func TestFailParser(t *testing.T) { - failParserTests := []failParserTest{ - {"unclosed action", "{.hello", "unclosed action"}, - {"unrecognized character", "{*}", "unrecognized character in action: U+002A '*'"}, - {"invalid number", "{+12.3.0}", "cannot parse number +12.3.0"}, - {"unterminated array", "{[1}", "unterminated array"}, - {"invalid index", "{[::-1]}", "invalid array index ::-1"}, - {"unterminated filter", "{[?(.price]}", "unterminated filter"}, - } - for _, test := range failParserTests { - _, err := Parse(test.name, test.text) - var out string - if err == nil { - out = "nil" - } else { - out = err.Error() - } - if out != test.err { - t.Errorf("in %s, expect to get error %v, got %v", test.name, test.err, out) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/keymutex/keymutex.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/keymutex/keymutex.go deleted file mode 100644 index 2bcc95175..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/keymutex/keymutex.go +++ /dev/null @@ -1,82 +0,0 @@ -/* -Copyright 2015 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 keymutex - -import ( - "fmt" - "github.com/golang/glog" - "sync" -) - -// KeyMutex is a thread-safe interface for acquiring locks on arbitrary strings. -type KeyMutex interface { - // Acquires a lock associated with the specified ID, creates the lock if one doesn't already exist. - LockKey(id string) - - // Releases the lock associated with the specified ID. - // Returns an error if the specified ID doesn't exist. - UnlockKey(id string) error -} - -// Returns a new instance of a key mutex. -func NewKeyMutex() KeyMutex { - return &keyMutex{ - mutexMap: make(map[string]*sync.Mutex), - } -} - -type keyMutex struct { - sync.RWMutex - mutexMap map[string]*sync.Mutex -} - -// Acquires a lock associated with the specified ID (creates the lock if one doesn't already exist). -func (km *keyMutex) LockKey(id string) { - glog.V(5).Infof("LockKey(...) called for id %q\r\n", id) - mutex := km.getOrCreateLock(id) - mutex.Lock() - glog.V(5).Infof("LockKey(...) for id %q completed.\r\n", id) -} - -// Releases the lock associated with the specified ID. -// Returns an error if the specified ID doesn't exist. -func (km *keyMutex) UnlockKey(id string) error { - glog.V(5).Infof("UnlockKey(...) called for id %q\r\n", id) - km.RLock() - defer km.RUnlock() - mutex, exists := km.mutexMap[id] - if !exists { - return fmt.Errorf("id %q not found", id) - } - glog.V(5).Infof("UnlockKey(...) for id. Mutex found, trying to unlock it. %q\r\n", id) - - mutex.Unlock() - glog.V(5).Infof("UnlockKey(...) for id %q completed.\r\n", id) - return nil -} - -// Returns lock associated with the specified ID, or creates the lock if one doesn't already exist. -func (km *keyMutex) getOrCreateLock(id string) *sync.Mutex { - km.Lock() - defer km.Unlock() - - if _, exists := km.mutexMap[id]; !exists { - km.mutexMap[id] = &sync.Mutex{} - } - - return km.mutexMap[id] -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/keymutex/keymutex_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/keymutex/keymutex_test.go deleted file mode 100644 index faa3be16a..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/keymutex/keymutex_test.go +++ /dev/null @@ -1,111 +0,0 @@ -/* -Copyright 2015 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 keymutex - -import ( - "testing" - "time" -) - -const ( - callbackTimeout = 1 * time.Second -) - -func Test_SingleLock_NoUnlock(t *testing.T) { - // Arrange - km := NewKeyMutex() - key := "fakeid" - callbackCh := make(chan interface{}) - - // Act - go lockAndCallback(km, key, callbackCh) - - // Assert - verifyCallbackHappens(t, callbackCh) -} - -func Test_SingleLock_SingleUnlock(t *testing.T) { - // Arrange - km := NewKeyMutex() - key := "fakeid" - callbackCh := make(chan interface{}) - - // Act & Assert - go lockAndCallback(km, key, callbackCh) - verifyCallbackHappens(t, callbackCh) - km.UnlockKey(key) -} - -func Test_DoubleLock_DoubleUnlock(t *testing.T) { - // Arrange - km := NewKeyMutex() - key := "fakeid" - callbackCh1stLock := make(chan interface{}) - callbackCh2ndLock := make(chan interface{}) - - // Act & Assert - go lockAndCallback(km, key, callbackCh1stLock) - verifyCallbackHappens(t, callbackCh1stLock) - go lockAndCallback(km, key, callbackCh2ndLock) - verifyCallbackDoesntHappens(t, callbackCh2ndLock) - km.UnlockKey(key) - verifyCallbackHappens(t, callbackCh2ndLock) - km.UnlockKey(key) -} - -func lockAndCallback(km KeyMutex, id string, callbackCh chan<- interface{}) { - km.LockKey(id) - callbackCh <- true -} - -func verifyCallbackHappens(t *testing.T, callbackCh <-chan interface{}) bool { - select { - case <-callbackCh: - return true - case <-time.After(callbackTimeout): - t.Fatalf("Timed out waiting for callback.") - return false - } -} - -func verifyCallbackDoesntHappens(t *testing.T, callbackCh <-chan interface{}) bool { - select { - case <-callbackCh: - t.Fatalf("Unexpected callback.") - return false - case <-time.After(callbackTimeout): - return true - } -} - -func verifyNoError(t *testing.T, err error, name string) { - if err != nil { - t.Fatalf("Unexpected response on %q. Expected: Actual: <%v>", name, err) - } -} - -func verifyError(t *testing.T, err error, name string) { - if err == nil { - t.Fatalf("Unexpected response on %q. Expected: Actual: ", name) - } -} - -func verifyMsg(t *testing.T, expected, actual string) { - if actual != expected { - t.Fatalf("Unexpected testMsg value. Expected: <%v> Actual: <%v>", expected, actual) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/labels/labels.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/labels/labels.go index c32b862cd..624d5ad68 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/labels/labels.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/labels/labels.go @@ -54,6 +54,19 @@ func CloneAndRemoveLabel(labels map[string]string, labelKey string) map[string]s return newLabels } +// AddLabel returns a map with the given key and value added to the given map. +func AddLabel(labels map[string]string, labelKey string, labelValue string) map[string]string { + if labelKey == "" { + // Don't need to add a label. + return labels + } + if labels == nil { + labels = make(map[string]string) + } + labels[labelKey] = labelValue + return labels +} + // Clones the given selector and returns a new selector with the given key and value added. // Returns the given selector, if labelKey is empty. func CloneSelectorAndAddLabel(selector *unversioned.LabelSelector, labelKey string, labelValue uint32) *unversioned.LabelSelector { @@ -93,3 +106,21 @@ func CloneSelectorAndAddLabel(selector *unversioned.LabelSelector, labelKey stri return newSelector } + +// AddLabelToSelector returns a selector with the given key and value added to the given selector's MatchLabels. +func AddLabelToSelector(selector *unversioned.LabelSelector, labelKey string, labelValue string) *unversioned.LabelSelector { + if labelKey == "" { + // Don't need to add a label. + return selector + } + if selector.MatchLabels == nil { + selector.MatchLabels = make(map[string]string) + } + selector.MatchLabels[labelKey] = labelValue + return selector +} + +// SelectorHasLabel checks if the given selector contains the given label key in its MatchLabels +func SelectorHasLabel(selector *unversioned.LabelSelector, labelKey string) bool { + return len(selector.MatchLabels[labelKey]) > 0 +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/labels/labels_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/labels/labels_test.go deleted file mode 100644 index 9adbd4554..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/labels/labels_test.go +++ /dev/null @@ -1,60 +0,0 @@ -/* -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 labels - -import ( - "reflect" - "testing" -) - -func TestCloneAndAddLabel(t *testing.T) { - labels := map[string]string{ - "foo1": "bar1", - "foo2": "bar2", - "foo3": "bar3", - } - - cases := []struct { - labels map[string]string - labelKey string - labelValue uint32 - want map[string]string - }{ - { - labels: labels, - want: labels, - }, - { - labels: labels, - labelKey: "foo4", - labelValue: uint32(42), - want: map[string]string{ - "foo1": "bar1", - "foo2": "bar2", - "foo3": "bar3", - "foo4": "42", - }, - }, - } - - for _, tc := range cases { - got := CloneAndAddLabel(tc.labels, tc.labelKey, tc.labelValue) - if !reflect.DeepEqual(got, tc.want) { - t.Errorf("got %v, want %v", got, tc.want) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/limitwriter/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/limitwriter/doc.go deleted file mode 100644 index 7daff9c07..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/limitwriter/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2015 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 limitwriter provides a writer that only allows a certain number of bytes to be -// written. -package limitwriter diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/limitwriter/limitwriter.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/limitwriter/limitwriter.go deleted file mode 100644 index 88890a9fa..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/limitwriter/limitwriter.go +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2015 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 limitwriter - -import ( - "errors" - "io" -) - -// New creates a writer that is limited to writing at most n bytes to w. This writer is not -// thread safe. -func New(w io.Writer, n int64) io.Writer { - return &limitWriter{ - w: w, - n: n, - } -} - -// ErrMaximumWrite is returned when all bytes have been written. -var ErrMaximumWrite = errors.New("maximum write") - -type limitWriter struct { - w io.Writer - n int64 -} - -func (w *limitWriter) Write(p []byte) (n int, err error) { - if int64(len(p)) > w.n { - p = p[:w.n] - } - if len(p) > 0 { - n, err = w.w.Write(p) - w.n -= int64(n) - } - if w.n == 0 { - err = ErrMaximumWrite - } - return -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/line_delimiter_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/line_delimiter_test.go deleted file mode 100644 index 8777bb949..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/line_delimiter_test.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2015 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 util - -import ( - "fmt" - "os" -) - -func ExampleTrailingNewline() { - ld := NewLineDelimiter(os.Stdout, "|") - defer ld.Flush() - fmt.Fprint(ld, " Hello \n World \n") - // Output: - // | Hello | - // | World | - // || -} -func ExampleNoTrailingNewline() { - ld := NewLineDelimiter(os.Stdout, "|") - defer ld.Flush() - fmt.Fprint(ld, " Hello \n World ") - // Output: - // | Hello | - // | World | -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/doc.go deleted file mode 100644 index 839c0a69a..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2014 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 mount defines an interface to mounting filesystems. -package mount diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/fake.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/fake.go deleted file mode 100644 index 115293813..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/fake.go +++ /dev/null @@ -1,126 +0,0 @@ -/* -Copyright 2015 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 mount - -import ( - "sync" - - "github.com/golang/glog" -) - -// FakeMounter implements mount.Interface for tests. -type FakeMounter struct { - MountPoints []MountPoint - Log []FakeAction - // Some tests run things in parallel, make sure the mounter does not produce - // any golang's DATA RACE warnings. - mutex sync.Mutex -} - -var _ Interface = &FakeMounter{} - -// Values for FakeAction.Action -const FakeActionMount = "mount" -const FakeActionUnmount = "unmount" - -// FakeAction objects are logged every time a fake mount or unmount is called. -type FakeAction struct { - Action string // "mount" or "unmount" - Target string // applies to both mount and unmount actions - Source string // applies only to "mount" actions - FSType string // applies only to "mount" actions -} - -func (f *FakeMounter) ResetLog() { - f.mutex.Lock() - defer f.mutex.Unlock() - - f.Log = []FakeAction{} -} - -func (f *FakeMounter) Mount(source string, target string, fstype string, options []string) error { - f.mutex.Lock() - defer f.mutex.Unlock() - - // find 'bind' option - for _, option := range options { - if option == "bind" { - // This is a bind-mount. In order to mimic linux behaviour, we must - // use the original device of the bind-mount as the real source. - // E.g. when mounted /dev/sda like this: - // $ mount /dev/sda /mnt/test - // $ mount -o bind /mnt/test /mnt/bound - // then /proc/mount contains: - // /dev/sda /mnt/test - // /dev/sda /mnt/bound - // (and not /mnt/test /mnt/bound) - // I.e. we must use /dev/sda as source instead of /mnt/test in the - // bind mount. - for _, mnt := range f.MountPoints { - if source == mnt.Path { - source = mnt.Device - break - } - } - break - } - } - - f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: target, Type: fstype}) - glog.V(5).Infof("Fake mounter: mouted %s to %s", source, target) - f.Log = append(f.Log, FakeAction{Action: FakeActionMount, Target: target, Source: source, FSType: fstype}) - return nil -} - -func (f *FakeMounter) Unmount(target string) error { - f.mutex.Lock() - defer f.mutex.Unlock() - - newMountpoints := []MountPoint{} - for _, mp := range f.MountPoints { - if mp.Path == target { - glog.V(5).Infof("Fake mounter: unmouted %s from %s", mp.Device, target) - // Don't copy it to newMountpoints - continue - } - newMountpoints = append(newMountpoints, MountPoint{Device: mp.Device, Path: mp.Path, Type: mp.Type}) - } - f.MountPoints = newMountpoints - f.Log = append(f.Log, FakeAction{Action: FakeActionUnmount, Target: target}) - return nil -} - -func (f *FakeMounter) List() ([]MountPoint, error) { - f.mutex.Lock() - defer f.mutex.Unlock() - - return f.MountPoints, nil -} - -func (f *FakeMounter) IsLikelyNotMountPoint(file string) (bool, error) { - f.mutex.Lock() - defer f.mutex.Unlock() - - for _, mp := range f.MountPoints { - if mp.Path == file { - glog.V(5).Infof("isLikelyMountPoint for %s: monted %s, false", file, mp.Path) - return false, nil - } - } - glog.V(5).Infof("isLikelyMountPoint for %s: true", file) - return true, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount.go deleted file mode 100644 index 79f3f6ec2..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount.go +++ /dev/null @@ -1,135 +0,0 @@ -/* -Copyright 2014 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. -*/ - -// TODO(thockin): This whole pkg is pretty linux-centric. As soon as we have -// an alternate platform, we will need to abstract further. -package mount - -import ( - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/util/exec" -) - -type Interface interface { - // Mount mounts source to target as fstype with given options. - Mount(source string, target string, fstype string, options []string) error - // Unmount unmounts given target. - Unmount(target string) error - // List returns a list of all mounted filesystems. This can be large. - // On some platforms, reading mounts is not guaranteed consistent (i.e. - // it could change between chunked reads). This is guaranteed to be - // consistent. - List() ([]MountPoint, error) - // IsLikelyNotMountPoint determines if a directory is a mountpoint. - IsLikelyNotMountPoint(file string) (bool, error) -} - -// This represents a single line in /proc/mounts or /etc/fstab. -type MountPoint struct { - Device string - Path string - Type string - Opts []string - Freq int - Pass int -} - -// SafeFormatAndMount probes a device to see if it is formatted. -// Namely it checks to see if a file system is present. If so it -// mounts it otherwise the device is formatted first then mounted. -type SafeFormatAndMount struct { - Interface - Runner exec.Interface -} - -// FormatAndMount formats the given disk, if needed, and mounts it. -// That is if the disk is not formatted and it is not being mounted as -// read-only it will format it first then mount it. Otherwise, if the -// disk is already formatted or it is being mounted as read-only, it -// will be mounted without formatting. -func (mounter *SafeFormatAndMount) FormatAndMount(source string, target string, fstype string, options []string) error { - // Don't attempt to format if mounting as readonly. Go straight to mounting. - for _, option := range options { - if option == "ro" { - return mounter.Interface.Mount(source, target, fstype, options) - } - } - return mounter.formatAndMount(source, target, fstype, options) -} - -// New returns a mount.Interface for the current system. -func New() Interface { - return &Mounter{} -} - -// GetMountRefs finds all other references to the device referenced -// by mountPath; returns a list of paths. -func GetMountRefs(mounter Interface, mountPath string) ([]string, error) { - mps, err := mounter.List() - if err != nil { - return nil, err - } - - // Find the device name. - deviceName := "" - for i := range mps { - if mps[i].Path == mountPath { - deviceName = mps[i].Device - break - } - } - - // Find all references to the device. - var refs []string - if deviceName == "" { - glog.Warningf("could not determine device for path: %q", mountPath) - } else { - for i := range mps { - if mps[i].Device == deviceName && mps[i].Path != mountPath { - refs = append(refs, mps[i].Path) - } - } - } - return refs, nil -} - -// GetDeviceNameFromMount: given a mnt point, find the device from /proc/mounts -// returns the device name, reference count, and error code -func GetDeviceNameFromMount(mounter Interface, mountPath string) (string, int, error) { - mps, err := mounter.List() - if err != nil { - return "", 0, err - } - - // Find the device name. - // FIXME if multiple devices mounted on the same mount path, only the first one is returned - device := "" - for i := range mps { - if mps[i].Path == mountPath { - device = mps[i].Device - break - } - } - - // Find all references to the device. - refCount := 0 - for i := range mps { - if mps[i].Device == device { - refCount++ - } - } - return device, refCount, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_linux.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_linux.go deleted file mode 100644 index 66959697c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_linux.go +++ /dev/null @@ -1,311 +0,0 @@ -// +build linux - -/* -Copyright 2014 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 mount - -import ( - "bufio" - "fmt" - "hash/adler32" - "io" - "os" - "os/exec" - "strconv" - "strings" - "syscall" - - "github.com/golang/glog" - utilExec "k8s.io/kubernetes/pkg/util/exec" -) - -const ( - // How many times to retry for a consistent read of /proc/mounts. - maxListTries = 3 - // Number of fields per line in /proc/mounts as per the fstab man page. - expectedNumFieldsPerLine = 6 - // Location of the mount file to use - procMountsPath = "/proc/mounts" -) - -const ( - // 'fsck' found errors and corrected them - fsckErrorsCorrected = 1 - // 'fsck' found errors but exited without correcting them - fsckErrorsUncorrected = 4 -) - -// Mounter provides the default implementation of mount.Interface -// for the linux platform. This implementation assumes that the -// kubelet is running in the host's root mount namespace. -type Mounter struct{} - -var _ = Interface(&Mounter{}) - -// Mount mounts source to target as fstype with given options. 'source' and 'fstype' must -// be an emtpy string in case it's not required, e.g. for remount, or for auto filesystem -// type, where kernel handles fs type for you. The mount 'options' is a list of options, -// currently come from mount(8), e.g. "ro", "remount", "bind", etc. If no more option is -// required, call Mount with an empty string list or nil. -func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error { - bind, bindRemountOpts := isBind(options) - - if bind { - err := doMount(source, target, fstype, []string{"bind"}) - if err != nil { - return err - } - return doMount(source, target, fstype, bindRemountOpts) - } else { - return doMount(source, target, fstype, options) - } -} - -// isBind detects whether a bind mount is being requested and makes the remount options to -// use in case of bind mount, due to the fact that bind mount doesn't respect mount options. -// The list equals: -// options - 'bind' + 'remount' (no duplicate) -func isBind(options []string) (bool, []string) { - bindRemountOpts := []string{"remount"} - bind := false - - if len(options) != 0 { - for _, option := range options { - switch option { - case "bind": - bind = true - break - case "remount": - break - default: - bindRemountOpts = append(bindRemountOpts, option) - } - } - } - - return bind, bindRemountOpts -} - -// doMount runs the mount command. -func doMount(source string, target string, fstype string, options []string) error { - glog.V(5).Infof("Mounting %s %s %s %v", source, target, fstype, options) - mountArgs := makeMountArgs(source, target, fstype, options) - command := exec.Command("mount", mountArgs...) - output, err := command.CombinedOutput() - if err != nil { - return fmt.Errorf("Mount failed: %v\nMounting arguments: %s %s %s %v\nOutput: %s\n", - err, source, target, fstype, options, string(output)) - } - return err -} - -// makeMountArgs makes the arguments to the mount(8) command. -func makeMountArgs(source, target, fstype string, options []string) []string { - // Build mount command as follows: - // mount [-t $fstype] [-o $options] [$source] $target - mountArgs := []string{} - if len(fstype) > 0 { - mountArgs = append(mountArgs, "-t", fstype) - } - if len(options) > 0 { - mountArgs = append(mountArgs, "-o", strings.Join(options, ",")) - } - if len(source) > 0 { - mountArgs = append(mountArgs, source) - } - mountArgs = append(mountArgs, target) - - return mountArgs -} - -// Unmount unmounts the target. -func (mounter *Mounter) Unmount(target string) error { - glog.V(5).Infof("Unmounting %s", target) - command := exec.Command("umount", target) - output, err := command.CombinedOutput() - if err != nil { - return fmt.Errorf("Unmount failed: %v\nUnmounting arguments: %s\nOutput: %s\n", err, target, string(output)) - } - return nil -} - -// List returns a list of all mounted filesystems. -func (*Mounter) List() ([]MountPoint, error) { - return listProcMounts(procMountsPath) -} - -// IsLikelyNotMountPoint determines if a directory is not a mountpoint. -// It is fast but not necessarily ALWAYS correct. If the path is in fact -// a bind mount from one part of a mount to another it will not be detected. -// mkdir /tmp/a /tmp/b; mount --bin /tmp/a /tmp/b; IsLikelyNotMountPoint("/tmp/b") -// will return true. When in fact /tmp/b is a mount point. If this situation -// if of interest to you, don't use this function... -func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) { - stat, err := os.Stat(file) - if err != nil { - return true, err - } - rootStat, err := os.Lstat(file + "/..") - if err != nil { - return true, err - } - // If the directory has a different device as parent, then it is a mountpoint. - if stat.Sys().(*syscall.Stat_t).Dev != rootStat.Sys().(*syscall.Stat_t).Dev { - return false, nil - } - - return true, nil -} - -func listProcMounts(mountFilePath string) ([]MountPoint, error) { - hash1, err := readProcMounts(mountFilePath, nil) - if err != nil { - return nil, err - } - - for i := 0; i < maxListTries; i++ { - mps := []MountPoint{} - hash2, err := readProcMounts(mountFilePath, &mps) - if err != nil { - return nil, err - } - if hash1 == hash2 { - // Success - return mps, nil - } - hash1 = hash2 - } - return nil, fmt.Errorf("failed to get a consistent snapshot of %v after %d tries", mountFilePath, maxListTries) -} - -// readProcMounts reads the given mountFilePath (normally /proc/mounts) and produces a hash -// of the contents. If the out argument is not nil, this fills it with MountPoint structs. -func readProcMounts(mountFilePath string, out *[]MountPoint) (uint32, error) { - file, err := os.Open(mountFilePath) - if err != nil { - return 0, err - } - defer file.Close() - return readProcMountsFrom(file, out) -} - -func readProcMountsFrom(file io.Reader, out *[]MountPoint) (uint32, error) { - hash := adler32.New() - scanner := bufio.NewReader(file) - for { - line, err := scanner.ReadString('\n') - if err == io.EOF { - break - } - fields := strings.Fields(line) - if len(fields) != expectedNumFieldsPerLine { - return 0, fmt.Errorf("wrong number of fields (expected %d, got %d): %s", expectedNumFieldsPerLine, len(fields), line) - } - - fmt.Fprintf(hash, "%s", line) - - if out != nil { - mp := MountPoint{ - Device: fields[0], - Path: fields[1], - Type: fields[2], - Opts: strings.Split(fields[3], ","), - } - - freq, err := strconv.Atoi(fields[4]) - if err != nil { - return 0, err - } - mp.Freq = freq - - pass, err := strconv.Atoi(fields[5]) - if err != nil { - return 0, err - } - mp.Pass = pass - - *out = append(*out, mp) - } - } - return hash.Sum32(), nil -} - -// formatAndMount uses unix utils to format and mount the given disk -func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error { - options = append(options, "defaults") - - // Run fsck on the disk to fix repairable issues - args := []string{"-a", source} - cmd := mounter.Runner.Command("fsck", args...) - out, err := cmd.CombinedOutput() - if err != nil { - ee, isExitError := err.(utilExec.ExitError) - switch { - case err == utilExec.ErrExecutableNotFound: - glog.Warningf("'fsck' not found on system; continuing mount without running 'fsck'.") - case isExitError && ee.ExitStatus() == fsckErrorsCorrected: - glog.Infof("Device %s has errors which were corrected by fsck.", source) - case isExitError && ee.ExitStatus() == fsckErrorsUncorrected: - return fmt.Errorf("'fsck' found errors on device %s but could not correct them: %s.", source, string(out)) - case isExitError && ee.ExitStatus() > fsckErrorsUncorrected: - glog.Infof("`fsck` error %s", string(out)) - } - } - - // Try to mount the disk - err = mounter.Interface.Mount(source, target, fstype, options) - if err != nil { - // It is possible that this disk is not formatted. Double check using diskLooksUnformatted - notFormatted, err := mounter.diskLooksUnformatted(source) - if err == nil && notFormatted { - args = []string{source} - // Disk is unformatted so format it. - // Use 'ext4' as the default - if len(fstype) == 0 { - fstype = "ext4" - } - if fstype == "ext4" || fstype == "ext3" { - args = []string{"-E", "lazy_itable_init=0,lazy_journal_init=0", "-F", source} - } - cmd := mounter.Runner.Command("mkfs."+fstype, args...) - _, err := cmd.CombinedOutput() - if err == nil { - // the disk has been formatted successfully try to mount it again. - return mounter.Interface.Mount(source, target, fstype, options) - } - return err - } - } - return err -} - -// diskLooksUnformatted uses 'lsblk' to see if the given disk is unformated -func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) { - args := []string{"-nd", "-o", "FSTYPE", disk} - cmd := mounter.Runner.Command("lsblk", args...) - dataOut, err := cmd.CombinedOutput() - output := strings.TrimSpace(string(dataOut)) - - // TODO (#13212): check if this disk has partitions and return false, and - // an error if so. - - if err != nil { - return false, err - } - - return output == "", nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_linux_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_linux_test.go deleted file mode 100644 index cd802d774..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_linux_test.go +++ /dev/null @@ -1,182 +0,0 @@ -// +build linux - -/* -Copyright 2014 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 mount - -import ( - "strings" - "testing" -) - -func TestReadProcMountsFrom(t *testing.T) { - successCase := - `/dev/0 /path/to/0 type0 flags 0 0 - /dev/1 /path/to/1 type1 flags 1 1 - /dev/2 /path/to/2 type2 flags,1,2=3 2 2 - ` - hash, err := readProcMountsFrom(strings.NewReader(successCase), nil) - if err != nil { - t.Errorf("expected success") - } - if hash != 0xa3522051 { - t.Errorf("expected 0xa3522051, got %#x", hash) - } - mounts := []MountPoint{} - hash, err = readProcMountsFrom(strings.NewReader(successCase), &mounts) - if err != nil { - t.Errorf("expected success") - } - if hash != 0xa3522051 { - t.Errorf("expected 0xa3522051, got %#x", hash) - } - if len(mounts) != 3 { - t.Fatalf("expected 3 mounts, got %d", len(mounts)) - } - mp := MountPoint{"/dev/0", "/path/to/0", "type0", []string{"flags"}, 0, 0} - if !mountPointsEqual(&mounts[0], &mp) { - t.Errorf("got unexpected MountPoint[0]: %#v", mounts[0]) - } - mp = MountPoint{"/dev/1", "/path/to/1", "type1", []string{"flags"}, 1, 1} - if !mountPointsEqual(&mounts[1], &mp) { - t.Errorf("got unexpected MountPoint[1]: %#v", mounts[1]) - } - mp = MountPoint{"/dev/2", "/path/to/2", "type2", []string{"flags", "1", "2=3"}, 2, 2} - if !mountPointsEqual(&mounts[2], &mp) { - t.Errorf("got unexpected MountPoint[2]: %#v", mounts[2]) - } - - errorCases := []string{ - "/dev/0 /path/to/mount\n", - "/dev/1 /path/to/mount type flags a 0\n", - "/dev/2 /path/to/mount type flags 0 b\n", - } - for _, ec := range errorCases { - _, err := readProcMountsFrom(strings.NewReader(ec), &mounts) - if err == nil { - t.Errorf("expected error") - } - } -} - -func mountPointsEqual(a, b *MountPoint) bool { - if a.Device != b.Device || a.Path != b.Path || a.Type != b.Type || !slicesEqual(a.Opts, b.Opts) || a.Pass != b.Pass || a.Freq != b.Freq { - return false - } - return true -} - -func slicesEqual(a, b []string) bool { - if len(a) != len(b) { - return false - } - for i := range a { - if a[i] != b[i] { - return false - } - } - return true -} - -func TestGetMountRefs(t *testing.T) { - fm := &FakeMounter{ - MountPoints: []MountPoint{ - {Device: "/dev/sdb", Path: "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd"}, - {Device: "/dev/sdb", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd-in-pod"}, - {Device: "/dev/sdc", Path: "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd2"}, - {Device: "/dev/sdc", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod"}, - {Device: "/dev/sdc", Path: "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod2"}, - }, - } - - tests := []struct { - mountPath string - expectedRefs []string - }{ - { - "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd-in-pod", - []string{ - "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd", - }, - }, - { - "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod", - []string{ - "/var/lib/kubelet/pods/some-pod/volumes/kubernetes.io~gce-pd/gce-pd2-in-pod2", - "/var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/gce-pd2", - }, - }, - } - - for i, test := range tests { - if refs, err := GetMountRefs(fm, test.mountPath); err != nil || !setEquivalent(test.expectedRefs, refs) { - t.Errorf("%d. getMountRefs(%q) = %v, %v; expected %v, nil", i, test.mountPath, refs, err, test.expectedRefs) - } - } -} - -func setEquivalent(set1, set2 []string) bool { - map1 := make(map[string]bool) - map2 := make(map[string]bool) - for _, s := range set1 { - map1[s] = true - } - for _, s := range set2 { - map2[s] = true - } - - for s := range map1 { - if !map2[s] { - return false - } - } - for s := range map2 { - if !map1[s] { - return false - } - } - return true -} - -func TestGetDeviceNameFromMount(t *testing.T) { - fm := &FakeMounter{ - MountPoints: []MountPoint{ - {Device: "/dev/disk/by-path/prefix-lun-1", - Path: "/mnt/111"}, - {Device: "/dev/disk/by-path/prefix-lun-1", - Path: "/mnt/222"}, - }, - } - - tests := []struct { - mountPath string - expectedDevice string - expectedRefs int - }{ - { - "/mnt/222", - "/dev/disk/by-path/prefix-lun-1", - 2, - }, - } - - for i, test := range tests { - if device, refs, err := GetDeviceNameFromMount(fm, test.mountPath); err != nil || test.expectedRefs != refs || test.expectedDevice != device { - t.Errorf("%d. GetDeviceNameFromMount(%s) = (%s, %d), %v; expected (%s,%d), nil", i, test.mountPath, device, refs, err, test.expectedDevice, test.expectedRefs) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go deleted file mode 100644 index 8942c0036..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go +++ /dev/null @@ -1,45 +0,0 @@ -// +build !linux - -/* -Copyright 2014 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 mount - -type Mounter struct{} - -func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error { - return nil -} - -func (mounter *Mounter) Unmount(target string) error { - return nil -} - -func (mounter *Mounter) List() ([]MountPoint, error) { - return []MountPoint{}, nil -} - -func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) { - return true, nil -} - -func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error { - return nil -} - -func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) { - return true, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go deleted file mode 100644 index 6735c024c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go +++ /dev/null @@ -1,200 +0,0 @@ -// +build linux - -/* -Copyright 2014 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 mount - -import ( - "os" - "path/filepath" - "strings" - - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/util/exec" -) - -// NsenterMounter is part of experimental support for running the kubelet -// in a container. Currently, all docker containers receive their own mount -// namespaces. NsenterMounter works by executing nsenter to run commands in -// the host's mount namespace. -// -// NsenterMounter requires: -// -// 1. Docker >= 1.6 due to the dependency on the slave propagation mode -// of the bind-mount of the kubelet root directory in the container. -// Docker 1.5 used a private propagation mode for bind-mounts, so mounts -// performed in the host's mount namespace do not propagate out to the -// bind-mount in this docker version. -// 2. The host's root filesystem must be available at /rootfs -// 3. The nsenter binary must be on the Kubelet process' PATH in the container's -// filesystem. -// 4. The Kubelet process must have CAP_SYS_ADMIN (required by nsenter); at -// the present, this effectively means that the kubelet is running in a -// privileged container. -// 5. The volume path used by the Kubelet must be the same inside and outside -// the container and be writable by the container (to initialize volume) -// contents. TODO: remove this requirement. -// 6. The host image must have mount, findmnt, and umount binaries in /bin, -// /usr/sbin, or /usr/bin -// -// For more information about mount propagation modes, see: -// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt -type NsenterMounter struct { - // a map of commands to their paths on the host filesystem - paths map[string]string -} - -func NewNsenterMounter() *NsenterMounter { - m := &NsenterMounter{ - paths: map[string]string{ - "mount": "", - "findmnt": "", - "umount": "", - }, - } - // search for the mount command in other locations besides /usr/bin - for binary := range m.paths { - // default to root - m.paths[binary] = filepath.Join("/", binary) - for _, path := range []string{"/bin", "/usr/sbin", "/usr/bin"} { - binPath := filepath.Join(path, binary) - if _, err := os.Stat(filepath.Join(hostRootFsPath, binPath)); err != nil { - continue - } - m.paths[binary] = binPath - break - } - // TODO: error, so that the kubelet can stop if the mounts don't exist - } - return m -} - -// NsenterMounter implements mount.Interface -var _ = Interface(&NsenterMounter{}) - -const ( - hostRootFsPath = "/rootfs" - hostProcMountsPath = "/rootfs/proc/mounts" - nsenterPath = "nsenter" -) - -// Mount runs mount(8) in the host's root mount namespace. Aside from this -// aspect, Mount has the same semantics as the mounter returned by mount.New() -func (n *NsenterMounter) Mount(source string, target string, fstype string, options []string) error { - bind, bindRemountOpts := isBind(options) - - if bind { - err := n.doNsenterMount(source, target, fstype, []string{"bind"}) - if err != nil { - return err - } - return n.doNsenterMount(source, target, fstype, bindRemountOpts) - } - - return n.doNsenterMount(source, target, fstype, options) -} - -// doNsenterMount nsenters the host's mount namespace and performs the -// requested mount. -func (n *NsenterMounter) doNsenterMount(source, target, fstype string, options []string) error { - glog.V(5).Infof("nsenter Mounting %s %s %s %v", source, target, fstype, options) - args := n.makeNsenterArgs(source, target, fstype, options) - - glog.V(5).Infof("Mount command: %v %v", nsenterPath, args) - exec := exec.New() - outputBytes, err := exec.Command(nsenterPath, args...).CombinedOutput() - if len(outputBytes) != 0 { - glog.V(5).Infof("Output from mount command: %v", string(outputBytes)) - } - - return err -} - -// makeNsenterArgs makes a list of argument to nsenter in order to do the -// requested mount. -func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) []string { - nsenterArgs := []string{ - "--mount=/rootfs/proc/1/ns/mnt", - "--", - n.absHostPath("mount"), - } - - args := makeMountArgs(source, target, fstype, options) - - return append(nsenterArgs, args...) -} - -// Unmount runs umount(8) in the host's mount namespace. -func (n *NsenterMounter) Unmount(target string) error { - args := []string{ - "--mount=/rootfs/proc/1/ns/mnt", - "--", - n.absHostPath("umount"), - target, - } - - glog.V(5).Infof("Unmount command: %v %v", nsenterPath, args) - exec := exec.New() - outputBytes, err := exec.Command(nsenterPath, args...).CombinedOutput() - if len(outputBytes) != 0 { - glog.V(5).Infof("Output from mount command: %v", string(outputBytes)) - } - - return err -} - -// List returns a list of all mounted filesystems in the host's mount namespace. -func (*NsenterMounter) List() ([]MountPoint, error) { - return listProcMounts(hostProcMountsPath) -} - -// IsLikelyNotMountPoint determines whether a path is a mountpoint by calling findmnt -// in the host's root mount namespace. -func (n *NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) { - file, err := filepath.Abs(file) - if err != nil { - return true, err - } - - args := []string{"--mount=/rootfs/proc/1/ns/mnt", "--", n.absHostPath("findmnt"), "-o", "target", "--noheadings", "--target", file} - glog.V(5).Infof("findmnt command: %v %v", nsenterPath, args) - - exec := exec.New() - out, err := exec.Command(nsenterPath, args...).CombinedOutput() - if err != nil { - glog.Errorf("Failed to nsenter mount, return file doesn't exist: %v", err) - // If the command itself is correct, then if we encountered error - // then most likely this means that the directory does not exist. - return true, os.ErrNotExist - } - strOut := strings.TrimSuffix(string(out), "\n") - - glog.V(5).Infof("IsLikelyNotMountPoint findmnt output: %v", strOut) - if strOut == file { - return false, nil - } - - return true, nil -} - -func (n *NsenterMounter) absHostPath(command string) string { - path, ok := n.paths[command] - if !ok { - return command - } - return path -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go deleted file mode 100644 index 210fab851..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go +++ /dev/null @@ -1,43 +0,0 @@ -// +build !linux - -/* -Copyright 2014 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 mount - -type NsenterMounter struct{} - -func NewNsenterMounter() *NsenterMounter { - return &NsenterMounter{} -} - -var _ = Interface(&NsenterMounter{}) - -func (*NsenterMounter) Mount(source string, target string, fstype string, options []string) error { - return nil -} - -func (*NsenterMounter) Unmount(target string) error { - return nil -} - -func (*NsenterMounter) List() ([]MountPoint, error) { - return []MountPoint{}, nil -} - -func (*NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) { - return true, nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/safe_format_and_mount_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/safe_format_and_mount_test.go deleted file mode 100644 index 03c0b8442..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/safe_format_and_mount_test.go +++ /dev/null @@ -1,224 +0,0 @@ -/* -Copyright 2014 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 mount - -import ( - "fmt" - "runtime" - "testing" - - "k8s.io/kubernetes/pkg/util/exec" -) - -type ErrorMounter struct { - *FakeMounter - errIndex int - err []error -} - -func (mounter *ErrorMounter) Mount(source string, target string, fstype string, options []string) error { - i := mounter.errIndex - mounter.errIndex++ - if mounter.err != nil && mounter.err[i] != nil { - return mounter.err[i] - } - return mounter.FakeMounter.Mount(source, target, fstype, options) -} - -type ExecArgs struct { - command string - args []string - output string - err error -} - -func TestSafeFormatAndMount(t *testing.T) { - if runtime.GOOS == "darwin" || runtime.GOOS == "windows" { - t.Skipf("not supported on GOOS=%s", runtime.GOOS) - } - tests := []struct { - description string - fstype string - mountOptions []string - execScripts []ExecArgs - mountErrs []error - expectedError error - }{ - { - description: "Test a read only mount", - fstype: "ext4", - mountOptions: []string{"ro"}, - }, - { - description: "Test a normal mount", - fstype: "ext4", - execScripts: []ExecArgs{ - {"fsck", []string{"-a", "/dev/foo"}, "", nil}, - }, - }, - { - description: "Test 'fsck' fails with exit status 4", - fstype: "ext4", - execScripts: []ExecArgs{ - {"fsck", []string{"-a", "/dev/foo"}, "", &exec.FakeExitError{Status: 4}}, - }, - expectedError: fmt.Errorf("'fsck' found errors on device /dev/foo but could not correct them: ."), - }, - { - description: "Test 'fsck' fails with exit status 1 (errors found and corrected)", - fstype: "ext4", - execScripts: []ExecArgs{ - {"fsck", []string{"-a", "/dev/foo"}, "", &exec.FakeExitError{Status: 1}}, - }, - }, - { - description: "Test 'fsck' fails with exit status other than 1 and 4 (likely unformatted device)", - fstype: "ext4", - execScripts: []ExecArgs{ - {"fsck", []string{"-a", "/dev/foo"}, "", &exec.FakeExitError{Status: 8}}, - }, - }, - { - description: "Test that 'lsblk' is called and fails", - fstype: "ext4", - mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")}, - execScripts: []ExecArgs{ - {"fsck", []string{"-a", "/dev/foo"}, "", nil}, - {"lsblk", []string{"-nd", "-o", "FSTYPE", "/dev/foo"}, "ext4", nil}, - }, - expectedError: fmt.Errorf("unknown filesystem type '(null)'"), - }, - { - description: "Test that 'lsblk' is called and confirms unformatted disk, format fails", - fstype: "ext4", - mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")}, - execScripts: []ExecArgs{ - {"fsck", []string{"-a", "/dev/foo"}, "", nil}, - {"lsblk", []string{"-nd", "-o", "FSTYPE", "/dev/foo"}, "", nil}, - {"mkfs.ext4", []string{"-E", "lazy_itable_init=0,lazy_journal_init=0", "-F", "/dev/foo"}, "", fmt.Errorf("formatting failed")}, - }, - expectedError: fmt.Errorf("formatting failed"), - }, - { - description: "Test that 'lsblk' is called and confirms unformatted disk, format passes, second mount fails", - fstype: "ext4", - mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), fmt.Errorf("Still cannot mount")}, - execScripts: []ExecArgs{ - {"fsck", []string{"-a", "/dev/foo"}, "", nil}, - {"lsblk", []string{"-nd", "-o", "FSTYPE", "/dev/foo"}, "", nil}, - {"mkfs.ext4", []string{"-E", "lazy_itable_init=0,lazy_journal_init=0", "-F", "/dev/foo"}, "", nil}, - }, - expectedError: fmt.Errorf("Still cannot mount"), - }, - { - description: "Test that 'lsblk' is called and confirms unformatted disk, format passes, second mount passes", - fstype: "ext4", - mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil}, - execScripts: []ExecArgs{ - {"fsck", []string{"-a", "/dev/foo"}, "", nil}, - {"lsblk", []string{"-nd", "-o", "FSTYPE", "/dev/foo"}, "", nil}, - {"mkfs.ext4", []string{"-E", "lazy_itable_init=0,lazy_journal_init=0", "-F", "/dev/foo"}, "", nil}, - }, - expectedError: nil, - }, - { - description: "Test that 'lsblk' is called and confirms unformatted disk, format passes, second mount passes with ext3", - fstype: "ext3", - mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil}, - execScripts: []ExecArgs{ - {"fsck", []string{"-a", "/dev/foo"}, "", nil}, - {"lsblk", []string{"-nd", "-o", "FSTYPE", "/dev/foo"}, "", nil}, - {"mkfs.ext3", []string{"-E", "lazy_itable_init=0,lazy_journal_init=0", "-F", "/dev/foo"}, "", nil}, - }, - expectedError: nil, - }, - { - description: "test that none ext4 fs does not get called with ext4 options.", - fstype: "xfs", - mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil}, - execScripts: []ExecArgs{ - {"fsck", []string{"-a", "/dev/foo"}, "", nil}, - {"lsblk", []string{"-nd", "-o", "FSTYPE", "/dev/foo"}, "", nil}, - {"mkfs.xfs", []string{"/dev/foo"}, "", nil}, - }, - expectedError: nil, - }, - } - - for _, test := range tests { - commandScripts := []exec.FakeCommandAction{} - for _, expected := range test.execScripts { - ecmd := expected.command - eargs := expected.args - output := expected.output - err := expected.err - commandScript := func(cmd string, args ...string) exec.Cmd { - if cmd != ecmd { - t.Errorf("Unexpected command %s. Expecting %s", cmd, ecmd) - } - - for j := range args { - if args[j] != eargs[j] { - t.Errorf("Unexpected args %v. Expecting %v", args, eargs) - } - } - fake := exec.FakeCmd{ - CombinedOutputScript: []exec.FakeCombinedOutputAction{ - func() ([]byte, error) { return []byte(output), err }, - }, - } - return exec.InitFakeCmd(&fake, cmd, args...) - } - commandScripts = append(commandScripts, commandScript) - } - - fake := exec.FakeExec{ - CommandScript: commandScripts, - } - - fakeMounter := ErrorMounter{&FakeMounter{}, 0, test.mountErrs} - mounter := SafeFormatAndMount{ - Interface: &fakeMounter, - Runner: &fake, - } - - device := "/dev/foo" - dest := "/mnt/bar" - err := mounter.FormatAndMount(device, dest, test.fstype, test.mountOptions) - if test.expectedError == nil { - if err != nil { - t.Errorf("test \"%s\" unexpected non-error: %v", test.description, err) - } - - // Check that something was mounted on the directory - isNotMountPoint, err := fakeMounter.IsLikelyNotMountPoint(dest) - if err != nil || isNotMountPoint { - t.Errorf("test \"%s\" the directory was not mounted", test.description) - } - - //check that the correct device was mounted - mountedDevice, _, err := GetDeviceNameFromMount(fakeMounter.FakeMounter, dest) - if err != nil || mountedDevice != device { - t.Errorf("test \"%s\" the correct device was not mounted", test.description) - } - } else { - if err == nil || test.expectedError.Error() != err.Error() { - t.Errorf("test \"%s\" unexpected error: \n [%v]. \nExpecting [%v]", test.description, err, test.expectedError) - } - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/http_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/http_test.go deleted file mode 100644 index 7990a51d1..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/http_test.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -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 net - -import ( - "net" - "net/http" - "reflect" - "testing" -) - -func TestGetClientIP(t *testing.T) { - ipString := "10.0.0.1" - ip := net.ParseIP(ipString) - invalidIPString := "invalidIPString" - testCases := []struct { - Request http.Request - ExpectedIP net.IP - }{ - { - Request: http.Request{}, - }, - { - Request: http.Request{ - Header: map[string][]string{ - "X-Real-Ip": {ipString}, - }, - }, - ExpectedIP: ip, - }, - { - Request: http.Request{ - Header: map[string][]string{ - "X-Real-Ip": {invalidIPString}, - }, - }, - }, - { - Request: http.Request{ - Header: map[string][]string{ - "X-Forwarded-For": {ipString}, - }, - }, - ExpectedIP: ip, - }, - { - Request: http.Request{ - Header: map[string][]string{ - "X-Forwarded-For": {invalidIPString}, - }, - }, - }, - { - Request: http.Request{ - Header: map[string][]string{ - "X-Forwarded-For": {invalidIPString + "," + ipString}, - }, - }, - ExpectedIP: ip, - }, - { - Request: http.Request{ - RemoteAddr: ipString, - }, - ExpectedIP: ip, - }, - { - Request: http.Request{ - RemoteAddr: invalidIPString, - }, - }, - { - Request: http.Request{ - Header: map[string][]string{ - "X-Forwarded-For": {invalidIPString}, - }, - RemoteAddr: ipString, - }, - ExpectedIP: ip, - }, - } - - for i, test := range testCases { - if a, e := GetClientIP(&test.Request), test.ExpectedIP; reflect.DeepEqual(e, a) != true { - t.Fatalf("test case %d failed. expected: %v, actual: %v", i+1, e, a) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/interface_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/interface_test.go deleted file mode 100644 index 9571e5b48..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/interface_test.go +++ /dev/null @@ -1,300 +0,0 @@ -/* -Copyright 2014 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 net - -import ( - "fmt" - "io" - "net" - "strings" - "testing" -) - -const gatewayfirst = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT -eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0 -eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0 -docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0 -virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0 -` -const gatewaylast = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT -docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0 -virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0 -eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0 -eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0 -` -const gatewaymiddle = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT -eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0 -docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0 -eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0 -virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0 -` -const noInternetConnection = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT -docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0 -virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0 -` -const nothing = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT -` -const gatewayfirstIpv6_1 = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT -eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0 -eth3 0000FE0AA1 00000000 0001 0 0 0 0080FFFF 0 0 0 -docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0 -virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0 -` -const gatewayfirstIpv6_2 = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT -eth3 00000000 0100FE0AA1 0003 0 0 1024 00000000 0 0 0 -eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0 -docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0 -virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0 -` -const route_Invalidhex = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT -eth3 00000000 0100FE0AA 0003 0 0 1024 00000000 0 0 0 -eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0 -docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0 -virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0 -` - -// Based on DigitalOcean COREOS -const gatewayfirstLinkLocal = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT -eth0 00000000 0120372D 0001 0 0 0 00000000 0 0 0 -eth0 00000000 00000000 0001 0 0 2048 00000000 0 0 0 -` - -func TestGetRoutes(t *testing.T) { - testCases := []struct { - tcase string - route string - expected int - }{ - {"gatewayfirst", gatewayfirst, 4}, - {"gatewaymiddle", gatewaymiddle, 4}, - {"gatewaylast", gatewaylast, 4}, - {"nothing", nothing, 0}, - {"gatewayfirstIpv6_1", gatewayfirstIpv6_1, 0}, - {"gatewayfirstIpv6_2", gatewayfirstIpv6_2, 0}, - {"route_Invalidhex", route_Invalidhex, 0}, - } - for _, tc := range testCases { - r := strings.NewReader(tc.route) - routes, err := getRoutes(r) - if len(routes) != tc.expected { - t.Errorf("case[%v]: expected %v, got %v .err : %v", tc.tcase, tc.expected, len(routes), err) - } - } -} - -func TestParseIP(t *testing.T) { - testCases := []struct { - tcase string - ip string - success bool - expected net.IP - }{ - {"empty", "", false, nil}, - {"too short", "AA", false, nil}, - {"too long", "0011223344", false, nil}, - {"invalid", "invalid!", false, nil}, - {"zero", "00000000", true, net.IP{0, 0, 0, 0}}, - {"ffff", "FFFFFFFF", true, net.IP{0xff, 0xff, 0xff, 0xff}}, - {"valid", "12345678", true, net.IP{120, 86, 52, 18}}, - } - for _, tc := range testCases { - ip, err := parseIP(tc.ip) - if !ip.Equal(tc.expected) { - t.Errorf("case[%v]: expected %q, got %q . err : %v", tc.tcase, tc.expected, ip, err) - } - } -} - -func TestIsInterfaceUp(t *testing.T) { - testCases := []struct { - tcase string - intf net.Interface - expected bool - }{ - {"up", net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp}, true}, - {"down", net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: 0}, false}, - {"nothing", net.Interface{}, false}, - } - for _, tc := range testCases { - it := isInterfaceUp(&tc.intf) - if it != tc.expected { - t.Errorf("case[%v]: expected %v, got %v .", tc.tcase, tc.expected, it) - } - } -} - -type addrStruct struct{ val string } - -func (a addrStruct) Network() string { - return a.val -} -func (a addrStruct) String() string { - return a.val -} - -func TestFinalIP(t *testing.T) { - testCases := []struct { - tcase string - addr []net.Addr - expected net.IP - }{ - {"ipv6", []net.Addr{addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}}, nil}, - {"invalidCIDR", []net.Addr{addrStruct{val: "fe80::2f7:67fff:fe6e:2956/64"}}, nil}, - {"loopback", []net.Addr{addrStruct{val: "127.0.0.1/24"}}, nil}, - {"ip4", []net.Addr{addrStruct{val: "10.254.12.132/17"}}, net.ParseIP("10.254.12.132")}, - - {"nothing", []net.Addr{}, nil}, - } - for _, tc := range testCases { - ip, err := getFinalIP(tc.addr) - if !ip.Equal(tc.expected) { - t.Errorf("case[%v]: expected %v, got %v .err : %v", tc.tcase, tc.expected, ip, err) - } - } -} - -func TestAddrs(t *testing.T) { - var nw networkInterfacer = validNetworkInterface{} - intf := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: 0} - addrs, err := nw.Addrs(&intf) - if err != nil { - t.Errorf("expected no error got : %v", err) - } - if len(addrs) != 2 { - t.Errorf("expected addrs: 2 got null") - } -} - -type validNetworkInterface struct { -} - -func (_ validNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) { - c := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp} - return &c, nil -} -func (_ validNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) { - var ifat []net.Addr - ifat = []net.Addr{ - addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}, addrStruct{val: "10.254.71.145/17"}} - return ifat, nil -} - -type validNetworkInterfaceWithLinkLocal struct { -} - -func (_ validNetworkInterfaceWithLinkLocal) InterfaceByName(intfName string) (*net.Interface, error) { - c := net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: net.FlagUp} - return &c, nil -} -func (_ validNetworkInterfaceWithLinkLocal) Addrs(intf *net.Interface) ([]net.Addr, error) { - var ifat []net.Addr - ifat = []net.Addr{addrStruct{val: "169.254.162.166/16"}, addrStruct{val: "45.55.47.146/19"}} - return ifat, nil -} - -type validNetworkInterfacewithIpv6Only struct { -} - -func (_ validNetworkInterfacewithIpv6Only) InterfaceByName(intfName string) (*net.Interface, error) { - c := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp} - return &c, nil -} -func (_ validNetworkInterfacewithIpv6Only) Addrs(intf *net.Interface) ([]net.Addr, error) { - var ifat []net.Addr - ifat = []net.Addr{addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}} - return ifat, nil -} - -type noNetworkInterface struct { -} - -func (_ noNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) { - return nil, fmt.Errorf("unable get Interface") -} -func (_ noNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) { - return nil, nil -} - -type networkInterfacewithNoAddrs struct { -} - -func (_ networkInterfacewithNoAddrs) InterfaceByName(intfName string) (*net.Interface, error) { - c := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp} - return &c, nil -} -func (_ networkInterfacewithNoAddrs) Addrs(intf *net.Interface) ([]net.Addr, error) { - return nil, fmt.Errorf("unable get Addrs") -} - -type networkInterfacewithIpv6addrs struct { -} - -func (_ networkInterfacewithIpv6addrs) InterfaceByName(intfName string) (*net.Interface, error) { - c := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp} - return &c, nil -} -func (_ networkInterfacewithIpv6addrs) Addrs(intf *net.Interface) ([]net.Addr, error) { - var ifat []net.Addr - ifat = []net.Addr{addrStruct{val: "fe80::2f7:6ffff:fe6e:2956/64"}} - return ifat, nil -} - -func TestGetIPFromInterface(t *testing.T) { - testCases := []struct { - tcase string - nwname string - nw networkInterfacer - expected net.IP - }{ - {"valid", "eth3", validNetworkInterface{}, net.ParseIP("10.254.71.145")}, - {"ipv6", "eth3", validNetworkInterfacewithIpv6Only{}, nil}, - {"nothing", "eth3", noNetworkInterface{}, nil}, - } - for _, tc := range testCases { - ip, err := getIPFromInterface(tc.nwname, tc.nw) - if !ip.Equal(tc.expected) { - t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err) - } - } -} - -func TestChooseHostInterfaceFromRoute(t *testing.T) { - testCases := []struct { - tcase string - inFile io.Reader - nw networkInterfacer - expected net.IP - }{ - {"valid_routefirst", strings.NewReader(gatewayfirst), validNetworkInterface{}, net.ParseIP("10.254.71.145")}, - {"valid_routelast", strings.NewReader(gatewaylast), validNetworkInterface{}, net.ParseIP("10.254.71.145")}, - {"valid_routemiddle", strings.NewReader(gatewaymiddle), validNetworkInterface{}, net.ParseIP("10.254.71.145")}, - {"valid_routemiddle_ipv6", strings.NewReader(gatewaymiddle), validNetworkInterfacewithIpv6Only{}, nil}, - {"no internet connection", strings.NewReader(noInternetConnection), validNetworkInterface{}, nil}, - {"no non-link-local ip", strings.NewReader(gatewayfirstLinkLocal), validNetworkInterfaceWithLinkLocal{}, net.ParseIP("45.55.47.146")}, - {"no route", strings.NewReader(nothing), validNetworkInterface{}, nil}, - {"no route file", nil, validNetworkInterface{}, nil}, - {"no interfaces", nil, noNetworkInterface{}, nil}, - {"no interface Addrs", strings.NewReader(gatewaymiddle), networkInterfacewithNoAddrs{}, nil}, - {"Invalid Addrs", strings.NewReader(gatewaymiddle), networkInterfacewithIpv6addrs{}, nil}, - } - for _, tc := range testCases { - ip, err := chooseHostInterfaceFromRoute(tc.inFile, tc.nw) - if !ip.Equal(tc.expected) { - t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err) - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/port_range_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/port_range_test.go deleted file mode 100644 index 9eb081aa7..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/port_range_test.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -Copyright 2015 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 net - -import ( - "testing" - - flag "github.com/spf13/pflag" -) - -func TestPortRange(t *testing.T) { - testCases := []struct { - input string - success bool - expected string - included int - excluded int - }{ - {"100-200", true, "100-200", 200, 201}, - {" 100-200 ", true, "100-200", 200, 201}, - {"0-0", true, "0-0", 0, 1}, - {"", true, "", -1, 0}, - {"100", false, "", -1, -1}, - {"100 - 200", false, "", -1, -1}, - {"-100", false, "", -1, -1}, - {"100-", false, "", -1, -1}, - } - - for i := range testCases { - tc := &testCases[i] - pr := &PortRange{} - var f flag.Value = pr - err := f.Set(tc.input) - if err != nil && tc.success == true { - t.Errorf("expected success, got %q", err) - continue - } else if err == nil && tc.success == false { - t.Errorf("expected failure") - continue - } else if tc.success { - if f.String() != tc.expected { - t.Errorf("expected %q, got %q", tc.expected, f.String()) - } - if tc.included >= 0 && !pr.Contains(tc.included) { - t.Errorf("expected %q to include %d", f.String(), tc.included) - } - if tc.excluded >= 0 && pr.Contains(tc.excluded) { - t.Errorf("expected %q to exclude %d", f.String(), tc.excluded) - } - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/port_split_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/port_split_test.go deleted file mode 100644 index 2e9e135c0..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/port_split_test.go +++ /dev/null @@ -1,111 +0,0 @@ -/* -Copyright 2015 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 net - -import ( - "testing" -) - -func TestSplitSchemeNamePort(t *testing.T) { - table := []struct { - in string - name, port, scheme string - valid bool - normalized bool - }{ - { - in: "aoeu:asdf", - name: "aoeu", - port: "asdf", - valid: true, - }, { - in: "http:aoeu:asdf", - scheme: "http", - name: "aoeu", - port: "asdf", - valid: true, - }, { - in: "https:aoeu:", - scheme: "https", - name: "aoeu", - port: "", - valid: true, - normalized: false, - }, { - in: "https:aoeu:asdf", - scheme: "https", - name: "aoeu", - port: "asdf", - valid: true, - }, { - in: "aoeu:", - name: "aoeu", - valid: true, - normalized: false, - }, { - in: ":asdf", - valid: false, - }, { - in: "aoeu:asdf:htns", - valid: false, - }, { - in: "aoeu", - name: "aoeu", - valid: true, - }, { - in: "", - valid: false, - }, - } - - for _, item := range table { - scheme, name, port, valid := SplitSchemeNamePort(item.in) - if e, a := item.scheme, scheme; e != a { - t.Errorf("%q: Wanted %q, got %q", item.in, e, a) - } - if e, a := item.name, name; e != a { - t.Errorf("%q: Wanted %q, got %q", item.in, e, a) - } - if e, a := item.port, port; e != a { - t.Errorf("%q: Wanted %q, got %q", item.in, e, a) - } - if e, a := item.valid, valid; e != a { - t.Errorf("%q: Wanted %t, got %t", item.in, e, a) - } - - // Make sure valid items round trip through JoinSchemeNamePort - if item.valid { - out := JoinSchemeNamePort(scheme, name, port) - if item.normalized && out != item.in { - t.Errorf("%q: Wanted %s, got %s", item.in, item.in, out) - } - scheme, name, port, valid := SplitSchemeNamePort(out) - if e, a := item.scheme, scheme; e != a { - t.Errorf("%q: Wanted %q, got %q", item.in, e, a) - } - if e, a := item.name, name; e != a { - t.Errorf("%q: Wanted %q, got %q", item.in, e, a) - } - if e, a := item.port, port; e != a { - t.Errorf("%q: Wanted %q, got %q", item.in, e, a) - } - if e, a := item.valid, valid; e != a { - t.Errorf("%q: Wanted %t, got %t", item.in, e, a) - } - } - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/sets/README.md b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/sets/README.md new file mode 100644 index 000000000..b0f238a26 --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/sets/README.md @@ -0,0 +1,17 @@ +This package contains hand-coded set implementations that should be similar to +the autogenerated ones in `pkg/util/sets`. + +We can't simply use net.IPNet as a map-key in Go (because it contains a +`[]byte`). + +We could use the same workaround we use here (a string representation as the +key) to autogenerate sets. If we do that, or decide on an alternate approach, +we should replace the implementations in this package with the autogenerated +versions. + +It is expected that callers will alias this import as `netsets` +i.e. `import netsets "k8s.io/kubernetes/pkg/util/net/sets"` + + + +[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/pkg/util/net/sets/README.md?pixel)]() diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/sets/ipnet.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/sets/ipnet.go new file mode 100644 index 000000000..db117f63e --- /dev/null +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/sets/ipnet.go @@ -0,0 +1,119 @@ +/* +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 sets + +import ( + "net" + "strings" +) + +type IPNet map[string]*net.IPNet + +func ParseIPNets(specs ...string) (IPNet, error) { + ipnetset := make(IPNet) + for _, spec := range specs { + spec = strings.TrimSpace(spec) + _, ipnet, err := net.ParseCIDR(spec) + if err != nil { + return nil, err + } + k := ipnet.String() // In case of normalization + ipnetset[k] = ipnet + } + return ipnetset, nil +} + +// Insert adds items to the set. +func (s IPNet) Insert(items ...*net.IPNet) { + for _, item := range items { + s[item.String()] = item + } +} + +// Delete removes all items from the set. +func (s IPNet) Delete(items ...*net.IPNet) { + for _, item := range items { + delete(s, item.String()) + } +} + +// Has returns true if and only if item is contained in the set. +func (s IPNet) Has(item *net.IPNet) bool { + _, contained := s[item.String()] + return contained +} + +// HasAll returns true if and only if all items are contained in the set. +func (s IPNet) HasAll(items ...*net.IPNet) bool { + for _, item := range items { + if !s.Has(item) { + return false + } + } + return true +} + +// Difference returns a set of objects that are not in s2 +// For example: +// s1 = {a1, a2, a3} +// s2 = {a1, a2, a4, a5} +// s1.Difference(s2) = {a3} +// s2.Difference(s1) = {a4, a5} +func (s IPNet) Difference(s2 IPNet) IPNet { + result := make(IPNet) + for k, i := range s { + _, found := s2[k] + if found { + continue + } + result[k] = i + } + return result +} + +// StringSlice returns a []string with the String representation of each element in the set. +// Order is undefined. +func (s IPNet) StringSlice() []string { + a := make([]string, 0, len(s)) + for k := range s { + a = append(a, k) + } + return a +} + +// IsSuperset returns true if and only if s1 is a superset of s2. +func (s1 IPNet) IsSuperset(s2 IPNet) bool { + for k := range s2 { + _, found := s1[k] + if !found { + return false + } + } + return true +} + +// Equal returns true if and only if s1 is equal (as a set) to s2. +// Two sets are equal if their membership is identical. +// (In practice, this means same elements, order doesn't matter) +func (s1 IPNet) Equal(s2 IPNet) bool { + return len(s1) == len(s2) && s1.IsSuperset(s2) +} + +// Len returns the size of the set. +func (s IPNet) Len() int { + return len(s) +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/node/node.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/node/node.go deleted file mode 100644 index cbc062fc9..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/node/node.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright 2015 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 node - -import ( - "fmt" - "net" - "os/exec" - "strings" - - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/api" -) - -func GetHostname(hostnameOverride string) string { - hostname := hostnameOverride - if string(hostname) == "" { - nodename, err := exec.Command("uname", "-n").Output() - if err != nil { - glog.Fatalf("Couldn't determine hostname: %v", err) - } - hostname = string(nodename) - } - return strings.ToLower(strings.TrimSpace(hostname)) -} - -// GetNodeHostIP returns the provided node's IP, based on the priority: -// 1. NodeInternalIP -// 2. NodeExternalIP -// 3. NodeLegacyHostIP -func GetNodeHostIP(node *api.Node) (net.IP, error) { - addresses := node.Status.Addresses - addressMap := make(map[api.NodeAddressType][]api.NodeAddress) - for i := range addresses { - addressMap[addresses[i].Type] = append(addressMap[addresses[i].Type], addresses[i]) - } - if addresses, ok := addressMap[api.NodeInternalIP]; ok { - return net.ParseIP(addresses[0].Address), nil - } - if addresses, ok := addressMap[api.NodeExternalIP]; ok { - return net.ParseIP(addresses[0].Address), nil - } - if addresses, ok := addressMap[api.NodeLegacyHostIP]; ok { - return net.ParseIP(addresses[0].Address), nil - } - return nil, fmt.Errorf("host IP unknown; known addresses: %v", addresses) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/doc.go deleted file mode 100644 index 539d6c43b..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2015 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 oom implements utility functions relating to out of memory management. -package oom diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom.go deleted file mode 100644 index c6d563ab2..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2015 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 oom - -// This is a struct instead of an interface to allow injection of process ID listers and -// applying OOM score in tests. -// TODO: make this an interface, and inject a mock ioutil struct for testing. -type OOMAdjuster struct { - pidLister func(cgroupName string) ([]int, error) - ApplyOOMScoreAdj func(pid int, oomScoreAdj int) error - ApplyOOMScoreAdjContainer func(cgroupName string, oomScoreAdj, maxTries int) error -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_fake.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_fake.go deleted file mode 100644 index bd0bf6f45..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_fake.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright 2015 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 oom - -type FakeOOMAdjuster struct{} - -func NewFakeOOMAdjuster() *OOMAdjuster { - return &OOMAdjuster{ - pidLister: func(cgroupName string) ([]int, error) { return make([]int, 0), nil }, - ApplyOOMScoreAdj: fakeApplyOOMScoreAdj, - ApplyOOMScoreAdjContainer: fakeApplyOOMScoreAdjContainer, - } -} - -func fakeApplyOOMScoreAdj(pid int, oomScoreAdj int) error { - return nil -} - -func fakeApplyOOMScoreAdjContainer(cgroupName string, oomScoreAdj, maxTries int) error { - return nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_linux.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_linux.go deleted file mode 100644 index 5503bad8c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_linux.go +++ /dev/null @@ -1,128 +0,0 @@ -// +build cgo,linux - -/* -Copyright 2015 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 oom - -import ( - "fmt" - "io/ioutil" - "os" - "path" - "strconv" - - "github.com/golang/glog" - "github.com/opencontainers/runc/libcontainer/cgroups/fs" - "github.com/opencontainers/runc/libcontainer/configs" -) - -func NewOOMAdjuster() *OOMAdjuster { - oomAdjuster := &OOMAdjuster{ - pidLister: getPids, - ApplyOOMScoreAdj: applyOOMScoreAdj, - } - oomAdjuster.ApplyOOMScoreAdjContainer = oomAdjuster.applyOOMScoreAdjContainer - return oomAdjuster -} - -func getPids(cgroupName string) ([]int, error) { - fsManager := fs.Manager{ - Cgroups: &configs.Cgroup{ - Parent: "/", - Name: cgroupName, - }, - } - return fsManager.GetPids() -} - -func syscallNotExists(err error) bool { - if err == nil { - return false - } - if e, ok := err.(*os.SyscallError); ok && os.IsNotExist(e) { - return true - } - return false -} - -// Writes 'value' to /proc//oom_score_adj. PID = 0 means self -// Returns os.ErrNotExist if the `pid` does not exist. -func applyOOMScoreAdj(pid int, oomScoreAdj int) error { - if pid < 0 { - return fmt.Errorf("invalid PID %d specified for oom_score_adj", pid) - } - - var pidStr string - if pid == 0 { - pidStr = "self" - } else { - pidStr = strconv.Itoa(pid) - } - - maxTries := 2 - oomScoreAdjPath := path.Join("/proc", pidStr, "oom_score_adj") - value := strconv.Itoa(oomScoreAdj) - var err error - for i := 0; i < maxTries; i++ { - if err = ioutil.WriteFile(oomScoreAdjPath, []byte(value), 0700); err != nil { - if syscallNotExists(err) { - return os.ErrNotExist - } - err = fmt.Errorf("failed to apply oom-score-adj to pid %d (%v)", err) - } - } - return err -} - -// Writes 'value' to /proc//oom_score_adj for all processes in cgroup cgroupName. -// Keeps trying to write until the process list of the cgroup stabilizes, or until maxTries tries. -func (oomAdjuster *OOMAdjuster) applyOOMScoreAdjContainer(cgroupName string, oomScoreAdj, maxTries int) error { - adjustedProcessSet := make(map[int]bool) - for i := 0; i < maxTries; i++ { - continueAdjusting := false - pidList, err := oomAdjuster.pidLister(cgroupName) - if err != nil { - if syscallNotExists(err) { - // Nothing to do since the container doesn't exist anymore. - return os.ErrNotExist - } - continueAdjusting = true - glog.Errorf("Error getting process list for cgroup %s: %+v", cgroupName, err) - } else if len(pidList) == 0 { - continueAdjusting = true - } else { - for _, pid := range pidList { - if !adjustedProcessSet[pid] { - continueAdjusting = true - if err = oomAdjuster.ApplyOOMScoreAdj(pid, oomScoreAdj); err == nil { - adjustedProcessSet[pid] = true - } - // Processes can come and go while we try to apply oom score adjust value. So ignore errors here. - } - } - } - if !continueAdjusting { - return nil - } - // There's a slight race. A process might have forked just before we write its OOM score adjust. - // The fork might copy the parent process's old OOM score, then this function might execute and - // update the parent's OOM score, but the forked process id might not be reflected in cgroup.procs - // for a short amount of time. So this function might return without changing the forked process's - // OOM score. Very unlikely race, so ignoring this for now. - } - return fmt.Errorf("exceeded maxTries, some processes might not have desired OOM score") -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_linux_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_linux_test.go deleted file mode 100644 index c3a850d91..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_linux_test.go +++ /dev/null @@ -1,110 +0,0 @@ -// +build cgo,linux - -/* -Copyright 2015 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 oom - -import ( - "testing" -) - -// Converts a sequence of PID lists into a PID lister. -// The PID lister returns pidListSequence[i] on the ith call. If i >= length of pidListSequence -// then return the last element of pidListSequence (the sequence is considered to have) stabilized. -func sequenceToPidLister(pidListSequence [][]int) func(string) ([]int, error) { - var numCalls int - return func(cgroupName string) ([]int, error) { - numCalls++ - if len(pidListSequence) == 0 { - return []int{}, nil - } else if numCalls > len(pidListSequence) { - return pidListSequence[len(pidListSequence)-1], nil - } - return pidListSequence[numCalls-1], nil - } -} - -// Tests that applyOOMScoreAdjContainer correctly applies OOM scores to relevant processes, or -// returns the right error. -func applyOOMScoreAdjContainerTester(pidListSequence [][]int, maxTries int, appliedPids []int, expectedError bool, t *testing.T) { - pidOOMs := make(map[int]bool) - - // Mock ApplyOOMScoreAdj and pidLister. - oomAdjuster := NewOOMAdjuster() - oomAdjuster.ApplyOOMScoreAdj = func(pid int, oomScoreAdj int) error { - pidOOMs[pid] = true - return nil - } - oomAdjuster.pidLister = sequenceToPidLister(pidListSequence) - err := oomAdjuster.ApplyOOMScoreAdjContainer("", 100, maxTries) - - // Check error value. - if expectedError && err == nil { - t.Errorf("Expected error %+v when running ApplyOOMScoreAdjContainer but got no error", expectedError) - return - } else if !expectedError && err != nil { - t.Errorf("Expected no error but got error %+v when running ApplyOOMScoreAdjContainer", err) - return - } else if err != nil { - return - } - - // Check that OOM scores were applied to the right processes. - if len(appliedPids) != len(pidOOMs) { - t.Errorf("Applied OOM scores to incorrect number of processes") - return - } - for _, pid := range appliedPids { - if !pidOOMs[pid] { - t.Errorf("Failed to apply OOM scores to process %d", pid) - } - } -} - -func TestOOMScoreAdjContainer(t *testing.T) { - pidListSequenceEmpty := [][]int{} - applyOOMScoreAdjContainerTester(pidListSequenceEmpty, 3, nil, true, t) - - pidListSequence1 := [][]int{ - {1, 2}, - } - applyOOMScoreAdjContainerTester(pidListSequence1, 1, nil, true, t) - applyOOMScoreAdjContainerTester(pidListSequence1, 2, []int{1, 2}, false, t) - applyOOMScoreAdjContainerTester(pidListSequence1, 3, []int{1, 2}, false, t) - - pidListSequence3 := [][]int{ - {1, 2}, - {1, 2, 4, 5}, - {2, 1, 4, 5, 3}, - } - applyOOMScoreAdjContainerTester(pidListSequence3, 1, nil, true, t) - applyOOMScoreAdjContainerTester(pidListSequence3, 2, nil, true, t) - applyOOMScoreAdjContainerTester(pidListSequence3, 3, nil, true, t) - applyOOMScoreAdjContainerTester(pidListSequence3, 4, []int{1, 2, 3, 4, 5}, false, t) - - pidListSequenceLag := [][]int{ - {}, - {}, - {}, - {1, 2, 4}, - {1, 2, 4, 5}, - } - for i := 1; i < 5; i++ { - applyOOMScoreAdjContainerTester(pidListSequenceLag, i, nil, true, t) - } - applyOOMScoreAdjContainerTester(pidListSequenceLag, 6, []int{1, 2, 4, 5}, false, t) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_unsupported.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_unsupported.go deleted file mode 100644 index 9c4473f7e..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/oom/oom_unsupported.go +++ /dev/null @@ -1,40 +0,0 @@ -// +build !cgo !linux - -/* -Copyright 2015 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 oom - -import ( - "errors" -) - -var unsupportedErr = errors.New("setting OOM scores is unsupported in this build") - -func NewOOMAdjuster() *OOMAdjuster { - return &OOMAdjuster{ - ApplyOOMScoreAdj: unsupportedApplyOOMScoreAdj, - ApplyOOMScoreAdjContainer: unsupportedApplyOOMScoreAdjContainer, - } -} - -func unsupportedApplyOOMScoreAdj(pid int, oomScoreAdj int) error { - return unsupportedErr -} - -func unsupportedApplyOOMScoreAdjContainer(cgroupName string, oomScoreAdj, maxTries int) error { - return unsupportedErr -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/pod/pod.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/pod/pod.go index 576b0cdab..8fb5cadd0 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/pod/pod.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/pod/pod.go @@ -17,10 +17,18 @@ limitations under the License. package pod import ( + "fmt" "hash/adler32" + "time" + + "github.com/golang/glog" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/errors" + unversionedcore "k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned" + errorsutil "k8s.io/kubernetes/pkg/util/errors" hashutil "k8s.io/kubernetes/pkg/util/hash" + "k8s.io/kubernetes/pkg/util/wait" ) func GetPodTemplateSpecHash(template api.PodTemplateSpec) uint32 { @@ -28,3 +36,54 @@ func GetPodTemplateSpecHash(template api.PodTemplateSpec) uint32 { hashutil.DeepHashObject(podTemplateSpecHasher, template) return podTemplateSpecHasher.Sum32() } + +// TODO: use client library instead when it starts to support update retries +// see https://github.com/kubernetes/kubernetes/issues/21479 +type updatePodFunc func(pod *api.Pod) error + +// UpdatePodWithRetries updates a pod with given applyUpdate function. Note that pod not found error is ignored. +// The returned bool value can be used to tell if the pod is actually updated. +func UpdatePodWithRetries(podClient unversionedcore.PodInterface, pod *api.Pod, applyUpdate updatePodFunc) (*api.Pod, bool, error) { + var err error + var podUpdated bool + oldPod := pod + if err = wait.Poll(10*time.Millisecond, 1*time.Minute, func() (bool, error) { + pod, err = podClient.Get(oldPod.Name) + if err != nil { + return false, err + } + // Apply the update, then attempt to push it to the apiserver. + if err = applyUpdate(pod); err != nil { + return false, err + } + if pod, err = podClient.Update(pod); err == nil { + // Update successful. + return true, nil + } + // TODO: don't retry on perm-failed errors and handle them gracefully + // Update could have failed due to conflict error. Try again. + return false, nil + }); err == nil { + // When there's no error, we've updated this pod. + podUpdated = true + } + + // Handle returned error from wait poll + if err == wait.ErrWaitTimeout { + err = fmt.Errorf("timed out trying to update pod: %+v", oldPod) + } + // Ignore the pod not found error, but the pod isn't updated. + if errors.IsNotFound(err) { + glog.V(4).Infof("%s %s/%s is not found, skip updating it.", oldPod.Kind, oldPod.Namespace, oldPod.Name) + err = nil + } + // Ignore the precondition violated error, but the pod isn't updated. + if err == errorsutil.ErrPreconditionViolated { + glog.V(4).Infof("%s %s/%s precondition doesn't hold, skip updating it.", oldPod.Kind, oldPod.Namespace, oldPod.Name) + err = nil + } + + // If the error is non-nil the returned pod cannot be trusted; if podUpdated is false, the pod isn't updated; + // if the error is nil and podUpdated is true, the returned pod contains the applied update. + return pod, podUpdated, err +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/doc.go deleted file mode 100644 index d94e6687c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2015 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 procfs implements utility functions relating to the /proc mount. -package procfs diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/example_proc_cgroup b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/example_proc_cgroup deleted file mode 100644 index 3e42ce927..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/example_proc_cgroup +++ /dev/null @@ -1,10 +0,0 @@ -11:name=systemd:/user/1000.user/c1.session -10:hugetlb:/user/1000.user/c1.session -9:perf_event:/user/1000.user/c1.session -8:blkio:/user/1000.user/c1.session -7:freezer:/user/1000.user/c1.session -6:devices:/user/1000.user/c1.session -5:memory:/user/1000.user/c1.session -4:cpuacct:/user/1000.user/c1.session -3:cpu:/user/1000.user/c1.session -2:cpuset:/ \ No newline at end of file diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs.go deleted file mode 100644 index cc432255f..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2015 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 procfs - -import ( - "fmt" - "io/ioutil" - "os" - "path" - "strconv" - "strings" -) - -type ProcFS struct{} - -func NewProcFS() ProcFSInterface { - return &ProcFS{} -} - -func containerNameFromProcCgroup(content string) (string, error) { - lines := strings.Split(content, "\n") - for _, line := range lines { - entries := strings.SplitN(line, ":", 3) - if len(entries) == 3 && entries[1] == "devices" { - return strings.TrimSpace(entries[2]), nil - } - } - return "", fmt.Errorf("could not find devices cgroup location") -} - -// getFullContainerName gets the container name given the root process id of the container. -// Eg. If the devices cgroup for the container is stored in /sys/fs/cgroup/devices/docker/nginx, -// return docker/nginx. Assumes that the process is part of exactly one cgroup hierarchy. -func (pfs *ProcFS) GetFullContainerName(pid int) (string, error) { - filePath := path.Join("/proc", strconv.Itoa(pid), "cgroup") - content, err := ioutil.ReadFile(filePath) - if err != nil { - if e, ok := err.(*os.SyscallError); ok && os.IsNotExist(e) { - return "", os.ErrNotExist - } - return "", err - } - return containerNameFromProcCgroup(string(content)) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs_fake.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs_fake.go deleted file mode 100644 index 8d16aa53c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs_fake.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 2015 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 procfs - -type FakeProcFS struct{} - -func NewFakeProcFS() ProcFSInterface { - return &FakeProcFS{} -} - -// GetFullContainerName gets the container name given the root process id of the container. -// Eg. If the devices cgroup for the container is stored in /sys/fs/cgroup/devices/docker/nginx, -// return docker/nginx. Assumes that the process is part of exactly one cgroup hierarchy. -func (fakePfs *FakeProcFS) GetFullContainerName(pid int) (string, error) { - return "", nil -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs_interface.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs_interface.go deleted file mode 100644 index d3bf14f0c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs_interface.go +++ /dev/null @@ -1,22 +0,0 @@ -/* -Copyright 2015 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 procfs - -type ProcFSInterface interface { - // GetFullContainerName gets the container name given the root process id of the container. - GetFullContainerName(pid int) (string, error) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs_test.go deleted file mode 100644 index 609543dca..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/procfs/procfs_test.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2015 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 procfs - -import ( - "io/ioutil" - "testing" -) - -func verifyContainerName(procCgroupText, expectedName string, expectedErr bool, t *testing.T) { - name, err := containerNameFromProcCgroup(procCgroupText) - if expectedErr && err == nil { - t.Errorf("Expected error but did not get error in verifyContainerName") - return - } else if !expectedErr && err != nil { - t.Errorf("Expected no error, but got error %+v in verifyContainerName", err) - return - } else if expectedErr { - return - } - if name != expectedName { - t.Errorf("Expected container name %s but got name %s", expectedName, name) - } -} - -func TestContainerNameFromProcCgroup(t *testing.T) { - procCgroupValid := "2:devices:docker/kubelet" - verifyContainerName(procCgroupValid, "docker/kubelet", false, t) - - procCgroupEmpty := "" - verifyContainerName(procCgroupEmpty, "", true, t) - - content, err := ioutil.ReadFile("example_proc_cgroup") - if err != nil { - t.Errorf("Could not read example /proc cgroup file") - } - verifyContainerName(string(content), "/user/1000.user/c1.session", false, t) - - procCgroupNoDevice := "2:freezer:docker/kubelet\n5:cpuacct:pkg/kubectl" - verifyContainerName(procCgroupNoDevice, "", true, t) - - procCgroupInvalid := "devices:docker/kubelet\ncpuacct:pkg/kubectl" - verifyContainerName(procCgroupInvalid, "", true, t) -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/dial.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/dial.go deleted file mode 100644 index 33cecb7ea..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/dial.go +++ /dev/null @@ -1,106 +0,0 @@ -/* -Copyright 2015 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 proxy - -import ( - "crypto/tls" - "fmt" - "net" - "net/http" - "net/url" - - "github.com/golang/glog" - - utilnet "k8s.io/kubernetes/pkg/util/net" - "k8s.io/kubernetes/third_party/golang/netutil" -) - -func DialURL(url *url.URL, transport http.RoundTripper) (net.Conn, error) { - dialAddr := netutil.CanonicalAddr(url) - - dialer, _ := utilnet.Dialer(transport) - - switch url.Scheme { - case "http": - if dialer != nil { - return dialer("tcp", dialAddr) - } - return net.Dial("tcp", dialAddr) - case "https": - // Get the tls config from the transport if we recognize it - var tlsConfig *tls.Config - var tlsConn *tls.Conn - var err error - tlsConfig, _ = utilnet.TLSClientConfig(transport) - - if dialer != nil { - // We have a dialer; use it to open the connection, then - // create a tls client using the connection. - netConn, err := dialer("tcp", dialAddr) - if err != nil { - return nil, err - } - if tlsConfig == nil { - // tls.Client requires non-nil config - glog.Warningf("using custom dialer with no TLSClientConfig. Defaulting to InsecureSkipVerify") - // tls.Handshake() requires ServerName or InsecureSkipVerify - tlsConfig = &tls.Config{ - InsecureSkipVerify: true, - } - } else if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify { - // tls.Handshake() requires ServerName or InsecureSkipVerify - // infer the ServerName from the hostname we're connecting to. - inferredHost := dialAddr - if host, _, err := net.SplitHostPort(dialAddr); err == nil { - inferredHost = host - } - // Make a copy to avoid polluting the provided config - tlsConfigCopy := *tlsConfig - tlsConfigCopy.ServerName = inferredHost - tlsConfig = &tlsConfigCopy - } - tlsConn = tls.Client(netConn, tlsConfig) - if err := tlsConn.Handshake(); err != nil { - netConn.Close() - return nil, err - } - - } else { - // Dial - tlsConn, err = tls.Dial("tcp", dialAddr, tlsConfig) - if err != nil { - return nil, err - } - } - - // Return if we were configured to skip validation - if tlsConfig != nil && tlsConfig.InsecureSkipVerify { - return tlsConn, nil - } - - // Verify - host, _, _ := net.SplitHostPort(dialAddr) - if err := tlsConn.VerifyHostname(host); err != nil { - tlsConn.Close() - return nil, err - } - - return tlsConn, nil - default: - return nil, fmt.Errorf("Unknown scheme: %s", url.Scheme) - } -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/doc.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/doc.go deleted file mode 100644 index 0d7519a0c..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2014 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 proxy provides transport and upgrade support for proxies -package proxy diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/transport.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/transport.go deleted file mode 100644 index 079670c0a..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/transport.go +++ /dev/null @@ -1,241 +0,0 @@ -/* -Copyright 2014 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 proxy - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "path" - "strings" - - "github.com/golang/glog" - "golang.org/x/net/html" - "golang.org/x/net/html/atom" - - "k8s.io/kubernetes/pkg/util/net" - "k8s.io/kubernetes/pkg/util/sets" -) - -// atomsToAttrs states which attributes of which tags require URL substitution. -// Sources: http://www.w3.org/TR/REC-html40/index/attributes.html -// http://www.w3.org/html/wg/drafts/html/master/index.html#attributes-1 -var atomsToAttrs = map[atom.Atom]sets.String{ - atom.A: sets.NewString("href"), - atom.Applet: sets.NewString("codebase"), - atom.Area: sets.NewString("href"), - atom.Audio: sets.NewString("src"), - atom.Base: sets.NewString("href"), - atom.Blockquote: sets.NewString("cite"), - atom.Body: sets.NewString("background"), - atom.Button: sets.NewString("formaction"), - atom.Command: sets.NewString("icon"), - atom.Del: sets.NewString("cite"), - atom.Embed: sets.NewString("src"), - atom.Form: sets.NewString("action"), - atom.Frame: sets.NewString("longdesc", "src"), - atom.Head: sets.NewString("profile"), - atom.Html: sets.NewString("manifest"), - atom.Iframe: sets.NewString("longdesc", "src"), - atom.Img: sets.NewString("longdesc", "src", "usemap"), - atom.Input: sets.NewString("src", "usemap", "formaction"), - atom.Ins: sets.NewString("cite"), - atom.Link: sets.NewString("href"), - atom.Object: sets.NewString("classid", "codebase", "data", "usemap"), - atom.Q: sets.NewString("cite"), - atom.Script: sets.NewString("src"), - atom.Source: sets.NewString("src"), - atom.Video: sets.NewString("poster", "src"), - - // TODO: css URLs hidden in style elements. -} - -// Transport is a transport for text/html content that replaces URLs in html -// content with the prefix of the proxy server -type Transport struct { - Scheme string - Host string - PathPrepend string - - http.RoundTripper -} - -// RoundTrip implements the http.RoundTripper interface -func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { - // Add reverse proxy headers. - forwardedURI := path.Join(t.PathPrepend, req.URL.Path) - if strings.HasSuffix(req.URL.Path, "/") { - forwardedURI = forwardedURI + "/" - } - req.Header.Set("X-Forwarded-Uri", forwardedURI) - if len(t.Host) > 0 { - req.Header.Set("X-Forwarded-Host", t.Host) - } - if len(t.Scheme) > 0 { - req.Header.Set("X-Forwarded-Proto", t.Scheme) - } - - rt := t.RoundTripper - if rt == nil { - rt = http.DefaultTransport - } - resp, err := rt.RoundTrip(req) - - if err != nil { - message := fmt.Sprintf("Error: '%s'\nTrying to reach: '%v'", err.Error(), req.URL.String()) - resp = &http.Response{ - StatusCode: http.StatusServiceUnavailable, - Body: ioutil.NopCloser(strings.NewReader(message)), - } - return resp, nil - } - - if redirect := resp.Header.Get("Location"); redirect != "" { - resp.Header.Set("Location", t.rewriteURL(redirect, req.URL)) - return resp, nil - } - - cType := resp.Header.Get("Content-Type") - cType = strings.TrimSpace(strings.SplitN(cType, ";", 2)[0]) - if cType != "text/html" { - // Do nothing, simply pass through - return resp, nil - } - - return t.rewriteResponse(req, resp) -} - -var _ = net.RoundTripperWrapper(&Transport{}) - -func (rt *Transport) WrappedRoundTripper() http.RoundTripper { - return rt.RoundTripper -} - -// rewriteURL rewrites a single URL to go through the proxy, if the URL refers -// to the same host as sourceURL, which is the page on which the target URL -// occurred. If any error occurs (e.g. parsing), it returns targetURL. -func (t *Transport) rewriteURL(targetURL string, sourceURL *url.URL) string { - url, err := url.Parse(targetURL) - if err != nil { - return targetURL - } - - isDifferentHost := url.Host != "" && url.Host != sourceURL.Host - isRelative := !strings.HasPrefix(url.Path, "/") - if isDifferentHost || isRelative { - return targetURL - } - - url.Scheme = t.Scheme - url.Host = t.Host - origPath := url.Path - // Do not rewrite URL if the sourceURL already contains the necessary prefix. - if strings.HasPrefix(url.Path, t.PathPrepend) { - return url.String() - } - url.Path = path.Join(t.PathPrepend, url.Path) - if strings.HasSuffix(origPath, "/") { - // Add back the trailing slash, which was stripped by path.Join(). - url.Path += "/" - } - - return url.String() -} - -// rewriteHTML scans the HTML for tags with url-valued attributes, and updates -// those values with the urlRewriter function. The updated HTML is output to the -// writer. -func rewriteHTML(reader io.Reader, writer io.Writer, urlRewriter func(string) string) error { - // Note: This assumes the content is UTF-8. - tokenizer := html.NewTokenizer(reader) - - var err error - for err == nil { - tokenType := tokenizer.Next() - switch tokenType { - case html.ErrorToken: - err = tokenizer.Err() - case html.StartTagToken, html.SelfClosingTagToken: - token := tokenizer.Token() - if urlAttrs, ok := atomsToAttrs[token.DataAtom]; ok { - for i, attr := range token.Attr { - if urlAttrs.Has(attr.Key) { - token.Attr[i].Val = urlRewriter(attr.Val) - } - } - } - _, err = writer.Write([]byte(token.String())) - default: - _, err = writer.Write(tokenizer.Raw()) - } - } - if err != io.EOF { - return err - } - return nil -} - -// rewriteResponse modifies an HTML response by updating absolute links referring -// to the original host to instead refer to the proxy transport. -func (t *Transport) rewriteResponse(req *http.Request, resp *http.Response) (*http.Response, error) { - origBody := resp.Body - defer origBody.Close() - - newContent := &bytes.Buffer{} - var reader io.Reader = origBody - var writer io.Writer = newContent - encoding := resp.Header.Get("Content-Encoding") - switch encoding { - case "gzip": - var err error - reader, err = gzip.NewReader(reader) - if err != nil { - return nil, fmt.Errorf("errorf making gzip reader: %v", err) - } - gzw := gzip.NewWriter(writer) - defer gzw.Close() - writer = gzw - // TODO: support flate, other encodings. - case "": - // This is fine - default: - // Some encoding we don't understand-- don't try to parse this - glog.Errorf("Proxy encountered encoding %v for text/html; can't understand this so not fixing links.", encoding) - return resp, nil - } - - urlRewriter := func(targetUrl string) string { - return t.rewriteURL(targetUrl, req.URL) - } - err := rewriteHTML(reader, writer, urlRewriter) - if err != nil { - glog.Errorf("Failed to rewrite URLs: %v", err) - return resp, err - } - - resp.Body = ioutil.NopCloser(newContent) - // Update header node with new content-length - // TODO: Remove any hash/signature headers here? - resp.Header.Del("Content-Length") - resp.ContentLength = int64(newContent.Len()) - - return resp, err -} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/transport_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/transport_test.go deleted file mode 100644 index 5a71bb1b5..000000000 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/proxy/transport_test.go +++ /dev/null @@ -1,262 +0,0 @@ -/* -Copyright 2014 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 proxy - -import ( - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - "net/url" - "strings" - "testing" -) - -func parseURLOrDie(inURL string) *url.URL { - parsed, err := url.Parse(inURL) - if err != nil { - panic(err) - } - return parsed -} - -func TestProxyTransport(t *testing.T) { - testTransport := &Transport{ - Scheme: "http", - Host: "foo.com", - PathPrepend: "/proxy/minion/minion1:10250", - } - testTransport2 := &Transport{ - Scheme: "https", - Host: "foo.com", - PathPrepend: "/proxy/minion/minion1:8080", - } - emptyHostTransport := &Transport{ - Scheme: "https", - PathPrepend: "/proxy/minion/minion1:10250", - } - emptySchemeTransport := &Transport{ - Host: "foo.com", - PathPrepend: "/proxy/minion/minion1:10250", - } - type Item struct { - input string - sourceURL string - transport *Transport - output string - contentType string - forwardedURI string - redirect string - redirectWant string - } - - table := map[string]Item{ - "normal": { - input: `

      `, - sourceURL: "http://myminion.com/logs/log.log", - transport: testTransport, - output: `
      kubelet.loggoogle.log
      `, - contentType: "text/html", - forwardedURI: "/proxy/minion/minion1:10250/logs/log.log", - }, - "full document": { - input: `
      kubelet.loggoogle.log
      `, - sourceURL: "http://myminion.com/logs/log.log", - transport: testTransport, - output: `
      kubelet.loggoogle.log
      `, - contentType: "text/html", - forwardedURI: "/proxy/minion/minion1:10250/logs/log.log", - }, - "trailing slash": { - input: `
      kubelet.loggoogle.log
      `, - sourceURL: "http://myminion.com/logs/log.log", - transport: testTransport, - output: `
      kubelet.loggoogle.log
      `, - contentType: "text/html", - forwardedURI: "/proxy/minion/minion1:10250/logs/log.log", - }, - "content-type charset": { - input: `
      kubelet.loggoogle.log
      `, - sourceURL: "http://myminion.com/logs/log.log", - transport: testTransport, - output: `
      kubelet.loggoogle.log
      `, - contentType: "text/html; charset=utf-8", - forwardedURI: "/proxy/minion/minion1:10250/logs/log.log", - }, - "content-type passthrough": { - input: `
      kubelet.loggoogle.log
      `, - sourceURL: "http://myminion.com/logs/log.log", - transport: testTransport, - output: `
      kubelet.loggoogle.log
      `, - contentType: "text/plain", - forwardedURI: "/proxy/minion/minion1:10250/logs/log.log", - }, - "subdir": { - input: `kubelet.loggoogle.log`, - sourceURL: "http://myminion.com/whatever/apt/somelog.log", - transport: testTransport2, - output: `kubelet.loggoogle.log`, - contentType: "text/html", - forwardedURI: "/proxy/minion/minion1:8080/whatever/apt/somelog.log", - }, - "image": { - input: `
      `, - sourceURL: "http://myminion.com/", - transport: testTransport, - output: `
      `, - contentType: "text/html", - forwardedURI: "/proxy/minion/minion1:10250/", - }, - "abs": { - input: `
      kubelet.loggoogle.log

    2. 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 z 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