├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CODE-OF-CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── build └── common.mk ├── examples ├── list │ ├── Pulumi.yaml │ ├── index.ts │ └── package.json └── logs │ ├── Pulumi.yaml │ ├── index.ts │ └── package.json ├── gen ├── nodejs-templates │ └── index.ts.mustache └── openapi-specs │ └── swagger-v1.16.0.json ├── go.mod ├── go.sum ├── internal ├── cmd │ └── pulumi-gen-query-kubernetes │ │ ├── copy.go │ │ └── main.go ├── gen │ ├── additionalComments.go │ ├── nodejs.go │ └── typegen.go └── version │ └── version.go ├── scripts ├── get-version ├── promote.js └── publish_packages.sh ├── sdk └── nodejs │ ├── Makefile │ ├── gen │ └── openapi-specs │ │ └── swagger-v1.16.0.json │ ├── index.ts │ ├── logs.ts │ ├── package.json │ ├── tsconfig.json │ └── tslint.json └── variables.mk /.gitignore: -------------------------------------------------------------------------------- 1 | .pulumi 2 | **/bin/ 3 | **/node_modules/ 4 | vendor/ 5 | **/Pulumi.*.yaml 6 | **/yarn-error.log 7 | yarn.lock 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # It may be tempting to add parens around each individual clause in this expression, but Travis then builds pushes anyway 2 | if: branch = master OR branch =~ ^release/ OR tag IS present 3 | language: go 4 | go: 1.12.1 5 | sudo: true # give us 7.5GB and >2 bursted cores. 6 | git: 7 | depth: false 8 | before_install: 9 | - git clone https://github.com/pulumi/scripts ${GOPATH}/src/github.com/pulumi/scripts 10 | - source ${GOPATH}/src/github.com/pulumi/scripts/ci/prepare-environment.sh 11 | - source ${PULUMI_SCRIPTS}/ci/keep-failed-tests.sh 12 | install: 13 | - source ${PULUMI_SCRIPTS}/ci/install-common-toolchain.sh 14 | before_script: 15 | - ${PULUMI_SCRIPTS}/ci/ensure-dependencies 16 | script: 17 | - make travis_${TRAVIS_EVENT_TYPE} 18 | after_failure: 19 | - ${PULUMI_SCRIPTS}/ci/upload-failed-tests 20 | notifications: 21 | webhooks: https://zlmgkhmhjc.execute-api.us-west-2.amazonaws.com/stage/travis 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## HEAD v0.1.0 2 | 3 | ### Major changes 4 | 5 | - Initial release. Includes `watch`, `list`, and `ResourceSet`. 6 | -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | education, socio-economic status, nationality, personal appearance, race, 10 | religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at code-of-conduct@pulumi.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Pulumi 2 | 3 | ## Building Source 4 | 5 | ### Prerequisites 6 | 7 | 1. Python: `python-setuptools`, `pip` 8 | 1. Go: [golangci-lint](https://github.com/golangci/golangci-lint) 9 | 1. JS: `npm`, `yarn` 10 | 11 | ### Restore Vendor Dependencies 12 | 13 | ``` 14 | $ make ensure 15 | ``` 16 | 17 | ### Build and Install 18 | 19 | Run the following command to build and install the source. 20 | 21 | The output will be stored in `/opt/pulumi/node_modules/@pulumi/kubernetes`. 22 | 23 | ```bash 24 | $ make build && make install 25 | ``` 26 | 27 | `cd` into your Pulumi program directory. After `make` has completed, 28 | link the recent `@pulumi/kubernetes` build from `/opt/` by running the following command: 29 | 30 | ``` 31 | $ yarn link @pulumi/kubernetes 32 | ``` 33 | 34 | ## Running Integration Tests 35 | 36 | The examples and integration tests in this repository will create and destroy 37 | real Kubernetes objects while running. Before running these tests, make sure that you have 38 | [configured Pulumi with your Kubernetes cluster](https://pulumi.io/install/kubernetes.html) 39 | successfully at least once before. 40 | 41 | You can run Kubernetes tests against `minikube` or against real Kubernetes 42 | clusters. Since the Pulumi Kubernetes provider uses the same 43 | [client-go](https://github.com/kubernetes/client-go) library as `kubectl`, 44 | if your cluster works with `kubectl`, it will also work with Pulumi. 45 | 46 | ```bash 47 | $ make test_all 48 | ``` 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT_NAME := pulumi-query-kubernetes 2 | SUB_PROJECTS := sdk/nodejs 3 | include build/common.mk 4 | include variables.mk 5 | 6 | .PHONY: publish_packages 7 | publish_packages: 8 | $(call STEP_MESSAGE) 9 | ./scripts/publish_packages.sh 10 | 11 | .PHONY: check_clean_worktree 12 | check_clean_worktree: 13 | $$(go env GOPATH)/src/github.com/pulumi/scripts/ci/check-worktree-is-clean.sh 14 | 15 | # The travis_* targets are entrypoints for CI. 16 | .PHONY: travis_cron travis_push travis_pull_request travis_api 17 | travis_cron: all 18 | travis_push: only_build check_clean_worktree only_test publish_packages 19 | travis_pull_request: only_build check_clean_worktree only_test_fast 20 | travis_api: all 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/pulumi/pulumi-query-kubernetes.svg?token=eHg7Zp5zdDDJfTjY8ejq&branch=master)](https://travis-ci.com/pulumi/pulumi-query-kubernetes) 2 | [![Slack](http://www.pulumi.com/images/docs/badges/slack.svg)](https://slack.pulumi.com) 3 | [![NPM version](https://badge.fury.io/js/%40pulumi%2Fquery%2Fkubernetes.svg)](https://www.npmjs.com/package/@pulumi/query-kubernetes) 4 | [![License](https://img.shields.io/github/license/pulumi/pulumi-query-kubernetes)](https://github.com/pulumi/pulumi-query-kubernetes/blob/master/LICENSE) 5 | 6 | # Kubernetes SDK for Pulumi Query 7 | 8 | A relational TypeScript SDK for querying Kubernetes resources in any cluster, either on-prem or in 9 | any cloud. 10 | 11 | Users write a program using the relational query SDK, and then run them with the `pulumi query` 12 | subcommand of the [Pulumi CLI](https://www.pulumi.com/docs/reference/cli/). There are many 13 | [examples][example]. This one finds all distinct versions of MySQL running in your cluster: 14 | 15 | ```typescript 16 | import * as kq from "@pulumi/query-kubernetes"; 17 | 18 | // Find all distinct versions of MySQL running in your cluster. 19 | const mySqlVersions = kq 20 | .list("v1", "Pod") 21 | .flatMap(pod => pod.spec.containers) 22 | .map(container => container.image) 23 | .filter(imageName => imageName.includes("mysql")) 24 | .distinct(); 25 | 26 | mySqlVersions.forEach(console.log); 27 | ``` 28 | 29 | `pulumi query` will inspect your local [kubeconfig] file for the active context, and run the query 30 | programmatically against the resources in the cluster it points to. 31 | 32 | ## Use cases 33 | 34 | **Operations:** 35 | * Which applications are scheduled on nodes that report high memory pressure? (See 36 | [example][example].) 37 | * What is the difference between the last two rollouts of a Deployment? (See [example][example].) 38 | * Which applications are currently emitting logs that contain the text "ERROR:", 39 | and why? 40 | 41 | **Security and Compliance:** 42 | * Which Service Accounts have access to this Secret? 43 | * Which CertificateSigningRequests were approved this week, and what are they 44 | being used for? (See [example][example].) 45 | 46 | **Governance:** 47 | * Which Services are publicly exposed to the Internet? 48 | * How many distinct versions of the mysql container are running in all of my clusters? (See 49 | [example][example].) 50 | 51 | ## Requirements 52 | 53 | * Pulumi CLI version > 1.5.0. 54 | 55 | ## Getting Started 56 | 57 | The directory `sdk/nodejs/examples/list` contains many example queries. 58 | 59 | ```sh 60 | cd sdk/nodejs/examples/list 61 | yarn install 62 | ``` 63 | 64 | Now from that directory, you can run `pulumi query`. The default example prints 65 | a simple report of all namespaces live in the cluster of your active `$KUBECONFIG` context. 66 | 67 | ```sh 68 | PULUMI_DEBUG_COMMANDS=true pulumi query 69 | ``` 70 | 71 | Once you've done this, have a look at the [`examples`](https://github.com/pulumi/pulumi-query-kubernetes/tree/master/examples) directory, which contains many more interesting 72 | sample queries which you can modify to your uses. 73 | 74 | 75 | [kubeconfig]: https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ 76 | [examples]: https://github.com/pulumi/pulumi-query-kubernetes/tree/master/examples/list 77 | [example]: https://github.com/pulumi/pulumi-query-kubernetes/blob/master/examples/list/index.ts 78 | -------------------------------------------------------------------------------- /build/common.mk: -------------------------------------------------------------------------------- 1 | # Copyright 2016-2018, Pulumi Corporation. All rights reserved. 2 | 3 | # common.mk provides most of the scalfholding for our build system. It 4 | # provides default targets for each project we want to build. 5 | # 6 | # The default targets we use are: 7 | # 8 | # - ensure: restores and dependencies needed for the build from 9 | # remote sources (e.g dep ensure or yarn install) 10 | # 11 | # - build: builds a project but does not install it. In the case of 12 | # go code, this usually means running go install (which 13 | # would place them in `GOBIN`, but not `PULUMI_ROOT` 14 | # 15 | # - install: copies the bits we plan to ship into a layout in 16 | # `PULUMI_ROOT` that looks like what a customer would get 17 | # when they download and install Pulumi. For JavaScript 18 | # projects, installing also runs yarn link to register 19 | # this package, so that other projects can depend on it. 20 | # 21 | # - lint: runs relevent linters for the project 22 | # 23 | # - test_fast: runs the fast tests for a project. These are often 24 | # go unit tests or javascript unit tests, they should 25 | # complete quickly, as we expect developers to run them 26 | # fequently as part of their "inner loop" development. 27 | # 28 | # - test_all: runs all of test_fast and then runs additional testing, 29 | # which may take longer (some times a lot longer!). These 30 | # are often integration tests which will use `pulumi` to 31 | # deploy example Pulumi projects, creating cloud 32 | # resources along the way. 33 | # 34 | # In addition, we have a few higher level targets that just depend on 35 | # these targets: 36 | # 37 | # - only_build: this target runs build and install targets 38 | # 39 | # - only_test: this target runs the list and test_all targets 40 | # (test_all itself runs test_fast) 41 | # 42 | # - default: this is the target that is run by default when no 43 | # arguments are passed to make, it runs the build, lint, 44 | # install and test_fast targets 45 | # 46 | # - core: this target behaves like `default` except for the case 47 | # where a project declares SUB_PROJECTS (see a discussion on 48 | # that later). In that case, building `core` target does not 49 | # build sub projects. 50 | # 51 | # - all: this target runs build, lint, install and test_all (which 52 | # itself runs test_fast). 53 | # 54 | # Before including this makefile, a project may define some values 55 | # that this makefile understands: 56 | # 57 | # - PROJECT_NAME: If set, make default and make all will print a banner 58 | # with the project name when they are built. 59 | # 60 | # - SUB_PROJECTS: If set, each item in the list is treated as a path 61 | # to another project (relative to the directory of the 62 | # main Makefile) which should be built as well. When 63 | # this happens, the default and all targets first 64 | # build the default or all target of each child 65 | # project. For each subproject we also create targets 66 | # with our standard names, prepended by the target 67 | # name and an underscore, which just calls Make for 68 | # that specific target. These can be handy targets to 69 | # build explicitly on the command line from time to 70 | # time. 71 | # 72 | # - NODE_MODULE_NAME: If set, an install target will be auto-generated 73 | # that installs the module to 74 | # $(PULUMI_ROOT)/node_modules/$(NODE_MODULE_NAME) 75 | # 76 | # This Makefile also provides some convience methods: 77 | # 78 | # STEP_MESSAGE is a macro that can be invoked with `$(call 79 | # STEP_MESSAGE)` and it will print the name of the current target (in 80 | # green text) to the console. All the targets provided by this makefile 81 | # do that by default. 82 | # 83 | # The ensure target also provides some default behavior, detecting if 84 | # there is a Gopkg.toml or package.json file in the current folder and 85 | # if so calling dep ensure -v or yarn install. This behavior means that 86 | # projects will not often need to augment the ensure target. 87 | # 88 | # Unlike the other leaf targets, ensure will call the ensure target on 89 | # any sub-projects. 90 | # 91 | # Importing common.mk should be the first thing your Makefile does, after 92 | # optionally setting SUB_PROJECTS, PROJECT_NAME and NODE_MODULE_NAME. 93 | SHELL := /bin/bash 94 | .SHELLFLAGS := -ec 95 | 96 | STEP_MESSAGE = @echo -e "\033[0;32m$(shell echo '$@' | tr a-z A-Z | tr '_' ' '):\033[0m" 97 | 98 | # Our install targets place items item into $PULUMI_ROOT, if it's 99 | # unset, default to /opt/pulumi. 100 | ifeq ($(PULUMI_ROOT),) 101 | PULUMI_ROOT:=/opt/pulumi 102 | endif 103 | 104 | PULUMI_BIN := $(PULUMI_ROOT)/bin 105 | PULUMI_NODE_MODULES := $(PULUMI_ROOT)/node_modules 106 | 107 | GO_TEST_FAST = go test -short -v -count=1 -cover -timeout 2h -parallel ${TESTPARALLELISM} 108 | GO_TEST = go test -v -count=1 -cover -timeout 2h -parallel ${TESTPARALLELISM} 109 | GOPROXY = 'https://proxy.golang.org' 110 | 111 | .PHONY: default all ensure only_build only_test build lint install test_all core 112 | 113 | # If there are sub projects, our default, all, and ensure targets will 114 | # recurse into them. 115 | ifneq ($(SUB_PROJECTS),) 116 | only_build:: $(SUB_PROJECTS:%=%_only_build) 117 | only_test:: $(SUB_PROJECTS:%=%_only_test) 118 | only_test_fast:: $(SUB_PROJECTS:%=%_only_test_fast) 119 | default:: $(SUB_PROJECTS:%=%_default) 120 | all:: $(SUB_PROJECTS:%=%_all) 121 | ensure:: $(SUB_PROJECTS:%=%_ensure) 122 | endif 123 | 124 | # `core` is like `default` except it does not build sub projects. 125 | core:: build lint install test_fast 126 | 127 | # If $(PROJECT_NAME) has been set, have our default and all targets 128 | # print a nice banner. 129 | ifneq ($(PROJECT_NAME),) 130 | default:: 131 | @echo -e "\033[1;37m$(shell echo '$(PROJECT_NAME)' | sed -e 's/./=/g')\033[1;37m" 132 | @echo -e "\033[1;37m$(PROJECT_NAME)\033[1;37m" 133 | @echo -e "\033[1;37m$(shell echo '$(PROJECT_NAME)' | sed -e 's/./=/g')\033[1;37m" 134 | all:: 135 | @echo -e "\033[1;37m$(shell echo '$(PROJECT_NAME)' | sed -e 's/./=/g')\033[1;37m" 136 | @echo -e "\033[1;37m$(PROJECT_NAME)\033[1;37m" 137 | @echo -e "\033[1;37m$(shell echo '$(PROJECT_NAME)' | sed -e 's/./=/g')\033[1;37m" 138 | endif 139 | 140 | default:: build install lint test_fast 141 | all:: build install lint test_all 142 | 143 | ensure:: 144 | $(call STEP_MESSAGE) 145 | ifeq ($(NOPROXY), true) 146 | @echo "GO111MODULE=on go mod tidy"; GO111MODULE=on go mod tidy 147 | @echo "GO111MODULE=on go mod vendor"; GO111MODULE=on go mod vendor 148 | else 149 | @echo "GO111MODULE=on GOPROXY=$(GOPROXY) go mod tidy"; GO111MODULE=on GOPROXY=$(GOPROXY) go mod tidy 150 | @echo "GO111MODULE=on GOPROXY=$(GOPROXY) go mod vendor"; GO111MODULE=on GOPROXY=$(GOPROXY) go mod vendor 151 | endif 152 | @if [ -e 'package.json' ]; then echo "yarn install"; yarn install; fi 153 | 154 | build:: 155 | $(call STEP_MESSAGE) 156 | 157 | lint:: 158 | $(call STEP_MESSAGE) 159 | 160 | test_fast:: 161 | $(call STEP_MESSAGE) 162 | 163 | install:: 164 | $(call STEP_MESSAGE) 165 | @mkdir -p $(PULUMI_BIN) 166 | @mkdir -p $(PULUMI_NODE_MODULES) 167 | 168 | test_all:: 169 | $(call STEP_MESSAGE) 170 | 171 | ifneq ($(NODE_MODULE_NAME),) 172 | install:: 173 | [ ! -e "$(PULUMI_NODE_MODULES)/$(NODE_MODULE_NAME)" ] || rm -rf "$(PULUMI_NODE_MODULES)/$(NODE_MODULE_NAME)" 174 | mkdir -p "$(PULUMI_NODE_MODULES)/$(NODE_MODULE_NAME)" 175 | cp -r bin/. "$(PULUMI_NODE_MODULES)/$(NODE_MODULE_NAME)" 176 | cp package.json "$(PULUMI_NODE_MODULES)/$(NODE_MODULE_NAME)" 177 | cp yarn.lock "$(PULUMI_NODE_MODULES)/$(NODE_MODULE_NAME)" 178 | rm -rf "$(PULUMI_NODE_MODULES)/$(NODE_MODULE_NAME)/node_modules" 179 | cd "$(PULUMI_NODE_MODULES)/$(NODE_MODULE_NAME)" && \ 180 | yarn install --offline --production && \ 181 | (yarn unlink > /dev/null 2>&1 || true) && \ 182 | yarn link 183 | endif 184 | 185 | only_build:: build install 186 | only_test:: lint test_all 187 | only_test_fast:: lint test_fast 188 | 189 | # Generate targets for each sub project. This project's default and 190 | # all targets will depend on the sub project's targets, and the 191 | # individual targets for sub projects are added as a convience when 192 | # invoking make from the command line 193 | ifneq ($(SUB_PROJECTS),) 194 | $(SUB_PROJECTS:%=%_default): 195 | @$(MAKE) -C ./$(@:%_default=%) default 196 | $(SUB_PROJECTS:%=%_all): 197 | @$(MAKE) -C ./$(@:%_all=%) all 198 | $(SUB_PROJECTS:%=%_ensure): 199 | @$(MAKE) -C ./$(@:%_ensure=%) ensure 200 | $(SUB_PROJECTS:%=%_build): 201 | @$(MAKE) -C ./$(@:%_build=%) build 202 | $(SUB_PROJECTS:%=%_lint): 203 | @$(MAKE) -C ./$(@:%_lint=%) lint 204 | $(SUB_PROJECTS:%=%_test_fast): 205 | @$(MAKE) -C ./$(@:%_test_fast=%) test_fast 206 | $(SUB_PROJECTS:%=%_install): 207 | @$(MAKE) -C ./$(@:%_install=%) install 208 | $(SUB_PROJECTS:%=%_only_build): 209 | @$(MAKE) -C ./$(@:%_only_build=%) only_build 210 | $(SUB_PROJECTS:%=%_only_test): 211 | @$(MAKE) -C ./$(@:%_only_test=%) only_test 212 | $(SUB_PROJECTS:%=%_only_test_fast): 213 | @$(MAKE) -C ./$(@:%_only_test_fast=%) only_test_fast 214 | endif 215 | 216 | # As a convinece, we provide a format target that folks can build to 217 | # run go fmt over all the go code in their tree. 218 | .PHONY: format 219 | format: 220 | find . -iname "*.go" -not -path "./vendor/*" | xargs gofmt -s -w 221 | -------------------------------------------------------------------------------- /examples/list/Pulumi.yaml: -------------------------------------------------------------------------------- 1 | name: query-test 2 | description: Simple exercise of `pulumi query`. 3 | runtime: nodejs 4 | -------------------------------------------------------------------------------- /examples/list/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2019, Pulumi Corporation. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as q from "@pulumi/query"; 16 | import * as kq from "@pulumi/query-kubernetes"; 17 | const jsondiff = require("jsondiffpatch"); 18 | 19 | // 20 | // Select the example query from the commented-out list below. 21 | // 22 | 23 | namespaceReport(); 24 | // certSignReqStatus(); 25 | // distinctMySqlVersions(); 26 | // warningAndErrorEvents(); 27 | // lastTwoRevisions(); 28 | // namespacesWithNoQuota(); 29 | 30 | // Print a small report of each namespace. 31 | function namespaceReport() { 32 | const report = kq.list("v1", "Namespace").map(async ns => { 33 | const namespace = ns.metadata!.name!; 34 | const pods = kq.list("v1", "Pod", namespace); 35 | const secrets = kq.list("v1", "Secret", namespace); 36 | const services = kq.list("v1", "Service", namespace); 37 | const configMaps = kq.list("v1", "ConfigMap", namespace); 38 | const pvcs = kq.list("v1", "PersistentVolumeClaim", namespace); 39 | 40 | const ps = await pods 41 | .filter(pod => pod.metadata!.namespace === ns.metadata!.name) 42 | .toArray(); 43 | 44 | return { 45 | namespace: ns, 46 | pods: await pods 47 | .filter(pod => pod.metadata!.namespace === ns.metadata!.name) 48 | .toArray(), 49 | secrets: await secrets 50 | .filter( 51 | secret => secret.metadata!.namespace === ns.metadata!.name 52 | ) 53 | .toArray(), 54 | services: await services 55 | .filter( 56 | service => service.metadata!.namespace === ns.metadata!.name 57 | ) 58 | .toArray(), 59 | configMaps: await configMaps 60 | .filter( 61 | configMap => 62 | configMap.metadata!.namespace === ns.metadata!.name 63 | ) 64 | .toArray(), 65 | pvcs: await pvcs 66 | .filter(pvc => pvc.metadata!.namespace === ns.metadata!.name) 67 | .toArray() 68 | }; 69 | }); 70 | 71 | // Print small report. 72 | report.forEach( 73 | ({ namespace, pods, secrets, services, configMaps, pvcs }) => { 74 | console.log(namespace.metadata.name); 75 | console.log(` Pods:\t\t${pods.length}`); 76 | console.log(` Secrets:\t${secrets.length}`); 77 | console.log(` Services:\t${services.length}`); 78 | console.log(` ConfigMaps:\t${configMaps.length}`); 79 | console.log(` PVCs:\t\t${pvcs.length}`); 80 | } 81 | ); 82 | } 83 | 84 | // Print a small report of the status of all certificate signing requests. 85 | function certSignReqStatus(namespace?: string) { 86 | const csrs = kq 87 | .list( 88 | "certificates.k8s.io/v1beta1", 89 | "CertificateSigningRequest", 90 | namespace 91 | ) 92 | .map(csr => { 93 | // Get status of the CSR. 94 | const pending = { 95 | type: "Pending", 96 | message: "Pending", 97 | reason: "Pending", 98 | lastUpdateTime: {} 99 | }; 100 | if (csr.status.conditions == null) { 101 | return { status: pending, request: csr }; 102 | } 103 | 104 | const conditions = csr.status.conditions.filter( 105 | cond => cond.type === "Approved" || cond.type === "Denied" 106 | ); 107 | 108 | return { 109 | status: 110 | conditions.length > 0 111 | ? conditions[conditions.length - 1] 112 | : pending, 113 | request: csr 114 | }; 115 | }) 116 | // Group CSRs by type (one of: `"Approved"`, `"Pending"`, or `"Denied"`). 117 | .groupBy(csr => csr.status.type); 118 | 119 | csrs.forEach(csrGroup => { 120 | console.log(csrGroup.key); 121 | csrGroup.forEach(({ request }) => { 122 | const usages = request.spec.usages.sort().join(", "); 123 | const groups = request.spec.groups.sort().join(", "); 124 | console.log(`\t${request.spec.username}\t[${usages}]\t[${groups}]`); 125 | }); 126 | }); 127 | } 128 | 129 | // Print the number of distinct MySQL versions running in your cluster. 130 | function distinctMySqlVersions(namespace?: string) { 131 | const mySqlVersions = kq 132 | .list("v1", "Pod", namespace) 133 | .flatMap(pod => pod.spec.containers) 134 | .map(container => container.image) 135 | .filter(imageName => imageName.includes("mysql")) 136 | .distinct(); 137 | 138 | mySqlVersions.forEach(console.log); 139 | } 140 | 141 | // Print all warning and error events. 142 | function warningAndErrorEvents(namespace?: string) { 143 | const warningsAndErrors = kq 144 | .list("v1", "Event", namespace) 145 | .filter(e => e.type === "Warning" || e.type === "Error") 146 | .groupBy(e => e.involvedObject.kind); 147 | 148 | warningsAndErrors.forEach(events => { 149 | console.log(`kind: ${events.key}`); 150 | events.forEach(e => 151 | console.log( 152 | ` ${e.type}\t(x${e.count})\t${e.involvedObject.name}\n Message: ${e.message}` 153 | ) 154 | ); 155 | }); 156 | } 157 | 158 | // Print the last two revisions of a deployment. 159 | function lastTwoRevisions(namespace?: string) { 160 | function getRevisionHistory(name: string) { 161 | return kq 162 | .list("extensions/v1beta1", "ReplicaSet", namespace) 163 | .filter( 164 | async rs => 165 | (await q 166 | .from(rs.metadata!.ownerReferences || []) 167 | .filter(oref => oref.name === name) 168 | .count()) > 0 169 | ) 170 | .orderBy( 171 | rs => 172 | rs.metadata.annotations["deployment.kubernetes.io/revision"] 173 | ); 174 | } 175 | 176 | const history = kq 177 | .list("apps/v1", "Deployment", namespace) 178 | .filter(d => d.metadata.name === "nginx") 179 | .flatMap(d => 180 | getRevisionHistory(d.metadata!.name!) 181 | .reverse() 182 | .take(2) 183 | .toArray() 184 | ); 185 | 186 | history.forEach(rollout => { 187 | jsondiff.console.log(jsondiff.diff(rollout[0], rollout[1])); 188 | }); 189 | } 190 | 191 | // Print namespaces with no resource quotas. 192 | function namespacesWithNoQuota() { 193 | const noQuotas = kq.list("v1", "Namespace").filter(async ns => { 194 | const namespace = ns.metadata!.name; 195 | return ( 196 | (await kq 197 | .list("v1", "ResourceQuota", namespace) 198 | // Retrieve only ResourceQuotas that (1) apply to this namespace, and (2) 199 | // specify hard limits on memory. 200 | .filter(rq => rq.spec.hard["limits.memory"] != null) 201 | .count()) === 0 202 | ); 203 | }); 204 | 205 | // Print. 206 | noQuotas.forEach(ns => console.log(ns.metadata.name)); 207 | } 208 | -------------------------------------------------------------------------------- /examples/list/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "steps", 3 | "license": "Apache-2.0", 4 | "main": "bin/index.js", 5 | "typings": "bin/index.d.ts", 6 | "devDependencies": { 7 | "@types/node": "^12.12.7", 8 | "typescript": "^3.7.0" 9 | }, 10 | "dependencies": { 11 | "@pulumi/kubernetes": "^1.3.0", 12 | "@pulumi/query-kubernetes": "dev", 13 | "@pulumi/pulumi": "^1.5.2", 14 | "jsondiffpatch": "^0.3.11" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/logs/Pulumi.yaml: -------------------------------------------------------------------------------- 1 | name: query-test 2 | description: Simple exercise of `pulumi query`. 3 | runtime: nodejs 4 | -------------------------------------------------------------------------------- /examples/logs/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2019, Pulumi Corporation. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as k8s from "@pulumi/kubernetes"; 16 | import * as pulumi from "@pulumi/pulumi"; 17 | import { ResolvedResource } from "@pulumi/pulumi/queryable"; 18 | import * as kq from "@pulumi/query-kubernetes"; 19 | import * as rx from "rxjs"; 20 | import { filter, flatMap, map, toArray, window } from "rxjs/operators"; 21 | 22 | import * as chalk from "chalk"; 23 | 24 | // const podRegex: RegExp = RegExp(".+", "g"); 25 | const podRegex: RegExp = RegExp("test-logger", "g"); 26 | 27 | // -------------------------------------------------------------------------- 28 | // Get logs, tail. 29 | // -------------------------------------------------------------------------- 30 | 31 | // Set namespace to retreive pods from. 32 | const currNs = "default"; 33 | 34 | function podLogs(namespace: string, name: string) { 35 | const obs = new rx.Subject(); 36 | (async function() { 37 | for await (const line of kq.podLogs(namespace, name)) { 38 | obs.next(line); 39 | } 40 | })(); 41 | return obs; 42 | } 43 | 44 | export const ktail = ( 45 | ns: string | undefined, 46 | podRegex: RegExp 47 | ): rx.Observable<{ 48 | name: string; 49 | logs: string[]; 50 | }> => { 51 | const pods = new rx.Subject< 52 | kq.WatchEvent> 53 | >(); 54 | (async function() { 55 | for await (const pod of kq.watch("v1", "Pod")) { 56 | pods.next(pod); 57 | } 58 | })(); 59 | 60 | return pods.pipe( 61 | flatMap(pod => { 62 | if (pod.object.metadata!.namespace !== ns) { 63 | return []; 64 | } 65 | 66 | // Ignore pod if it doesn't match the regex. 67 | if (!podRegex.test(pod.object.metadata.name)) { 68 | return []; 69 | } 70 | 71 | // Get a log stream if `--stream` was passed in, else just get the output of 72 | // the standard `logs` request. 73 | const logs = podLogs( 74 | pod.object.metadata.namespace, 75 | pod.object.metadata.name 76 | ); 77 | 78 | // For each particular stream of logs, emit output in windowed intervals of 79 | // 1 second. This makes the logs slightly more contiguous, so that a bunch 80 | // of logs from one pod end up output together. 81 | return logs.pipe( 82 | filter(logs => logs != null), 83 | window(rx.timer(0, 1000)), 84 | flatMap(window => 85 | window.pipe( 86 | toArray(), 87 | flatMap(logs => (logs.length == 0 ? [] : [logs])) 88 | ) 89 | ), 90 | map(logs => { 91 | return { name: pod.object.metadata.name, logs }; 92 | }) 93 | ); 94 | }) 95 | ); 96 | }; 97 | 98 | ktail(currNs, podRegex).forEach(({ name, logs }) => { 99 | console.log(`${chalk.default.green(name)}:`); 100 | logs.forEach(line => console.log(`${line}`)); 101 | }); 102 | -------------------------------------------------------------------------------- /examples/logs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "steps", 3 | "license": "Apache-2.0", 4 | "main": "bin/index.js", 5 | "typings": "bin/index.d.ts", 6 | "devDependencies": { 7 | "@types/node": "^12.12.7", 8 | "typescript": "^3.7.0" 9 | }, 10 | "dependencies": { 11 | "@pulumi/kubernetes": "^1.3.0", 12 | "@pulumi/query-kubernetes": "dev", 13 | "@pulumi/pulumi": "^1.5.2", 14 | "chalk": "^2.4.2", 15 | "rxjs": "^6.5.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /gen/nodejs-templates/index.ts.mustache: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2019, Pulumi Corporation. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // 16 | // *** WARNING: this file was generated by the Pulumi Kubernetes codegen tool. *** 17 | // *** Do not edit by hand unless you're certain you know what you are doing! *** 18 | // 19 | 20 | import * as k8s from "@pulumi/kubernetes"; 21 | import * as pulumi from "@pulumi/pulumi"; 22 | import { Resource } from "@pulumi/pulumi"; 23 | import { ResolvedResource } from "@pulumi/pulumi/queryable"; 24 | import { streamInvoke } from "@pulumi/pulumi/runtime"; 25 | import * as query from "@pulumi/query"; 26 | import { AsyncIterable } from "@pulumi/query/interfaces"; 27 | import * as rx from "rxjs"; 28 | import { map, scan } from "rxjs/operators"; 29 | 30 | // 31 | // Implementation of `list`. 32 | // 33 | 34 | /** 35 | * Lists all Kubernetes resources of a specific type live in a cluster. Resource types are 36 | * identified with traditional Kubernetes `apiVersion` and `kind`. For example `list("v1", "Pod")` 37 | * produces a set of Pods. 38 | * 39 | * By default, `list` operates on the cluster and namespace identified by the active context of the 40 | * kubeconfig file. Namespace can be specified manually with the optional `namespace` parameter. 41 | * 42 | * The return value of `list` implements `AsyncQueryable`, which exposes powerful facilities for 43 | * filtering, mapping, and joining resources sets to other resource sets. The following example 44 | * produces the distinct versions of MySQL running in the active context in the kubeconfig file. 45 | * 46 | * ```typescript 47 | * import * as kq from "@pulumi/query-kubernetes"; 48 | * 49 | * // Find all distinct versions of MySQL running in your cluster. 50 | * const mySqlVersions = kq 51 | * .list("v1", "Pod") 52 | * .flatMap(pod => pod.spec.containers) 53 | * .map(container => container.image) 54 | * .filter(imageName => imageName.includes("mysql")) 55 | * .distinct(); 56 | * 57 | * mySqlVersions.forEach(console.log); 58 | * ``` 59 | */ 60 | {{#Groups}} 61 | {{#Versions}} 62 | {{#Kinds}} 63 | export function list(apiVersion: "{{RawAPIVersion}}", kind: "{{Kind}}", namespace?: string): query.AsyncQueryable>; 64 | {{/Kinds}} 65 | {{/Versions}} 66 | {{/Groups}} 67 | export function list(apiVersion: string, kind: string, namespace: string = "default"): query.AsyncQueryable> { 68 | 69 | let gvk: { group: string; version: string; kind: string }; 70 | let pulumiType: string; 71 | let filter: (obj: any) => obj is ResolvedResource; 72 | switch (`${apiVersion}::${kind}`) { 73 | {{#Groups}} 74 | {{#Versions}} 75 | {{#Kinds}} 76 | case "{{RawAPIVersion}}::{{Kind}}": { 77 | const [group, version] = "{{RawAPIVersion}}".split("/"); 78 | gvk = version === undefined 79 | ? { group: "", version: group, kind: "{{Kind}}" } 80 | : { group, version, kind: "{{Kind}}" }; 81 | pulumiType = "kubernetes:{{URNAPIVersion}}:{{Kind}}"; 82 | filter = k8s.{{Group}}.{{Version}}.{{Kind}}.isInstance; 83 | break; 84 | } 85 | {{/Kinds}} 86 | {{/Versions}} 87 | {{/Groups}} 88 | default: 89 | throw new Error(`Can't list unsupported type '${apiVersion}::${kind}'`); 90 | } 91 | 92 | const invokeIterator = { 93 | [Symbol.asyncIterator]: function(): AsyncIterator { 94 | let inv: AsyncIterator; 95 | return { 96 | async next() { 97 | if (inv === undefined) { 98 | inv = (await pulumi.runtime.streamInvoke( 99 | "kubernetes:kubernetes:list", 100 | { ...gvk, namespace }, 101 | ))[Symbol.asyncIterator](); 102 | } 103 | return inv.next(); 104 | }, 105 | }; 106 | }, 107 | }; 108 | 109 | return query 110 | .from(() => invokeIterator) 111 | .filter(resource => resource !== undefined) 112 | .map>(({ type: typ, ...outputs }) => { 113 | return { ...outputs, __pulumiType: pulumiType }; 114 | }) 115 | .ofType(filter); 116 | } 117 | 118 | // 119 | // Implementation of `watch`. 120 | // 121 | 122 | export type WatchTypes = "ADDED" | "MODIFIED" | "DELETED" | "ERROR"; 123 | export type WatchEvent = { object: T; type: WatchTypes }; 124 | 125 | /** 126 | * Watches indefinitely for updates on all Kubernetes resources of a specific type live in a 127 | * cluster. Resource types are identified with traditional Kubernetes `apiVersion` and `kind`. For 128 | * example `watch("v1", "Pod")` will produce an infinite stream of updates to Pods in the active 129 | * kubeconfig context. 130 | * 131 | * By default, `watch` operates on the cluster and namespace identified by the active context of 132 | * the kubeconfig file. Namespace can be specified manually with the optional `namespace` 133 | * parameter. 134 | * 135 | * The return value of `watch` implements `AsyncIterable`, so it can be enumerated using 136 | * `for await`, as below. 137 | * 138 | * ``` 139 | * for await (const pod of watch("v1", "Pod")) { 140 | * console.log(pod.metadata!.name); 141 | * } 142 | * ``` 143 | */ 144 | {{#Groups}} 145 | {{#Versions}} 146 | {{#Kinds}} 147 | export function watch(apiVersion: "{{RawAPIVersion}}", kind: "{{Kind}}", namespace?: string): AsyncIterable>>; 148 | {{/Kinds}} 149 | {{/Versions}} 150 | {{/Groups}} 151 | export async function* watch(apiVersion: string, kind: string, namespace: string = "default") { 152 | 153 | let gvk: { group: string; version: string; kind: string }; 154 | let pulumiType: string; 155 | let filter: (obj: any) => obj is ResolvedResource; 156 | switch (`${apiVersion}::${kind}`) { 157 | {{#Groups}} 158 | {{#Versions}} 159 | {{#Kinds}} 160 | case "{{RawAPIVersion}}::{{Kind}}": { 161 | const [group, version] = "{{RawAPIVersion}}".split("/"); 162 | gvk = version === undefined 163 | ? { group: "", version: group, kind: "{{Kind}}" } 164 | : { group, version, kind: "{{Kind}}" }; 165 | pulumiType = "kubernetes:{{URNAPIVersion}}:{{Kind}}"; 166 | filter = k8s.{{Group}}.{{Version}}.{{Kind}}.isInstance; 167 | break; 168 | } 169 | {{/Kinds}} 170 | {{/Versions}} 171 | {{/Groups}} 172 | default: 173 | throw new Error(`Can't list unsupported type '${apiVersion}::${kind}'`); 174 | } 175 | 176 | const resources = streamInvoke("kubernetes:kubernetes:watch", gvk); 177 | 178 | for await (const {object, type} of await resources) { 179 | const typed = { ...object, __pulumiType: pulumiType }; 180 | if (filter(typed)) { 181 | yield { object: typed, type } as WatchEvent>; 182 | } 183 | } 184 | } 185 | 186 | export interface KubernetesResource { 187 | metadata?: { 188 | namespace?: string, 189 | name?: string, 190 | }; 191 | } 192 | 193 | /** 194 | * `ResourceSet` watches the Kubernetes API server for resource updates, and uses them to maintain 195 | * an up-to-date copy of a set of resource definitions. Using `onUpdate`, users can take an 196 | * arbitrary action any time any resource in the set is updated. 197 | * 198 | * For example, in the following code, `ResourceSet` consumes updates to all `Pod`s in the active 199 | * kubeconfig context, and when any `Pod` is updated, it prints every `Pod`'s name. 200 | * 201 | * ``` 202 | * new kq.ResourceSet([kq.watch("v1", "Pod")]).onUpdate(([events]) => { 203 | * events.orderBy(p => p.metadata!.name).forEach(p => { 204 | * console.log(`${p.metadata!.name}`) 205 | * }) 206 | * }); 207 | * ``` 208 | */ 209 | export class ResourceSet< 210 | A extends KubernetesResource, 211 | B = never | KubernetesResource, 212 | C = never | KubernetesResource, 213 | D = never | KubernetesResource, 214 | E = never | KubernetesResource, 215 | F = never | KubernetesResource, 216 | G = never | KubernetesResource, 217 | H = never | KubernetesResource, 218 | I = never | KubernetesResource, 219 | J = never | KubernetesResource, 220 | > { 221 | 222 | static toResourceSet( 223 | acc: Map, 224 | e: WatchEvent, 225 | ): Map { 226 | const { type, object } = e; 227 | const [ns, name] = [object.metadata!.namespace!, object.metadata!.name!]; 228 | if (type === "DELETED") { 229 | acc.delete(`${ns}/${name}`); 230 | } else { 231 | acc.set(`${ns}/${name}`, object); 232 | } 233 | return acc; 234 | } 235 | 236 | constructor(watches: [AsyncIterable>]) 237 | constructor(watches: [AsyncIterable>, AsyncIterable>]) 238 | constructor(watches: [AsyncIterable>, AsyncIterable>, AsyncIterable>]) 239 | constructor(watches: [AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>]) 240 | constructor(watches: [AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>]) 241 | constructor(watches: [AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>]) 242 | constructor(watches: [AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>]) 243 | constructor(watches: [AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>]) 244 | constructor(watches: [AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>]) 245 | constructor(watches: [AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>, AsyncIterable>]) 246 | constructor(private watches: AsyncIterable[]) { 247 | if (watches.length === 0) { 248 | throw new Error("Argument to ResourceSet must be a list with at least 1 element"); 249 | } 250 | } 251 | 252 | public onUpdate(callback: (sets: [query.AsyncQueryable, query.AsyncQueryable, query.AsyncQueryable, query.AsyncQueryable, query.AsyncQueryable, query.AsyncQueryable, query.AsyncQueryable, query.AsyncQueryable, query.AsyncQueryable, query.AsyncQueryable]) => void): void; 253 | public onUpdate(callback: (sets: any) => void): void { 254 | const watches = this.watches.map(w => { 255 | const subject = new rx.Subject>(); 256 | (async () => { 257 | for await (const resource of w) { 258 | subject.next(resource); 259 | } 260 | })(); 261 | return subject.pipe( 262 | scan(ResourceSet.toResourceSet, new Map()), 263 | map(m => query.from(() => m.values())), 264 | ); 265 | }); 266 | 267 | (async () => { 268 | rx.combineLatest(...watches).forEach(callback); 269 | })(); 270 | } 271 | } 272 | 273 | export * from "./logs"; 274 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/pulumi/pulumi-query-kubernetes 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/ahmetb/go-linq v3.0.0+incompatible 7 | github.com/cbroglie/mustache v1.0.1 8 | github.com/jinzhu/copier v0.0.0-20180308034124-7e38e58719c3 9 | github.com/mitchellh/go-wordwrap v1.0.0 10 | github.com/pulumi/pulumi v1.0.0-beta.4.0.20190826221914-99d70e4610d2 11 | github.com/pulumi/pulumi-kubernetes v1.2.3 12 | k8s.io/apimachinery v0.15.7 13 | ) 14 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= 4 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 5 | cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts= 6 | contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= 7 | contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA= 8 | contrib.go.opencensus.io/exporter/stackdriver v0.11.0/go.mod h1:hA7rlmtavV03FGxzWXAPBUnZeZBhWN/QYQAuMtxc9Bk= 9 | contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE= 10 | contrib.go.opencensus.io/resource v0.0.0-20190131005048-21591786a5e0/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA= 11 | github.com/Azure/azure-amqp-common-go v1.1.3/go.mod h1:FhZtXirFANw40UXI2ntweO+VOkfaw8s6vZxUiRhLYW8= 12 | github.com/Azure/azure-amqp-common-go v1.1.4/go.mod h1:FhZtXirFANw40UXI2ntweO+VOkfaw8s6vZxUiRhLYW8= 13 | github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg= 14 | github.com/Azure/azure-pipeline-go v0.1.9/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg= 15 | github.com/Azure/azure-sdk-for-go v21.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= 16 | github.com/Azure/azure-sdk-for-go v27.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= 17 | github.com/Azure/azure-service-bus-go v0.4.1/go.mod h1:d9ho9e/06euiTwGpKxmlbpPhFUsfCsq6a4tZ68r51qI= 18 | github.com/Azure/azure-storage-blob-go v0.6.0/go.mod h1:oGfmITT1V6x//CswqY2gtAHND+xIP64/qL7a5QJix0Y= 19 | github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= 20 | github.com/Azure/go-autorest v11.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= 21 | github.com/Azure/go-autorest v11.1.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= 22 | github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= 23 | github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg= 24 | github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= 25 | github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= 26 | github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= 27 | github.com/Azure/go-autorest/autorest/azure/auth v0.1.0/go.mod h1:Gf7/i2FUpyb/sGBLIFxTBzrNzBo7aPXXE3ZVeDRwdpM= 28 | github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U= 29 | github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= 30 | github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= 31 | github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= 32 | github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= 33 | github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= 34 | github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= 35 | github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88= 36 | github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= 37 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 38 | github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190418212003-6ac0b49e7197/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo= 39 | github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= 40 | github.com/PuerkitoBio/purell v1.0.0 h1:0GoNN3taZV6QI81IXgCbxMyEaJDXMSIjArYBCYzVVvs= 41 | github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 42 | github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2 h1:JCHLVE3B+kJde7bIEo5N4J+ZbLhp0J1Fs+ulyRws4gE= 43 | github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 44 | github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= 45 | github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= 46 | github.com/Sirupsen/logrus v1.0.5/go.mod h1:rmk17hk6i8ZSAJkSDa7nOxamrG+SP4P0mm+DAvExv4U= 47 | github.com/ahmetb/go-linq v3.0.0+incompatible h1:qQkjjOXKrKOTy83X8OpRmnKflXKQIL/mC/gMVVDMhOA= 48 | github.com/ahmetb/go-linq v3.0.0+incompatible/go.mod h1:PFffvbdbtw+QTB0WKRP0cNht7vnCfnGlEpak/DVg5cY= 49 | github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= 50 | github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= 51 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 52 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 53 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= 54 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= 55 | github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 56 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 57 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 58 | github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= 59 | github.com/aws/aws-sdk-go v1.18.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 60 | github.com/aws/aws-sdk-go v1.19.16/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 61 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 62 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 63 | github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= 64 | github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= 65 | github.com/cbroglie/mustache v1.0.1 h1:ivMg8MguXq/rrz2eu3tw6g3b16+PQhoTn6EZAhst2mw= 66 | github.com/cbroglie/mustache v1.0.1/go.mod h1:R/RUa+SobQ14qkP4jtx5Vke5sDytONDQXNLPY/PO69g= 67 | github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 68 | github.com/cheggaaa/pb v1.0.18 h1:G/DgkKaBP0V5lnBg/vx61nVxxAU+VqU5yMzSc0f2PPE= 69 | github.com/cheggaaa/pb v1.0.18/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= 70 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 71 | github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= 72 | github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= 73 | github.com/cpuguy83/go-md2man v1.0.8/go.mod h1:N6JayAiVKtlHSnuTCeuLSQVs75hb8q+dYQLjr7cDsKY= 74 | github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 75 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 76 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 77 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 78 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 79 | github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= 80 | github.com/djherbis/times v1.0.1 h1:nVRrVOTFd2r0C7wCQdIDz/fqt8yO0EEzr5f6aXfXiS0= 81 | github.com/djherbis/times v1.0.1/go.mod h1:CGMZlo255K5r4Yw0b9RRfFQpM2y7uOmxg4jm9HsaVf8= 82 | github.com/docker/docker v0.0.0-20170504205632-89658bed64c2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 83 | github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 84 | github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= 85 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 86 | github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= 87 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= 88 | github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= 89 | github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= 90 | github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= 91 | github.com/emirpasic/gods v1.9.0 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo= 92 | github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= 93 | github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 94 | github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= 95 | github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 96 | github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= 97 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 98 | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 99 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= 100 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 101 | github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= 102 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 103 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 104 | github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 105 | github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= 106 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 107 | github.com/gliderlabs/ssh v0.1.1 h1:j3L6gSLQalDETeEg/Jg0mGY0/y/N6zI2xX1978P0Uqw= 108 | github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 109 | github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= 110 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 111 | github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= 112 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 113 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= 114 | github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1 h1:wSt/4CYxs70xbATrGXhokKF1i0tZjENLOo1ioIO13zk= 115 | github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= 116 | github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9 h1:tF+augKRWlWx0J0B7ZyyKSiTyV6E1zZe+7b3qQlcEf8= 117 | github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= 118 | github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501 h1:C1JKChikHGpXwT5UQDFaryIpDtyyGL/CR6C2kB7F1oc= 119 | github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= 120 | github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87 h1:zP3nY8Tk2E6RTkqGYrarZXuzh+ffyLDljLxCy1iJw80= 121 | github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= 122 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 123 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 124 | github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= 125 | github.com/gofrs/flock v0.7.0 h1:pGFUjl501gafK9HBt1VGL1KCOd/YhIooID+xgyJCf3g= 126 | github.com/gofrs/flock v0.7.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= 127 | github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 128 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 129 | github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 130 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 131 | github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I= 132 | github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 133 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 134 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 135 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 136 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 137 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 138 | github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 139 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 140 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 141 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 142 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 143 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 144 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 145 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 146 | github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= 147 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 148 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 149 | github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= 150 | github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= 151 | github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= 152 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 153 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 154 | github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 155 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 156 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 157 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 158 | github.com/google/wire v0.2.2/go.mod h1:7FHVg6mFpFQrjeUZrm+BaD50N5jnDKm50uVPTpyYOmU= 159 | github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= 160 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 161 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= 162 | github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= 163 | github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= 164 | github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= 165 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 166 | github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 167 | github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 168 | github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 169 | github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20171105060200-01f8541d5372 h1:f6w4aFLm4Ux4hBK/p9njvD5WpBS0alYlNUVTdA4U9Ao= 170 | github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20171105060200-01f8541d5372/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= 171 | github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= 172 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 173 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 174 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 175 | github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= 176 | github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= 177 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 178 | github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= 179 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 180 | github.com/hashicorp/go-plugin v1.0.0/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= 181 | github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= 182 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 183 | github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= 184 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 185 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 186 | github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 187 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 188 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 189 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 190 | github.com/hashicorp/vault/api v1.0.2/go.mod h1:AV/+M5VPDpB90arloVX0rVDUIHkONiwz5Uza9HRtpUE= 191 | github.com/hashicorp/vault/sdk v0.1.8/go.mod h1:tHZfc6St71twLizWNHvnnbiGFo1aq0eD2jGPLtP8kAU= 192 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= 193 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= 194 | github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= 195 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 196 | github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84= 197 | github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 198 | github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 199 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 200 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 201 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 202 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 203 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 204 | github.com/jinzhu/copier v0.0.0-20180308034124-7e38e58719c3 h1:sHsPfNMAG70QAvKbddQ0uScZCHQoZsT5NykGRCeeeIs= 205 | github.com/jinzhu/copier v0.0.0-20180308034124-7e38e58719c3/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= 206 | github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 207 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 208 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 209 | github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 210 | github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 211 | github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= 212 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 213 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 214 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 215 | github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8= 216 | github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 217 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 218 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 219 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 220 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 221 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 222 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 223 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 224 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 225 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 226 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 227 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 228 | github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a h1:TpvdAwDAt1K4ANVOfcihouRdvP+MgAfDWwBuct4l6ZY= 229 | github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 230 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 231 | github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= 232 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 233 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 234 | github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= 235 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 236 | github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4= 237 | github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 238 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 239 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 240 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 241 | github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 242 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 243 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 244 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 245 | github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936 h1:kw1v0NlnN+GZcU8Ma8CLF2Zzgjfx95gs3/GN3vYAPpo= 246 | github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= 247 | github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 248 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 249 | github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= 250 | github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= 251 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 252 | github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 253 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 254 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 255 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 256 | github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 257 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 258 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 259 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 260 | github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 261 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 262 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= 263 | github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= 264 | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 265 | github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 266 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 267 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 268 | github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= 269 | github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 270 | github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 271 | github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 272 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 273 | github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= 274 | github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 275 | github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg= 276 | github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 277 | github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= 278 | github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 279 | github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA= 280 | github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= 281 | github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= 282 | github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 283 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 284 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 285 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 286 | github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 287 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 288 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 289 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 290 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 291 | github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= 292 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 293 | github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 294 | github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 295 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 296 | github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 297 | github.com/pulumi/pulumi v1.0.0-beta.4.0.20190826221914-99d70e4610d2 h1:7vMIliNeAaCWLhTxg5G02Fc4l1XIHL1sXftzXu/ZxHo= 298 | github.com/pulumi/pulumi v1.0.0-beta.4.0.20190826221914-99d70e4610d2/go.mod h1:eSrIzt/kTqyrir8kE4KnCPYbxJm1ChSg1DD7lkBw0mA= 299 | github.com/pulumi/pulumi-kubernetes v1.2.3 h1:FglyotVFWgB1RQwl99gkJnXoWsfZC12cAmUB18Y1pMs= 300 | github.com/pulumi/pulumi-kubernetes v1.2.3/go.mod h1:984HSgZMVWjRiwx7iICSerJ9Eml3bZFERWhgwEzCv0o= 301 | github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 302 | github.com/reconquest/loreley v0.0.0-20160708080500-2ab6b7470a54 h1:J2RvHxEMIzMV6XbaZIj9s5G4lG3hhqWxS7Cl1Jii44c= 303 | github.com/reconquest/loreley v0.0.0-20160708080500-2ab6b7470a54/go.mod h1:1NF/j951kWm+ZnRXpOkBqweImgwhlzFVwTA4A0V7TEU= 304 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 305 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 306 | github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 307 | github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= 308 | github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94/go.mod h1:b18R55ulyQ/h3RaWyloPyER7fWQVZvimKKhnI5OfrJQ= 309 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 310 | github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= 311 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 312 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 313 | github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 314 | github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= 315 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 316 | github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= 317 | github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= 318 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 319 | github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 320 | github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 321 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 322 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 323 | github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= 324 | github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= 325 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 326 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 327 | github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 328 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 329 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 330 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 331 | github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e h1:T5PdfK/M1xyrHwynxMIVMWLS7f/qHwfslZphxtGnw7s= 332 | github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e/go.mod h1:XDKHRm5ThF8YJjx001LtgelzsoaEcvnA7lVWz9EeX3g= 333 | github.com/tidwall/pretty v0.0.0-20190325153808-1166b9ac2b65/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= 334 | github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= 335 | github.com/uber/jaeger-client-go v2.15.0+incompatible h1:NP3qsSqNxh8VYr956ur1N/1C1PjvOJnJykCzcD5QHbk= 336 | github.com/uber/jaeger-client-go v2.15.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= 337 | github.com/uber/jaeger-lib v1.5.0 h1:OHbgr8l656Ub3Fw5k9SWnBfIEwvoHQ+W2y+Aa9D1Uyo= 338 | github.com/uber/jaeger-lib v1.5.0/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= 339 | github.com/xanzy/ssh-agent v0.2.0 h1:Adglfbi5p9Z0BmK2oKU9nTG+zKfniSfnaMYB+ULd+Ro= 340 | github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8= 341 | github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= 342 | github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= 343 | go.mongodb.org/mongo-driver v1.0.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= 344 | go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= 345 | go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 346 | go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 347 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 348 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 349 | gocloud.dev v0.15.0/go.mod h1:ShXCyJaGrJu9y/7a6+DSCyBb9MFGZ1P5wwPa0Wu6w34= 350 | gocloud.dev/secrets/hashivault v0.0.0-20190724225620-1d5466654942/go.mod h1:BrezLDqRTdfTKSlRqpHicQvnuJQnFfop1YM1e4BYgGM= 351 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 352 | golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 353 | golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 354 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 355 | golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 356 | golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 357 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU= 358 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 359 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 360 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 361 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 362 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 363 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 364 | golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 365 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 366 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 367 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 368 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 369 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 370 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 371 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 372 | golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 373 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 374 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 375 | golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 376 | golang.org/x/net v0.0.0-20190322120337-addf6b3196f6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 377 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 378 | golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 379 | golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 380 | golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68= 381 | golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 382 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 383 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 384 | golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 385 | golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 386 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= 387 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 388 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 389 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 390 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 391 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 392 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 393 | golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 394 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 395 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 396 | golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 397 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 398 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 399 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 400 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 401 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 402 | golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 403 | golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 404 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 405 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 406 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 407 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 408 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 409 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 410 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4= 411 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 412 | golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 413 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 414 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 415 | golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 416 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 417 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 418 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 419 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= 420 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 421 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 422 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 423 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 424 | golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 425 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 426 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 427 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 428 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 429 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 430 | golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 431 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 432 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 433 | google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= 434 | google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= 435 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 436 | google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 437 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 438 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 439 | google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= 440 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 441 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 442 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 443 | google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 444 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 445 | google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8 h1:x913Lq/RebkvUmRSdQ8MNb0GZKn+SR1ESfoetcQSeak= 446 | google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 447 | google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 448 | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 449 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 450 | google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 451 | google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU= 452 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 453 | gopkg.in/AlecAivazis/survey.v1 v1.4.1/go.mod h1:2Ehl7OqkBl3Xb8VmC4oFW2bItAhnUfzIjrOzwRxCrOU= 454 | gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= 455 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 456 | gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= 457 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 458 | gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 459 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 460 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 461 | gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk= 462 | gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 463 | gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= 464 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 465 | gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= 466 | gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 467 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 468 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 469 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 470 | gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 471 | gopkg.in/src-d/go-billy.v4 v4.2.1 h1:omN5CrMrMcQ+4I8bJ0wEhOBPanIRWzFC953IiXKdYzo= 472 | gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= 473 | gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= 474 | gopkg.in/src-d/go-git-fixtures.v3 v3.4.0 h1:KFpaNTUcLHLoP/OkdcRXR+MA5p55MhA41YVb7Wd8EfM= 475 | gopkg.in/src-d/go-git-fixtures.v3 v3.4.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= 476 | gopkg.in/src-d/go-git.v4 v4.8.1 h1:aAyBmkdE1QUUEHcP4YFCGKmsMQRAuRmUcPEQR7lOAa0= 477 | gopkg.in/src-d/go-git.v4 v4.8.1/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk= 478 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 479 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 480 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 481 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 482 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 483 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 484 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 485 | gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= 486 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 487 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 488 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 489 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 490 | k8s.io/api v0.0.0-20190918155943-95b840bb6a1f h1:8FRUST8oUkEI45WYKyD8ed7Ad0Kg5v11zHyPkEVb2xo= 491 | k8s.io/api v0.0.0-20190918155943-95b840bb6a1f/go.mod h1:uWuOHnjmNrtQomJrvEBg0c0HRNyQ+8KTEERVsK0PW48= 492 | k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4= 493 | k8s.io/apimachinery v0.15.7 h1:H6pN003RwDju/3BzRGuymY4ymvMjHNbD8MVWhtDeaOI= 494 | k8s.io/apimachinery v0.15.7/go.mod h1:Xc10RHc1U+F/e9GCloJ8QAeCGevSVP5xhOhqlE+e1kM= 495 | k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90 h1:mLmhKUm1X+pXu0zXMEzNsOF5E2kKFGe5o6BZBIIqA6A= 496 | k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk= 497 | k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= 498 | k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 499 | k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 500 | k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= 501 | k8s.io/klog v0.4.0 h1:lCJCxf/LIowc2IGS9TPjWDyXY4nOmdGdfcwwDQCOURQ= 502 | k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= 503 | k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= 504 | k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf h1:EYm5AW/UUDbnmnI+gK0TJDVK9qPLhM+sRHYanNKw0EQ= 505 | k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= 506 | k8s.io/kubernetes v1.14.1 h1:I9F52h5sqVxBmoSsBlNQ0YygNcukDilkpGxUbJRoBoY= 507 | k8s.io/kubernetes v1.14.1/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= 508 | k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE= 509 | k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= 510 | pack.ag/amqp v0.8.0/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4= 511 | pack.ag/amqp v0.11.0/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4= 512 | sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= 513 | sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= 514 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 515 | -------------------------------------------------------------------------------- /internal/cmd/pulumi-gen-query-kubernetes/copy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2019, Pulumi Corporation. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "io/ioutil" 21 | "os" 22 | "path" 23 | ) 24 | 25 | // CopyFile copies a single file from src to dst 26 | // From https://blog.depado.eu/post/copy-files-and-directories-in-go 27 | func CopyFile(src, dst string) error { 28 | var err error 29 | var srcfd *os.File 30 | var dstfd *os.File 31 | var srcinfo os.FileInfo 32 | 33 | if srcfd, err = os.Open(src); err != nil { 34 | return err 35 | } 36 | defer srcfd.Close() 37 | 38 | if dstfd, err = os.Create(dst); err != nil { 39 | return err 40 | } 41 | defer dstfd.Close() 42 | 43 | if _, err = io.Copy(dstfd, srcfd); err != nil { 44 | return err 45 | } 46 | if srcinfo, err = os.Stat(src); err != nil { 47 | return err 48 | } 49 | return os.Chmod(dst, srcinfo.Mode()) 50 | } 51 | 52 | // CopyDir copies a whole directory recursively 53 | // From https://blog.depado.eu/post/copy-files-and-directories-in-go 54 | func CopyDir(src string, dst string) error { 55 | var err error 56 | var fds []os.FileInfo 57 | var srcinfo os.FileInfo 58 | 59 | if srcinfo, err = os.Stat(src); err != nil { 60 | return err 61 | } 62 | 63 | if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil { 64 | return err 65 | } 66 | 67 | if fds, err = ioutil.ReadDir(src); err != nil { 68 | return err 69 | } 70 | for _, fd := range fds { 71 | srcfp := path.Join(src, fd.Name()) 72 | dstfp := path.Join(dst, fd.Name()) 73 | 74 | if fd.IsDir() { 75 | if err = CopyDir(srcfp, dstfp); err != nil { 76 | fmt.Println(err) 77 | } 78 | } else { 79 | if err = CopyFile(srcfp, dstfp); err != nil { 80 | fmt.Println(err) 81 | } 82 | } 83 | } 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /internal/cmd/pulumi-gen-query-kubernetes/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2018, Pulumi Corporation. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "encoding/json" 19 | "fmt" 20 | "io/ioutil" 21 | "log" 22 | "os" 23 | 24 | "github.com/pulumi/pulumi-query-kubernetes/internal/gen" 25 | ) 26 | 27 | func main() { 28 | if len(os.Args) < 5 { 29 | log.Fatal("Usage: gen ") 30 | } 31 | 32 | language := os.Args[1] 33 | 34 | swagger, err := ioutil.ReadFile(os.Args[2]) 35 | if err != nil { 36 | panic(err) 37 | } 38 | 39 | data := map[string]interface{}{} 40 | err = json.Unmarshal(swagger, &data) 41 | if err != nil { 42 | panic(err) 43 | } 44 | 45 | templateDir := os.Args[3] 46 | outdir := fmt.Sprintf("%s/%s", os.Args[4], language) 47 | 48 | switch language { 49 | case "nodejs": 50 | writeNodeJSClient(data, outdir, templateDir) 51 | default: 52 | panic(fmt.Sprintf("Unrecognized language '%s'", language)) 53 | } 54 | } 55 | 56 | func writeNodeJSClient(data map[string]interface{}, outdir, templateDir string) { 57 | indexts, err := gen.NodeJSClient(data, templateDir) 58 | if err != nil { 59 | panic(err) 60 | } 61 | 62 | err = os.MkdirAll(outdir, 0700) 63 | if err != nil { 64 | panic(err) 65 | } 66 | 67 | err = ioutil.WriteFile(fmt.Sprintf("%s/index.ts", outdir), []byte(indexts), 0777) 68 | if err != nil { 69 | panic(err) 70 | } 71 | 72 | fmt.Printf("%s/package.json\n", outdir) 73 | fmt.Println(err) 74 | } 75 | -------------------------------------------------------------------------------- /internal/gen/additionalComments.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2019, Pulumi Corporation. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package gen 16 | 17 | import ( 18 | "fmt" 19 | "strconv" 20 | 21 | "github.com/pulumi/pulumi-kubernetes/pkg/await" 22 | "github.com/pulumi/pulumi-kubernetes/pkg/kinds" 23 | "k8s.io/apimachinery/pkg/runtime/schema" 24 | ) 25 | 26 | func awaitComments(kind kinds.Kind) string { 27 | const preamble = `This resource waits until its status is ready before registering success 28 | for create/update, and populating output properties from the current state of the resource. 29 | The following conditions are used to determine whether the resource creation has 30 | succeeded or failed: 31 | ` 32 | timeoutComment := func(kind kinds.Kind) string { 33 | const timeoutOverride = `setting the 'customTimeouts' option on the resource.` 34 | 35 | var v int 36 | switch kind { 37 | case kinds.Deployment: 38 | v = await.DefaultDeploymentTimeoutMins 39 | case kinds.Ingress: 40 | v = await.DefaultIngressTimeoutMins 41 | case kinds.Job: 42 | v = await.DefaultJobTimeoutMins 43 | case kinds.Pod: 44 | v = await.DefaultPodTimeoutMins 45 | case kinds.Service: 46 | v = await.DefaultServiceTimeoutMins 47 | case kinds.StatefulSet: 48 | v = await.DefaultStatefulSetTimeoutMins 49 | default: 50 | // No timeout defined for other resource Kinds. 51 | return "" 52 | } 53 | timeoutStr := strconv.Itoa(v) + " minutes" 54 | 55 | return fmt.Sprintf(` 56 | If the %s has not reached a Ready state after %s, it will 57 | time out and mark the resource update as Failed. You can override the default timeout value 58 | by %s`, kind, timeoutStr, timeoutOverride) 59 | } 60 | 61 | comment := preamble 62 | switch kind { 63 | case kinds.Deployment: 64 | comment += ` 65 | 1. The Deployment has begun to be updated by the Deployment controller. If the current 66 | generation of the Deployment is > 1, then this means that the current generation must 67 | be different from the generation reported by the last outputs. 68 | 2. There exists a ReplicaSet whose revision is equal to the current revision of the 69 | Deployment. 70 | 3. The Deployment's '.status.conditions' has a status of type 'Available' whose 'status' 71 | member is set to 'True'. 72 | 4. If the Deployment has generation > 1, then '.status.conditions' has a status of type 73 | 'Progressing', whose 'status' member is set to 'True', and whose 'reason' is 74 | 'NewReplicaSetAvailable'. For generation <= 1, this status field does not exist, 75 | because it doesn't do a rollout (i.e., it simply creates the Deployment and 76 | corresponding ReplicaSet), and therefore there is no rollout to mark as 'Progressing'. 77 | ` 78 | case kinds.Ingress: 79 | comment += ` 80 | 1. Ingress object exists. 81 | 2. Endpoint objects exist with matching names for each Ingress path (except when Service 82 | type is ExternalName). 83 | 3. Ingress entry exists for '.status.loadBalancer.ingress'. 84 | ` 85 | case kinds.Job: 86 | comment += ` 87 | 1. The Job's '.status.startTime' is set, which indicates that the Job has started running. 88 | 2. The Job's '.status.conditions' has a status of type 'Complete', and a 'status' set 89 | to 'True'. 90 | 3. The Job's '.status.conditions' do not have a status of type 'Failed', with a 91 | 'status' set to 'True'. If this condition is set, we should fail the Job immediately. 92 | ` 93 | case kinds.Pod: 94 | comment += ` 95 | 1. The Pod is scheduled ("PodScheduled"" '.status.condition' is true). 96 | 2. The Pod is initialized ("Initialized" '.status.condition' is true). 97 | 3. The Pod is ready ("Ready" '.status.condition' is true) and the '.status.phase' is 98 | set to "Running". 99 | Or (for Jobs): The Pod succeeded ('.status.phase' set to "Succeeded"). 100 | ` 101 | case kinds.Service: 102 | comment += ` 103 | 1. Service object exists. 104 | 2. Related Endpoint objects are created. Each time we get an update, wait 10 seconds 105 | for any stragglers. 106 | 3. The endpoints objects target some number of living objects (unless the Service is 107 | an "empty headless" Service [1] or a Service with '.spec.type: ExternalName'). 108 | 4. External IP address is allocated (if Service has '.spec.type: LoadBalancer'). 109 | 110 | Known limitations: 111 | Services targeting ReplicaSets (and, by extension, Deployments, 112 | StatefulSets, etc.) with '.spec.replicas' set to 0 are not handled, and will time 113 | out. To work around this limitation, set 'pulumi.com/skipAwait: "true"' on 114 | '.metadata.annotations' for the Service. Work to handle this case is in progress [2]. 115 | 116 | [1] https://kubernetes.io/docs/concepts/services-networking/service/#headless-services 117 | [2] https://github.com/pulumi/pulumi-kubernetes/pull/703 118 | ` 119 | case kinds.StatefulSet: 120 | comment += ` 121 | 1. The value of 'spec.replicas' matches '.status.replicas', '.status.currentReplicas', 122 | and '.status.readyReplicas'. 123 | 2. The value of '.status.updateRevision' matches '.status.currentRevision'. 124 | ` 125 | default: 126 | panic("awaitComments: unhandled kind") 127 | } 128 | 129 | comment += timeoutComment(kind) 130 | return comment 131 | } 132 | 133 | func helpfulLinkComments(kind kinds.Kind) string { 134 | switch kind { 135 | case kinds.Secret: 136 | return `Note: While Pulumi automatically encrypts the 'data' and 'stringData' 137 | fields, this encryption only applies to Pulumi's context, including the state file, 138 | the Service, the CLI, etc. Kubernetes does not encrypt Secret resources by default, 139 | and the contents are visible to users with access to the Secret in Kubernetes using 140 | tools like 'kubectl'. 141 | 142 | For more information on securing Kubernetes Secrets, see the following links: 143 | https://kubernetes.io/docs/concepts/configuration/secret/#security-properties 144 | https://kubernetes.io/docs/concepts/configuration/secret/#risks` 145 | default: 146 | return "" 147 | } 148 | } 149 | 150 | // PulumiComment adds additional information to the docs generated automatically from the OpenAPI specs. 151 | // This includes information about Pulumi's await behavior, deprecation information, etc. 152 | func PulumiComment(kind string) string { 153 | const prefix = "\n\n" 154 | 155 | k := kinds.Kind(kind) 156 | switch k { 157 | case kinds.Deployment, kinds.Ingress, kinds.Job, kinds.Pod, kinds.Service, kinds.StatefulSet: 158 | return prefix + awaitComments(k) 159 | case kinds.Secret: 160 | return prefix + helpfulLinkComments(k) 161 | default: 162 | return "" 163 | } 164 | } 165 | 166 | func ApiVersionComment(gvk schema.GroupVersionKind) string { 167 | const template = `%s is not supported by Kubernetes 1.16+ clusters. Use %s instead. 168 | 169 | ` 170 | gvkStr := gvk.GroupVersion().String() + "/" + gvk.Kind 171 | return fmt.Sprintf(template, gvkStr, kinds.SuggestedApiVersion(gvk)) 172 | } 173 | -------------------------------------------------------------------------------- /internal/gen/nodejs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2018, Pulumi Corporation. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package gen 16 | 17 | import ( 18 | "fmt" 19 | 20 | "github.com/cbroglie/mustache" 21 | ) 22 | 23 | // -------------------------------------------------------------------------- 24 | 25 | // Main interface. 26 | 27 | // -------------------------------------------------------------------------- 28 | 29 | type GroupTS struct { 30 | Versions map[string]*VersionTS 31 | Index string 32 | } 33 | 34 | type VersionTS struct { 35 | Kinds map[string]string 36 | Index string 37 | } 38 | 39 | // NodeJSClient will generate a Pulumi Kubernetes provider client SDK for nodejs. 40 | func NodeJSClient(swagger map[string]interface{}, templateDir string, 41 | ) (indexts string, err error) { 42 | definitions := swagger["definitions"].(map[string]interface{}) 43 | 44 | groupsSlice := createGroups(definitions, nodeJSInputs()) 45 | groupsSlice = createGroups(definitions, nodeJSOutputs()) 46 | groupsSlice = createGroups(definitions, nodeJSProvider()) 47 | for _, group := range groupsSlice { 48 | groupTS := &GroupTS{} 49 | for _, version := range group.Versions() { 50 | if groupTS.Versions == nil { 51 | groupTS.Versions = make(map[string]*VersionTS) 52 | } 53 | versionTS := &VersionTS{} 54 | for _, kind := range version.Kinds() { 55 | if versionTS.Kinds == nil { 56 | versionTS.Kinds = make(map[string]string) 57 | } 58 | inputMap := map[string]interface{}{ 59 | "Comment": kind.Comment(), 60 | "Group": group.Group(), 61 | "Kind": kind.Kind(), 62 | "Properties": kind.Properties(), 63 | "RequiredInputProperties": kind.RequiredInputProperties(), 64 | "OptionalInputProperties": kind.OptionalInputProperties(), 65 | "AdditionalSecretOutputs": kind.AdditionalSecretOutputs(), 66 | "Aliases": kind.Aliases(), 67 | "URNAPIVersion": kind.URNAPIVersion(), 68 | "Version": version.Version(), 69 | "PulumiComment": kind.pulumiComment, 70 | } 71 | // Since mustache templates are logic-less, we have to add some extra variables 72 | // to selectively disable code generation for empty lists. 73 | additionalSecretOutputsPresent := len(kind.AdditionalSecretOutputs()) > 0 74 | aliasesPresent := len(kind.Aliases()) > 0 75 | inputMap["MergeOptsRequired"] = additionalSecretOutputsPresent || aliasesPresent 76 | inputMap["AdditionalSecretOutputsPresent"] = additionalSecretOutputsPresent 77 | inputMap["AliasesPresent"] = aliasesPresent 78 | } 79 | } 80 | } 81 | 82 | indexts, err = mustache.RenderFile(fmt.Sprintf("%s/index.ts.mustache", templateDir), 83 | map[string]interface{}{ 84 | "Groups": groupsSlice, 85 | }) 86 | if err != nil { 87 | return 88 | } 89 | 90 | return indexts, nil 91 | } 92 | -------------------------------------------------------------------------------- /internal/gen/typegen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2018, Pulumi Corporation. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package gen 16 | 17 | import ( 18 | "fmt" 19 | "regexp" 20 | "strings" 21 | 22 | "github.com/ahmetb/go-linq" 23 | "github.com/jinzhu/copier" 24 | "github.com/mitchellh/go-wordwrap" 25 | "github.com/pulumi/pulumi-kubernetes/pkg/kinds" 26 | "k8s.io/apimachinery/pkg/runtime/schema" 27 | "k8s.io/apimachinery/pkg/util/sets" 28 | 29 | pycodegen "github.com/pulumi/pulumi/pkg/codegen/python" 30 | ) 31 | 32 | const ( 33 | tsObject = "object" 34 | tsStringT = "string" 35 | pyDictT = "dict" 36 | pyStringT = "str" 37 | pyIntT = "int" 38 | pyListT = "list" 39 | pyBoolT = "bool" 40 | pyAnyT = "Any" 41 | ) 42 | 43 | const ( 44 | apiRegistration = "apiregistration.k8s.io" 45 | ) 46 | 47 | // -------------------------------------------------------------------------- 48 | 49 | // A collection of data structures and utility functions to transform an OpenAPI spec for the 50 | // Kubernetes API into something that we can use for codegen'ing nodejs and Python clients. 51 | 52 | // -------------------------------------------------------------------------- 53 | 54 | // GroupConfig represents a Kubernetes API group (e.g., core, apps, extensions, etc.) 55 | type GroupConfig struct { 56 | group string 57 | versions []*VersionConfig 58 | } 59 | 60 | // Group returns the name of the group (e.g., `core` for core, etc.) 61 | func (gc *GroupConfig) Group() string { return gc.group } 62 | 63 | // Versions returns the set of version for some Kubernetes API group. For example, the `apps` group 64 | // has `v1beta1`, `v1beta2`, and `v1`. 65 | func (gc *GroupConfig) Versions() []*VersionConfig { return gc.versions } 66 | 67 | // VersionConfig represents a version of a Kubernetes API group (e.g., the `apps` group has 68 | // `v1beta1`, `v1beta2`, and `v1`.) 69 | type VersionConfig struct { 70 | version string 71 | kinds []*KindConfig 72 | 73 | gv *schema.GroupVersion // Used for sorting. 74 | apiVersion string 75 | rawAPIVersion string 76 | } 77 | 78 | // Version returns the name of the version (e.g., `apps/v1beta1` would return `v1beta1`). 79 | func (vc *VersionConfig) Version() string { return vc.version } 80 | 81 | // Kinds returns the set of kinds in some Kubernetes API group/version combination (e.g., 82 | // `apps/v1beta1` has the `Deployment` kind, etc.). 83 | func (vc *VersionConfig) Kinds() []*KindConfig { return vc.kinds } 84 | 85 | // KindsAndAliases will produce a list of kinds, including aliases (e.g., both `apiregistration` and 86 | // `apiregistration.k8s.io`). 87 | func (vc *VersionConfig) KindsAndAliases() []*KindConfig { 88 | kindsAndAliases := []*KindConfig{} 89 | for _, kind := range vc.kinds { 90 | kindsAndAliases = append(kindsAndAliases, kind) 91 | if strings.HasPrefix(kind.APIVersion(), apiRegistration) { 92 | alias := KindConfig{} 93 | err := copier.Copy(&alias, kind) 94 | if err != nil { 95 | panic(err) 96 | } 97 | rawAPIVersion := "apiregistration" + strings.TrimPrefix(kind.APIVersion(), apiRegistration) 98 | alias.rawAPIVersion = rawAPIVersion 99 | kindsAndAliases = append(kindsAndAliases, &alias) 100 | } 101 | } 102 | return kindsAndAliases 103 | } 104 | 105 | // ListKindsAndAliases will return all known `Kind`s that are lists, or aliases of lists. These 106 | // `Kind`s are not instantiated by the API server, and we must "flatten" them client-side to get an 107 | // accurate view of what resource operations we need to perform. 108 | func (vc *VersionConfig) ListKindsAndAliases() []*KindConfig { 109 | listKinds := []*KindConfig{} 110 | for _, kind := range vc.KindsAndAliases() { 111 | hasItems := false 112 | for _, prop := range kind.properties { 113 | if prop.name == "items" { 114 | hasItems = true 115 | break 116 | } 117 | } 118 | 119 | if strings.HasSuffix(kind.Kind(), "List") && hasItems { 120 | listKinds = append(listKinds, kind) 121 | } 122 | } 123 | 124 | return listKinds 125 | } 126 | 127 | // APIVersion returns the fully-qualified apiVersion (e.g., `storage.k8s.io/v1` for storage, etc.) 128 | func (vc *VersionConfig) APIVersion() string { return vc.apiVersion } 129 | 130 | // RawAPIVersion returns the "raw" apiVersion (e.g., `v1` rather than `core/v1`). 131 | func (vc *VersionConfig) RawAPIVersion() string { return vc.rawAPIVersion } 132 | 133 | // KindConfig represents a Kubernetes API kind (e.g., the `Deployment` type in 134 | // `apps/v1beta1/Deployment`). 135 | type KindConfig struct { 136 | kind string 137 | comment string 138 | pulumiComment string 139 | properties []*Property 140 | requiredInputProperties []*Property 141 | optionalInputProperties []*Property 142 | additionalSecretOutputs []string 143 | aliases []string 144 | 145 | gvk *schema.GroupVersionKind // Used for sorting. 146 | apiVersion string 147 | rawAPIVersion string 148 | typeGuard string 149 | } 150 | 151 | // Kind returns the name of the Kubernetes API kind (e.g., `Deployment` for 152 | // `apps/v1beta1/Deployment`). 153 | func (kc *KindConfig) Kind() string { return kc.kind } 154 | 155 | // Comment returns the comments associated with some Kubernetes API kind. 156 | func (kc *KindConfig) Comment() string { return kc.comment } 157 | 158 | // PulumiComment returns the await logic documentation associated with some Kubernetes API kind. 159 | func (kc *KindConfig) PulumiComment() string { return kc.pulumiComment } 160 | 161 | // Properties returns the list of properties that exist on some Kubernetes API kind (i.e., things 162 | // that we will want to `.` into, like `thing.apiVersion`, `thing.kind`, `thing.metadata`, etc.). 163 | func (kc *KindConfig) Properties() []*Property { return kc.properties } 164 | 165 | // RequiredInputProperties returns the list of properties that are required input properties on some 166 | // Kubernetes API kind (i.e., things that we will want to provide, like `thing.metadata`, etc.). 167 | func (kc *KindConfig) RequiredInputProperties() []*Property { return kc.requiredInputProperties } 168 | 169 | // OptionalInputProperties returns the list of properties that are optional input properties on some 170 | // Kubernetes API kind (i.e., things that we will want to provide, like `thing.metadata`, etc.). 171 | func (kc *KindConfig) OptionalInputProperties() []*Property { return kc.optionalInputProperties } 172 | 173 | // AdditionalSecretOutputs returns the list of strings to set as additionalSecretOutputs on some 174 | // Kubernetes API kind. 175 | func (kc *KindConfig) AdditionalSecretOutputs() []string { return kc.additionalSecretOutputs } 176 | 177 | // Aliases returns the list of aliases for a Kubernetes API kind. 178 | func (kc *KindConfig) Aliases() []string { return kc.aliases } 179 | 180 | // APIVersion returns the fully-qualified apiVersion (e.g., `storage.k8s.io/v1` for storage, etc.) 181 | func (kc *KindConfig) APIVersion() string { return kc.apiVersion } 182 | 183 | // RawAPIVersion returns the "raw" apiVersion (e.g., `v1` rather than `core/v1`). 184 | func (kc *KindConfig) RawAPIVersion() string { return kc.rawAPIVersion } 185 | 186 | // URNAPIVersion returns API version that can be used in a URN (e.g., using the backwards-compatible 187 | // alias `apiextensions` instead of `apiextensions.k8s.io`). 188 | func (kc *KindConfig) URNAPIVersion() string { 189 | if strings.HasPrefix(kc.apiVersion, apiRegistration) { 190 | return "apiregistration" + strings.TrimPrefix(kc.apiVersion, apiRegistration) 191 | } 192 | return kc.apiVersion 193 | } 194 | 195 | // TypeGuard returns the text of a TypeScript type guard for the given kind. 196 | func (kc *KindConfig) TypeGuard() string { return kc.typeGuard } 197 | 198 | // Property represents a property we want to expose on a Kubernetes API kind (i.e., things that we 199 | // will want to `.` into, like `thing.apiVersion`, `thing.kind`, `thing.metadata`, etc.). 200 | type Property struct { 201 | name string 202 | languageName string 203 | comment string 204 | pythonConstructorComment string 205 | propType string 206 | pythonConstructorPropType string 207 | defaultValue string 208 | } 209 | 210 | // Name returns the name of the property. 211 | func (p *Property) Name() string { return p.name } 212 | 213 | // LanguageName returns the name of the property. 214 | func (p *Property) LanguageName() string { return p.languageName } 215 | 216 | // Comment returns the comments associated with some property. 217 | func (p *Property) Comment() string { return p.comment } 218 | 219 | // PythonConstructorComment returns the comments associated with some property, formatted for Python 220 | // constructor documentation. 221 | func (p *Property) PythonConstructorComment() string { return p.pythonConstructorComment } 222 | 223 | // PropType returns the type of the property. 224 | func (p *Property) PropType() string { return p.propType } 225 | 226 | // PythonConstructorPropType returns the type of the property, typed for the Python constructor 227 | // resource inputs. 228 | func (p *Property) PythonConstructorPropType() string { return p.pythonConstructorPropType } 229 | 230 | // DefaultValue returns the type of the property. 231 | func (p *Property) DefaultValue() string { return p.defaultValue } 232 | 233 | // -------------------------------------------------------------------------- 234 | 235 | // Utility functions. 236 | 237 | // -------------------------------------------------------------------------- 238 | 239 | func gvkFromRef(ref string) schema.GroupVersionKind { 240 | // TODO(hausdorff): Surely there is an official k8s function somewhere for doing this. 241 | split := strings.Split(ref, ".") 242 | return schema.GroupVersionKind{ 243 | Kind: split[len(split)-1], 244 | Version: split[len(split)-2], 245 | Group: split[len(split)-3], 246 | } 247 | } 248 | 249 | func stripPrefix(name string) string { 250 | const prefix = "#/definitions/" 251 | return strings.TrimPrefix(name, prefix) 252 | } 253 | 254 | func replaceDeprecationComment(comment string, gvk schema.GroupVersionKind, language language) string { 255 | // The deprecation warning doesn't always appear in the same place in the OpenAPI comments. 256 | // Standardize the message and where it appears in our docs. 257 | re1 := regexp.MustCompile(`^DEPRECATED - .* is deprecated by .* for more information\.\s*`) 258 | re2 := regexp.MustCompile(`DEPRECATED - .* is deprecated by .* for more information\.\s*`) 259 | 260 | var prefix, replacement string 261 | switch language { 262 | case typescript: 263 | prefix = "@deprecated " 264 | replacement = prefix + ApiVersionComment(gvk) 265 | case python: 266 | prefix = "DEPRECATED - " 267 | replacement = prefix + ApiVersionComment(gvk) 268 | default: 269 | panic(fmt.Sprintf("Unsupported language '%s'", language)) 270 | } 271 | 272 | if re1.MatchString(comment) { 273 | return re1.ReplaceAllString(comment, replacement) 274 | } else if re2.MatchString(comment) { 275 | return prefix + ApiVersionComment(gvk) + re2.ReplaceAllString(comment, "") 276 | } else { 277 | return comment 278 | } 279 | } 280 | 281 | func fmtComment( 282 | comment interface{}, prefix string, bareRender bool, opts groupOpts, gvk schema.GroupVersionKind, 283 | ) string { 284 | if comment == nil { 285 | return "" 286 | } 287 | 288 | var wrapParagraph func(line string) []string 289 | var renderComment func(lines []string) string 290 | switch opts.language { 291 | case python: 292 | wrapParagraph = func(paragraph string) []string { 293 | borderLen := len(prefix) 294 | wrapped := wordwrap.WrapString(paragraph, 100-uint(borderLen)) 295 | return strings.Split(wrapped, "\n") 296 | } 297 | renderComment = func(lines []string) string { 298 | joined := strings.Join(lines, fmt.Sprintf("\n%s", prefix)) 299 | if !bareRender { 300 | return fmt.Sprintf("\"\"\"\n%s%s\n%s\"\"\"", prefix, joined, prefix) 301 | } 302 | return joined 303 | } 304 | case typescript: 305 | wrapParagraph = func(paragraph string) []string { 306 | // Escape comment termination. 307 | escaped := strings.Replace(paragraph, "*/", "*‍/", -1) 308 | borderLen := len(prefix + " * ") 309 | wrapped := wordwrap.WrapString(escaped, 100-uint(borderLen)) 310 | return strings.Split(wrapped, "\n") 311 | } 312 | renderComment = func(lines []string) string { 313 | joined := strings.Join(lines, fmt.Sprintf("\n%s * ", prefix)) 314 | if !bareRender { 315 | return fmt.Sprintf("/**\n%s * %s\n%s */", prefix, joined, prefix) 316 | } 317 | return joined 318 | } 319 | default: 320 | panic(fmt.Sprintf("Unsupported language '%s'", opts.language)) 321 | } 322 | 323 | commentstr, _ := comment.(string) 324 | if len(commentstr) > 0 { 325 | 326 | // hack(levi): The OpenAPI docs currently include broken links to k8s docs. Until this is fixed 327 | // upstream, manually replace these with working links. 328 | // Upstream issue: https://github.com/kubernetes/kubernetes/issues/81526 329 | // Upstream PR: https://github.com/kubernetes/kubernetes/pull/74245 330 | commentstr = strings.Replace( 331 | commentstr, 332 | `https://git.k8s.io/community/contributors/devel/api-conventions.md`, 333 | `https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md`, 334 | -1) 335 | 336 | commentstr = replaceDeprecationComment(commentstr, gvk, opts.language) 337 | 338 | split := strings.Split(commentstr, "\n") 339 | var lines []string 340 | for _, paragraph := range split { 341 | lines = append(lines, wrapParagraph(paragraph)...) 342 | } 343 | return renderComment(lines) 344 | } 345 | return "" 346 | } 347 | 348 | const ( 349 | apiextensionsV1beta1 = "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1" 350 | apiextensionsV1 = "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1" 351 | quantity = "io.k8s.apimachinery.pkg.api.resource.Quantity" 352 | rawExtension = "io.k8s.apimachinery.pkg.runtime.RawExtension" 353 | intOrString = "io.k8s.apimachinery.pkg.util.intstr.IntOrString" 354 | v1Fields = "io.k8s.apimachinery.pkg.apis.meta.v1.Fields" 355 | v1FieldsV1 = "io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1" 356 | v1Time = "io.k8s.apimachinery.pkg.apis.meta.v1.Time" 357 | v1MicroTime = "io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime" 358 | v1beta1JSONSchemaPropsOrBool = apiextensionsV1beta1 + ".JSONSchemaPropsOrBool" 359 | v1beta1JSONSchemaPropsOrArray = apiextensionsV1beta1 + ".JSONSchemaPropsOrArray" 360 | v1beta1JSON = apiextensionsV1beta1 + ".JSON" 361 | v1beta1CRSubresourceStatus = apiextensionsV1beta1 + ".CustomResourceSubresourceStatus" 362 | v1JSONSchemaPropsOrBool = apiextensionsV1 + ".JSONSchemaPropsOrBool" 363 | v1JSONSchemaPropsOrArray = apiextensionsV1 + ".JSONSchemaPropsOrArray" 364 | v1JSON = apiextensionsV1 + ".JSON" 365 | v1CRSubresourceStatus = apiextensionsV1 + ".CustomResourceSubresourceStatus" 366 | ) 367 | 368 | func makeTypescriptType(resourceType, propName string, prop map[string]interface{}, opts groupOpts) string { 369 | wrapType := func(typ string) string { 370 | switch opts.generatorType { 371 | case provider: 372 | return fmt.Sprintf("pulumi.Output<%s>", typ) 373 | case outputsAPI: 374 | return typ 375 | case inputsAPI: 376 | return fmt.Sprintf("pulumi.Input<%s>", typ) 377 | default: 378 | panic(fmt.Sprintf("unrecognized generator type %d", opts.generatorType)) 379 | } 380 | } 381 | 382 | refPrefix := "" 383 | if opts.generatorType == provider { 384 | refPrefix = "outputs" 385 | } 386 | 387 | if t, exists := prop["type"]; exists { 388 | tstr := t.(string) 389 | if tstr == "array" { 390 | elemType := makeTypescriptType( 391 | resourceType, propName, prop["items"].(map[string]interface{}), opts) 392 | switch opts.generatorType { 393 | case provider: 394 | return fmt.Sprintf("%s[]>", elemType[:len(elemType)-1]) 395 | case outputsAPI: 396 | return fmt.Sprintf("%s[]", elemType) 397 | case inputsAPI: 398 | return fmt.Sprintf("pulumi.Input<%s[]>", elemType) 399 | } 400 | } else if tstr == "integer" { 401 | return wrapType("number") 402 | } else if tstr == tsObject { 403 | // `additionalProperties` with a single member, `type`, denotes a map whose keys and 404 | // values both have type `type`. This type is never a `$ref`. 405 | if additionalProperties, exists := prop["additionalProperties"]; exists { 406 | mapType := additionalProperties.(map[string]interface{}) 407 | if ktype, exists := mapType["type"]; exists && len(mapType) == 1 { 408 | switch opts.generatorType { 409 | case inputsAPI: 410 | return fmt.Sprintf("pulumi.Input<{[key: %s]: pulumi.Input<%s>}>", ktype, ktype) 411 | case outputsAPI: 412 | return fmt.Sprintf("{[key: %s]: %s}", ktype, ktype) 413 | case provider: 414 | return fmt.Sprintf("pulumi.Output<{[key: %s]: pulumi.Output<%s>}>", ktype, ktype) 415 | } 416 | } 417 | } 418 | } else if tstr == "string" && resourceType == "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" && propName == "namespace" { 419 | // Special case: `.metadata.namespace` should either take a string or a namespace object 420 | // itself. 421 | 422 | switch opts.generatorType { 423 | case inputsAPI: 424 | // TODO: Enable metadata to take explicit namespaces, like: 425 | // return "pulumi.Input | Namespace" 426 | return "pulumi.Input" 427 | case outputsAPI: 428 | return "string" 429 | } 430 | } 431 | 432 | return wrapType(tstr) 433 | } 434 | 435 | ref := stripPrefix(prop["$ref"].(string)) 436 | 437 | isSimpleRef := true 438 | switch ref { 439 | case quantity: 440 | ref = tsStringT 441 | case intOrString: 442 | ref = "number | string" 443 | case v1Fields, v1FieldsV1, rawExtension: 444 | ref = tsObject 445 | case v1Time, v1MicroTime: 446 | // TODO: Automatically deserialized with `DateConstructor`. 447 | ref = tsStringT 448 | case v1beta1JSONSchemaPropsOrBool: 449 | ref = "apiextensions.v1beta1.JSONSchemaProps | boolean" 450 | case v1JSONSchemaPropsOrBool: 451 | ref = "apiextensions.v1.JSONSchemaProps | boolean" 452 | case v1beta1JSONSchemaPropsOrArray: 453 | ref = "apiextensions.v1beta1.JSONSchemaProps | any[]" 454 | case v1JSONSchemaPropsOrArray: 455 | ref = "apiextensions.v1.JSONSchemaProps | any[]" 456 | case v1beta1JSON, v1beta1CRSubresourceStatus, v1JSON, v1CRSubresourceStatus: 457 | ref = "any" 458 | default: 459 | isSimpleRef = false 460 | } 461 | 462 | if isSimpleRef { 463 | return wrapType(ref) 464 | } 465 | 466 | gvk := gvkFromRef(ref) 467 | var gvkRefStr string 468 | if refPrefix == "" { 469 | gvkRefStr = fmt.Sprintf("%s.%s.%s", gvk.Group, gvk.Version, gvk.Kind) 470 | } else { 471 | gvkRefStr = fmt.Sprintf("%s.%s.%s.%s", refPrefix, gvk.Group, gvk.Version, gvk.Kind) 472 | } 473 | 474 | return wrapType(gvkRefStr) 475 | } 476 | 477 | func makePythonType(resourceType, propName string, prop map[string]interface{}, opts groupOpts) string { 478 | wrapType := func(typ string) string { 479 | switch opts.generatorType { 480 | case provider: 481 | return fmt.Sprintf("pulumi.Output[%s]", typ) 482 | case outputsAPI: 483 | return typ 484 | case inputsAPI: 485 | return fmt.Sprintf("pulumi.Input[%s]", typ) 486 | default: 487 | panic(fmt.Sprintf("unrecognized generator type %d", opts.generatorType)) 488 | } 489 | } 490 | 491 | if t, exists := prop["type"]; exists { 492 | tstr := t.(string) 493 | if tstr == "array" { 494 | return wrapType(pyListT) 495 | } else if tstr == "integer" { 496 | return wrapType(pyIntT) 497 | } else if tstr == tsObject { 498 | return wrapType(pyDictT) 499 | } else if tstr == "string" { 500 | return wrapType(pyStringT) 501 | } else if tstr == "boolean" { 502 | return wrapType(pyBoolT) 503 | } 504 | 505 | return wrapType(tstr) 506 | } 507 | 508 | ref := stripPrefix(prop["$ref"].(string)) 509 | 510 | switch ref { 511 | case quantity: 512 | ref = pyStringT 513 | case intOrString: 514 | ref = fmt.Sprintf("Union[%s, %s]", pyIntT, pyStringT) 515 | case v1Fields, v1FieldsV1, rawExtension: 516 | ref = pyDictT 517 | case v1Time, v1MicroTime: 518 | // TODO: Automatically deserialized with `DateConstructor`. 519 | ref = pyStringT 520 | case v1beta1JSONSchemaPropsOrBool, v1JSONSchemaPropsOrBool: 521 | ref = fmt.Sprintf("Union[%s, %s]", pyDictT, pyBoolT) 522 | case v1beta1JSONSchemaPropsOrArray, v1JSONSchemaPropsOrArray: 523 | ref = fmt.Sprintf("Union[%s, %s]", pyDictT, pyListT) 524 | case v1beta1JSON, v1beta1CRSubresourceStatus, v1JSON, v1CRSubresourceStatus: 525 | ref = pyAnyT 526 | default: 527 | ref = pyDictT 528 | } 529 | 530 | return wrapType(ref) 531 | } 532 | 533 | func makeType(resourceType, propName string, prop map[string]interface{}, opts groupOpts) string { 534 | switch opts.language { 535 | case typescript: 536 | return makeTypescriptType(resourceType, propName, prop, opts) 537 | case python: 538 | return makePythonType(resourceType, propName, prop, opts) 539 | default: 540 | panic("Unrecognized generator type") 541 | } 542 | } 543 | 544 | func isTopLevel(d *definition) bool { 545 | gvks, gvkExists := 546 | d.data["x-kubernetes-group-version-kind"].([]interface{}) 547 | hasGVK := gvkExists && len(gvks) > 0 548 | if !hasGVK { 549 | return false 550 | } 551 | 552 | // Return `false` for the handful of top-level imperative resource types that can't be managed 553 | // by Pulumi. 554 | switch fmt.Sprintf("%s/%s", d.gvk.GroupVersion().String(), d.gvk.Kind) { 555 | case "policy/v1beta1/Eviction", "v1/Status", "apps/v1beta1/Scale", "apps/v1beta2/Scale", 556 | "autoscaling/v1/Scale", "extensions/v1beta1/Scale": 557 | return false 558 | } 559 | 560 | properties, hasProperties := d.data["properties"].(map[string]interface{}) 561 | if !hasProperties { 562 | return false 563 | } 564 | 565 | meta, hasMetadata := properties["metadata"].(map[string]interface{}) 566 | if !hasMetadata { 567 | return false 568 | } 569 | 570 | ref, hasRef := meta["$ref"] 571 | if !hasRef { 572 | return false 573 | } 574 | 575 | return ref == "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" || 576 | ref == "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" 577 | } 578 | 579 | // -------------------------------------------------------------------------- 580 | 581 | // Core grouping logic. 582 | 583 | // -------------------------------------------------------------------------- 584 | 585 | type definition struct { 586 | gvk schema.GroupVersionKind 587 | name string 588 | data map[string]interface{} 589 | } 590 | 591 | type gentype int 592 | 593 | const ( 594 | provider gentype = iota 595 | inputsAPI 596 | outputsAPI 597 | ) 598 | 599 | type language string 600 | 601 | const ( 602 | python = "python" 603 | typescript = "typescript" 604 | ) 605 | 606 | type groupOpts struct { 607 | generatorType gentype 608 | language language 609 | } 610 | 611 | func nodeJSInputs() groupOpts { return groupOpts{generatorType: inputsAPI, language: typescript} } 612 | func nodeJSOutputs() groupOpts { return groupOpts{generatorType: outputsAPI, language: typescript} } 613 | func nodeJSProvider() groupOpts { return groupOpts{generatorType: provider, language: typescript} } 614 | 615 | func pythonProvider() groupOpts { return groupOpts{generatorType: provider, language: python} } 616 | 617 | func allCamelCasePropertyNames(definitionsJSON map[string]interface{}, opts groupOpts) []string { 618 | // Map definition JSON object -> `definition` with metadata. 619 | definitions := make([]*definition, 0) 620 | linq.From(definitionsJSON). 621 | WhereT(func(kv linq.KeyValue) bool { 622 | // Skip these objects, special case. They're deprecated and empty. 623 | defName := kv.Key.(string) 624 | return !strings.HasPrefix(defName, "io.k8s.kubernetes.pkg") 625 | }). 626 | SelectT(func(kv linq.KeyValue) *definition { 627 | defName := kv.Key.(string) 628 | return &definition{ 629 | gvk: gvkFromRef(defName), 630 | name: defName, 631 | data: definitionsJSON[defName].(map[string]interface{}), 632 | } 633 | }). 634 | ToSlice(&definitions) 635 | 636 | properties := sets.String{} 637 | // Only select camel-cased property names 638 | re := regexp.MustCompile(`[a-z]+[A-Z]`) 639 | for _, d := range definitions { 640 | if pmap, exists := d.data["properties"]; exists { 641 | ps := pmap.(map[string]interface{}) 642 | for p := range ps { 643 | if re.MatchString(p) { 644 | properties.Insert(p) 645 | } 646 | } 647 | } 648 | } 649 | 650 | return properties.List() 651 | } 652 | 653 | func createGroups(definitionsJSON map[string]interface{}, opts groupOpts) []*GroupConfig { 654 | // Map definition JSON object -> `definition` with metadata. 655 | definitions := []*definition{} 656 | linq.From(definitionsJSON). 657 | WhereT(func(kv linq.KeyValue) bool { 658 | // Skip these objects, special case. They're deprecated and empty. 659 | // 660 | // TODO(hausdorff): We can remove these now that we don't emit a `KindConfig` for an object 661 | // that has no properties. 662 | defName := kv.Key.(string) 663 | return !strings.HasPrefix(defName, "io.k8s.kubernetes.pkg") 664 | }). 665 | SelectT(func(kv linq.KeyValue) *definition { 666 | defName := kv.Key.(string) 667 | return &definition{ 668 | gvk: gvkFromRef(defName), 669 | name: defName, 670 | data: definitionsJSON[defName].(map[string]interface{}), 671 | } 672 | }). 673 | ToSlice(&definitions) 674 | 675 | // 676 | // Assemble a `KindConfig` for each Kubernetes kind. 677 | // 678 | 679 | kinds := []*KindConfig{} 680 | linq.From(definitions). 681 | OrderByT(func(d *definition) string { return d.gvk.String() }). 682 | SelectManyT(func(d *definition) linq.Query { 683 | // Skip if there are no properties on the type. 684 | if _, exists := d.data["properties"]; !exists { 685 | return linq.From([]KindConfig{}) 686 | } 687 | 688 | // Make fully-qualified and "default" GroupVersion. The "default" GV is the `apiVersion` that 689 | // appears when writing Kubernetes YAML (e.g., `v1` instead of `core/v1`), while the 690 | // fully-qualified version is the "official" GV (e.g., `core/v1` instead of `v1` or 691 | // `admissionregistration.k8s.io/v1alpha1` instead of `admissionregistration/v1alpha1`). 692 | defaultGroupVersion := d.gvk.Group 693 | var fqGroupVersion string 694 | isTopLevel := isTopLevel(d) 695 | if gvks, gvkExists := d.data["x-kubernetes-group-version-kind"].([]interface{}); gvkExists && len(gvks) > 0 { 696 | gvk := gvks[0].(map[string]interface{}) 697 | group := gvk["group"].(string) 698 | version := gvk["version"].(string) 699 | if group == "" { 700 | defaultGroupVersion = version 701 | fqGroupVersion = fmt.Sprintf(`core/%s`, version) 702 | } else { 703 | defaultGroupVersion = fmt.Sprintf(`%s/%s`, group, version) 704 | fqGroupVersion = fmt.Sprintf(`%s/%s`, group, version) 705 | } 706 | } else { 707 | gv := d.gvk.GroupVersion().String() 708 | if strings.HasPrefix(gv, "apiextensions/") && strings.HasPrefix(d.gvk.Kind, "CustomResource") { 709 | // Special case. Kubernetes OpenAPI spec should have an `x-kubernetes-group-version-kind` 710 | // CustomResource, but it doesn't. Hence, we hard-code it. 711 | gv = fmt.Sprintf("apiextensions.k8s.io/%s", d.gvk.Version) 712 | } 713 | defaultGroupVersion = gv 714 | fqGroupVersion = gv 715 | } 716 | 717 | ps := linq.From(d.data["properties"]). 718 | OrderByT(func(kv linq.KeyValue) string { return kv.Key.(string) }). 719 | WhereT(func(kv linq.KeyValue) bool { 720 | propName := kv.Key.(string) 721 | if opts.language == python && (propName == "apiVersion" || propName == "kind") { 722 | return false 723 | } 724 | return true 725 | }). 726 | SelectT(func(kv linq.KeyValue) *Property { 727 | propName := kv.Key.(string) 728 | prop := d.data["properties"].(map[string]interface{})[propName].(map[string]interface{}) 729 | 730 | var prefix string 731 | var t, pyConstructorT string 732 | switch opts.language { 733 | case typescript: 734 | prefix = " " 735 | t = makeType(d.name, propName, prop, opts) 736 | case python: 737 | prefix = " " 738 | t = makeType(d.name, propName, prop, opts) 739 | pyConstructorT = makeType(d.name, propName, prop, 740 | groupOpts{language: python, generatorType: inputsAPI}) 741 | } 742 | 743 | // `-` is invalid in TS variable names, so replace with `_` 744 | propName = strings.ReplaceAll(propName, "-", "_") 745 | 746 | // Create a default value for the field. 747 | defaultValue := fmt.Sprintf("args && args.%s || undefined", propName) 748 | switch propName { 749 | case "apiVersion": 750 | defaultValue = fmt.Sprintf(`"%s"`, defaultGroupVersion) 751 | if isTopLevel { 752 | switch opts.generatorType { 753 | case provider: 754 | t = fmt.Sprintf(`pulumi.Output<"%s">`, defaultGroupVersion) 755 | case outputsAPI: 756 | t = fmt.Sprintf(`"%s"`, defaultGroupVersion) 757 | case inputsAPI: 758 | t = fmt.Sprintf(`pulumi.Input<"%s">`, defaultGroupVersion) 759 | } 760 | } 761 | case "kind": 762 | defaultValue = fmt.Sprintf(`"%s"`, d.gvk.Kind) 763 | if isTopLevel { 764 | switch opts.generatorType { 765 | case provider: 766 | t = fmt.Sprintf(`pulumi.Output<"%s">`, d.gvk.Kind) 767 | case outputsAPI: 768 | t = fmt.Sprintf(`"%s"`, d.gvk.Kind) 769 | case inputsAPI: 770 | t = fmt.Sprintf(`pulumi.Input<"%s">`, d.gvk.Kind) 771 | } 772 | } 773 | } 774 | 775 | return &Property{ 776 | comment: fmtComment(prop["description"], prefix, false, opts, d.gvk), 777 | pythonConstructorComment: fmtComment(prop["description"], prefix+prefix+" ", true, opts, d.gvk), 778 | propType: t, 779 | pythonConstructorPropType: pyConstructorT, 780 | name: propName, 781 | languageName: pycodegen.PyName(propName), 782 | defaultValue: defaultValue, 783 | } 784 | }) 785 | 786 | // All properties. 787 | properties := []*Property{} 788 | ps.ToSlice(&properties) 789 | 790 | // Required properties. 791 | reqdProps := sets.NewString() 792 | if reqd, hasReqd := d.data["required"]; hasReqd { 793 | for _, propName := range reqd.([]interface{}) { 794 | reqdProps.Insert(propName.(string)) 795 | } 796 | } 797 | 798 | requiredInputProperties := []*Property{} 799 | ps. 800 | WhereT(func(p *Property) bool { 801 | return reqdProps.Has(p.name) 802 | }). 803 | ToSlice(&requiredInputProperties) 804 | 805 | optionalInputProperties := []*Property{} 806 | ps. 807 | WhereT(func(p *Property) bool { 808 | return !reqdProps.Has(p.name) && p.name != "status" 809 | }). 810 | ToSlice(&optionalInputProperties) 811 | 812 | if len(properties) == 0 { 813 | return linq.From([]*KindConfig{}) 814 | } 815 | 816 | if opts.generatorType == provider && (!isTopLevel) { 817 | return linq.From([]*KindConfig{}) 818 | } 819 | 820 | var typeGuard string 821 | props := d.data["properties"].(map[string]interface{}) 822 | _, apiVersionExists := props["apiVersion"] 823 | if apiVersionExists { 824 | typeGuard = fmt.Sprintf(` 825 | export function is%s(o: any): o is %s { 826 | return o.apiVersion == "%s" && o.kind == "%s"; 827 | }`, d.gvk.Kind, d.gvk.Kind, defaultGroupVersion, d.gvk.Kind) 828 | } 829 | 830 | prefix := " " 831 | return linq.From([]*KindConfig{ 832 | { 833 | kind: d.gvk.Kind, 834 | // NOTE: This transformation assumes git users on Windows to set 835 | // the "check in with UNIX line endings" setting. 836 | comment: fmtComment(d.data["description"], " ", true, opts, d.gvk), 837 | pulumiComment: fmtComment(PulumiComment(d.gvk.Kind), prefix, true, opts, d.gvk), 838 | properties: properties, 839 | requiredInputProperties: requiredInputProperties, 840 | optionalInputProperties: optionalInputProperties, 841 | additionalSecretOutputs: additionalSecretOutputs(d.gvk), 842 | aliases: aliasesForGVK(d.gvk), 843 | gvk: &d.gvk, 844 | apiVersion: fqGroupVersion, 845 | rawAPIVersion: defaultGroupVersion, 846 | typeGuard: typeGuard, 847 | }, 848 | }) 849 | }). 850 | ToSlice(&kinds) 851 | 852 | // 853 | // Assemble a `VersionConfig` for each group of kinds. 854 | // 855 | 856 | versions := []*VersionConfig{} 857 | linq.From(kinds). 858 | GroupByT( 859 | func(e *KindConfig) schema.GroupVersion { return e.gvk.GroupVersion() }, 860 | func(e *KindConfig) *KindConfig { return e }). 861 | OrderByT(func(kinds linq.Group) string { 862 | return kinds.Key.(schema.GroupVersion).String() 863 | }). 864 | SelectManyT(func(kinds linq.Group) linq.Query { 865 | gv := kinds.Key.(schema.GroupVersion) 866 | kindsGroup := []*KindConfig{} 867 | linq.From(kinds.Group).ToSlice(&kindsGroup) 868 | if len(kindsGroup) == 0 { 869 | return linq.From([]*VersionConfig{}) 870 | } 871 | 872 | return linq.From([]*VersionConfig{ 873 | { 874 | version: gv.Version, 875 | kinds: kindsGroup, 876 | gv: &gv, 877 | apiVersion: kindsGroup[0].apiVersion, // NOTE: This is safe. 878 | rawAPIVersion: kindsGroup[0].rawAPIVersion, // NOTE: This is safe. 879 | }, 880 | }) 881 | }). 882 | ToSlice(&versions) 883 | 884 | // 885 | // Assemble a `GroupConfig` for each group of versions. 886 | // 887 | 888 | groups := []*GroupConfig{} 889 | linq.From(versions). 890 | GroupByT( 891 | func(e *VersionConfig) string { return e.gv.Group }, 892 | func(e *VersionConfig) *VersionConfig { return e }). 893 | OrderByT(func(versions linq.Group) string { return versions.Key.(string) }). 894 | SelectManyT(func(versions linq.Group) linq.Query { 895 | versionsGroup := []*VersionConfig{} 896 | linq.From(versions.Group).ToSlice(&versionsGroup) 897 | if len(versionsGroup) == 0 { 898 | return linq.From([]*GroupConfig{}) 899 | } 900 | 901 | return linq.From([]*GroupConfig{ 902 | { 903 | group: versions.Key.(string), 904 | versions: versionsGroup, 905 | }, 906 | }) 907 | }). 908 | WhereT(func(gc *GroupConfig) bool { 909 | return len(gc.Versions()) != 0 910 | }). 911 | ToSlice(&groups) 912 | 913 | return groups 914 | } 915 | 916 | func additionalSecretOutputs(gvk schema.GroupVersionKind) []string { 917 | kind := kinds.Kind(gvk.Kind) 918 | 919 | switch kind { 920 | case kinds.Secret: 921 | return []string{"data", "stringData"} 922 | default: 923 | return []string{} 924 | } 925 | } 926 | 927 | func aliasesForGVK(gvk schema.GroupVersionKind) []string { 928 | kind := kinds.Kind(gvk.Kind) 929 | 930 | switch kind { 931 | case kinds.DaemonSet: 932 | return []string{ 933 | "kubernetes:apps/v1:DaemonSet", 934 | "kubernetes:apps/v1beta2:DaemonSet", 935 | "kubernetes:extensions/v1beta1:DaemonSet", 936 | } 937 | case kinds.Deployment: 938 | return []string{ 939 | "kubernetes:apps/v1:Deployment", 940 | "kubernetes:apps/v1beta1:Deployment", 941 | "kubernetes:apps/v1beta2:Deployment", 942 | "kubernetes:extensions/v1beta1:Deployment", 943 | } 944 | case kinds.Ingress: 945 | return []string{ 946 | "kubernetes:networking/v1beta1:Ingress", 947 | "kubernetes:extensions/v1beta1:Ingress", 948 | } 949 | case kinds.ReplicaSet: 950 | return []string{ 951 | "kubernetes:apps/v1:ReplicaSet", 952 | "kubernetes:apps/v1beta2:ReplicaSet", 953 | "kubernetes:extensions/v1beta1:ReplicaSet", 954 | } 955 | case kinds.StatefulSet: 956 | return []string{ 957 | "kubernetes:apps/v1:StatefulSet", 958 | "kubernetes:apps/v1beta1:StatefulSet", 959 | "kubernetes:apps/v1beta2:StatefulSet", 960 | "kubernetes:extensions/v1beta1:StatefulSet", 961 | } 962 | default: 963 | return []string{} 964 | } 965 | } 966 | -------------------------------------------------------------------------------- /internal/version/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2018, Pulumi Corporation. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package version 16 | 17 | // Version is initialized by the Go linker to contain the semver of this build. 18 | var Version string 19 | -------------------------------------------------------------------------------- /scripts/get-version: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -o nounset -o errexit -o pipefail 3 | SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )" 4 | COMMITISH=${1:-HEAD} 5 | DIRTY_TAG="" 6 | 7 | # Figure out if the worktree is dirty, we run update-index first 8 | # as we've seen cases in Travis where not doing so causes git to 9 | # treat the worktree as dirty when it is not. 10 | git update-index -q --refresh 11 | if ! git diff-files --quiet; then 12 | DIRTY_TAG="dirty" 13 | fi 14 | 15 | # If we have an exact tag, just use it. 16 | if git describe --tags --exact-match "${COMMITISH}" >/dev/null 2>&1; then 17 | echo -n "$(git describe --tags --exact-match "${COMMITISH}")" 18 | if [ ! -z "${DIRTY_TAG}" ]; then 19 | echo -n "+${DIRTY_TAG}" 20 | fi 21 | 22 | echo "" 23 | exit 0 24 | fi 25 | 26 | # Otherwise, increment the patch version, add the -dev tag and some 27 | # commit metadata. If there's no existing tag, pretend a v0.0.0 was 28 | # there so we'll produce v0.0.1-dev builds. 29 | if git describe --tags --abbrev=0 "${COMMITISH}" > /dev/null 2>&1; then 30 | TAG=$(git describe --tags --abbrev=0 "${COMMITISH}") 31 | else 32 | TAG="v0.0.0" 33 | fi 34 | 35 | # Strip off any pre-release tag we might have (e.g. from doing a -rc build) 36 | TAG=${TAG%%-*} 37 | 38 | MAJOR=$(cut -d. -f1 <<< "${TAG}") 39 | MINOR=$(cut -d. -f2 <<< "${TAG}") 40 | PATCH=$(cut -d. -f3 <<< "${TAG}") 41 | 42 | # We want to include some additional information. To the base tag we 43 | # add a timestamp and commit hash. We use the timestamp of the commit 44 | # itself, not the date it was authored (so it will change when someone 45 | # rebases a PR into master, for example). 46 | echo -n "${MAJOR}.${MINOR}.$((${PATCH}+1))-dev.$(git show -s --format='%ct+g%h' ${COMMITISH})" 47 | if [ ! -z "${DIRTY_TAG}" ]; then 48 | echo -n ".${DIRTY_TAG}" 49 | fi 50 | 51 | echo "" 52 | -------------------------------------------------------------------------------- /scripts/promote.js: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2018, Pulumi Corporation. All rights reserved. 2 | 3 | // This program simply reads a package.json from stdin, takes a set of arguments representing 4 | // package names, and for each one, promotes that package from a peerDependency to a real dependency. 5 | 6 | // Read the package.json from stdin. 7 | let packageJSONText = ""; 8 | const readline = require("readline"); 9 | const stdin = readline.createInterface({ 10 | input: process.stdin, 11 | output: process.stdout, 12 | terminal: false, 13 | }); 14 | stdin.on("line", function(line) { 15 | packageJSONText += `${line}\n`; 16 | }); 17 | stdin.on("close", function() { 18 | // All stdin is available. Parse the JSON and move dependencies around. 19 | const packageJSON = JSON.parse(packageJSONText); 20 | for (const arg of process.argv.slice(2)) { 21 | if (!packageJSON.peerDependencies || !packageJSON.peerDependencies[arg]) { 22 | throw new Error(`No peerDependency for "${arg}" found`); 23 | } 24 | 25 | // Add this dependency. 26 | if (!packageJSON.dependencies) { 27 | packageJSON.dependencies = {}; 28 | } 29 | packageJSON.dependencies[arg] = packageJSON.peerDependencies[arg]; 30 | 31 | // And now delete the peer dependency. 32 | delete packageJSON.peerDependencies[arg]; 33 | if (Object.keys(packageJSON.peerDependencies).length === 0) { 34 | delete packageJSON.peerDependencies; 35 | } 36 | } 37 | 38 | // Now print out the result to stdout. 39 | console.log(JSON.stringify(packageJSON, null, 4)); 40 | }); 41 | -------------------------------------------------------------------------------- /scripts/publish_packages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # publish.sh builds and publishes a release. 3 | set -o nounset -o errexit -o pipefail 4 | ROOT=$(dirname $0)/.. 5 | 6 | echo "Publishing NPM packages to NPMjs.com:" 7 | 8 | # For each package, first create the package.json to publish. This must be different than the one we use for 9 | # development and testing the SDK, since we use symlinking for those workflows. Namely, we must promote the SDK 10 | # dependencies from peerDependencies that are resolved via those links, to real installable dependencies. 11 | publish() { 12 | node $(dirname $0)/promote.js ${@:2} < \ 13 | ${ROOT}/sdk/nodejs/bin/package.json > \ 14 | ${ROOT}/sdk/nodejs/bin/package.json.publish 15 | pushd ${ROOT}/sdk/nodejs/bin 16 | mv package.json package.json.dev 17 | mv package.json.publish package.json 18 | 19 | NPM_TAG="dev" 20 | 21 | # If the package doesn't have a pre-release tag, use the tag of latest instead of 22 | # dev. NPM uses this tag as the default version to add, so we want it to mean 23 | # the newest released version. 24 | if [[ $(jq -r .version < package.json) != *-* ]]; then 25 | NPM_TAG="latest" 26 | fi 27 | 28 | # Now, perform the publish. 29 | npm publish -tag ${NPM_TAG} 30 | npm info 2>/dev/null 31 | 32 | # And finally restore the original package.json. 33 | mv package.json package.json.publish 34 | mv package.json.dev package.json 35 | popd 36 | } 37 | 38 | publish query 39 | -------------------------------------------------------------------------------- /sdk/nodejs/Makefile: -------------------------------------------------------------------------------- 1 | PROJECT_NAME := pulumi-query-kubernetes 2 | NODE_MODULE_NAME := @pulumi/query-kubernetes 3 | include ../../build/common.mk 4 | include ../../variables.mk 5 | 6 | VERSION := $(shell ../../scripts/get-version) 7 | 8 | export PATH := $(shell yarn bin 2>/dev/null):$(PATH) 9 | 10 | TESTPARALLELISM := 10 11 | 12 | build:: generate 13 | yarn install 14 | rm -rf bin/ 15 | tsc 16 | sed -e 's/\$${VERSION}/$(VERSION)/g' < package.json > bin/package.json 17 | cp ../../README.md ../../LICENSE bin/ 18 | 19 | lint:: 20 | tslint -c tslint.json -p tsconfig.json 21 | 22 | # instanbul_tests:: 23 | # istanbul test --print none _mocha -- --timeout 15000 'bin/tests/**/*.spec.js' 24 | # istanbul report text-summary 25 | # istanbul report text 26 | 27 | # test_fast:: instanbul_tests 28 | 29 | # test_all:: instanbul_tests 30 | -------------------------------------------------------------------------------- /sdk/nodejs/logs.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2019, Pulumi Corporation. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as pulumi from "@pulumi/pulumi"; 16 | 17 | export async function* podLogs(namespace: string, name: string) { 18 | const logLines = pulumi.runtime.streamInvoke( 19 | "kubernetes:kubernetes:podLogs", 20 | { 21 | namespace, 22 | name, 23 | }, 24 | ); 25 | 26 | for await (const batch of await logLines) { 27 | if (batch !== undefined) { 28 | const lines = batch.lines as string[]; 29 | yield* lines; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /sdk/nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@pulumi/query-kubernetes", 3 | "version": "${VERSION}", 4 | "description": "Kubernetes SDK for Pulumi Query", 5 | "license": "Apache-2.0", 6 | "keywords": [ 7 | "pulumi", 8 | "kubernetes", 9 | "query" 10 | ], 11 | "homepage": "https://pulumi.io", 12 | "repository": "https://github.com/pulumi/pulumi-query-kubernetes", 13 | "dependencies": { 14 | "@pulumi/kubernetes": "^1.3.0", 15 | "@pulumi/pulumi": "^1.5.2", 16 | "@pulumi/query": "^0.6.0", 17 | "rxjs": "^6.5.3" 18 | }, 19 | "devDependencies": { 20 | "@types/mocha": "^2.2.42", 21 | "@types/node": "^10.12.7", 22 | "istanbul": "^0.4.5", 23 | "mocha": "^3.5.0", 24 | "tslint": "^5.11.0", 25 | "typescript": "^3.0.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sdk/nodejs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "bin", 4 | "target": "es6", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "declaration": true, 8 | "sourceMap": false, 9 | "stripInternal": true, 10 | "experimentalDecorators": true, 11 | "pretty": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "noImplicitAny": true, 14 | "noImplicitReturns": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "strictNullChecks": true, 17 | }, 18 | "files": [ 19 | "index.ts", 20 | ] 21 | } 22 | 23 | -------------------------------------------------------------------------------- /sdk/nodejs/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "align": [ 4 | true, 5 | "parameters", 6 | "statements" 7 | ], 8 | "ban": false, 9 | "class-name": true, 10 | "comment-format": [ 11 | true, 12 | "check-space" 13 | ], 14 | "curly": true, 15 | "eofline": true, 16 | "file-header": [ 17 | true, 18 | "Copyright 2016-2019, Pulumi Corporation." 19 | ], 20 | "forin": true, 21 | "indent": [ 22 | true, 23 | "spaces" 24 | ], 25 | "interface-name": false, 26 | "jsdoc-format": false, 27 | "label-position": true, 28 | "member-access": false, 29 | "member-ordering": [ 30 | true, 31 | "static-before-instance", 32 | "variables-before-functions" 33 | ], 34 | "no-any": false, 35 | "no-arg": true, 36 | "no-bitwise": false, 37 | "no-conditional-assignment": false, 38 | "no-consecutive-blank-lines": false, 39 | "no-console": [ 40 | true, 41 | "debug", 42 | "info", 43 | "time", 44 | "timeEnd", 45 | "trace" 46 | ], 47 | "no-construct": true, 48 | "no-debugger": true, 49 | "no-duplicate-variable": true, 50 | "no-empty": true, 51 | "no-eval": true, 52 | "no-inferrable-types": false, 53 | "no-internal-module": true, 54 | "no-parameter-properties": false, 55 | "no-require-imports": false, 56 | "no-shadowed-variable": true, 57 | "no-string-literal": false, 58 | "no-switch-case-fall-through": true, 59 | "no-trailing-whitespace": true, 60 | "no-unused-expression": true, 61 | "no-use-before-declare": false, 62 | "no-var-keyword": true, 63 | "no-var-requires": false, 64 | "object-literal-sort-keys": false, 65 | "one-line": [ 66 | true, 67 | "check-open-brace", 68 | "check-whitespace" 69 | ], 70 | "ordered-imports": true, 71 | "prefer-const": true, 72 | "quotemark": [ 73 | true, 74 | "double", 75 | "avoid-escape" 76 | ], 77 | "radix": true, 78 | "semicolon": true, 79 | "switch-default": true, 80 | "trailing-comma": [ 81 | true, 82 | { 83 | "multiline": "always", 84 | "singleline": "never", 85 | "esSpecCompliant": true 86 | } 87 | ], 88 | "triple-equals": [ 89 | true, 90 | "allow-null-check" 91 | ], 92 | "typedef": [ 93 | false, 94 | "call-signature", 95 | "parameter", 96 | "property-declaration", 97 | "variable-declaration", 98 | "member-variable-declaration" 99 | ], 100 | "typedef-whitespace": [ 101 | true, 102 | { 103 | "call-signature": "nospace", 104 | "index-signature": "nospace", 105 | "parameter": "nospace", 106 | "property-declaration": "nospace", 107 | "variable-declaration": "nospace" 108 | } 109 | ], 110 | "variable-name": [ 111 | true, 112 | "check-format", 113 | "allow-leading-underscore", 114 | "ban-keywords" 115 | ], 116 | "whitespace": [ 117 | true, 118 | "check-branch", 119 | "check-decl", 120 | "check-module", 121 | "check-separator", 122 | "check-type" 123 | ] 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /variables.mk: -------------------------------------------------------------------------------- 1 | PACK := query-kubernetes 2 | PACKDIR := .. 3 | PROJECT := github.com/pulumi/pulumi-query-kubernetes 4 | 5 | CODEGEN := pulumi-gen-${PACK} 6 | VERSION ?= $(shell scripts/get-version) 7 | KUBE_VERSION ?= v1.16.0 8 | SWAGGER_URL ?= https://github.com/kubernetes/kubernetes/raw/${KUBE_VERSION}/api/openapi-spec/swagger.json 9 | PROJ_ROOT := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) 10 | OPENAPI_DIR := $(PROJ_ROOT)/gen/openapi-specs 11 | OPENAPI_FILE := ${OPENAPI_DIR}/swagger-${KUBE_VERSION}.json 12 | 13 | VERSION_FLAGS := -ldflags "-X github.com/pulumi/pulumi-kubernetes/pkg/version.Version=${VERSION}" 14 | 15 | GO ?= go 16 | CURL ?= curl 17 | 18 | $(OPENAPI_FILE):: 19 | @mkdir -p $(OPENAPI_DIR) 20 | test -f $(OPENAPI_FILE) || $(CURL) -s -L $(SWAGGER_URL) > $(OPENAPI_FILE) 21 | 22 | generate:: $(OPENAPI_FILE) 23 | $(GO) install $(VERSION_FLAGS) $(PROJECT)/internal/cmd/$(CODEGEN) 24 | for LANGUAGE in "nodejs" ; do \ 25 | $(CODEGEN) $$LANGUAGE $(OPENAPI_FILE) $(PROJ_ROOT)/gen/$${LANGUAGE}-templates $(PACKDIR) || exit 3 ; \ 26 | done 27 | --------------------------------------------------------------------------------