├── .dockerignore ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── cli.go ├── command ├── create.go ├── create_test.go ├── delete.go ├── delete_test.go ├── flag.go ├── get.go ├── get_test.go ├── list.go ├── list_test.go ├── meta.go ├── notification.go ├── notification_test.go ├── team.go ├── team_test.go ├── user.go ├── user_test.go └── version.go ├── commands.go ├── glide.lock ├── glide.yaml ├── main.go ├── quay ├── notification.go ├── permission.go ├── quay.go └── repository.go ├── utils ├── httpdelete.go ├── httpget.go ├── httppost.go └── httpput.go └── version.go /.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !bin/qucli 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | /bin 27 | /vendor 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | services: 3 | - docker 4 | language: go 5 | go: 6 | - '1.8' 7 | before_install: 8 | - sudo add-apt-repository ppa:masterminds/glide -y 9 | - sudo apt-get update -q 10 | - sudo apt-get install glide -y 11 | - mkdir -p $GOPATH/bin 12 | install: 13 | - make deps 14 | script: 15 | - make test 16 | before_deploy: 17 | - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 make 18 | - make cross-build 19 | - make dist 20 | deploy: 21 | - provider: releases 22 | skip_cleanup: true 23 | api_key: 24 | secure: N/ojFclq4yqXzB3ojWpywNeD/BIviybd4UzG3+23x8qDhEfnPiqAdcrv05Cl+LvuIlYGZcuy42cnuac/aQDC8ZJSgtkwv6EIiA+me+eYcXQeqbCybMJFfA9tI5l1hayMsZCwnvi3x+8Lx+AegA/3waXZks3ud7TAFIc6oKoBo9PSivY/Jv6hjmypF7VszkZpsO3juI98sSaKvH5anQSh+uErEwU0EtUq/KI7SHVbf21ZuP8GDK6oxbGXlssS896qwLb1fmN00tQfqpCGhRpwpUpxlrSLiyUv+VVfon38U2o6OoqCPeUA12y5P80tlGAZkU8tMP0eaQTLygBAjahaV28EXorbEuXZeWRGWqNodbguW5yaXhwMPiEfZHkY1vii1p0DKihnmyFQAmsb7+owgNo1QPHCnVX6qtEhUCYEzqe6MyePvPoO3mR2TYXdVRmEUNQqaNQWaN6UT0GSd7l1ZMWutVvTKN6ehYrDrfP9f4LOkjOHoz2zN5qdHcfludmK5MTtmJ2BEIiy6Z9JB/NrXnK+xphBVv+tYK7Mtz0Ql2IePdHaUFue504sFB+LnNfiFAzM9YriMRDTKKqxy+3XQskwuJd93fslDOWuktRgoYkqU7OtIlXxo1xmbbWuUX6eL+4Jwx785jdZGgjFZ6e3dejz75d3EmeE/LhgSjirikE= 25 | file_glob: true 26 | file: 'dist/*.{tar.gz,zip}' 27 | on: 28 | repo: koudaiii/qucli 29 | tags: true 30 | go: '1.8' 31 | - provider: script 32 | skip_cleanup: true 33 | script: make ci-docker-release 34 | on: 35 | branch: master 36 | go: '1.8' 37 | - provider: script 38 | skip_cleanup: true 39 | script: DOCKER_IMAGE_TAG=$TRAVIS_TAG make ci-docker-release 40 | on: 41 | tags: true 42 | go: '1.8' 43 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.6.6 (2019-04-19) 2 | 3 | - [Fix bug] Default not use `HOSTNAME` env #44 4 | 5 | ## 0.6.5 (2017-10-18) 6 | 7 | - Add validate #37 8 | 9 | ## 0.6.4 (2017-10-18) 10 | 11 | - [Fix bug] #34 #32 12 | - Bump up to golang 1.8 in Travis CI #35 13 | 14 | ## 0.6.3 (2017-10-18) 15 | 16 | - [Fix bug] os.Getenv #33 #32 17 | 18 | ## 0.6.2 (2017-10-17) 19 | 20 | - fix version v0.6.2 and fix link in READE #30 #29 21 | 22 | ## 0.6.1 (2017-10-17) 23 | 24 | - Fix bug `--is-publuc` option #28 25 | 26 | ## 0.6.0 (2017-10-17) 27 | 28 | - Add notification command #27 29 | 30 | ## 0.5.0 (2017-09-30) 31 | 32 | - Allow a different hostname other than quay.io #22 33 | 34 | ## 0.4.0 (2017-01-24) 35 | 36 | - Change repository name from dockerepos to qucli. 37 | 38 | ## 0.3.0 (2017-01-23) 39 | 40 | - Supported only Quay 41 | 42 | ## 0.2.0 (2017-01-18) 43 | 44 | - list command 45 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.4 2 | 3 | RUN apk add --no-cache --update ca-certificates 4 | 5 | COPY bin/qucli /qucli 6 | 7 | ENTRYPOINT ["/qucli"] 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Kodai Sakabe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME := qucli 2 | VERSION := v0.6.6 3 | REVISION := $(shell git rev-parse --short HEAD) 4 | 5 | SRCS := $(shell find . -name '*.go' -type f) 6 | LDFLAGS := -ldflags="-s -w -X \"main.Version=$(VERSION)\" -X \"main.GitCommit=$(REVISION)\"" 7 | 8 | DIST_DIRS := find * -type d -exec 9 | 10 | DOCKER_REPOSITORY := quay.io 11 | DOCKER_IMAGE_NAME := $(DOCKER_REPOSITORY)/koudaiii/qucli 12 | DOCKER_IMAGE_TAG ?= latest 13 | DOCKER_IMAGE := $(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) 14 | 15 | .DEFAULT_GOAL := bin/$(NAME) 16 | 17 | bin/$(NAME): $(SRCS) 18 | go build $(LDFLAGS) -o bin/$(NAME) 19 | 20 | .PHONY: ci-docker-release 21 | ci-docker-release: docker-build 22 | @docker login -u="$(DOCKER_QUAY_USERNAME)" -p="$(DOCKER_QUAY_PASSWORD)" $(DOCKER_REPOSITORY) 23 | docker push $(DOCKER_IMAGE) 24 | 25 | .PHONY: clean 26 | clean: 27 | rm -rf bin/* 28 | rm -rf vendor/* 29 | 30 | .PHONY: cross-build 31 | cross-build: deps 32 | for os in darwin linux windows; do \ 33 | for arch in amd64 386; do \ 34 | GOOS=$$os GOARCH=$$arch CGO_ENABLED=0 go build $(LDFLAGS) -o dist/$$os-$$arch/$(NAME); \ 35 | done; \ 36 | done 37 | 38 | .PHONY: deps 39 | deps: glide 40 | glide install 41 | 42 | .PHONY: dist 43 | dist: 44 | cd dist && \ 45 | $(DIST_DIRS) cp ../LICENSE {} \; && \ 46 | $(DIST_DIRS) cp ../README.md {} \; && \ 47 | $(DIST_DIRS) tar -zcf $(NAME)-$(VERSION)-{}.tar.gz {} \; && \ 48 | $(DIST_DIRS) zip -r $(NAME)-$(VERSION)-{}.zip {} \; && \ 49 | cd .. 50 | 51 | .PHONY: docker-build 52 | docker-build: 53 | ifeq ($(findstring ELF 64-bit LSB,$(shell file bin/$(NAME) 2> /dev/null)),) 54 | @echo "bin/$(NAME) is not a binary of Linux 64bit binary." 55 | @exit 1 56 | endif 57 | docker build -t $(DOCKER_IMAGE) . 58 | 59 | .PHONY: glide 60 | glide: 61 | ifeq ($(shell command -v glide 2> /dev/null),) 62 | curl https://glide.sh/get | sh 63 | endif 64 | 65 | .PHONY: install 66 | install: 67 | go install $(LDFLAGS) 68 | 69 | .PHONY: test 70 | test: 71 | go test -cover -v `glide novendor` 72 | 73 | .PHONY: update-deps 74 | update-deps: glide 75 | glide update 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qucli 2 | 3 | [![Build Status](https://travis-ci.org/koudaiii/qucli.svg?branch=master)](https://travis-ci.org/koudaiii/qucli) 4 | [![Docker Repository on Quay](https://quay.io/repository/koudaiii/qucli/status "Docker Repository on Quay")](https://quay.io/repository/koudaiii/qucli) 5 | [![GitHub release](https://img.shields.io/github/release/koudaiii/qucli.svg)](https://github.com/koudaiii/qucli/releases) 6 | 7 | ## Description 8 | 9 | Manage repositories in Quay.io 10 | 11 | ## Table of Contents 12 | 13 | * [qucli](#qucli) 14 | * [Description](#description) 15 | * [Table of Contents](#table-of-contents) 16 | * [Requirements](#requirements) 17 | * [Installation](#installation) 18 | * [Using Homebrew (OS X only)](#using-homebrew-os-x-only) 19 | * [Precompiled binary](#precompiled-binary) 20 | * [From source](#from-source) 21 | * [Run in a Docker container](#run-in-a-docker-container) 22 | * [Usage](#usage) 23 | * [list](#list) 24 | * [get](#get) 25 | * [create](#create) 26 | * [delete](#delete) 27 | * [add-user](#add-user) 28 | * [add-team](#add-team) 29 | * [delete-user](#delete-user) 30 | * [delete-team](#delete-team) 31 | * [add-notification](#add-notification) 32 | * [test-notification](#test-notification) 33 | * [delete-notification](#delete-notification) 34 | * [Options](#options) 35 | * [Development](#development) 36 | * [Contribution](#contribution) 37 | * [Author](#author) 38 | * [License](#license) 39 | 40 | ## Requirements 41 | 42 | - Enviroment QUAY_API_TOKEN 43 | - Get Api Token. [Applications and Tokens](https://docs.quay.io/api/) 44 | 45 | ```shell-session 46 | $ export QUAY_API_TOKEN=foobar 47 | ``` 48 | 49 | if Quay Enterprise user, add Enviroment QUAY_HOSTNAME or `--hostname` 50 | 51 | ```shell-session 52 | $ export QUAY_HOSTNAME=quay.example.com 53 | or 54 | $ qucli xxx --hostname=quay.example.com 55 | ``` 56 | 57 | ## Installation 58 | 59 | ### Using Homebrew (OS X only) 60 | 61 | Formula is available at [koudaiii/homebrew-tools](https://github.com/koudaiii/homebrew-tools). 62 | 63 | ```shell-session 64 | $ brew tap koudaiii/tools 65 | $ brew install qucli 66 | ``` 67 | 68 | ### Precompiled binary 69 | 70 | Precompiled binaries for Windows, OS X, Linux are available at [Releases](https://github.com/koudaiii/qucli/releases). 71 | 72 | ### From source 73 | To install, use `go get`: 74 | 75 | ```shell-session 76 | $ go get -d github.com/koudaiii/qucli 77 | $ cd $GOPATH/src/github.com/koudaiii/qucli 78 | $ make deps 79 | $ make install 80 | ``` 81 | 82 | ### Run in a Docker container 83 | 84 | docker image is available at [quay.io/koudaiii/qucli](https://quay.io/repository/koudaiii/qucli). 85 | 86 | ```shell-session 87 | # -t is required to colorize logs 88 | $ docker run \ 89 | --rm \ 90 | -t \ 91 | -e QUAY_API_TOKEN=foobar \ 92 | -e QUAY_HOSTNAME=quay.io \ 93 | quay.io/koudaiii/qucli:latest 94 | ``` 95 | 96 | ## Usage 97 | 98 | ```shell-session 99 | $ qucli 100 | usage: qucli [--version] [--help] [] 101 | 102 | Available commands are: 103 | add-notification Add notification in repository 104 | add-team Add team in repository 105 | add-user Add user in repository 106 | create Create repository in Quay 107 | delete Delete repository in Quay 108 | delete-notification Delete notification in repository 109 | delete-team Delete team in repository 110 | delete-user Delete user in repository 111 | get Get Repository and Permissions and Notifications in Quay 112 | list List repository and Permissions in Quay 113 | test-notification Test notification in repository 114 | version Print qucli version and quit 115 | ``` 116 | 117 | ### `list` 118 | 119 | List repository in namespace 120 | 121 | With `--is-public` option, you can `true` or `false` 122 | 123 | ```bsah 124 | $ qucli list koudaiii 125 | NAME isPublic DESCRIPTION 126 | quay.io/koudaiii/apig-sample true 127 | quay.io/koudaiii/qucli true 128 | quay.io/koudaiii/kubeps true 129 | quay.io/koudaiii/test true 130 | ``` 131 | 132 | ### `get` 133 | 134 | Get repository and Permissions in Quay 135 | 136 | ```shell-session 137 | $ qucli get koudaiii/test 138 | Repository: 139 | quay.io/koudaiii/test 140 | Visibility: 141 | private 142 | Permissions: 143 | koudaiii(admin) 144 | Notifications: 145 | b0736be9-f0eb-4c3c-8d33-2e331b1e5b0f Some title repo_push map[] slack map[url:https://hooks.slack.com/service/some/token/here] 146 | ``` 147 | 148 | ### `create` 149 | 150 | Create repository in Quay 151 | 152 | With `--visibility` option, you can `public` or `private` 153 | 154 | ```shell-session 155 | $ qucli create koudaiii/test --visibility private 156 | Created! quay.io/koudaiii/test 157 | ``` 158 | 159 | ### `delete` 160 | 161 | Delete repository in Quay 162 | 163 | ```shell-session 164 | $ qucli delete koudaiii/test 165 | Deleted! quay.io/koudaiii/test 166 | ``` 167 | 168 | ```shell-session 169 | $ qucli get koudaiii/test 170 | err: HTTP error! 171 | URL: https://quay.io/api/v1/repository/koudaiii/test 172 | status code: 404 173 | body: 174 | {"status": 404, "error_message": "Not Found", "title": "not_found", "error_type": "not_found", "detail": "Not Found", "type": "https://quay.io/api/v1/error/not_found"} 175 | ``` 176 | 177 | ### `add-user` 178 | 179 | Add user in repository 180 | 181 | With `--role` option, you can `read` or `write` or `admin` 182 | 183 | ```shell-session 184 | $ qucli add-user koudaiii/test dtan4 --role write 185 | Added! dtan4(write) in quay.io/koudaiii/test 186 | ``` 187 | 188 | ```shell-session 189 | $ qucli get koudaiii/test 190 | Repository: 191 | quay.io/koudaiii/test 192 | Visibility: 193 | private 194 | Permissions: 195 | koudaiii(admin) 196 | dtan4(write) 197 | ``` 198 | 199 | ### `add-team` 200 | 201 | Add team in repository 202 | 203 | With `--role` option, you can `read` or `write` or `admin` 204 | 205 | ```shell-session 206 | $ qucli add-team koudaiii/test infrastructure --role write 207 | Added! infrastructure(write) in quay.io/koudaiii/test 208 | ``` 209 | 210 | ```shell-session 211 | $ qucli get koudaiii/test 212 | Repository: 213 | quay.io/koudaiii/test 214 | Visibility: 215 | private 216 | Permissions: 217 | koudaiii(admin) 218 | dtan4(write) 219 | infrastructure(write) 220 | ``` 221 | 222 | ### `delete-user` 223 | 224 | Delete user from repository 225 | 226 | ```shell-session 227 | $ qucli delete-user koudaiii/test dtan4 228 | Deleted! dtan4 in quay.io/koudaiii/test 229 | ``` 230 | 231 | ```shell-session 232 | $ qucli get koudaiii/test 233 | Repository: 234 | quay.io/koudaiii/test 235 | Visibility: 236 | private 237 | Permissions: 238 | koudaiii(admin) 239 | infrastructure(write) 240 | ``` 241 | 242 | ### `delete-team` 243 | 244 | Delete team from repository 245 | 246 | ```shell-session 247 | $ qucli delete-team koudaiii/test infrastructure 248 | Deleted! infrastructure in quay.io/koudaiii/test 249 | ``` 250 | 251 | ```shell-session 252 | $ qucli get koudaiii/test 253 | Repository: 254 | quay.io/koudaiii/test 255 | Visibility: 256 | private 257 | Permissions: 258 | koudaiii(admin) 259 | ``` 260 | 261 | ### `add-notification` 262 | 263 | Add notification in repository with some options. 264 | 265 | - `webhook` method 266 | 267 | ```shell-session 268 | $ qucli add-notification koudaiii/test --event="repo_push" --method="webhook" --url="http://url/goes/here" 269 | Added! 3c3c142c-2161-42ae-9414-39c787386b5c repo_push map[] webhook map[url:http://url/goes/here] in quay.io/koudaiii/test 270 | ``` 271 | 272 | - `slack` method 273 | 274 | ```shell-session 275 | $ qucli add-notification koudaiii/test --event="repo_push" --method="slack" --url="https://hooks.slack.com/service/{some}/{token}/{here}" 276 | Added! 61ae254f-89f0-4a36-a439-9b78004f2ab0 repo_push map[] slack map[url:https://hooks.slack.com/service/{some}/{token}/{here}] in quay.io/koudaiii/test 277 | ``` 278 | 279 | - options 280 | 281 | ```shell-session 282 | $ qucli add-notification 283 | qucli supported only Quay.io 284 | Usage: add-notification 285 | qucli add-notification koudaiii/qucli --event="repo_push" --method="webhook" --url="http://url/goes/here" 286 | 287 | Option: 288 | --event string set 'evnet'. ['repo_push', 'build_queued', 'build_start', 'build_success', 'build_failure', 'build_cancelled', 'vulnerability_found']. 289 | --level string if you use 'vulnerability_found' evnet, A vulnerability must have a severity of the chosen level (highest level is 0).[0-6] 290 | --ref string if you use event excluding 'repo_push' event, an optional regular expression for matching the git branch or tag git ref. If left blank, the notification will fire for all builds.(refs/heads/somebranch)|(refs/tags/sometag) 291 | --method string set 'method'. ['webhook', 'slack', 'email']. 292 | --email string if you use 'email' method, set E-mail address. 'test@example.com'. 293 | --url string if you use 'webhook' or 'slack' method, set url. 'http://url/goes/here' or 'https://hooks.slack.com/service/{some}/{token}/{here}'. 294 | --title string The title for a notification is an optional field for a human-readable title for the notification. 295 | ``` 296 | 297 | ### `test-notification` 298 | 299 | Test notification from repository. 300 | 301 | ```shell-session 302 | $ qucli test-notification koudaiii/qucli 0c91e746-9d9e-4845-8dff-3c0995976dfa 303 | Test Notification! 0c91e746-9d9e-4845-8dff-3c0995976dfa notification in quay.io/koudaiii/qucli 304 | ``` 305 | 306 | ### `delete-notification` 307 | 308 | Delete notification from repository. 309 | 310 | ```shell-session 311 | $ ./bin/qucli delete-notification koudaiii/test 3c3c142c-2161-42ae-9414-39c787386b5c 312 | Deleted! 3c3c142c-2161-42ae-9414-39c787386b5c notification in quay.io/koudaiii/test 313 | ``` 314 | 315 | ### Options 316 | 317 | |Option|Description|Required|Default| 318 | |---------|-----------|-------|-------| 319 | |`--visibility=VISIBILITY`| "visibility set to 'public' or 'private'||`public`| 320 | |`--role=ROLE`|role to use for the user or team ROLE='read' or 'write' or 'admin'||`read`| 321 | |`--is-public=bool`| repository type is public. `true` or `false`||`true`| 322 | |`--hostname=HOSTNAME`| if Quay Enterprise user, set hostname. ||`quay.io`| 323 | |`--event=EVENT` | set 'evnet'. EVENT='repo_push' or 'build_queued' or 'build_start' or 'build_success' or 'build_failure' or 'build_cancelled' or 'vulnerability_found'. |true|| 324 | |`--level=LEVEL`| if you use 'vulnerability_found' evnet, A vulnerability must have a severity of the chosen level (highest level is 0).LEVEL=0-6 ||| 325 | |`--ref=REF`|if you use event excluding 'repo_push' event, an optional regular expression for matching the git branch or tag git ref. If left blank, the notification will fire for all builds.(refs/heads/somebranch) or (refs/tags/sometag) ||| 326 | |`--method=METHOD`|set 'method'. METHOD='webhook' or 'slack' or 'email'.|true|| 327 | |`--email=EMAIL`|if you use 'email' method, set E-mail address. EMAIL='test@example.com'.||| 328 | |`--url=URL`|if you use 'webhook' or 'slack' method, set url. 'http://url/goes/here' or 'https://hooks.slack.com/service/{some}/{token}/{here}'.||| 329 | |`--title=TITLE`|The title for a notification is an optional field for a human-readable title for the notification.||| 330 | |`--help`|Print command line usage||| 331 | |`-v`, `--version`|Print version||| 332 | 333 | ## Development 334 | 335 | Clone this repository and build using `make`. 336 | 337 | ```shell-session 338 | $ go get -d github.com/koudaiii/qucli 339 | $ cd $GOPATH/src/github.com/koudaiii/qucli 340 | $ make 341 | ``` 342 | 343 | ## Contribution 344 | 345 | 1. Fork ([https://github.com/koudaiii/qucli/fork](https://github.com/koudaiii/qucli/fork)) 346 | 1. Create a feature branch 347 | 1. Commit your changes 348 | 1. Rebase your local changes against the master branch 349 | 1. Run test suite with the `go test ./...` command and confirm that it passes 350 | 1. Run `gofmt -s` 351 | 1. Create a new Pull Request 352 | 353 | ## Author 354 | 355 | [koudaiii](https://github.com/koudaiii) 356 | 357 | ## License 358 | 359 | [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE) 360 | -------------------------------------------------------------------------------- /cli.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/koudaiii/qucli/command" 8 | "github.com/mitchellh/cli" 9 | ) 10 | 11 | func Run(args []string) int { 12 | 13 | // Meta-option for executables. 14 | // It defines output color and its stdout/stderr stream. 15 | meta := &command.Meta{ 16 | Ui: &cli.ColoredUi{ 17 | InfoColor: cli.UiColorBlue, 18 | ErrorColor: cli.UiColorRed, 19 | Ui: &cli.BasicUi{ 20 | Writer: os.Stdout, 21 | ErrorWriter: os.Stderr, 22 | Reader: os.Stdin, 23 | }, 24 | }} 25 | 26 | return RunCustom(args, Commands(meta)) 27 | } 28 | 29 | func RunCustom(args []string, commands map[string]cli.CommandFactory) int { 30 | 31 | // Get the command line args. We shortcut "--version" and "-v" to 32 | // just show the version. 33 | for _, arg := range args { 34 | if arg == "-v" || arg == "-version" || arg == "--version" { 35 | newArgs := make([]string, len(args)+1) 36 | newArgs[0] = "version" 37 | copy(newArgs[1:], args) 38 | args = newArgs 39 | break 40 | } 41 | } 42 | 43 | cli := &cli.CLI{ 44 | Args: args, 45 | Commands: commands, 46 | Version: Version, 47 | HelpFunc: cli.BasicHelpFunc(Name), 48 | HelpWriter: os.Stdout, 49 | } 50 | exitCode, err := cli.Run() 51 | if err != nil { 52 | fmt.Fprintf(os.Stderr, "Failed to execute: %s\n", err.Error()) 53 | } 54 | 55 | return exitCode 56 | } 57 | -------------------------------------------------------------------------------- /command/create.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/koudaiii/qucli/quay" 9 | ) 10 | 11 | type CreateCommand struct { 12 | Meta 13 | } 14 | 15 | func (c *CreateCommand) Run(args []string) int { 16 | if err := FlagInit(args); err != nil { 17 | fmt.Fprintln(os.Stderr, c.Help()) 18 | os.Exit(1) 19 | } 20 | 21 | if len(subcommandArgs) < 1 { 22 | fmt.Fprintln(os.Stderr, c.Help()) 23 | os.Exit(1) 24 | } 25 | 26 | ss := strings.Split(subcommandArgs[0], "/") 27 | if len(ss) != 2 { 28 | fmt.Fprintln(os.Stderr, c.Help()) 29 | os.Exit(1) 30 | } 31 | 32 | repos, err := quay.CreateRepository(ss[0], ss[1], visibility, hostname) 33 | if err != nil { 34 | fmt.Fprintf(os.Stderr, "err: %v\n", err) 35 | os.Exit(1) 36 | } 37 | fmt.Fprintf(os.Stdout, "Created! %v/%v/%v\n", hostname, repos.Namespace, repos.Name) 38 | 39 | return 0 40 | } 41 | 42 | func (c *CreateCommand) Synopsis() string { 43 | return fmt.Sprint("Create repository in Quay") 44 | } 45 | 46 | func (c *CreateCommand) Help() string { 47 | helpText := ` 48 | qucli supported only Quay.io 49 | Usage: create 50 | qucli create koudaiii/qucli --visibility private 51 | ` 52 | return strings.TrimSpace(helpText) 53 | } 54 | -------------------------------------------------------------------------------- /command/create_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mitchellh/cli" 7 | ) 8 | 9 | func TestCreateCommand_implement(t *testing.T) { 10 | var _ cli.Command = &CreateCommand{} 11 | } 12 | -------------------------------------------------------------------------------- /command/delete.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/koudaiii/qucli/quay" 9 | ) 10 | 11 | type DeleteCommand struct { 12 | Meta 13 | } 14 | 15 | func (c *DeleteCommand) Run(args []string) int { 16 | if err := FlagInit(args); err != nil { 17 | fmt.Fprintln(os.Stderr, c.Help()) 18 | os.Exit(1) 19 | } 20 | 21 | if len(subcommandArgs) != 1 { 22 | fmt.Fprintln(os.Stderr, c.Help()) 23 | os.Exit(1) 24 | } 25 | 26 | ss := strings.Split(subcommandArgs[0], "/") 27 | if len(ss) != 2 { 28 | fmt.Fprintln(os.Stderr, c.Help()) 29 | os.Exit(1) 30 | } 31 | 32 | err := quay.DeleteRepository(ss[0], ss[1], hostname) 33 | if err != nil { 34 | fmt.Fprintf(os.Stderr, "err: %v\n", err) 35 | os.Exit(1) 36 | } 37 | fmt.Fprintf(os.Stdout, "Deleted! %v/%v/%v\n", hostname, ss[0], ss[1]) 38 | return 0 39 | } 40 | 41 | func (c *DeleteCommand) Synopsis() string { 42 | return fmt.Sprint("Delete repository in Quay") 43 | } 44 | 45 | func (c *DeleteCommand) Help() string { 46 | helpText := ` 47 | qucli supported only Quay.io 48 | Usage: delete 49 | qucli delete koudaiii/qucli 50 | ` 51 | return strings.TrimSpace(helpText) 52 | } 53 | -------------------------------------------------------------------------------- /command/delete_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mitchellh/cli" 7 | ) 8 | 9 | func TestDeleteCommand_implement(t *testing.T) { 10 | var _ cli.Command = &DeleteCommand{} 11 | } 12 | -------------------------------------------------------------------------------- /command/flag.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | flag "github.com/spf13/pflag" 5 | ) 6 | 7 | var ( 8 | subcommandArgs []string 9 | role string 10 | visibility string 11 | public bool 12 | event string 13 | method string 14 | url string 15 | email string 16 | title string 17 | hostname string 18 | ref string 19 | level string 20 | ) 21 | 22 | func FlagInit(args []string) error { 23 | flags := flag.NewFlagSet("qucli", flag.ExitOnError) 24 | 25 | flags.Usage = func() { 26 | flags.PrintDefaults() 27 | } 28 | 29 | flags.StringVar(&visibility, "visibility", "public", "visibility set to 'public' or 'private'.") 30 | flags.StringVar(&role, "role", "read", "role to use for the user = ['read', 'write', 'admin'].") 31 | flags.BoolVar(&public, "is-public", true, "'--is-public=true' or '--is-public=false'.") 32 | flags.StringVar(&event, "event", "", "set 'evnet'. ['repo_push', 'build_queued', 'build_start', 'build_success', 'build_failure', 'build_cancelled', 'vulnerability_found'].") 33 | flags.StringVar(&level, "level", "", "if you use 'vulnerability_found' method, A vulnerability must have a severity of the chosen level (highest level is 0).[0-6]") 34 | flags.StringVar(&ref, "ref", "", "if you use event excluding 'repo_push' event, an optional regular expression for matching the git branch or tag git ref. If left blank, the notification will fire for all builds.(refs/heads/somebranch)|(refs/tags/sometag)") 35 | flags.StringVar(&method, "method", "", "set 'method'. ['webhook', 'slack', 'email'].") 36 | flags.StringVar(&email, "email", "", "if you use 'email' method, set E-mail address. 'test@example.com'.") 37 | flags.StringVar(&url, "url", "", "if you use 'webhook' or 'slack' method, set url. 'http://url/goes/here' or 'https://hooks.slack.com/service/{some}/{token}/{here}'.") 38 | flags.StringVar(&title, "title", "", "The title for a notification is an optional field for a human-readable title for the notification.") 39 | flags.StringVar(&hostname, "hostname", "quay.io", "If you use enterprise plan, set hostname option. ex '--hostname=quay.example.com'") 40 | 41 | if err := flags.Parse(args[0:]); err != nil { 42 | return err 43 | } 44 | subcommandArgs = flags.Args() 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /command/get.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/koudaiii/qucli/quay" 9 | ) 10 | 11 | type GetCommand struct { 12 | Meta 13 | } 14 | 15 | func (c *GetCommand) Run(args []string) int { 16 | if err := FlagInit(args); err != nil { 17 | fmt.Fprintln(os.Stderr, c.Help()) 18 | os.Exit(1) 19 | } 20 | 21 | if len(subcommandArgs) != 1 { 22 | fmt.Fprintln(os.Stderr, c.Help()) 23 | os.Exit(1) 24 | } 25 | 26 | ss := strings.Split(subcommandArgs[0], "/") 27 | if len(ss) != 2 { 28 | fmt.Fprintln(os.Stderr, c.Help()) 29 | os.Exit(1) 30 | } 31 | 32 | repos, err := quay.GetRepository(ss[0], ss[1], hostname) 33 | if err != nil { 34 | fmt.Fprintf(os.Stderr, "err: %v\n", err) 35 | os.Exit(1) 36 | } 37 | 38 | fmt.Fprintln(os.Stdout, "Repository:") 39 | fmt.Fprintf(os.Stdout, "\t%v/%v/%v\n", hostname, repos.Namespace, repos.Name) 40 | 41 | fmt.Fprintln(os.Stdout, "Visibility:") 42 | if repos.IsPublic == true { 43 | fmt.Fprintln(os.Stdout, "\tpublic") 44 | } else { 45 | fmt.Fprintln(os.Stdout, "\tprivate") 46 | } 47 | fmt.Fprintln(os.Stdout, "Permissions:") 48 | 49 | permissions, err := quay.GetPermissions(ss[0], ss[1], "user", hostname) 50 | if err != nil { 51 | fmt.Fprintf(os.Stderr, "err: %v\n", err) 52 | os.Exit(1) 53 | } 54 | for _, p := range permissions.Items { 55 | fmt.Fprintf(os.Stdout, "\t%v(%v)\n", p.Name, p.Role) 56 | } 57 | 58 | permissions, err = quay.GetPermissions(ss[0], ss[1], "team", hostname) 59 | if err != nil { 60 | fmt.Fprintf(os.Stderr, "err: %v\n", err) 61 | os.Exit(1) 62 | } 63 | for _, p := range permissions.Items { 64 | fmt.Fprintf(os.Stdout, "\t%v(%v)\n", p.Name, p.Role) 65 | } 66 | 67 | fmt.Fprintln(os.Stdout, "Notifications:") 68 | 69 | notitications, err := quay.ListRepositoryNotifications(ss[0], ss[1], hostname) 70 | if err != nil { 71 | fmt.Fprintf(os.Stderr, "err: %v\n", err) 72 | os.Exit(1) 73 | } 74 | 75 | for _, n := range notitications.Items { 76 | fmt.Fprintf(os.Stdout, "\t%v\t%v\t%v\t%v\t%v\t%v\n", n.UUID, n.Title, n.Event, n.EventConfig, n.Method, n.Config) 77 | } 78 | return 0 79 | } 80 | 81 | func (c *GetCommand) Synopsis() string { 82 | return fmt.Sprint("Get Repository and Permissions and Notifications in Quay") 83 | } 84 | 85 | func (c *GetCommand) Help() string { 86 | helpText := ` 87 | qucli supported only Quay.io 88 | Usage: get 89 | qucli get koudaiii/qucli 90 | ` 91 | return strings.TrimSpace(helpText) 92 | } 93 | -------------------------------------------------------------------------------- /command/get_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mitchellh/cli" 7 | ) 8 | 9 | func TestGetCommand_implement(t *testing.T) { 10 | var _ cli.Command = &GetCommand{} 11 | } 12 | -------------------------------------------------------------------------------- /command/list.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "text/tabwriter" 8 | 9 | "strconv" 10 | 11 | "github.com/koudaiii/qucli/quay" 12 | ) 13 | 14 | type ListCommand struct { 15 | Meta 16 | } 17 | 18 | func (c *ListCommand) Run(args []string) int { 19 | var repositoryColumns = []string{"NAME", "isPublic", "DESCRIPTION"} 20 | 21 | if err := FlagInit(args); err != nil { 22 | fmt.Fprintln(os.Stderr, c.Help()) 23 | os.Exit(1) 24 | } 25 | 26 | if len(subcommandArgs) < 1 { 27 | fmt.Fprintln(os.Stderr, c.Help()) 28 | os.Exit(1) 29 | } 30 | 31 | repositories, err := quay.ListRepository(subcommandArgs[0], public, hostname) 32 | if err != nil { 33 | fmt.Fprintf(os.Stderr, "err: %v\n", err) 34 | os.Exit(1) 35 | } 36 | 37 | repositoryPrint := new(tabwriter.Writer) 38 | repositoryPrint.Init(os.Stdout, 0, 8, 1, '\t', 0) 39 | 40 | fmt.Fprintln(repositoryPrint, strings.Join(repositoryColumns, "\t")) 41 | 42 | for _, repos := range repositories.Items { 43 | if public == repos.IsPublic { 44 | fmt.Fprintln(repositoryPrint, strings.Join( 45 | []string{hostname + "/" + repos.Namespace + "/" + repos.Name, strconv.FormatBool(repos.IsPublic), repos.Description}, "\t", 46 | )) 47 | } 48 | } 49 | repositoryPrint.Flush() 50 | return 0 51 | } 52 | 53 | func (c *ListCommand) Synopsis() string { 54 | return fmt.Sprint("List repository and Permissions in Quay") 55 | } 56 | 57 | func (c *ListCommand) Help() string { 58 | helpText := ` 59 | qucli supported only Quay.io 60 | Usage: list 61 | qucli list koudaiii 62 | ` 63 | return strings.TrimSpace(helpText) 64 | } 65 | -------------------------------------------------------------------------------- /command/list_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mitchellh/cli" 7 | ) 8 | 9 | func TestListCommand_implement(t *testing.T) { 10 | var _ cli.Command = &ListCommand{} 11 | } 12 | -------------------------------------------------------------------------------- /command/meta.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import "github.com/mitchellh/cli" 4 | 5 | // Meta contain the meta-option that nearly all subcommand inherited. 6 | type Meta struct { 7 | Ui cli.Ui 8 | } 9 | -------------------------------------------------------------------------------- /command/notification.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/koudaiii/qucli/quay" 9 | ) 10 | 11 | type AddNotificationCommand struct { 12 | Meta 13 | } 14 | 15 | type DeleteNotificationCommand struct { 16 | Meta 17 | } 18 | 19 | type TestNotificationCommand struct { 20 | Meta 21 | } 22 | 23 | func (c *AddNotificationCommand) Run(args []string) int { 24 | if err := FlagInit(args); err != nil { 25 | fmt.Fprintln(os.Stderr, c.Help()) 26 | os.Exit(1) 27 | } 28 | 29 | if len(subcommandArgs) < 1 { 30 | fmt.Fprintln(os.Stderr, c.Help()) 31 | os.Exit(1) 32 | } 33 | 34 | ss := strings.Split(subcommandArgs[0], "/") 35 | if len(ss) != 2 { 36 | fmt.Fprintln(os.Stderr, c.Help()) 37 | os.Exit(1) 38 | } 39 | 40 | // if you use add-notification command, you must set 'event' option and 'method' option. 41 | if event == "" || method == "" { 42 | fmt.Fprintln(os.Stderr, "if you use add-notification command, you must set 'event' option and 'method' option.") 43 | fmt.Fprintln(os.Stderr, c.Help()) 44 | os.Exit(1) 45 | } 46 | 47 | // if you use 'vulnerability_found' event, you need set 'level' option. 48 | if event == "vulnerability_found" && level == "" { 49 | fmt.Fprintln(os.Stderr, "if you use 'vulnerability_found' event, you need set 'level' option.") 50 | fmt.Fprintln(os.Stderr, c.Help()) 51 | os.Exit(1) 52 | } 53 | 54 | // if you set event excluding 'vulnerability_found' event, you can not set 'level' option. 55 | if event != "vulnerability_found" && level != "" { 56 | fmt.Fprintln(os.Stderr, "if you set event excluding 'vulnerability_found' event, you can not set 'level' option.") 57 | fmt.Fprintln(os.Stderr, c.Help()) 58 | os.Exit(1) 59 | } 60 | 61 | // if you use 'email' method, you need set 'email' option. 62 | if method == "email" && email == "" { 63 | fmt.Fprintln(os.Stderr, "if you use 'email' method, you need set 'email' option.") 64 | fmt.Fprintln(os.Stderr, c.Help()) 65 | os.Exit(1) 66 | } 67 | 68 | // if you use 'slack' or 'webhook' method, you need set 'url' option. 69 | if (method == "slack" || method == "webhook") && url == "" { 70 | fmt.Fprintln(os.Stderr, "if you use 'slack' or 'webhook' method, you need set 'url' option.") 71 | fmt.Fprintln(os.Stderr, c.Help()) 72 | os.Exit(1) 73 | } 74 | 75 | // if you use 'repo_push' event, you can not set 'ref' option. 76 | if event == "repo_push" && ref != "" { 77 | fmt.Fprintln(os.Stderr, "if you use 'repo_push' event, you can not set 'ref' option.") 78 | fmt.Fprintln(os.Stderr, c.Help()) 79 | os.Exit(1) 80 | } 81 | 82 | req := quay.RequestRepositoryNotification{ 83 | Title: title, 84 | Event: event, 85 | EventConfig: quay.NotificationEventConfig{ 86 | Level: level, 87 | RefRegex: ref, 88 | }, 89 | Method: method, 90 | Config: quay.NotificationConfig{ 91 | Email: email, 92 | URL: url, 93 | }, 94 | } 95 | 96 | repos, err := quay.AddRepositoryNotification(ss[0], ss[1], req, hostname) 97 | if err != nil { 98 | fmt.Fprintf(os.Stderr, "err: %v\n", err) 99 | os.Exit(1) 100 | } 101 | fmt.Fprintf(os.Stdout, "Added! \t%v\t%v\t%v\t%v\t%v\t%v\tin %v/%v/%v\n", repos.UUID, repos.Title, repos.Event, repos.EventConfig, repos.Method, repos.Config, hostname, ss[0], ss[1]) 102 | return 0 103 | } 104 | 105 | func (c *AddNotificationCommand) Synopsis() string { 106 | return fmt.Sprint("Add notification in repository") 107 | } 108 | 109 | func (c *AddNotificationCommand) Help() string { 110 | helpText := ` 111 | qucli supported only Quay.io 112 | Usage: add-notification 113 | qucli add-notification koudaiii/qucli --event="repo_push" --method="webhook" --url="http://url/goes/here" 114 | 115 | Option: 116 | --event string set 'evnet'. ['repo_push', 'build_queued', 'build_start', 'build_success', 'build_failure', 'build_cancelled', 'vulnerability_found']. 117 | --level string if you use 'vulnerability_found' evnet, A vulnerability must have a severity of the chosen level (highest level is 0).[0-6] 118 | --ref string if you use event excluding 'repo_push' event, an optional regular expression for matching the git branch or tag git ref. If left blank, the notification will fire for all builds.(refs/heads/somebranch)|(refs/tags/sometag) 119 | --method string set 'method'. ['webhook', 'slack', 'email']. 120 | --email string if you use 'email' method, set E-mail address. 'test@example.com'. 121 | --url string if you use 'webhook' or 'slack' method, set url. 'http://url/goes/here' or 'https://hooks.slack.com/service/{some}/{token}/{here}'. 122 | --title string The title for a notification is an optional field for a human-readable title for the notification. 123 | ` 124 | return strings.TrimSpace(helpText) 125 | } 126 | 127 | func (c *DeleteNotificationCommand) Run(args []string) int { 128 | if err := FlagInit(args); err != nil { 129 | fmt.Fprintln(os.Stderr, c.Help()) 130 | os.Exit(1) 131 | } 132 | 133 | if len(subcommandArgs) < 2 { 134 | fmt.Fprintln(os.Stderr, c.Help()) 135 | os.Exit(1) 136 | } 137 | 138 | ss := strings.Split(subcommandArgs[0], "/") 139 | if len(ss) != 2 { 140 | fmt.Fprintln(os.Stderr, c.Help()) 141 | os.Exit(1) 142 | } 143 | 144 | err := quay.DeleteRepositoryNotification(ss[0], ss[1], subcommandArgs[1], hostname) 145 | if err != nil { 146 | fmt.Fprintf(os.Stderr, "err: %v\n", err) 147 | os.Exit(1) 148 | } 149 | fmt.Fprintf(os.Stdout, "Deleted! %v notification in %v/%v/%v\n", subcommandArgs[1], hostname, ss[0], ss[1]) 150 | return 0 151 | } 152 | 153 | func (c *DeleteNotificationCommand) Synopsis() string { 154 | return fmt.Sprint("Delete notification in repository") 155 | } 156 | 157 | func (c *DeleteNotificationCommand) Help() string { 158 | helpText := ` 159 | qucli supported only Quay.io 160 | Usage: delete-notification 161 | qucli delete-notification koudaiii/qucli xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 162 | ` 163 | return strings.TrimSpace(helpText) 164 | } 165 | 166 | func (c *TestNotificationCommand) Run(args []string) int { 167 | if err := FlagInit(args); err != nil { 168 | fmt.Fprintln(os.Stderr, c.Help()) 169 | os.Exit(1) 170 | } 171 | 172 | if len(subcommandArgs) < 2 { 173 | fmt.Fprintln(os.Stderr, c.Help()) 174 | os.Exit(1) 175 | } 176 | 177 | ss := strings.Split(subcommandArgs[0], "/") 178 | if len(ss) != 2 { 179 | fmt.Fprintln(os.Stderr, c.Help()) 180 | os.Exit(1) 181 | } 182 | 183 | err := quay.TestRepositoryNotification(ss[0], ss[1], subcommandArgs[1], hostname) 184 | if err != nil { 185 | fmt.Fprintf(os.Stderr, "err: %v\n", err) 186 | os.Exit(1) 187 | } 188 | fmt.Fprintf(os.Stdout, "Test Notification! %v notification in %v/%v/%v\n", subcommandArgs[1], hostname, ss[0], ss[1]) 189 | return 0 190 | } 191 | 192 | func (c *TestNotificationCommand) Synopsis() string { 193 | return fmt.Sprint("Test notification in repository") 194 | } 195 | 196 | func (c *TestNotificationCommand) Help() string { 197 | helpText := ` 198 | qucli supported only Quay.io 199 | Usage: test-notification 200 | qucli test-notification koudaiii/qucli xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 201 | ` 202 | return strings.TrimSpace(helpText) 203 | } 204 | -------------------------------------------------------------------------------- /command/notification_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mitchellh/cli" 7 | ) 8 | 9 | func TestAddNotificationCommand_implement(t *testing.T) { 10 | var _ cli.Command = &AddNotificationCommand{} 11 | } 12 | 13 | func TestDeleteNotificationCommand_implement(t *testing.T) { 14 | var _ cli.Command = &DeleteNotificationCommand{} 15 | } 16 | 17 | func TestTestNotificationCommand_implement(t *testing.T) { 18 | var _ cli.Command = &TestNotificationCommand{} 19 | } 20 | -------------------------------------------------------------------------------- /command/team.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/koudaiii/qucli/quay" 9 | ) 10 | 11 | type AddTeamCommand struct { 12 | Meta 13 | } 14 | 15 | type DeleteTeamCommand struct { 16 | Meta 17 | } 18 | 19 | func (c *AddTeamCommand) Run(args []string) int { 20 | if err := FlagInit(args); err != nil { 21 | fmt.Fprintln(os.Stderr, c.Help()) 22 | os.Exit(1) 23 | } 24 | 25 | if len(subcommandArgs) < 2 { 26 | fmt.Fprintln(os.Stderr, c.Help()) 27 | os.Exit(1) 28 | } 29 | 30 | ss := strings.Split(subcommandArgs[0], "/") 31 | if len(ss) != 2 { 32 | fmt.Fprintln(os.Stderr, c.Help()) 33 | os.Exit(1) 34 | } 35 | 36 | repos, err := quay.AddPermission(ss[0], ss[1], "team", subcommandArgs[1], role, hostname) 37 | if err != nil { 38 | fmt.Fprintf(os.Stderr, "err: %v\n", err) 39 | os.Exit(1) 40 | } 41 | fmt.Fprintf(os.Stdout, "Added! %v(%v) in %v/%v/%v\n", repos.Name, repos.Role, hostname, ss[0], ss[1]) 42 | return 0 43 | } 44 | 45 | func (c *AddTeamCommand) Synopsis() string { 46 | return fmt.Sprint("Add team in repository") 47 | } 48 | 49 | func (c *AddTeamCommand) Help() string { 50 | helpText := ` 51 | qucli supported only Quay.io 52 | Usage: add-team 53 | qucli add-user koudaiii/qucli infrastructure --role admin 54 | ` 55 | return strings.TrimSpace(helpText) 56 | } 57 | 58 | func (c *DeleteTeamCommand) Run(args []string) int { 59 | if err := FlagInit(args); err != nil { 60 | fmt.Fprintln(os.Stderr, c.Help()) 61 | os.Exit(1) 62 | } 63 | 64 | if len(subcommandArgs) != 2 { 65 | fmt.Fprintln(os.Stderr, c.Help()) 66 | os.Exit(1) 67 | } 68 | 69 | ss := strings.Split(subcommandArgs[0], "/") 70 | if len(ss) != 2 { 71 | fmt.Fprintln(os.Stderr, c.Help()) 72 | os.Exit(1) 73 | } 74 | 75 | err := quay.DeletePermission(ss[0], ss[1], "team", subcommandArgs[1], hostname) 76 | if err != nil { 77 | fmt.Fprintf(os.Stderr, "err: %v\n", err) 78 | os.Exit(1) 79 | } 80 | fmt.Fprintf(os.Stdout, "Deleted! %v in %v/%v/%v\n", subcommandArgs[1], hostname, ss[0], ss[1]) 81 | return 0 82 | } 83 | 84 | func (c *DeleteTeamCommand) Synopsis() string { 85 | return fmt.Sprint("Delete team in repository") 86 | } 87 | 88 | func (c *DeleteTeamCommand) Help() string { 89 | helpText := ` 90 | qucli supported only Quay.io 91 | Usage: delete-team 92 | qucli delete-team koudaiii/qucli infrastructure 93 | ` 94 | return strings.TrimSpace(helpText) 95 | } 96 | -------------------------------------------------------------------------------- /command/team_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mitchellh/cli" 7 | ) 8 | 9 | func TestAddTeamCommand_implement(t *testing.T) { 10 | var _ cli.Command = &AddTeamCommand{} 11 | } 12 | 13 | func TestDeleteTeamCommand_implement(t *testing.T) { 14 | var _ cli.Command = &DeleteTeamCommand{} 15 | } 16 | -------------------------------------------------------------------------------- /command/user.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/koudaiii/qucli/quay" 9 | ) 10 | 11 | type AddUserCommand struct { 12 | Meta 13 | } 14 | 15 | type DeleteUserCommand struct { 16 | Meta 17 | } 18 | 19 | func (c *AddUserCommand) Run(args []string) int { 20 | if err := FlagInit(args); err != nil { 21 | fmt.Fprintln(os.Stderr, c.Help()) 22 | os.Exit(1) 23 | } 24 | 25 | if len(subcommandArgs) < 2 { 26 | fmt.Fprintln(os.Stderr, c.Help()) 27 | os.Exit(1) 28 | } 29 | 30 | ss := strings.Split(subcommandArgs[0], "/") 31 | if len(ss) != 2 { 32 | fmt.Fprintln(os.Stderr, c.Help()) 33 | os.Exit(1) 34 | } 35 | 36 | repos, err := quay.AddPermission(ss[0], ss[1], "user", subcommandArgs[1], role, hostname) 37 | if err != nil { 38 | fmt.Fprintf(os.Stderr, "err: %v\n", err) 39 | os.Exit(1) 40 | } 41 | fmt.Fprintf(os.Stdout, "Added! %v(%v) in %v/%v/%v\n", repos.Name, repos.Role, hostname, ss[0], ss[1]) 42 | return 0 43 | } 44 | 45 | func (c *AddUserCommand) Synopsis() string { 46 | return fmt.Sprint("Add user in repository") 47 | } 48 | 49 | func (c *AddUserCommand) Help() string { 50 | helpText := ` 51 | qucli supported only Quay.io 52 | Usage: add-user 53 | qucli add-user koudaiii/qucli koudaiii --role admin 54 | ` 55 | return strings.TrimSpace(helpText) 56 | } 57 | 58 | func (c *DeleteUserCommand) Run(args []string) int { 59 | if err := FlagInit(args); err != nil { 60 | fmt.Fprintln(os.Stderr, c.Help()) 61 | os.Exit(1) 62 | } 63 | 64 | if len(subcommandArgs) != 2 { 65 | fmt.Fprintln(os.Stderr, c.Help()) 66 | os.Exit(1) 67 | } 68 | 69 | ss := strings.Split(subcommandArgs[0], "/") 70 | if len(ss) != 2 { 71 | fmt.Fprintln(os.Stderr, c.Help()) 72 | os.Exit(1) 73 | } 74 | 75 | err := quay.DeletePermission(ss[0], ss[1], "user", subcommandArgs[1], hostname) 76 | if err != nil { 77 | fmt.Fprintf(os.Stderr, "err: %v\n", err) 78 | os.Exit(1) 79 | } 80 | fmt.Fprintf(os.Stdout, "Deleted! %v in %v/%v/%v\n", subcommandArgs[1], hostname, ss[0], ss[1]) 81 | return 0 82 | } 83 | 84 | func (c *DeleteUserCommand) Synopsis() string { 85 | return fmt.Sprint("Delete user in repository") 86 | } 87 | 88 | func (c *DeleteUserCommand) Help() string { 89 | helpText := ` 90 | qucli supported only Quay.io 91 | Usage: delete-user 92 | qucli delete-user koudaiii/qucli koudaiii 93 | ` 94 | return strings.TrimSpace(helpText) 95 | } 96 | -------------------------------------------------------------------------------- /command/user_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mitchellh/cli" 7 | ) 8 | 9 | func TestAddUserCommand_implement(t *testing.T) { 10 | var _ cli.Command = &AddUserCommand{} 11 | } 12 | 13 | func TestDeleteUserCommand_implement(t *testing.T) { 14 | var _ cli.Command = &DeleteUserCommand{} 15 | } 16 | -------------------------------------------------------------------------------- /command/version.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | ) 7 | 8 | type VersionCommand struct { 9 | Meta 10 | 11 | Name string 12 | Version string 13 | Revision string 14 | } 15 | 16 | func (c *VersionCommand) Run(args []string) int { 17 | var versionString bytes.Buffer 18 | 19 | fmt.Fprintf(&versionString, "%s version %s", c.Name, c.Version) 20 | if c.Revision != "" { 21 | fmt.Fprintf(&versionString, " (%s)", c.Revision) 22 | } 23 | 24 | c.Ui.Output(versionString.String()) 25 | return 0 26 | } 27 | 28 | func (c *VersionCommand) Synopsis() string { 29 | return fmt.Sprintf("Print %s version and quit", c.Name) 30 | } 31 | 32 | func (c *VersionCommand) Help() string { 33 | return "" 34 | } 35 | -------------------------------------------------------------------------------- /commands.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/koudaiii/qucli/command" 5 | "github.com/mitchellh/cli" 6 | ) 7 | 8 | func Commands(meta *command.Meta) map[string]cli.CommandFactory { 9 | return map[string]cli.CommandFactory{ 10 | "add-team": func() (cli.Command, error) { 11 | return &command.AddTeamCommand{ 12 | Meta: *meta, 13 | }, nil 14 | }, 15 | "delete-team": func() (cli.Command, error) { 16 | return &command.DeleteTeamCommand{ 17 | Meta: *meta, 18 | }, nil 19 | }, 20 | "add-user": func() (cli.Command, error) { 21 | return &command.AddUserCommand{ 22 | Meta: *meta, 23 | }, nil 24 | }, 25 | "delete-user": func() (cli.Command, error) { 26 | return &command.DeleteUserCommand{ 27 | Meta: *meta, 28 | }, nil 29 | }, 30 | "add-notification": func() (cli.Command, error) { 31 | return &command.AddNotificationCommand{ 32 | Meta: *meta, 33 | }, nil 34 | }, 35 | "delete-notification": func() (cli.Command, error) { 36 | return &command.DeleteNotificationCommand{ 37 | Meta: *meta, 38 | }, nil 39 | }, 40 | "test-notification": func() (cli.Command, error) { 41 | return &command.TestNotificationCommand{ 42 | Meta: *meta, 43 | }, nil 44 | }, 45 | "list": func() (cli.Command, error) { 46 | return &command.ListCommand{ 47 | Meta: *meta, 48 | }, nil 49 | }, 50 | "get": func() (cli.Command, error) { 51 | return &command.GetCommand{ 52 | Meta: *meta, 53 | }, nil 54 | }, 55 | "create": func() (cli.Command, error) { 56 | return &command.CreateCommand{ 57 | Meta: *meta, 58 | }, nil 59 | }, 60 | "delete": func() (cli.Command, error) { 61 | return &command.DeleteCommand{ 62 | Meta: *meta, 63 | }, nil 64 | }, 65 | 66 | "version": func() (cli.Command, error) { 67 | return &command.VersionCommand{ 68 | Meta: *meta, 69 | Version: Version, 70 | Revision: GitCommit, 71 | Name: Name, 72 | }, nil 73 | }, 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /glide.lock: -------------------------------------------------------------------------------- 1 | hash: accfc6c403acdb3d82324d083c42efe02a2f401ac682fbed20446fe16054f537 2 | updated: 2017-10-18T21:24:50.002878546+09:00 3 | imports: 4 | - name: github.com/armon/go-radix 5 | version: 1fca145dffbcaa8fe914309b1ec0cfc67500fe61 6 | - name: github.com/bgentry/speakeasy 7 | version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd 8 | - name: github.com/hashicorp/errwrap 9 | version: 7554cd9344cec97297fa6649b055a8c98c2a1e55 10 | - name: github.com/hashicorp/go-multierror 11 | version: 83588e72410abfbe4df460eeb6f30841ae47d4c4 12 | - name: github.com/kelseyhightower/envconfig 13 | version: f611eb38b3875cc3bd991ca91c51d06446afa14c 14 | - name: github.com/mattn/go-isatty 15 | version: 30a891c33c7cde7b02a981314b4228ec99380cca 16 | - name: github.com/mitchellh/cli 17 | version: 65fcae5817c8600da98ada9d7edf26dd1a84837b 18 | - name: github.com/posener/complete 19 | version: 88e59760adaddb8276c9b15511302890690e2dae 20 | subpackages: 21 | - cmd 22 | - cmd/install 23 | - match 24 | - name: github.com/spf13/pflag 25 | version: e57e3eeb33f795204c1ca35f56c44f83227c6e66 26 | - name: golang.org/x/sys 27 | version: d5645953809d8b4752afb2c3224b1f1ad73dfa70 28 | subpackages: 29 | - unix 30 | testImports: [] 31 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/koudaiii/qucli 2 | import: 3 | - package: github.com/kelseyhightower/envconfig 4 | version: ^1.3.0 5 | - package: github.com/mitchellh/cli 6 | - package: github.com/spf13/pflag 7 | version: ^1.0.0 8 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "os" 4 | 5 | func main() { 6 | os.Exit(Run(os.Args[1:])) 7 | } 8 | -------------------------------------------------------------------------------- /quay/notification.go: -------------------------------------------------------------------------------- 1 | package quay 2 | 3 | import ( 4 | "encoding/json" 5 | "path" 6 | 7 | "github.com/koudaiii/qucli/utils" 8 | ) 9 | 10 | func ListRepositoryNotifications(namespace string, name string, hostname string) (RepositoryNotifications, error) { 11 | var repos ResponseRepositoryNotifications 12 | var notifications RepositoryNotifications 13 | 14 | u := QuayURLParse(hostname) 15 | 16 | u.Path = path.Join(u.Path, "repository", namespace, name, "notification") 17 | 18 | body, err := utils.HttpGet(u.String(), config.QuayAPIToken) 19 | if err != nil { 20 | return notifications, err 21 | } 22 | 23 | if err := json.Unmarshal([]byte(body), &repos); err != nil { 24 | return notifications, err 25 | } 26 | 27 | for _, item := range repos.Items { 28 | if item["title"] == nil { 29 | item["title"] = "" 30 | } 31 | notifications.Items = append(notifications.Items, 32 | RepositoryNotification{ 33 | Title: item["title"].(string), 34 | Event: item["event"].(string), 35 | Method: item["method"].(string), 36 | EventConfig: item["event_config"].(map[string]interface{}), 37 | UUID: item["uuid"].(string), 38 | NumberOfFailures: item["number_of_failures"].(float64), 39 | Config: item["config"].(map[string]interface{}), 40 | }) 41 | } 42 | return notifications, nil 43 | } 44 | 45 | func DeleteRepositoryNotification(namespace string, name string, uuid string, hostname string) error { 46 | u := QuayURLParse(hostname) 47 | 48 | u.Path = path.Join(u.Path, "repository", namespace, name, "notification", uuid) 49 | 50 | _, err := utils.HttpDelete(u.String(), config.QuayAPIToken) 51 | if err != nil { 52 | return err 53 | } 54 | 55 | return nil 56 | } 57 | 58 | func AddRepositoryNotification(namespace string, name string, request RequestRepositoryNotification, hostname string) (RepositoryNotification, error) { 59 | var repos RepositoryNotification 60 | req, err := json.Marshal(request) 61 | 62 | u := QuayURLParse(hostname) 63 | u.Path = path.Join(u.Path, "repository", namespace, name, "notification") 64 | 65 | body, err := utils.HttpPost(u.String()+"/", config.QuayAPIToken, req) 66 | if err != nil { 67 | return repos, err 68 | } 69 | 70 | if err := json.Unmarshal([]byte(body), &repos); err != nil { 71 | return repos, err 72 | } 73 | 74 | return repos, nil 75 | } 76 | 77 | func TestRepositoryNotification(namespace string, name string, uuid string, hostname string) error { 78 | u := QuayURLParse(hostname) 79 | 80 | u.Path = path.Join(u.Path, "repository", namespace, name, "notification", uuid, "test") 81 | 82 | _, err := utils.HttpPost(u.String(), config.QuayAPIToken, nil) 83 | if err != nil { 84 | return err 85 | } 86 | 87 | return nil 88 | } 89 | -------------------------------------------------------------------------------- /quay/permission.go: -------------------------------------------------------------------------------- 1 | package quay 2 | 3 | import ( 4 | "encoding/json" 5 | "path" 6 | 7 | "github.com/koudaiii/qucli/utils" 8 | ) 9 | 10 | func GetPermissions(namespace string, name string, accountType string, hostname string) (QuayPermissions, error) { 11 | var resp QuayPermissionsResponse 12 | var permissions QuayPermissions 13 | 14 | u := QuayURLParse(hostname) 15 | u.Path = path.Join(u.Path, "repository", namespace, name, "permissions", accountType) + "/" 16 | 17 | body, err := utils.HttpGet(u.String(), config.QuayAPIToken) 18 | if err != nil { 19 | return permissions, err 20 | } 21 | if err := json.Unmarshal([]byte(body), &resp); err != nil { 22 | return permissions, err 23 | } 24 | for _, item := range resp.Items { 25 | permissions.Items = append(permissions.Items, 26 | QuayPermission{ 27 | Name: item.(map[string]interface{})["name"].(string), 28 | Role: item.(map[string]interface{})["role"].(string), 29 | }) 30 | } 31 | return permissions, nil 32 | } 33 | 34 | func DeletePermission(namespace string, name string, accountType string, account string, hostname string) error { 35 | u := QuayURLParse(hostname) 36 | u.Path = path.Join(u.Path, "repository", namespace, name, "permissions", accountType, account) 37 | 38 | _, err := utils.HttpDelete(u.String(), config.QuayAPIToken) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | return nil 44 | } 45 | 46 | func AddPermission(namespace string, name string, accountType string, account string, role string, hostname string) (QuayPermission, error) { 47 | var repos QuayPermission 48 | req, err := json.Marshal(QuayPermission{ 49 | Role: role, 50 | }) 51 | 52 | u := QuayURLParse(hostname) 53 | u.Path = path.Join(u.Path, "repository", namespace, name, "permissions", accountType, account) 54 | 55 | body, err := utils.HttpPut(u.String(), config.QuayAPIToken, req) 56 | if err != nil { 57 | return repos, err 58 | } 59 | 60 | if err := json.Unmarshal([]byte(body), &repos); err != nil { 61 | return repos, err 62 | } 63 | 64 | return repos, nil 65 | } 66 | -------------------------------------------------------------------------------- /quay/quay.go: -------------------------------------------------------------------------------- 1 | package quay 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "os" 7 | 8 | "github.com/kelseyhightower/envconfig" 9 | ) 10 | 11 | type Config struct { 12 | QuayHostname string `envconfig:"QUAY_HOSTNAME"` 13 | QuayAPIToken string `envconfig:"QUAY_API_TOKEN"` 14 | } 15 | 16 | var config *Config 17 | 18 | func init() { 19 | c := &Config{} 20 | if err := envconfig.Process("", c); err != nil { 21 | fmt.Fprintln(os.Stderr, err) 22 | } 23 | config = c 24 | } 25 | 26 | type QuayPermission struct { 27 | Name string `json:"name"` 28 | Role string `json:"role"` 29 | } 30 | 31 | type QuayPermissions struct { 32 | Items []QuayPermission 33 | } 34 | 35 | type QuayPermissionsResponse struct { 36 | Items map[string]interface{} `json:"permissions"` 37 | } 38 | 39 | type QuayRepositories struct { 40 | Items []ResponseRepository 41 | } 42 | 43 | type ResponseRepositories struct { 44 | Items []map[string]interface{} `json:"repositories"` 45 | } 46 | 47 | type RequestRepositoryNotification struct { 48 | Title string `json:"title,omitempty"` 49 | Event string `json:"event,omitempty"` 50 | Method string `json:"method,omitempty"` 51 | Config NotificationConfig `json:"config,omitempty"` 52 | EventConfig NotificationEventConfig `json:"eventConfig,omitempty"` 53 | } 54 | 55 | type NotificationConfig struct { 56 | URL string `json:"url,omitempty"` 57 | Email string `json:"email,omitempty"` 58 | } 59 | 60 | type NotificationEventConfig struct { 61 | Level string `json:"level,omitempty"` 62 | RefRegex string `json:"ref-regex,omitempty"` 63 | } 64 | 65 | type ResponseRepositoryNotifications struct { 66 | Items []map[string]interface{} `json:"notifications"` 67 | } 68 | 69 | type RepositoryNotifications struct { 70 | Items []RepositoryNotification 71 | } 72 | 73 | type RepositoryNotification struct { 74 | Title string `json:"title,omitempty"` 75 | Event string `json:"event,omitempty"` 76 | Method string `json:"method,omitempty"` 77 | EventConfig map[string]interface{} `json:"event_config,omitempty"` 78 | UUID string `json:"uuid,omitempty"` 79 | NumberOfFailures float64 `json:"number_of_failures,omitempty"` 80 | Config map[string]interface{} `json:"config,omitempty"` 81 | } 82 | 83 | type QuayRepository struct { 84 | Namespace string `json:"namespace"` 85 | Name string `json:"name"` 86 | } 87 | 88 | type ResponseRepository struct { 89 | Namespace string `json:"namespace"` 90 | IsPublic bool `json:"is_public"` 91 | Name string `json:"name"` 92 | Description string `json:"description"` 93 | } 94 | 95 | type RequestRepository struct { 96 | Namespace string `json:"namespace"` 97 | Visibility string `json:"visibility"` 98 | Repository string `json:"repository"` 99 | Description string `json:"description"` 100 | } 101 | 102 | func QuayURLParse(hostname string) *url.URL { 103 | if config.QuayHostname != "" { 104 | hostname = config.QuayHostname 105 | } 106 | u, err := url.Parse("https://" + hostname + "/api/v1/") 107 | if err != nil { 108 | fmt.Fprintf(os.Stderr, "err: %v\n", err) 109 | os.Exit(1) 110 | } 111 | return u 112 | } 113 | -------------------------------------------------------------------------------- /quay/repository.go: -------------------------------------------------------------------------------- 1 | package quay 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | "path" 7 | "strconv" 8 | 9 | "github.com/koudaiii/qucli/utils" 10 | ) 11 | 12 | func ListRepository(namespace string, public bool, hostname string) (QuayRepositories, error) { 13 | var repos ResponseRepositories 14 | var repositories QuayRepositories 15 | u := QuayURLParse(hostname) 16 | 17 | values := url.Values{} 18 | values.Set("public", strconv.FormatBool(public)) 19 | values.Add("namespace", namespace) 20 | 21 | u.Path = path.Join(u.Path, "repository") 22 | 23 | body, err := utils.HttpGet(u.String()+"?"+values.Encode(), config.QuayAPIToken) 24 | if err != nil { 25 | return repositories, err 26 | } 27 | 28 | if err := json.Unmarshal([]byte(body), &repos); err != nil { 29 | return repositories, err 30 | } 31 | 32 | for _, item := range repos.Items { 33 | repositories.Items = append(repositories.Items, 34 | ResponseRepository{ 35 | Namespace: item["namespace"].(string), 36 | Name: item["name"].(string), 37 | IsPublic: item["is_public"].(bool), 38 | }) 39 | } 40 | return repositories, nil 41 | } 42 | 43 | func GetRepository(namespace string, name string, hostname string) (ResponseRepository, error) { 44 | var repos ResponseRepository 45 | u := QuayURLParse(hostname) 46 | u.Path = path.Join(u.Path, "repository", namespace, name) 47 | 48 | body, err := utils.HttpGet(u.String(), config.QuayAPIToken) 49 | if err != nil { 50 | return repos, err 51 | } 52 | 53 | if err := json.Unmarshal([]byte(body), &repos); err != nil { 54 | return repos, err 55 | } 56 | 57 | return repos, nil 58 | } 59 | 60 | func DeleteRepository(namespace string, name string, hostname string) error { 61 | u := QuayURLParse(hostname) 62 | u.Path = path.Join(u.Path, "repository", namespace, name) 63 | 64 | _, err := utils.HttpDelete(u.String(), config.QuayAPIToken) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | return nil 70 | } 71 | 72 | func CreateRepository(namespace string, name string, visibility string, hostname string) (QuayRepository, error) { 73 | var repos QuayRepository 74 | req, err := json.Marshal(RequestRepository{ 75 | Namespace: namespace, 76 | Repository: name, 77 | Visibility: visibility, 78 | }) 79 | 80 | u := QuayURLParse(hostname) 81 | u.Path = path.Join(u.Path, "repository") 82 | 83 | body, err := utils.HttpPost(u.String(), config.QuayAPIToken, req) 84 | if err != nil { 85 | return repos, err 86 | } 87 | 88 | if err := json.Unmarshal([]byte(body), &repos); err != nil { 89 | return repos, err 90 | } 91 | 92 | return repos, nil 93 | } 94 | -------------------------------------------------------------------------------- /utils/httpdelete.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | ) 8 | 9 | func HttpDelete(url string, apitoken string) (string, error) { 10 | req, _ := http.NewRequest("DELETE", url, nil) 11 | req.Header.Set("Content-Type", "application/json") 12 | if apitoken != "" { 13 | req.Header.Set("Authorization", "Bearer "+apitoken) 14 | } 15 | 16 | client := new(http.Client) 17 | resp, err := client.Do(req) 18 | if err != nil { 19 | return "", err 20 | } 21 | defer resp.Body.Close() 22 | 23 | b, err := ioutil.ReadAll(resp.Body) 24 | if err != nil { 25 | return "", err 26 | } 27 | 28 | if resp.StatusCode != http.StatusNoContent { 29 | return "", fmt.Errorf("HTTP error!\nURL: %s\nstatus code: %d\nbody:\n%s\n", url, resp.StatusCode, string(b)) 30 | } 31 | 32 | return string(b), nil 33 | } 34 | -------------------------------------------------------------------------------- /utils/httpget.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | ) 8 | 9 | func HttpGet(url string, apitoken string) (string, error) { 10 | req, _ := http.NewRequest("GET", url, nil) 11 | req.Header.Set("Content-Type", "application/json") 12 | if apitoken != "" { 13 | req.Header.Set("Authorization", "Bearer "+apitoken) 14 | } 15 | 16 | client := new(http.Client) 17 | resp, err := client.Do(req) 18 | if err != nil { 19 | return "", err 20 | } 21 | defer resp.Body.Close() 22 | 23 | b, err := ioutil.ReadAll(resp.Body) 24 | if err != nil { 25 | return "", err 26 | } 27 | 28 | if resp.StatusCode != http.StatusOK { 29 | return "", fmt.Errorf("HTTP error!\nURL: %s\nstatus code: %d\nbody:\n%s\n", url, resp.StatusCode, string(b)) 30 | } 31 | 32 | return string(b), nil 33 | } 34 | -------------------------------------------------------------------------------- /utils/httppost.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | func HttpPost(url string, apitoken string, body []byte) (string, error) { 11 | req, _ := http.NewRequest("POST", url, bytes.NewBuffer([]byte(body))) 12 | req.Header.Set("Content-Type", "application/json") 13 | if apitoken != "" { 14 | req.Header.Set("Authorization", "Bearer "+apitoken) 15 | } 16 | 17 | client := new(http.Client) 18 | resp, err := client.Do(req) 19 | if err != nil { 20 | return "", err 21 | } 22 | defer resp.Body.Close() 23 | 24 | b, err := ioutil.ReadAll(resp.Body) 25 | if err != nil { 26 | return "", err 27 | } 28 | 29 | if (resp.StatusCode != http.StatusCreated) && (resp.StatusCode != http.StatusOK) { 30 | return "", fmt.Errorf("HTTP error!\nURL: %s\nstatus code: %d\nbody:\n%s\n", url, resp.StatusCode, string(b)) 31 | } 32 | 33 | return string(b), nil 34 | } 35 | -------------------------------------------------------------------------------- /utils/httpput.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | func HttpPut(url string, apitoken string, body []byte) (string, error) { 11 | req, _ := http.NewRequest("PUT", url, bytes.NewBuffer([]byte(body))) 12 | req.Header.Set("Content-Type", "application/json") 13 | if apitoken != "" { 14 | req.Header.Set("Authorization", "Bearer "+apitoken) 15 | } 16 | 17 | client := new(http.Client) 18 | resp, err := client.Do(req) 19 | if err != nil { 20 | return "", err 21 | } 22 | defer resp.Body.Close() 23 | 24 | b, err := ioutil.ReadAll(resp.Body) 25 | if err != nil { 26 | return "", err 27 | } 28 | 29 | if resp.StatusCode != http.StatusOK { 30 | return "", fmt.Errorf("HTTP error!\nURL: %s\nstatus code: %d\nbody:\n%s\n", url, resp.StatusCode, string(b)) 31 | } 32 | 33 | return string(b), nil 34 | } 35 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const Name string = "qucli" 4 | 5 | // To set this from outside, use go build -ldflags "-X main.Version \"$(VERSION)\"" 6 | var Version string 7 | 8 | // GitCommit describes latest commit hash. 9 | // This value is extracted by git command when building. 10 | // To set this from outside, use go build -ldflags "-X main.GitCommit \"$(COMMIT)\"" 11 | var GitCommit string 12 | --------------------------------------------------------------------------------